UNPKG

preact-context-menu

Version:
128 lines 5.39 kB
var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; import { h, createContext } from "preact"; import { useState, useRef, useEffect, useCallback } from "preact/hooks"; import { createPortal } from "preact/compat"; export var menuOffset = 8; export var contextMenus = new Map(); export var MenuContext = createContext(undefined); var menuContainer; if (typeof window !== 'undefined') { menuContainer = document.createElement("div"); menuContainer.classList.add("preact-context-menu"); menuContainer.setAttribute("style", "overflow: hidden; pointer-events: none;"); document.body.appendChild(menuContainer); } export var ContextMenuWithData = function (props) { var id = props.id, children = props.children, className = props.className, shouldOpen = props.shouldOpen, onClose = props.onClose, style = props.style, divProps = __rest(props, ["id", "children", "className", "shouldOpen", "onClose", "style"]); var _a = useState(false), render = _a[0], setRender = _a[1]; var _b = useState(false), wasOpen = _b[0], setWasOpen = _b[1]; var _c = useState(undefined), placement = _c[0], setPlacement = _c[1]; var _d = useState(undefined), eventCoords = _d[0], setEventCoords = _d[1]; var _e = useState(undefined), data = _e[0], setData = _e[1]; var ref = useRef(null); var trigger = useCallback(function (coords, data) { if (shouldOpen !== undefined && !shouldOpen(data)) return; setEventCoords(coords); setRender(true); setData(data); }, [shouldOpen]); var closeMenu = useCallback(function (data) { setRender(false); setPlacement(undefined); setData(undefined); if (props.onClose !== undefined) props.onClose(data); }, [props.onClose]); var onClickAway = useCallback(function (event) { if (ref.current && !ref.current.contains(event.target)) { closeMenu(undefined); } }, []); useEffect(function () { if (contextMenus.has(id)) throw new Error("There is another ContextMenu element with the ID " + id); contextMenus.set(id, trigger); return function () { contextMenus.delete(id); }; }, [id]); useEffect(function () { if (render && typeof window !== 'undefined') { var div = ref.current; if (div === null) return; var coords = eventCoords || { x: 0, y: 0 }; var x = coords.x + menuOffset; var y = coords.y + menuOffset; var width = window.innerWidth; var height = window.innerHeight; if (x + div.offsetWidth > width - 8) { x = coords.x - div.offsetWidth; } if (y + div.offsetHeight > height - 8) { y = height - div.offsetHeight - 8; } setPlacement({ x: x, y: y }); document.addEventListener('mousedown', onClickAway); return function () { return document.removeEventListener('mousedown', onClickAway); }; } return undefined; }, [render, eventCoords]); if (render) { var finalStyle = style || {}; finalStyle.position = "fixed"; if (placement !== undefined) { finalStyle.top = placement.y; finalStyle.left = placement.x; finalStyle.pointerEvents = "initial"; } else { finalStyle.opacity = 0; finalStyle.pointerEvents = "none"; } var out = children(data); if (!wasOpen && (!out || out.length === 0)) { setRender(false); setPlacement(undefined); setData(undefined); return null; } setWasOpen(true); return createPortal(h(MenuContext.Provider, { value: function (data) { return closeMenu(data); } }, h("div", __assign({ ref: ref, id: id }, divProps, { // This is to stop the browser context menu from opening onContextMenu: function (event) { return event.preventDefault(); }, className: className !== undefined ? "context-menu " + className : "context-menu", style: finalStyle }), out)), menuContainer); } else { setWasOpen(false); return null; } }; var ContextMenu = function (props) { var children = props.children, rest = __rest(props, ["children"]); return (h(ContextMenuWithData, __assign({}, rest), function (_) { return children; })); }; export default ContextMenu; //# sourceMappingURL=menu.js.map