button-menu.tsx 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import { FC, memo, useEffect, useRef } from 'react'
  2. import useDropdown from '../../../../shared/hooks/use-dropdown'
  3. import OLListGroup from '@/shared/components/ol/ol-list-group'
  4. import OLTooltip from '@/shared/components/ol/ol-tooltip'
  5. import OLOverlay from '@/shared/components/ol/ol-overlay'
  6. import OLPopover from '@/shared/components/ol/ol-popover'
  7. import { EditorView } from '@codemirror/view'
  8. import { emitToolbarEvent } from '../../extensions/toolbar/utils/analytics'
  9. import { useCodeMirrorViewContext } from '../codemirror-context'
  10. export const ToolbarButtonMenu: FC<
  11. React.PropsWithChildren<{
  12. id: string
  13. label: string
  14. icon: React.ReactNode
  15. disablePopover?: boolean
  16. altCommand?: (view: EditorView) => void
  17. }>
  18. > = memo(function ButtonMenu({
  19. icon,
  20. id,
  21. label,
  22. altCommand,
  23. disablePopover,
  24. children,
  25. }) {
  26. const target = useRef<any>(null)
  27. const { open, onToggle, ref } = useDropdown()
  28. const view = useCodeMirrorViewContext()
  29. useEffect(() => {
  30. if (disablePopover && open) {
  31. onToggle(false)
  32. }
  33. }, [open, disablePopover, onToggle])
  34. const button = (
  35. <button
  36. type="button"
  37. className="ol-cm-toolbar-button"
  38. aria-label={label}
  39. onMouseDown={event => {
  40. event.preventDefault()
  41. event.stopPropagation()
  42. }}
  43. onClick={event => {
  44. if (event.altKey && altCommand && open === false) {
  45. emitToolbarEvent(view, id)
  46. event.preventDefault()
  47. altCommand(view)
  48. view.focus()
  49. } else {
  50. onToggle(!open)
  51. }
  52. }}
  53. ref={target}
  54. >
  55. {icon}
  56. </button>
  57. )
  58. const overlay = (
  59. <OLOverlay
  60. show={open && !disablePopover}
  61. target={target.current}
  62. placement="bottom"
  63. container={view.dom}
  64. containerPadding={0}
  65. transition
  66. rootClose
  67. onHide={() => onToggle(false)}
  68. >
  69. <OLPopover
  70. id={`${id}-menu`}
  71. ref={ref}
  72. className="ol-cm-toolbar-button-menu-popover"
  73. >
  74. <OLListGroup
  75. role="menu"
  76. onClick={() => {
  77. onToggle(false)
  78. }}
  79. >
  80. {children}
  81. </OLListGroup>
  82. </OLPopover>
  83. </OLOverlay>
  84. )
  85. if (!label) {
  86. return (
  87. <>
  88. {button}
  89. {overlay}
  90. </>
  91. )
  92. }
  93. return (
  94. <>
  95. <OLTooltip
  96. hidden={open}
  97. id={id}
  98. description={<div>{label}</div>}
  99. overlayProps={{ placement: 'bottom' }}
  100. >
  101. {button}
  102. </OLTooltip>
  103. {overlay}
  104. </>
  105. )
  106. })