button-menu.tsx 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  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. disabled?: boolean
  16. disablePopover?: boolean
  17. altCommand?: (view: EditorView) => void
  18. }>
  19. > = memo(function ButtonMenu({
  20. icon,
  21. id,
  22. label,
  23. altCommand,
  24. disabled,
  25. disablePopover,
  26. children,
  27. }) {
  28. const target = useRef<any>(null)
  29. const { open, onToggle, ref } = useDropdown()
  30. const view = useCodeMirrorViewContext()
  31. useEffect(() => {
  32. if (disablePopover && open) {
  33. onToggle(false)
  34. }
  35. }, [open, disablePopover, onToggle])
  36. const button = (
  37. <button
  38. type="button"
  39. className="ol-cm-toolbar-button"
  40. aria-label={label}
  41. aria-disabled={disabled}
  42. onMouseDown={event => {
  43. event.preventDefault()
  44. event.stopPropagation()
  45. }}
  46. onClick={event => {
  47. if (disabled) {
  48. event.preventDefault()
  49. return
  50. }
  51. if (event.altKey && altCommand && open === false) {
  52. emitToolbarEvent(view, id)
  53. event.preventDefault()
  54. altCommand(view)
  55. view.focus()
  56. } else {
  57. onToggle(!open)
  58. }
  59. }}
  60. ref={target}
  61. >
  62. {icon}
  63. </button>
  64. )
  65. const overlay = (
  66. <OLOverlay
  67. show={open && !disablePopover}
  68. target={target.current}
  69. placement="bottom"
  70. container={view.dom}
  71. containerPadding={0}
  72. transition
  73. rootClose
  74. onHide={() => onToggle(false)}
  75. >
  76. <OLPopover
  77. id={`${id}-menu`}
  78. ref={ref}
  79. className="ol-cm-toolbar-button-menu-popover"
  80. >
  81. <OLListGroup
  82. role="menu"
  83. onClick={() => {
  84. onToggle(false)
  85. }}
  86. >
  87. {children}
  88. </OLListGroup>
  89. </OLPopover>
  90. </OLOverlay>
  91. )
  92. if (!label) {
  93. return (
  94. <>
  95. {button}
  96. {overlay}
  97. </>
  98. )
  99. }
  100. return (
  101. <>
  102. <OLTooltip
  103. hidden={open}
  104. id={id}
  105. description={<div>{label}</div>}
  106. overlayProps={{ placement: 'bottom' }}
  107. >
  108. {button}
  109. </OLTooltip>
  110. {overlay}
  111. </>
  112. )
  113. })