Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

useContextMenu.web.ts 2.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. import { useCallback, useRef, useState } from 'react';
  2. import { findAncestorByClass } from '../functions.web';
  3. type RaiseContext = {
  4. /**
  5. * The entity for which the menu is context menu is raised.
  6. */
  7. entity?: string | Object;
  8. /**
  9. * Target elements against which positioning calculations are made.
  10. */
  11. offsetTarget?: HTMLElement | null;
  12. };
  13. const initialState = Object.freeze({});
  14. const useContextMenu = (): [(force?: boolean | Object) => void,
  15. (entity: string | Object, target: HTMLElement | null) => void,
  16. (entity: string | Object) => (e: MouseEvent) => void,
  17. () => void,
  18. () => void,
  19. RaiseContext] => {
  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: HTMLElement | null) => {
  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 as HTMLElement);
  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;