toolbar-items.tsx 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. import { ComponentType, FC, memo } from 'react'
  2. import { EditorState } from '@codemirror/state'
  3. import { useEditorContext } from '../../../../shared/context/editor-context'
  4. import { ToolbarButton } from './toolbar-button'
  5. import { redo, undo } from '@codemirror/commands'
  6. import * as commands from '../../extensions/toolbar/commands'
  7. import { SectionHeadingDropdown } from './section-heading-dropdown'
  8. import getMeta from '../../../../utils/meta'
  9. import { InsertFigureDropdown } from './insert-figure-dropdown'
  10. import { useTranslation } from 'react-i18next'
  11. import { MathDropdown } from './math-dropdown'
  12. import { InsertListDropdown } from './insert-list-dropdown'
  13. import { TableDropdown } from './table-dropdown'
  14. import { LegacyTableDropdown } from './table-inserter-dropdown-legacy'
  15. import { withinFormattingCommand } from '@/features/source-editor/utils/tree-operations/formatting'
  16. import { isMac } from '@/shared/utils/os'
  17. import { useProjectContext } from '@/shared/context/project-context'
  18. import { useEditorPropertiesContext } from '@/features/ide-react/context/editor-properties-context'
  19. import { usePermissionsContext } from '@/features/ide-react/context/permissions-context'
  20. import { isCursorOnEmptyLine } from '@/features/source-editor/utils/is-cursor-on-empty-line'
  21. import importOverleafModules from '../../../../../macros/import-overleaf-module.macro'
  22. const sourceEditorToolbarButtonGroups = importOverleafModules(
  23. 'sourceEditorToolbarButtonGroups'
  24. ) as {
  25. import: { default: ComponentType; overflowGroupId: string }
  26. path: string
  27. }[]
  28. const addCommentFromToolbar = () => commands.addComment('toolbar')
  29. export const ToolbarItems: FC<{
  30. state: EditorState
  31. overflowed?: Set<string>
  32. languageName?: string
  33. visual: boolean
  34. listDepth: number
  35. }> = memo(function ToolbarItems({
  36. state,
  37. overflowed,
  38. languageName,
  39. visual,
  40. listDepth,
  41. }) {
  42. const { t } = useTranslation()
  43. const { showSymbolPalette, toggleSymbolPalette } =
  44. useEditorPropertiesContext()
  45. const { writefullInstance } = useEditorContext()
  46. const { features } = useProjectContext()
  47. const permissions = usePermissionsContext()
  48. const isActive = withinFormattingCommand(state)
  49. const symbolPaletteAvailable = getMeta('ol-symbolPaletteAvailable')
  50. const showAiFeaturesDisabled = getMeta('ol-showAiFeaturesDisabled')
  51. const showGroup = (group: string) => !overflowed || overflowed.has(group)
  52. return (
  53. <>
  54. {showGroup('group-history') && (
  55. <div
  56. className="ol-cm-toolbar-button-group"
  57. aria-label={t('toolbar_undo_redo_actions')}
  58. >
  59. <ToolbarButton
  60. id="toolbar-undo"
  61. label={t('toolbar_undo')}
  62. command={undo}
  63. icon="undo"
  64. shortcut={isMac ? '⌘Z' : 'Ctrl+Z'}
  65. />
  66. <ToolbarButton
  67. id="toolbar-redo"
  68. label={t('toolbar_redo')}
  69. command={redo}
  70. icon="redo"
  71. shortcut={isMac ? '⇧⌘Z' : 'Ctrl+Y'}
  72. />
  73. </div>
  74. )}
  75. {sourceEditorToolbarButtonGroups.map(
  76. ({ import: { default: Component, overflowGroupId }, path }) =>
  77. showGroup(overflowGroupId) && <Component key={path} />
  78. )}
  79. {languageName === 'latex' && (
  80. <>
  81. {showGroup('group-section') && (
  82. <div
  83. className="ol-cm-toolbar-button-group"
  84. data-overflow="group-section"
  85. aria-label={t('toolbar_text_formatting')}
  86. >
  87. <SectionHeadingDropdown />
  88. </div>
  89. )}
  90. {showGroup('group-format') && (
  91. <div
  92. className="ol-cm-toolbar-button-group"
  93. aria-label={t('toolbar_text_style')}
  94. >
  95. <ToolbarButton
  96. id="toolbar-format-bold"
  97. label={t('toolbar_bold')}
  98. command={commands.toggleBold}
  99. active={isActive('\\textbf')}
  100. icon="format_bold"
  101. shortcut={isMac ? '⌘B' : 'Ctrl+B'}
  102. />
  103. <ToolbarButton
  104. id="toolbar-format-italic"
  105. label={t('toolbar_italic')}
  106. command={commands.toggleItalic}
  107. active={isActive('\\textit')}
  108. icon="format_italic"
  109. shortcut={isMac ? '⌘I' : 'Ctrl+I'}
  110. />
  111. </div>
  112. )}
  113. {showGroup('group-math') && (
  114. <div
  115. className="ol-cm-toolbar-button-group"
  116. data-overflow="group-math"
  117. aria-label={t('toolbar_insert_math_and_symbols')}
  118. >
  119. <MathDropdown />
  120. {symbolPaletteAvailable && (
  121. <ToolbarButton
  122. id="toolbar-toggle-symbol-palette"
  123. label={t('toolbar_insert_symbol')}
  124. active={showSymbolPalette}
  125. command={toggleSymbolPalette}
  126. icon="Ω"
  127. textIcon
  128. className="ol-cm-toolbar-button-math"
  129. />
  130. )}
  131. </div>
  132. )}
  133. <div
  134. className="ol-cm-toolbar-button-group"
  135. data-overflow="group-misc"
  136. aria-label={t('toolbar_insert_misc')}
  137. >
  138. {showGroup('misc-href') && (
  139. <div data-overflow="misc-href">
  140. <ToolbarButton
  141. id="toolbar-href"
  142. label={t('toolbar_insert_link')}
  143. command={commands.wrapInHref}
  144. icon="add_link"
  145. />
  146. </div>
  147. )}
  148. {features.trackChangesVisible &&
  149. permissions.comment &&
  150. showGroup('misc-comment') && (
  151. <div data-overflow="misc-comment">
  152. <ToolbarButton
  153. id="toolbar-add-comment"
  154. label={t('add_comment')}
  155. disabled={isCursorOnEmptyLine(state)}
  156. command={addCommentFromToolbar}
  157. icon="add_comment"
  158. />
  159. </div>
  160. )}
  161. {showGroup('misc-ref') && (
  162. <div data-overflow="misc-ref">
  163. <ToolbarButton
  164. id="toolbar-ref"
  165. label={t('toolbar_insert_cross_reference')}
  166. command={commands.insertRef}
  167. icon="sell"
  168. />
  169. </div>
  170. )}
  171. {showGroup('misc-cite') && (
  172. <div data-overflow="misc-cite">
  173. <ToolbarButton
  174. id="toolbar-cite"
  175. label={t('toolbar_insert_citation')}
  176. command={commands.insertCite}
  177. icon="book_5"
  178. />
  179. </div>
  180. )}
  181. {showGroup('misc-figure') && (
  182. <div data-overflow="misc-figure">
  183. <InsertFigureDropdown />
  184. </div>
  185. )}
  186. {showGroup('misc-table') && (
  187. <div data-overflow="misc-table">
  188. {writefullInstance || showAiFeaturesDisabled ? (
  189. <TableDropdown />
  190. ) : (
  191. <LegacyTableDropdown />
  192. )}
  193. </div>
  194. )}
  195. </div>
  196. {showGroup('group-list') && (
  197. <div
  198. className="ol-cm-toolbar-button-group"
  199. data-overflow="group-list"
  200. aria-label={t('toolbar_list_indentation')}
  201. >
  202. <InsertListDropdown />
  203. {listDepth >= 1 && (
  204. <>
  205. <ToolbarButton
  206. id="toolbar-format-indent-decrease"
  207. label={t('toolbar_decrease_indent')}
  208. command={commands.indentDecrease}
  209. icon="format_indent_decrease"
  210. shortcut={visual ? (isMac ? '⌘[' : 'Ctrl+[') : undefined}
  211. disabled={listDepth < 2}
  212. />
  213. <ToolbarButton
  214. id="toolbar-format-indent-increase"
  215. label={t('toolbar_increase_indent')}
  216. command={commands.indentIncrease}
  217. icon="format_indent_increase"
  218. shortcut={visual ? (isMac ? '⌘]' : 'Ctrl+]') : undefined}
  219. />
  220. </>
  221. )}
  222. </div>
  223. )}
  224. </>
  225. )}
  226. </>
  227. )
  228. })