react-aim-menu
Version:
A React version of Amazon's jQuery-menu-aim plugin
263 lines (243 loc) • 10 kB
JavaScript
var $gati5$reactjsxruntime = require("react/jsx-runtime");
var $gati5$react = require("react");
function $parcel$export(e, n, v, s) {
Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
}
function $parcel$interopDefault(a) {
return a && a.__esModule ? a.default : a;
}
$parcel$export(module.exports, "MenuItem", () => $d20f2444b9cc1ca0$export$2e2bcd8739ae039);
$parcel$export(module.exports, "Menu", () => $d1df290c0e43fda8$export$2e2bcd8739ae039);
function $847379c021b3641b$export$567fc7097e064344() {
return '_' + Math.random().toString(36).substr(2, 9);
}
const $847379c021b3641b$export$642213d516a9b928 = function(mouseEvent) {
let pos = {
x: 0,
y: 0
};
if (mouseEvent.pageX || mouseEvent.pageY) pos = {
x: mouseEvent.pageX,
y: mouseEvent.pageY
};
else if (mouseEvent.clientX || mouseEvent.clientY) pos = {
x: mouseEvent.clientX + document.body.scrollLeft + document.documentElement.scrollLeft,
y: mouseEvent.clientY + document.body.scrollTop + document.documentElement.scrollTop
};
return pos;
};
const $847379c021b3641b$export$c32a249941905394 = function(elem) {
const boundingBox = elem.getBoundingClientRect();
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
return {
topLeft: {
x: boundingBox.left + scrollLeft,
y: boundingBox.top + scrollTop
},
topRight: {
x: boundingBox.left + scrollLeft + boundingBox.width,
y: boundingBox.top + scrollTop
},
bottomLeft: {
x: boundingBox.left + scrollLeft,
y: boundingBox.top + scrollTop + boundingBox.height
},
bottomRight: {
x: boundingBox.left + scrollLeft + boundingBox.width,
y: boundingBox.top + scrollTop + boundingBox.height
}
};
};
const $847379c021b3641b$export$534bdd1067f4ef34 = function(menuBox, point, direction = 'right') {
if (direction === 'right') {
const height = menuBox.bottomRight.y - menuBox.topRight.y;
return {
A: {
x: point.x,
y: point.y
},
B: {
x: menuBox.topRight.x,
y: Math.min(0, menuBox.topRight.y - height)
},
C: {
x: menuBox.bottomRight.x,
y: Math.max(window.innerHeight + height, menuBox.bottomRight.y + height)
}
};
} else if (direction === 'down') return {
A: point,
B: menuBox.topRight,
C: menuBox.bottomRight
};
};
const $847379c021b3641b$export$20fc610e43966a58 = function(triangle, point) {
const { A: A , B: B , C: C } = triangle;
return $847379c021b3641b$var$checkSameSide($847379c021b3641b$var$toEdge(A, B), C, point) && $847379c021b3641b$var$checkSameSide($847379c021b3641b$var$toEdge(A, C), B, point) && $847379c021b3641b$var$checkSameSide($847379c021b3641b$var$toEdge(B, C), A, point);
};
const $847379c021b3641b$var$checkSameSide = function(edge, point1, point2) {
return $847379c021b3641b$var$getPolarity(edge, point1) === $847379c021b3641b$var$getPolarity(edge, point2);
};
const $847379c021b3641b$var$getPolarity = function(edge, point) {
const vectorA = $847379c021b3641b$var$toVector(edge.v1, edge.v2);
const vectorB = $847379c021b3641b$var$toVector(edge.v1, point);
const scalar = vectorA.x * vectorB.y - vectorA.y * vectorB.x;
return scalar >= 0 ? 1 : -1;
};
const $847379c021b3641b$var$toEdge = function(pointA, pointB) {
return {
v1: {
...pointA
},
v2: {
...pointB
}
};
};
const $847379c021b3641b$var$toVector = function(pointA, pointB) {
return {
x: pointB.x - pointA.x,
y: pointB.y - pointA.y
};
};
function $9bfb57088e8a5943$var$setRef(ref, value) {
if (!ref) return;
if (typeof ref === 'function') ref(value);
else if (ref) ref.current = value;
}
/**
* useForkRef
* Joins refs together and returns a combination of the two as a new ref
*
* @param refA
* @param refB
*/ function $9bfb57088e8a5943$export$ebc1e1560c2dffa5(refA, refB) {
/**
* This will create a new function if the ref props change and are defined.
* This means react will call the old forkRef with `null` and the new forkRef
* with the ref. Cleanup naturally emerges from this behavior
*/ return $gati5$react.useMemo(()=>{
if (refA == null && refB == null) return null;
return (refValue)=>{
$9bfb57088e8a5943$var$setRef(refA, refValue);
$9bfb57088e8a5943$var$setRef(refB, refValue);
};
}, [
refA,
refB
]);
}
const $d1df290c0e43fda8$var$state = {
pendingExpand: {
item: null,
timeoutId: null
},
mouseHistory: []
};
function $d1df290c0e43fda8$var$clearPendingExpand() {
if ($d1df290c0e43fda8$var$state.pendingExpand && $d1df290c0e43fda8$var$state.pendingExpand.timeoutId) clearTimeout($d1df290c0e43fda8$var$state.pendingExpand.timeoutId);
$d1df290c0e43fda8$var$state.pendingExpand = null;
}
function $d1df290c0e43fda8$var$checkAim(menuElem) {
if ($d1df290c0e43fda8$var$state.mouseHistory.length < 2) return false;
const currentPosition = $d1df290c0e43fda8$var$state.mouseHistory[$d1df290c0e43fda8$var$state.mouseHistory.length - 1];
const prevPosition = $d1df290c0e43fda8$var$state.mouseHistory[0];
const menuBox = $847379c021b3641b$export$c32a249941905394(menuElem);
const triangle = $847379c021b3641b$export$534bdd1067f4ef34(menuBox, prevPosition);
return $847379c021b3641b$export$20fc610e43966a58(triangle, currentPosition);
}
const $d1df290c0e43fda8$var$defaultState = {
};
const $d1df290c0e43fda8$var$DEFAULT_HOVER_DELAY = 450;
const $d1df290c0e43fda8$var$MOUSE_HISTORY_SIZE = 3;
const $d1df290c0e43fda8$export$c7e742effb1c51e2 = /*#__PURE__*/ $gati5$react.createContext($d1df290c0e43fda8$var$defaultState);
const $d1df290c0e43fda8$var$Menu = /*#__PURE__*/ $gati5$react.forwardRef(function Menu({ hoverDelay: hoverDelay = $d1df290c0e43fda8$var$DEFAULT_HOVER_DELAY , ...props }, ref) {
const menuRef = $gati5$react.useRef(null);
const forkedRef = $9bfb57088e8a5943$export$ebc1e1560c2dffa5(ref, menuRef);
const [expandedItem, setExpandedItem] = $gati5$react.useState(null);
function updateExpand() {
if (!$d1df290c0e43fda8$var$state.pendingExpand || expandedItem === $d1df290c0e43fda8$var$state.pendingExpand.item) return;
setExpandedItem($d1df290c0e43fda8$var$state.pendingExpand.item);
$d1df290c0e43fda8$var$state.mouseHistory = [];
$d1df290c0e43fda8$var$clearPendingExpand();
}
function onItemEnter(item) {
$d1df290c0e43fda8$var$clearPendingExpand();
$d1df290c0e43fda8$var$state.pendingExpand = {
item: item
};
if (!expandedItem || !$d1df290c0e43fda8$var$checkAim(menuRef.current)) return updateExpand();
$d1df290c0e43fda8$var$state.pendingExpand.timeoutId = setTimeout(()=>updateExpand()
, hoverDelay);
}
function onItemLeave() {
$d1df290c0e43fda8$var$state.pendingExpand = {
item: null,
timeoutId: setTimeout(()=>updateExpand()
, hoverDelay + 100)
};
}
function onMouseMove(event) {
props.onMouseMove && props.onMouseMove(event);
$d1df290c0e43fda8$var$state.mouseHistory.push($847379c021b3641b$export$642213d516a9b928(event.nativeEvent));
if ($d1df290c0e43fda8$var$state.mouseHistory.length > $d1df290c0e43fda8$var$MOUSE_HISTORY_SIZE) $d1df290c0e43fda8$var$state.mouseHistory.shift();
}
function handleMenuLeave(event) {
props.onMouseLeave && props.onMouseLeave(event);
$d1df290c0e43fda8$var$clearPendingExpand();
setExpandedItem(null);
}
return(/*#__PURE__*/ $gati5$reactjsxruntime.jsx($d1df290c0e43fda8$export$c7e742effb1c51e2.Provider, {
value: {
onItemEnter: onItemEnter,
onItemLeave: onItemLeave,
expandedItem: expandedItem
},
children: /*#__PURE__*/ $gati5$reactjsxruntime.jsx("div", {
...props,
ref: forkedRef,
onMouseLeave: handleMenuLeave,
onMouseMove: onMouseMove,
children: props.children
})
}));
});
var $d1df290c0e43fda8$export$2e2bcd8739ae039 = $d1df290c0e43fda8$var$Menu;
const $d20f2444b9cc1ca0$var$MenuItem = /*#__PURE__*/ ($parcel$interopDefault($gati5$react)).forwardRef(function MenuItem({ onHover: onHover , onLeave: onLeave , ...props }, ref) {
const [id, setId] = $gati5$react.useState(null);
const [expanded, setExpanded] = $gati5$react.useState(false);
const menu = $gati5$react.useContext($d1df290c0e43fda8$export$c7e742effb1c51e2);
$gati5$react.useEffect(()=>{
setId($847379c021b3641b$export$567fc7097e064344());
}, []);
$gati5$react.useEffect(()=>{
if (menu.expandedItem && menu.expandedItem === id && !expanded) {
setExpanded(true);
onHover && onHover();
}
if (menu.expandedItem !== id && expanded) {
setExpanded(false);
onLeave && onLeave();
}
}, [
menu.expandedItem
]);
function handleMouseEnter(event) {
menu.onItemEnter(id);
props.onMouseEnter && props.onMouseEnter(event);
}
function handleMouseLeave(event) {
menu.onItemLeave();
props.onMouseLeave && props.onMouseLeave(event);
}
return(/*#__PURE__*/ $gati5$reactjsxruntime.jsx("div", {
...props,
ref: ref,
onMouseEnter: handleMouseEnter,
onMouseLeave: handleMouseLeave,
children: props.children
}));
});
var $d20f2444b9cc1ca0$export$2e2bcd8739ae039 = $d20f2444b9cc1ca0$var$MenuItem;
//# sourceMappingURL=index.js.map