jiti-meet/react/features/base/ui/hooks/useContextMenu.web.ts

79 lines
2.3 KiB
TypeScript

import { useCallback, useRef, useState } from 'react';
import { findAncestorByClass } from '../functions.web';
type RaiseContext = {
/**
* The entity for which the menu is context menu is raised.
*/
entity?: string | Object;
/**
* Target elements against which positioning calculations are made.
*/
offsetTarget?: HTMLElement | null;
};
const initialState = Object.freeze({});
const useContextMenu = (): [(force?: boolean | Object) => void,
(entity: string | Object, target: HTMLElement | null) => void,
(entity: string | Object) => (e: MouseEvent) => void,
() => void,
() => void,
RaiseContext] => {
const [ raiseContext, setRaiseContext ] = useState < RaiseContext >(initialState);
const isMouseOverMenu = useRef(false);
const lowerMenu = useCallback((force: boolean | Object = false) => {
/**
* We are tracking mouse movement over the active participant item and
* the context menu. Due to the order of enter/leave events, we need to
* defer checking if the mouse is over the context menu with
* queueMicrotask.
*/
window.queueMicrotask(() => {
if (isMouseOverMenu.current && !(force === true)) {
return;
}
if (raiseContext !== initialState) {
setRaiseContext(initialState);
}
});
}, [ raiseContext ]);
const raiseMenu = useCallback((entity: string | Object, target: HTMLElement | null) => {
setRaiseContext({
entity,
offsetTarget: findAncestorByClass(target, 'list-item-container')
});
}, [ raiseContext ]);
const toggleMenu = useCallback((entity: string | Object) => (e: MouseEvent) => {
e.stopPropagation();
const { entity: raisedEntity } = raiseContext;
if (raisedEntity && raisedEntity === entity) {
lowerMenu();
} else {
raiseMenu(entity, e.target as HTMLElement);
}
}, [ raiseContext ]);
const menuEnter = useCallback(() => {
isMouseOverMenu.current = true;
}, []);
const menuLeave = useCallback(() => {
isMouseOverMenu.current = false;
lowerMenu();
}, [ lowerMenu ]);
return [ lowerMenu, raiseMenu, toggleMenu, menuEnter, menuLeave, raiseContext ];
};
export default useContextMenu;