pdf-compile-button.tsx 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. import { useTranslation } from 'react-i18next'
  2. import { memo, useCallback } from 'react'
  3. import classNames from 'classnames'
  4. import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
  5. import { useStopOnFirstError } from '../../../shared/hooks/use-stop-on-first-error'
  6. import * as eventTracking from '../../../infrastructure/event-tracking'
  7. import OLTooltip from '@/shared/components/ol/ol-tooltip'
  8. import {
  9. DropdownToggleCustom,
  10. Dropdown,
  11. DropdownDivider,
  12. DropdownHeader,
  13. DropdownItem,
  14. DropdownMenu,
  15. DropdownToggle,
  16. } from '@/shared/components/dropdown/dropdown-menu'
  17. import OLButton from '@/shared/components/ol/ol-button'
  18. import OLButtonGroup from '@/shared/components/ol/ol-button-group'
  19. import { useLayoutContext } from '@/shared/context/layout-context'
  20. import { useCommandProvider } from '@/features/ide-react/hooks/use-command-provider'
  21. const modifierKey = /Mac/i.test(navigator.platform) ? 'Cmd' : 'Ctrl'
  22. function sendEventAndSet<T extends boolean>(
  23. value: T,
  24. setter: (value: T) => void,
  25. settingName: string
  26. ) {
  27. eventTracking.sendMB('recompile-setting-changed', {
  28. setting: settingName,
  29. settingVal: value,
  30. })
  31. setter(value)
  32. }
  33. function PdfCompileButton() {
  34. const {
  35. animateCompileDropdownArrow,
  36. autoCompile,
  37. compiling,
  38. draft,
  39. hasChanges,
  40. setAutoCompile,
  41. setDraft,
  42. setStopOnValidationError,
  43. stopOnFirstError,
  44. stopOnValidationError,
  45. startCompile,
  46. stopCompile,
  47. recompileFromScratch,
  48. } = useCompileContext()
  49. const { enableStopOnFirstError, disableStopOnFirstError } =
  50. useStopOnFirstError({ eventSource: 'dropdown' })
  51. const { t } = useTranslation()
  52. const { detachRole } = useLayoutContext()
  53. const fromScratchWithEvent = useCallback(() => {
  54. eventTracking.sendMB('recompile-setting-changed', {
  55. setting: 'from-scratch',
  56. })
  57. recompileFromScratch()
  58. }, [recompileFromScratch])
  59. const tooltipElement = (
  60. <>
  61. {t('recompile_pdf')}{' '}
  62. <span className="keyboard-shortcut">({modifierKey} + Enter)</span>
  63. </>
  64. )
  65. const dropdownToggleClassName = classNames(
  66. {
  67. 'detach-compile-button-animate': animateCompileDropdownArrow,
  68. 'btn-striped-animated': hasChanges,
  69. },
  70. 'no-left-border',
  71. 'dropdown-button-toggle',
  72. 'compile-dropdown-toggle'
  73. )
  74. const buttonClassName = classNames(
  75. 'align-items-center py-0 no-left-radius px-3',
  76. 'compile-button',
  77. {
  78. 'btn-striped-animated': hasChanges,
  79. }
  80. )
  81. useCommandProvider(
  82. () => [
  83. {
  84. id: 'compile',
  85. handler: () => startCompile(),
  86. label: t('recompile'),
  87. disabled: compiling,
  88. },
  89. {
  90. id: 'stop-compile',
  91. handler: () => stopCompile(),
  92. label: t('stop_compile'),
  93. disabled: !compiling,
  94. },
  95. {
  96. id: 'recompile-from-scratch',
  97. handler: fromScratchWithEvent,
  98. label: t('recompile_from_scratch'),
  99. disabled: compiling,
  100. },
  101. ],
  102. [startCompile, t, compiling, stopCompile, fromScratchWithEvent]
  103. )
  104. return (
  105. <Dropdown as={OLButtonGroup} className="compile-button-group">
  106. <OLTooltip
  107. description={tooltipElement}
  108. id="compile"
  109. tooltipProps={{ className: 'keyboard-tooltip' }}
  110. overlayProps={{
  111. delay: { show: 500, hide: 0 },
  112. placement: detachRole === 'detached' ? 'bottom' : undefined,
  113. }}
  114. >
  115. <OLButton
  116. variant="primary"
  117. disabled={compiling}
  118. isLoading={compiling}
  119. onClick={() => startCompile()}
  120. className={buttonClassName}
  121. loadingLabel={`${t('compiling')}…`}
  122. >
  123. {t('recompile')}
  124. </OLButton>
  125. </OLTooltip>
  126. <DropdownToggle
  127. as={DropdownToggleCustom}
  128. split
  129. variant="primary"
  130. id="pdf-recompile-dropdown"
  131. size="sm"
  132. aria-label={t('toggle_compile_options_menu')}
  133. className={dropdownToggleClassName}
  134. />
  135. <DropdownMenu>
  136. <DropdownHeader>{t('auto_compile')}</DropdownHeader>
  137. <li role="none">
  138. <DropdownItem
  139. as="button"
  140. onClick={() =>
  141. sendEventAndSet(true, setAutoCompile, 'auto-compile')
  142. }
  143. trailingIcon={autoCompile ? 'check' : null}
  144. >
  145. {t('on')}
  146. </DropdownItem>
  147. </li>
  148. <li role="none">
  149. <DropdownItem
  150. as="button"
  151. onClick={() =>
  152. sendEventAndSet(false, setAutoCompile, 'auto-compile')
  153. }
  154. trailingIcon={!autoCompile ? 'check' : null}
  155. >
  156. {t('off')}
  157. </DropdownItem>
  158. </li>
  159. <DropdownDivider />
  160. <DropdownHeader>{t('compile_mode')}</DropdownHeader>
  161. <li role="none">
  162. <DropdownItem
  163. as="button"
  164. onClick={() => sendEventAndSet(false, setDraft, 'compile-mode')}
  165. trailingIcon={!draft ? 'check' : null}
  166. >
  167. {t('normal')}
  168. </DropdownItem>
  169. </li>
  170. <li role="none">
  171. <DropdownItem
  172. as="button"
  173. onClick={() => sendEventAndSet(true, setDraft, 'compile-mode')}
  174. trailingIcon={draft ? 'check' : null}
  175. >
  176. {t('fast')}&nbsp;<span className="subdued">[draft]</span>
  177. </DropdownItem>
  178. </li>
  179. <DropdownDivider />
  180. <DropdownHeader>{t('syntax_checks')}</DropdownHeader>
  181. <li role="none">
  182. <DropdownItem
  183. as="button"
  184. onClick={() =>
  185. sendEventAndSet(true, setStopOnValidationError, 'syntax-check')
  186. }
  187. trailingIcon={stopOnValidationError ? 'check' : null}
  188. >
  189. {t('stop_on_validation_error')}
  190. </DropdownItem>
  191. </li>
  192. <li role="none">
  193. <DropdownItem
  194. as="button"
  195. onClick={() =>
  196. sendEventAndSet(false, setStopOnValidationError, 'syntax-check')
  197. }
  198. trailingIcon={!stopOnValidationError ? 'check' : null}
  199. >
  200. {t('ignore_validation_errors')}
  201. </DropdownItem>
  202. </li>
  203. <DropdownDivider />
  204. <DropdownHeader>{t('compile_error_handling')}</DropdownHeader>
  205. <li role="none">
  206. <DropdownItem
  207. as="button"
  208. onClick={enableStopOnFirstError}
  209. trailingIcon={stopOnFirstError ? 'check' : null}
  210. >
  211. {t('stop_on_first_error')}
  212. </DropdownItem>
  213. </li>
  214. <li role="none">
  215. <DropdownItem
  216. as="button"
  217. onClick={disableStopOnFirstError}
  218. trailingIcon={!stopOnFirstError ? 'check' : null}
  219. >
  220. {t('try_to_compile_despite_errors')}
  221. </DropdownItem>
  222. </li>
  223. <DropdownDivider />
  224. <li role="none">
  225. <DropdownItem
  226. as="button"
  227. onClick={() => stopCompile()}
  228. disabled={!compiling}
  229. aria-disabled={!compiling}
  230. >
  231. {t('stop_compile')}
  232. </DropdownItem>
  233. </li>
  234. <li role="none">
  235. <DropdownItem
  236. as="button"
  237. onClick={fromScratchWithEvent}
  238. disabled={compiling}
  239. aria-disabled={compiling}
  240. >
  241. {t('recompile_from_scratch')}
  242. </DropdownItem>
  243. </li>
  244. </DropdownMenu>
  245. </Dropdown>
  246. )
  247. }
  248. export default memo(PdfCompileButton)