editor-switch.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import { ChangeEvent, FC, memo, useCallback, useRef } from 'react'
  2. import useScopeValue from '@/shared/hooks/use-scope-value'
  3. import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
  4. import useTutorial from '@/shared/hooks/promotions/use-tutorial'
  5. import { sendMB } from '../../../infrastructure/event-tracking'
  6. import { isValidTeXFile } from '../../../main/is-valid-tex-file'
  7. import { useTranslation } from 'react-i18next'
  8. import {
  9. EditorSwitchBeginnerPopover,
  10. codeEditorModePrompt,
  11. } from './editor-switch-beginner-popover'
  12. import { useEditorManagerContext } from '@/features/ide-react/context/editor-manager-context'
  13. function EditorSwitch() {
  14. const { t } = useTranslation()
  15. const [visual, setVisual] = useScopeValue('editor.showVisual')
  16. const [codeEditorOpened] = useScopeValue('editor.codeEditorOpened')
  17. const { openDocName } = useEditorManagerContext()
  18. const codeEditorRef = useRef<HTMLLabelElement>(null)
  19. const richTextAvailable = openDocName ? isValidTeXFile(openDocName) : false
  20. const { completeTutorial } = useTutorial(codeEditorModePrompt, {
  21. location: 'logs',
  22. name: codeEditorModePrompt,
  23. })
  24. const handleChange = useCallback(
  25. event => {
  26. const editorType = event.target.value
  27. switch (editorType) {
  28. case 'cm6':
  29. setVisual(false)
  30. if (!codeEditorOpened) {
  31. completeTutorial({ event: 'promo-click', action: 'complete' })
  32. }
  33. break
  34. case 'rich-text':
  35. setVisual(true)
  36. break
  37. }
  38. sendMB('editor-switch-change', { editorType })
  39. },
  40. [codeEditorOpened, completeTutorial, setVisual]
  41. )
  42. return (
  43. <div
  44. className="editor-toggle-switch"
  45. aria-label={t('toolbar_code_visual_editor_switch')}
  46. >
  47. <fieldset className="toggle-switch">
  48. <legend className="sr-only">Editor mode.</legend>
  49. <input
  50. type="radio"
  51. name="editor"
  52. value="cm6"
  53. id="editor-switch-cm6"
  54. className="toggle-switch-input"
  55. checked={!richTextAvailable || !visual}
  56. onChange={handleChange}
  57. />
  58. <EditorSwitchBeginnerPopover targetRef={codeEditorRef}>
  59. <label
  60. ref={codeEditorRef}
  61. htmlFor="editor-switch-cm6"
  62. className="toggle-switch-label"
  63. >
  64. <span>{t('code_editor')}</span>
  65. </label>
  66. </EditorSwitchBeginnerPopover>
  67. <RichTextToggle
  68. checked={richTextAvailable && visual}
  69. disabled={!richTextAvailable}
  70. handleChange={handleChange}
  71. />
  72. </fieldset>
  73. </div>
  74. )
  75. }
  76. const RichTextToggle: FC<{
  77. checked: boolean
  78. disabled: boolean
  79. handleChange: (event: ChangeEvent<HTMLInputElement>) => void
  80. }> = ({ checked, disabled, handleChange }) => {
  81. const { t } = useTranslation()
  82. const toggle = (
  83. <span>
  84. <input
  85. type="radio"
  86. name="editor"
  87. value="rich-text"
  88. id="editor-switch-rich-text"
  89. className="toggle-switch-input"
  90. checked={checked}
  91. onChange={handleChange}
  92. disabled={disabled}
  93. />
  94. <label htmlFor="editor-switch-rich-text" className="toggle-switch-label">
  95. <span>{t('visual_editor')}</span>
  96. </label>
  97. </span>
  98. )
  99. if (disabled) {
  100. return (
  101. <OLTooltip
  102. description={t('visual_editor_is_only_available_for_tex_files')}
  103. id="rich-text-toggle-tooltip"
  104. overlayProps={{ placement: 'bottom' }}
  105. tooltipProps={{ className: 'tooltip-wide' }}
  106. >
  107. {toggle}
  108. </OLTooltip>
  109. )
  110. }
  111. return toggle
  112. }
  113. export default memo(EditorSwitch)