99 lines
2.8 KiB
TypeScript
99 lines
2.8 KiB
TypeScript
|
import { useCallback, useRef, useState } from "react";
|
||
|
import { TRANSITIONS_IN_MILLISECONDS } from "utils/constants";
|
||
|
import { isSafari } from "utils/functions";
|
||
|
|
||
|
export type MenuItem = {
|
||
|
action?: () => void;
|
||
|
checked?: boolean;
|
||
|
disabled?: boolean;
|
||
|
icon?: string;
|
||
|
label?: string;
|
||
|
menu?: MenuItem[];
|
||
|
primary?: boolean;
|
||
|
seperator?: boolean;
|
||
|
toggle?: boolean;
|
||
|
};
|
||
|
|
||
|
export type MenuState = {
|
||
|
items?: MenuItem[];
|
||
|
x?: number;
|
||
|
y?: number;
|
||
|
};
|
||
|
|
||
|
export type ContextMenuCapture = {
|
||
|
onContextMenuCapture: (
|
||
|
event?: React.MouseEvent | React.TouchEvent,
|
||
|
domRect?: DOMRect
|
||
|
) => void;
|
||
|
onTouchEnd?: React.TouchEventHandler;
|
||
|
onTouchMove?: React.TouchEventHandler;
|
||
|
onTouchStart?: React.TouchEventHandler;
|
||
|
};
|
||
|
|
||
|
type MenuContextState = {
|
||
|
contextMenu: (getItems: () => MenuItem[]) => ContextMenuCapture;
|
||
|
menu: MenuState;
|
||
|
setMenu: React.Dispatch<React.SetStateAction<MenuState>>;
|
||
|
};
|
||
|
|
||
|
const useMenuContextState = (): MenuContextState => {
|
||
|
const [menu, setMenu] = useState<MenuState>(Object.create(null) as MenuState);
|
||
|
const touchTimer = useRef<number>(0);
|
||
|
const touchEvent = useRef<React.TouchEvent>();
|
||
|
const contextMenu = useCallback(
|
||
|
(getItems: () => MenuItem[]): ContextMenuCapture => {
|
||
|
const onContextMenuCapture = (
|
||
|
event?: React.MouseEvent | React.TouchEvent,
|
||
|
domRect?: DOMRect
|
||
|
): void => {
|
||
|
let x = 0;
|
||
|
let y = 0;
|
||
|
|
||
|
if (event) {
|
||
|
if (event.cancelable) event.preventDefault();
|
||
|
|
||
|
({ pageX: x, pageY: y } =
|
||
|
"touches" in event ? event.touches.item?.(0) || event : event);
|
||
|
} else if (domRect) {
|
||
|
const { height, x: inputX, y: inputY } = domRect;
|
||
|
|
||
|
x = inputX;
|
||
|
y = inputY + height;
|
||
|
}
|
||
|
|
||
|
const items = getItems();
|
||
|
|
||
|
setMenu({ items: items.length > 0 ? items : undefined, x, y });
|
||
|
};
|
||
|
|
||
|
return {
|
||
|
onContextMenuCapture,
|
||
|
...(isSafari() && {
|
||
|
onTouchEnd: () => {
|
||
|
if (touchEvent.current) {
|
||
|
onContextMenuCapture(touchEvent.current);
|
||
|
touchEvent.current = undefined;
|
||
|
}
|
||
|
window.clearTimeout(touchTimer.current);
|
||
|
},
|
||
|
onTouchMove: () => {
|
||
|
touchEvent.current = undefined;
|
||
|
window.clearTimeout(touchTimer.current);
|
||
|
},
|
||
|
onTouchStart: (event: React.TouchEvent) => {
|
||
|
window.clearTimeout(touchTimer.current);
|
||
|
touchTimer.current = window.setTimeout(() => {
|
||
|
touchEvent.current = event;
|
||
|
}, TRANSITIONS_IN_MILLISECONDS.LONG_PRESS);
|
||
|
},
|
||
|
}),
|
||
|
};
|
||
|
},
|
||
|
[]
|
||
|
);
|
||
|
|
||
|
return { contextMenu, menu, setMenu };
|
||
|
};
|
||
|
|
||
|
export default useMenuContextState;
|