You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

153 lines
4.6 KiB
JavaScript

"use strict";
const React = require("react");
const ReactDOM = require("react-dom");
const Promise = require("bluebird");
const documentReadyPromise = require("document-ready-promise");
const debounce = require("debounce");
const { expression } = require("dataprog");
const generateSelector = require("./generate-selector");
const useStateRef = require("./use-state-ref");
const useMemoizedPosition = require("./use-memoized-position");
const elementsFromPoint = require("./elements-from-point");
const uniqueElementId = require("./unique-element-id");
function Overlay() {
let [ scrollX, setScrollX ] = React.useState();
let [ scrollY, setScrollY ] = React.useState();
let [ hoveredElement, setHoveredElement ] = React.useState();
let [ isHovering, setIsHovering, isHoveringRef ] = useStateRef(true);
let [ isPicking, setIsPicking, isPickingRef ] = useStateRef(false);
let [ elementList, setElementList ] = React.useState([]);
let [ selectedElement, setSelectedElement ] = React.useState();
let [ pickHoveredElement, setPickHoveredElement ] = React.useState();
let [ pickedElement, setPickedElement ] = React.useState();
let enabledRef = React.useRef(true);
React.useEffect(() => {
window.addEventListener("scroll", debounce((event) => {
setScrollX(window.scrollX);
setScrollY(window.scrollY);
}), 20);
let allElements = document.querySelectorAll("*");
for (let element of allElements) {
/* TODO: Investigate whether switching to mousemove + elementFromPoint is more performant */
element.addEventListener("mouseover", (event) => {
event.stopPropagation();
if (isHoveringRef.current) {
setHoveredElement(element);
}
});
function clickHandler(event) {
if (enabledRef.current) {
event.preventDefault();
event.stopPropagation();
if (isHoveringRef.current) {
setIsHovering(false);
setIsPicking(true);
let candidateElements = elementsFromPoint(event.clientX, event.clientY);
setElementList(candidateElements);
setSelectedElement(candidateElements[0]);
}
}
}
element.addEventListener("click", clickHandler);
element.addEventListener("mousedown", clickHandler);
element.addEventListener("mouseup", clickHandler);
}
}, []);
let hoveredPosition = useMemoizedPosition(hoveredElement, [ scrollX, scrollY ]);
let selectedPosition = useMemoizedPosition(selectedElement, [ scrollX, scrollY ]);
let pickedPosition = useMemoizedPosition(pickedElement, [ scrollX, scrollY ]);
let pickHoveredPosition = useMemoizedPosition(pickHoveredElement, [ scrollX, scrollY ]);
return (
<div className="___scraping___tool___overlay active">
{(hoveredPosition != null && isHovering)
? <HoverHighlight element={hoveredElement} {... hoveredPosition} />
: null
}
{(selectedPosition != null)
? <PrimarySelectionHighlight element={selectedElement} {... selectedPosition} />
: null
}
{(pickedPosition != null && isPicking)
? <HoverHighlight element={pickedElement} {... pickedPosition} />
: null
}
{(pickHoveredPosition != null && isPicking)
? <HoverHighlight element={pickHoveredElement} {... pickHoveredPosition} />
: null
}
{(isPicking)
? <Picker candidates={elementList} onHover={setPickHoveredElement} />
: null
}
</div>
);
}
function Picker({ candidates, onHover }) {
return (
<div className="___scraping___tool___candidatePicker">
{candidates.map((candidate) => {
return <PickerCandidate
key={uniqueElementId(candidate)}
element={candidate}
onEnter={() => onHover(candidate)}
onLeave={() => onHover(null)}
/>;
})}
</div>
);
}
function PickerCandidate({ element, onEnter, onLeave }) {
return (
<div className="___scraping___tool___candidate" onMouseEnter={onEnter} onMouseLeave={onLeave}>
{generateSelector(element)}
</div>
);
}
function HoverHighlight({ x, y, width, height, element }) {
let boxStyle = { left: x, top: y, width: width, height: height };
let tooltipStyle = { left: x, top: y + height };
return (<>
<div className="___scraping___tool___hover" style={boxStyle} />
{(element != null)
? <div className="___scraping___tool___tooltip" style={tooltipStyle}>{ generateSelector(element) }</div>
: null
}
</>);
}
function PrimarySelectionHighlight({ x, y, width, height, element }) {
let boxStyle = { left: x, top: y, width: width, height: height };
return (<>
<div className="___scraping___tool___selection" style={boxStyle} />
</>);
}
Promise.try(() => {
return documentReadyPromise();
}).then(() => {
let $overlay = document.querySelector(".___scraping___tool___overlay");
let $tool = document.querySelector(".___scraping___tool");
ReactDOM.render(<Overlay />, $overlay);
});