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

useContextMenu.js 2.2KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. // @flow
  2. import { useCallback, useRef, useState } from 'react';
  3. import { findAncestorByClass } from '../../../participants-pane/functions';
  4. type NullProto = {
  5. [key: string]: any,
  6. __proto__: null
  7. };
  8. type RaiseContext = NullProto | {|
  9. /**
  10. * Target elements against which positioning calculations are made.
  11. */
  12. offsetTarget?: HTMLElement,
  13. /**
  14. * The entity for which the menu is context menu is raised.
  15. */
  16. entity?: string | Object,
  17. |};
  18. const initialState = Object.freeze(Object.create(null));
  19. const useContextMenu = () => {
  20. const [ raiseContext, setRaiseContext ] = useState < RaiseContext >(initialState);
  21. const isMouseOverMenu = useRef(false);
  22. const lowerMenu = useCallback((force: boolean | Object = false) => {
  23. /**
  24. * We are tracking mouse movement over the active participant item and
  25. * the context menu. Due to the order of enter/leave events, we need to
  26. * defer checking if the mouse is over the context menu with
  27. * queueMicrotask.
  28. */
  29. window.queueMicrotask(() => {
  30. if (isMouseOverMenu.current && !(force === true)) {
  31. return;
  32. }
  33. if (raiseContext !== initialState) {
  34. setRaiseContext(initialState);
  35. }
  36. });
  37. }, [ raiseContext ]);
  38. const raiseMenu = useCallback((entity: string | Object, target: EventTarget) => {
  39. setRaiseContext({
  40. entity,
  41. offsetTarget: findAncestorByClass(target, 'list-item-container')
  42. });
  43. }, [ raiseContext ]);
  44. const toggleMenu = useCallback((entity: string | Object) => (e: MouseEvent) => {
  45. e.stopPropagation();
  46. const { entity: raisedEntity } = raiseContext;
  47. if (raisedEntity && raisedEntity === entity) {
  48. lowerMenu();
  49. } else {
  50. raiseMenu(entity, e.target);
  51. }
  52. }, [ raiseContext ]);
  53. const menuEnter = useCallback(() => {
  54. isMouseOverMenu.current = true;
  55. }, []);
  56. const menuLeave = useCallback(() => {
  57. isMouseOverMenu.current = false;
  58. lowerMenu();
  59. }, [ lowerMenu ]);
  60. return [ lowerMenu, raiseMenu, toggleMenu, menuEnter, menuLeave, raiseContext ];
  61. };
  62. export default useContextMenu;