toolbar-items.tsx 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. import { FC, memo, useCallback } from 'react'
  2. import { EditorSelection, EditorState } from '@codemirror/state'
  3. import { EditorView } from '@codemirror/view'
  4. import { useEditorContext } from '../../../../shared/context/editor-context'
  5. import useScopeEventEmitter from '../../../../shared/hooks/use-scope-event-emitter'
  6. import { useLayoutContext } from '../../../../shared/context/layout-context'
  7. import {
  8. minimumListDepthForSelection,
  9. withinFormattingCommand,
  10. } from '../../utils/tree-operations/ancestors'
  11. import { ToolbarButton } from './toolbar-button'
  12. import { redo, undo } from '@codemirror/commands'
  13. import * as commands from '../../extensions/toolbar/commands'
  14. import { SectionHeadingDropdown } from './section-heading-dropdown'
  15. import { canAddComment } from '../../extensions/toolbar/comments'
  16. import { useTranslation } from 'react-i18next'
  17. import getMeta from '../../../../utils/meta'
  18. import { InsertFigureDropdown } from './insert-figure-dropdown'
  19. const isMac = /Mac/.test(window.navigator?.platform)
  20. export const ToolbarItems: FC<{
  21. state: EditorState
  22. overflowed?: Set<string>
  23. }> = memo(function ToolbarItems({ state, overflowed }) {
  24. const { t } = useTranslation()
  25. const { toggleSymbolPalette, showSymbolPalette } = useEditorContext()
  26. const isActive = withinFormattingCommand(state)
  27. const listDepth = minimumListDepthForSelection(state)
  28. const addCommentEmitter = useScopeEventEmitter('comment:start_adding')
  29. const { setReviewPanelOpen } = useLayoutContext()
  30. const splitTestVariants = getMeta('ol-splitTestVariants', {})
  31. const addComment = useCallback(
  32. (view: EditorView) => {
  33. const range = view.state.selection.main
  34. if (range.empty) {
  35. const line = view.state.doc.lineAt(range.head)
  36. view.dispatch({
  37. selection: EditorSelection.range(line.from, line.to),
  38. })
  39. }
  40. setReviewPanelOpen(true)
  41. addCommentEmitter()
  42. },
  43. [addCommentEmitter, setReviewPanelOpen]
  44. )
  45. const showFigureModal = splitTestVariants['figure-modal'] === 'enabled'
  46. const showGroup = (group: string) => !overflowed || overflowed.has(group)
  47. return (
  48. <>
  49. {showGroup('group-history') && (
  50. <div className="ol-cm-toolbar-button-group">
  51. <ToolbarButton
  52. id="toolbar-undo"
  53. label={t('toolbar_undo')}
  54. command={undo}
  55. icon="undo"
  56. shortcut={isMac ? '⌘Z' : 'Ctrl+Z'}
  57. />
  58. <ToolbarButton
  59. id="toolbar-redo"
  60. label={t('toolbar_redo')}
  61. command={redo}
  62. icon="repeat"
  63. shortcut={isMac ? '⇧⌘Z' : 'Ctrl+Y'}
  64. />
  65. </div>
  66. )}
  67. {showGroup('group-section') && (
  68. <div
  69. className="ol-cm-toolbar-button-group"
  70. data-overflow="group-section"
  71. >
  72. <SectionHeadingDropdown />
  73. </div>
  74. )}
  75. {showGroup('group-format') && (
  76. <div className="ol-cm-toolbar-button-group">
  77. <ToolbarButton
  78. id="toolbar-format-bold"
  79. label={t('toolbar_format_bold')}
  80. command={commands.toggleBold}
  81. active={isActive('\\textbf')}
  82. icon="bold"
  83. shortcut={isMac ? '⌘B' : 'Ctrl+B'}
  84. />
  85. <ToolbarButton
  86. id="toolbar-format-italic"
  87. label={t('toolbar_format_italic')}
  88. command={commands.toggleItalic}
  89. active={isActive('\\textit')}
  90. icon="italic"
  91. shortcut={isMac ? '⌘I' : 'Ctrl+I'}
  92. />
  93. </div>
  94. )}
  95. {showGroup('group-math') && (
  96. <div className="ol-cm-toolbar-button-group" data-overflow="group-math">
  97. <ToolbarButton
  98. id="toolbar-inline-math"
  99. label={t('toolbar_insert_inline_math')}
  100. command={commands.wrapInInlineMath}
  101. icon="π"
  102. textIcon
  103. className="ol-cm-toolbar-button-math"
  104. />
  105. <ToolbarButton
  106. id="toolbar-display-math"
  107. label={t('toolbar_insert_display_math')}
  108. command={commands.wrapInDisplayMath}
  109. icon="Σ"
  110. textIcon
  111. className="ol-cm-toolbar-button-math"
  112. />
  113. <ToolbarButton
  114. id="toolbar-toggle-symbol-palette"
  115. label={t('toolbar_toggle_symbol_palette')}
  116. active={showSymbolPalette}
  117. command={toggleSymbolPalette}
  118. icon="Ω"
  119. textIcon
  120. className="ol-cm-toolbar-button-math"
  121. />
  122. </div>
  123. )}
  124. {showGroup('group-misc') && (
  125. <div className="ol-cm-toolbar-button-group" data-overflow="group-misc">
  126. <ToolbarButton
  127. id="toolbar-href"
  128. label={t('toolbar_insert_link')}
  129. command={commands.wrapInHref}
  130. icon="link"
  131. />
  132. <ToolbarButton
  133. id="toolbar-ref"
  134. label={t('toolbar_insert_cross_reference')}
  135. command={commands.insertRef}
  136. icon="tag"
  137. />
  138. <ToolbarButton
  139. id="toolbar-cite"
  140. label={t('toolbar_insert_citation')}
  141. command={commands.insertCite}
  142. icon="book"
  143. />
  144. <ToolbarButton
  145. id="toolbar-add-comment"
  146. label={t('toolbar_add_comment')}
  147. command={addComment}
  148. disabled={!canAddComment(state)}
  149. icon="comment"
  150. hidden // enable this if an alternative to the floating "Add Comment" button is needed
  151. />
  152. {showFigureModal ? (
  153. <InsertFigureDropdown />
  154. ) : (
  155. <ToolbarButton
  156. id="toolbar-figure"
  157. label={t('toolbar_insert_figure')}
  158. command={commands.insertFigure}
  159. icon="picture-o"
  160. />
  161. )}
  162. <ToolbarButton
  163. id="toolbar-table"
  164. label={t('toolbar_insert_table')}
  165. command={commands.insertTable}
  166. icon="table"
  167. hidden
  168. />
  169. </div>
  170. )}
  171. {showGroup('group-list') && (
  172. <div className="ol-cm-toolbar-button-group" data-overflow="group-list">
  173. <ToolbarButton
  174. id="toolbar-bullet-list"
  175. label={t('toolbar_bullet_list')}
  176. command={commands.toggleBulletList}
  177. icon="list-ul"
  178. />
  179. <ToolbarButton
  180. id="toolbar-numbered-list"
  181. label={t('toolbar_numbered_list')}
  182. command={commands.toggleNumberedList}
  183. icon="list-ol"
  184. />
  185. <ToolbarButton
  186. id="toolbar-format-indent-decrease"
  187. label={t('toolbar_decrease_indent')}
  188. command={commands.indentDecrease}
  189. icon="outdent"
  190. shortcut={isMac ? '⌘[' : 'Ctrl+['}
  191. disabled={listDepth < 2}
  192. />
  193. <ToolbarButton
  194. id="toolbar-format-indent-increase"
  195. label={t('toolbar_increase_indent')}
  196. command={commands.indentIncrease}
  197. icon="indent"
  198. shortcut={isMac ? '⌘]' : 'Ctrl+]'}
  199. disabled={listDepth < 1}
  200. />
  201. </div>
  202. )}
  203. </>
  204. )
  205. })