editor-switch-legacy.tsx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. import { ChangeEvent, FC, memo, useCallback } from 'react'
  2. import useScopeValue from '../../../shared/hooks/use-scope-value'
  3. import Tooltip from '../../../shared/components/tooltip'
  4. import { sendMB } from '../../../infrastructure/event-tracking'
  5. import getMeta from '../../../utils/meta'
  6. import isValidTeXFile from '../../../main/is-valid-tex-file'
  7. import { useTranslation } from 'react-i18next'
  8. import SplitTestBadge from '../../../shared/components/split-test-badge'
  9. function Badge() {
  10. const content = (
  11. <>
  12. Overleaf has upgraded the source editor.
  13. <br />
  14. You can still use the old editor by selecting "Source (legacy)".
  15. <br />
  16. <br />
  17. Click to learn more and give feedback
  18. </>
  19. )
  20. return (
  21. <Tooltip
  22. id="editor-switch"
  23. description={content}
  24. overlayProps={{
  25. placement: 'bottom',
  26. delayHide: 100,
  27. }}
  28. tooltipProps={{ className: 'tooltip-wide' }}
  29. >
  30. <a
  31. href="https://forms.gle/GmSs6odZRKRp3VX98"
  32. target="_blank"
  33. rel="noopener noreferrer"
  34. className="info-badge"
  35. >
  36. <span className="sr-only">{content}</span>
  37. </a>
  38. </Tooltip>
  39. )
  40. }
  41. const showLegacySourceEditor: boolean = getMeta('ol-showLegacySourceEditor')
  42. function EditorSwitch() {
  43. const { t } = useTranslation()
  44. const [newSourceEditor, setNewSourceEditor] = useScopeValue(
  45. 'editor.newSourceEditor'
  46. )
  47. const [richText, setRichText] = useScopeValue('editor.showRichText')
  48. const [visual, setVisual] = useScopeValue('editor.showVisual')
  49. const [docName] = useScopeValue('editor.open_doc_name')
  50. const richTextAvailable = isValidTeXFile(docName)
  51. const richTextOrVisual = richText || (richTextAvailable && visual)
  52. const handleChange = useCallback(
  53. event => {
  54. const editorType = event.target.value
  55. switch (editorType) {
  56. case 'ace':
  57. setRichText(false)
  58. setVisual(false)
  59. setNewSourceEditor(false)
  60. break
  61. case 'cm6':
  62. setRichText(false)
  63. setVisual(false)
  64. setNewSourceEditor(true)
  65. break
  66. case 'rich-text':
  67. if (getMeta('ol-richTextVariant') === 'cm6') {
  68. setRichText(false)
  69. setVisual(true)
  70. setNewSourceEditor(true)
  71. } else {
  72. setRichText(true)
  73. setVisual(false)
  74. }
  75. break
  76. }
  77. sendMB('editor-switch-change', { editorType })
  78. },
  79. [setRichText, setVisual, setNewSourceEditor]
  80. )
  81. return (
  82. <div className="editor-toggle-switch">
  83. {showLegacySourceEditor ? <Badge /> : null}
  84. <fieldset className="toggle-switch">
  85. <legend className="sr-only">Editor mode.</legend>
  86. <input
  87. type="radio"
  88. name="editor"
  89. value="cm6"
  90. id="editor-switch-cm6"
  91. className="toggle-switch-input"
  92. checked={!richTextOrVisual && !!newSourceEditor}
  93. onChange={handleChange}
  94. />
  95. <label htmlFor="editor-switch-cm6" className="toggle-switch-label">
  96. <span>{t('code_editor')}</span>
  97. </label>
  98. {showLegacySourceEditor ? (
  99. <>
  100. <input
  101. type="radio"
  102. name="editor"
  103. value="ace"
  104. id="editor-switch-ace"
  105. className="toggle-switch-input"
  106. checked={!richTextOrVisual && !newSourceEditor}
  107. onChange={handleChange}
  108. />
  109. <label htmlFor="editor-switch-ace" className="toggle-switch-label">
  110. <span>Source (legacy)</span>
  111. </label>
  112. </>
  113. ) : null}
  114. <RichTextToggle
  115. checked={!!richTextOrVisual}
  116. disabled={!richTextAvailable}
  117. handleChange={handleChange}
  118. />
  119. </fieldset>
  120. {!!richTextOrVisual && (
  121. <SplitTestBadge splitTestName="rich-text" displayOnVariants={['cm6']} />
  122. )}
  123. </div>
  124. )
  125. }
  126. const RichTextToggle: FC<{
  127. checked: boolean
  128. disabled: boolean
  129. handleChange: (event: ChangeEvent<HTMLInputElement>) => void
  130. }> = ({ checked, disabled, handleChange }) => {
  131. const { t } = useTranslation()
  132. const toggle = (
  133. <span>
  134. <input
  135. type="radio"
  136. name="editor"
  137. value="rich-text"
  138. id="editor-switch-rich-text"
  139. className="toggle-switch-input"
  140. checked={checked}
  141. onChange={handleChange}
  142. disabled={disabled}
  143. />
  144. <label htmlFor="editor-switch-rich-text" className="toggle-switch-label">
  145. <span>{t('visual_editor')}</span>
  146. </label>
  147. </span>
  148. )
  149. if (disabled) {
  150. return (
  151. <Tooltip
  152. description={t('the_visual_editor_is_only_available_for_tex_files')}
  153. id="rich-text-toggle-tooltip"
  154. overlayProps={{ placement: 'bottom' }}
  155. tooltipProps={{ className: 'tooltip-wide' }}
  156. >
  157. {toggle}
  158. </Tooltip>
  159. )
  160. }
  161. return toggle
  162. }
  163. export default memo(EditorSwitch)