button-menu.tsx 2.3 KB

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