editor-context-menu.tsx 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. import { FC, Fragment, memo } from 'react'
  2. import ReactDOM from 'react-dom'
  3. import { useTranslation } from 'react-i18next'
  4. import { getTooltip } from '@codemirror/view'
  5. import {
  6. useCodeMirrorStateContext,
  7. useCodeMirrorViewContext,
  8. } from './codemirror-context'
  9. import { contextMenuStateField } from '../extensions/context-menu'
  10. import { useFeatureFlag } from '@/shared/context/split-test-context'
  11. import { useContextMenuItems } from '../hooks/use-context-menu-items'
  12. const EditorContextMenu: FC = () => {
  13. const state = useCodeMirrorStateContext()
  14. const view = useCodeMirrorViewContext()
  15. const editorContextMenuEnabled = useFeatureFlag('editor-context-menu')
  16. const menuState = state.field(contextMenuStateField, false)
  17. if (!editorContextMenuEnabled || !menuState?.tooltip) {
  18. return null
  19. }
  20. const tooltipView = getTooltip(view, menuState.tooltip)
  21. if (!tooltipView) {
  22. return null
  23. }
  24. return ReactDOM.createPortal(<EditorContextMenuContent />, tooltipView.dom)
  25. }
  26. const EditorContextMenuContent: FC = memo(function EditorContextMenuContent() {
  27. const { t } = useTranslation()
  28. const menuItems = useContextMenuItems()
  29. return (
  30. <div className="editor-context-menu" role="menu" aria-label={t('menu')}>
  31. {menuItems.map((menuItem, index) => (
  32. <Fragment key={index}>
  33. {menuItem.separatorAbove && (
  34. <div className="editor-context-menu-separator" />
  35. )}
  36. <ContextMenuItem
  37. label={menuItem.label}
  38. onClick={() => menuItem.handler()}
  39. disabled={menuItem.disabled}
  40. shortcut={menuItem.shortcut}
  41. />
  42. </Fragment>
  43. ))}
  44. </div>
  45. )
  46. })
  47. type ContextMenuItemProps = {
  48. label: string
  49. onClick: () => void
  50. disabled?: boolean
  51. shortcut?: string
  52. }
  53. const ContextMenuItem: FC<ContextMenuItemProps> = ({
  54. label,
  55. shortcut,
  56. onClick,
  57. disabled,
  58. }) => (
  59. <button
  60. type="button"
  61. role="menuitem"
  62. className="editor-context-menu-item"
  63. onClick={onClick}
  64. disabled={disabled}
  65. >
  66. <span className="editor-context-menu-item-label">{label}</span>
  67. <span className="editor-context-menu-item-shortcut">{shortcut ?? ''}</span>
  68. </button>
  69. )
  70. export default EditorContextMenu