Преглед изворни кода

Merge pull request #6779 from overleaf/ae-cm-editor-switch

[web] [cm6] Add a three-way switch for editor choice

GitOrigin-RevId: fff788ddad8d10488e8446de7f1503702da0985f
Brian Gough пре 4 година
родитељ
комит
fde4f72adf

+ 1 - 0
services/web/app/views/project/editor.pug

@@ -145,6 +145,7 @@ block append meta
 	meta(name="ol-showPdfDetach" data-type="boolean" content=showPdfDetach)
 	meta(name="ol-debugPdfDetach" data-type="boolean" content=debugPdfDetach)
 	meta(name="ol-showNewPdfPreview" data-type="boolean" content=showNewPdfPreview)
+	meta(name="ol-showNewSourceEditorOption" data-type="boolean" content=showNewSourceEditorOption)
 	meta(name="ol-enablePdfCaching" data-type="boolean" content=enablePdfCaching)
 	meta(name="ol-trackPdfDownload" data-type="boolean" content=trackPdfDownload)
 	meta(name="ol-resetServiceWorker" data-type="boolean" content=resetServiceWorker)

+ 104 - 0
services/web/frontend/js/features/source-editor/components/editor-switch.js

@@ -0,0 +1,104 @@
+import { memo, useCallback, useMemo } from 'react'
+import useScopeValue from '../../../shared/hooks/use-scope-value'
+import BetaBadge from '../../../shared/components/beta-badge'
+
+function EditorSwitch() {
+  const [newSourceEditor, setNewSourceEditor] = useScopeValue(
+    'editor.newSourceEditor'
+  )
+  const [richText, setRichText] = useScopeValue('editor.showRichText')
+
+  const handleChange = useCallback(
+    event => {
+      const choice = event.target.value
+
+      switch (choice) {
+        case 'ace':
+          setRichText(false)
+          setNewSourceEditor(false)
+          break
+
+        case 'cm6':
+          setRichText(false)
+          setNewSourceEditor(true)
+          break
+
+        case 'rich-text':
+          setRichText(true)
+          break
+      }
+    },
+    [setRichText, setNewSourceEditor]
+  )
+
+  const tooltip = useMemo(() => {
+    return {
+      id: 'editor-switch-tooltip',
+      placement: 'bottom',
+      className: 'tooltip-wide',
+      text: (
+        <>
+          We are upgrading the source editor. Please test it by selecting
+          "Source (Beta)".
+          <br />
+          <br />
+          Click to learn more and give feedback
+        </>
+      ),
+    }
+  }, [])
+
+  return (
+    <div className="editor-toggle-switch">
+      <BetaBadge tooltip={tooltip} url="https://forms.gle/GmSs6odZRKRp3VX98" />
+
+      <fieldset className="toggle-switch">
+        <legend className="sr-only">Editor mode.</legend>
+
+        <input
+          type="radio"
+          name="editor"
+          value="cm6"
+          id="editor-switch-cm6"
+          className="toggle-switch-input"
+          checked={!richText && !!newSourceEditor}
+          onChange={handleChange}
+        />
+        <label htmlFor="editor-switch-cm6" className="toggle-switch-label">
+          <span>Source (Beta)</span>
+        </label>
+
+        <input
+          type="radio"
+          name="editor"
+          value="ace"
+          id="editor-switch-ace"
+          className="toggle-switch-input"
+          checked={!richText && !newSourceEditor}
+          onChange={handleChange}
+        />
+        <label htmlFor="editor-switch-ace" className="toggle-switch-label">
+          <span>Source</span>
+        </label>
+
+        <input
+          type="radio"
+          name="editor"
+          value="rich-text"
+          id="editor-switch-rich-text"
+          className="toggle-switch-input"
+          checked={!!richText}
+          onChange={handleChange}
+        />
+        <label
+          htmlFor="editor-switch-rich-text"
+          className="toggle-switch-label"
+        >
+          <span>Rich Text</span>
+        </label>
+      </fieldset>
+    </div>
+  )
+}
+
+export default memo(EditorSwitch)

+ 6 - 0
services/web/frontend/js/features/source-editor/controllers/editor-switch-controller.js

@@ -0,0 +1,6 @@
+import App from '../../../base'
+import { react2angular } from 'react2angular'
+import EditorSwitch from '../components/editor-switch'
+import { rootContext } from '../../../shared/context/root-context'
+
+App.component('editorSwitch', react2angular(rootContext.use(EditorSwitch)))

+ 1 - 0
services/web/frontend/js/ide.js

@@ -68,6 +68,7 @@ import './shared/context/controllers/root-context-controller'
 import './features/editor-navigation-toolbar/controllers/editor-navigation-toolbar-controller'
 import './features/pdf-preview/controllers/pdf-preview-controller'
 import './features/share-project-modal/controllers/react-share-project-modal-controller'
+import './features/source-editor/controllers/editor-switch-controller'
 import getMeta from './utils/meta'
 
 App.controller(

+ 6 - 0
services/web/frontend/js/ide/editor/EditorManager.js

@@ -20,6 +20,7 @@ import './directives/aceEditor'
 import './directives/toggleSwitch'
 import './controllers/SavingNotificationController'
 import './controllers/CompileButton'
+import getMeta from '../../utils/meta'
 let EditorManager
 
 export default EditorManager = (function () {
@@ -139,6 +140,11 @@ export default EditorManager = (function () {
     }
 
     newSourceEditor() {
+      // only use the new source editor if the option to switch is available
+      if (!getMeta('ol-showNewSourceEditorOption')) {
+        return false
+      }
+
       return (
         this.localStorage(`editor.source_editor.${this.$scope.project_id}`) ===
         'cm6'

+ 7 - 2
services/web/frontend/js/shared/components/beta-badge.js

@@ -5,7 +5,11 @@ export default function BetaBadge({ tooltip, url = '/beta/participate' }) {
   return (
     <OverlayTrigger
       placement={tooltip.placement || 'bottom'}
-      overlay={<Tooltip id={tooltip.id}>{tooltip.text}</Tooltip>}
+      overlay={
+        <Tooltip id={tooltip.id} className={tooltip.className}>
+          {tooltip.text}
+        </Tooltip>
+      }
       delayHide={100}
     >
       <a
@@ -23,8 +27,9 @@ export default function BetaBadge({ tooltip, url = '/beta/participate' }) {
 BetaBadge.propTypes = {
   tooltip: PropTypes.exact({
     id: PropTypes.string.isRequired,
-    text: PropTypes.string.isRequired,
+    text: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired,
     placement: PropTypes.string,
+    className: PropTypes.string,
   }),
   url: PropTypes.string,
 }

+ 19 - 0
services/web/frontend/stories/editor-switch.stories.js

@@ -0,0 +1,19 @@
+import EditorSwitch from '../js/features/source-editor/components/editor-switch'
+import { setupContext } from './fixtures/context'
+import { withContextRoot } from './utils/with-context-root'
+
+export default {
+  title: 'Editor Switch',
+  component: EditorSwitch,
+}
+
+setupContext()
+
+export const Switcher = () => {
+  return withContextRoot(<EditorSwitch />, {
+    editor: {
+      richText: false,
+      newSourceEditor: false,
+    },
+  })
+}

+ 30 - 0
services/web/frontend/stylesheets/app/editor/toolbar.less

@@ -317,6 +317,36 @@
   }
 }
 
+.editor-toggle-switch {
+  display: flex;
+  align-items: center;
+
+  .toggle-switch {
+    margin-left: 5px;
+    border: 1px solid #cdd0d6;
+  }
+
+  .toggle-switch-label span {
+    padding: 1px 6px;
+    background: none;
+    transition: background 0.12s ease-out;
+    border-right: 1px solid #cdd0d6;
+  }
+
+  .toggle-switch-label:first-of-type span {
+    padding-left: 8px;
+  }
+
+  .toggle-switch-label:last-of-type span {
+    padding-right: 8px;
+    border-right: none;
+  }
+
+  .toggle-switch-input:checked + .toggle-switch-label {
+    background: @toggle-switch-highlight-color;
+  }
+}
+
 /**************************************
 	Formatting buttons
 ***************************************/

+ 4 - 0
services/web/frontend/stylesheets/components/beta-badges.less

@@ -44,3 +44,7 @@
     content: 'β';
   }
 }
+
+.tooltip-wide .tooltip-inner {
+  min-width: 275px;
+}