Last active
September 13, 2022 06:23
-
-
Save devoNOTbevo/c38258006925533981072245b52bf62b to your computer and use it in GitHub Desktop.
SelectElement: A sample React Component that has a button that initiates actions to hover over and element and drop a pin on that element. While "dropping", elements are outlined to show visibility. It then opens a modal to initiate a workflow from there.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { ToastContainer, toast } from 'react-toastify'; | |
import Modal from 'react-modal'; | |
import { useEffect, useState } from 'react'; | |
import useScreenshot from '../../hooks/screenshot-hook'; | |
import useWishes from '../../hooks/wishes-hook'; | |
import { MAP_MARKER_URL } from '../../config/constants'; | |
import 'react-toastify/dist/ReactToastify.min.css'; | |
Modal.setAppElement(document.body); | |
interface ActionBarProps { | |
closePanels: () => void; | |
openPanel: () => void; | |
} | |
const cleanUpDocument = () => { | |
// remove borders | |
const borderElems = document.getElementsByClassName('show-border'); | |
Array.from(borderElems).forEach((e) => { | |
e.classList.remove('show-border'); | |
}); | |
// remove map marker pins | |
const mapMarkerElems = document.getElementsByClassName('dropped-pin'); | |
Array.from(mapMarkerElems).forEach((e) => { | |
e.remove(); | |
}); | |
// show the tab again | |
const tab = document.getElementById('popout-tab'); | |
tab?.classList.remove('hidden'); | |
}; | |
export default function ActionBar({ openPanel, closePanels }: ActionBarProps) { | |
const { attachScreenshot } = useWishes(); | |
const [image, takeScreenshot, error] = useScreenshot(); | |
const [markerXPos, setMarkerXPos] = useState(0); | |
const [markerYPos, setMarkerYPos] = useState(0); | |
const [showBordersOnHover, setShowBordersOnHover] = useState(false); | |
const [targetElement, setTargetElement] = useState<HTMLElement | null>(null); | |
const [modalIsOpen, setModalIsOpen] = useState(false); | |
// workflow starts with this function | |
const startCreateWishWorkflow = () => { | |
// set the cursor to be the map marker | |
document.body.classList.add('map-marker-cursor'); | |
// use state + effect to create event listeners to | |
// show / hide element border | |
setShowBordersOnHover(true); | |
// add a one time click event listener to initiate workflow | |
// and "drop" pin into body of document | |
window.addEventListener( | |
'click', | |
(e) => { | |
const clickTarget = e.target as HTMLElement; | |
toast.dismiss(); | |
// 1. turn off these event listeners | |
setShowBordersOnHover(false); | |
// 2. drop a pin onto the clicked target | |
setTargetElement(clickTarget); | |
// 3. remove cursor | |
document.body.classList.remove('map-marker-cursor'); | |
// 4. insert image | |
const image = document.createElement('img'); | |
image.classList.add('dropped-pin'); | |
image.style.top = '-18px'; //height of image | |
image.style.left = '0'; | |
image.src = MAP_MARKER_URL; | |
image.crossOrigin = 'anonymous'; | |
image.onload = (e) => { | |
// 6. take the screen shot - the effect for the image | |
// itself will take workfflow from here | |
takeScreenshot(() => { | |
// 4. set modal open to display loader using callback | |
setModalIsOpen(true); | |
}); | |
}; | |
image.onerror = (err) => { | |
console.log('Error loading map marker image: ', err); | |
}; | |
clickTarget.insertAdjacentElement('afterbegin', image); | |
}, | |
{ once: true } | |
); | |
}; | |
// do a useEffect for event listeners per React recommendation | |
// in short, the cleanup method is best way to remove | |
// event listeners. | |
useEffect(() => { | |
if (!showBordersOnHover) { | |
return; | |
} | |
// create event listeners to show/hide element border | |
// use functions so you can remove them later | |
const mouseoverListener: EventListener = (e) => { | |
(e.target as HTMLElement)?.classList.add('show-border'); | |
}; | |
const mouseoutListener: EventListener = (e) => { | |
(e.target as HTMLElement)?.classList.remove('show-border'); | |
}; | |
document.body.addEventListener('mouseover', mouseoverListener); | |
document.body.addEventListener('mouseout', mouseoutListener); | |
return () => { | |
document.body.removeEventListener('mouseout', mouseoutListener, false); | |
document.body.removeEventListener('mouseover', mouseoverListener, false); | |
}; | |
}, [showBordersOnHover]); | |
// workflow handlers | |
const handleWorkflowStart = (e: React.MouseEvent) => { | |
// stop propogation to keep event handlers clean | |
e.stopPropagation(); | |
// show toast for user feedback | |
toast.info('Now drop the pin to start a new wish!'); | |
// set the x and y for future reference | |
setMarkerXPos(e.clientX); | |
setMarkerYPos(e.clientY); | |
startCreateWishWorkflow(); | |
}; | |
const handleWorkflowEnd = () => { | |
openPanel(); | |
handleRequestToClose(); | |
}; | |
// modal handlers | |
const handleAfterModalHasOpened = () => { | |
console.log('modal did open'); | |
}; | |
const handleRequestToClose = () => { | |
console.log('modal close requested'); | |
setModalIsOpen(false); | |
cleanUpDocument(); | |
}; | |
const handleSubmit = () => { | |
console.log('submitting...'); | |
handleWorkflowEnd(); | |
}; | |
return ( | |
<div className="grid-container single-column"> | |
<button | |
className="action-button button-big secondary text-light" | |
onClick={handleWorkflowStart} | |
> | |
Start the workflow | |
</button> | |
<ToastContainer | |
position="top-right" | |
autoClose={15000} | |
hideProgressBar={false} | |
newestOnTop={false} | |
closeOnClick | |
rtl={false} | |
pauseOnFocusLoss | |
pauseOnHover | |
></ToastContainer> | |
<Modal | |
isOpen={modalIsOpen} | |
onAfterOpen={handleAfterModalHasOpened} | |
onRequestClose={handleRequestToClose} | |
contentLabel="Create New Wish" | |
className="primary-light" | |
> | |
<SomeDialog onSubmit={handleSubmit} /> | |
</Modal> | |
</div> | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment