Просмотр исходного кода

[WEB] Move Review Toggle into the toolbar - 2 (#34578)

* wip of adding Review Toggle into the toolbar

* renaming to isToolbarMigration

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* removing extra comments

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
GitOrigin-RevId: 2c0b54ae983a32c083def8e9e64ddd0fb7ddf334
Davinder Singh 1 месяц назад
Родитель
Сommit
6613ef2f18

+ 105 - 71
services/web/frontend/js/features/review-panel/components/review-mode-switcher.tsx

@@ -18,8 +18,9 @@ import { useProjectContext } from '@/shared/context/project-context'
 import { useCodeMirrorViewContext } from '@/features/source-editor/components/codemirror-context'
 import { useEditorContext } from '@/shared/context/editor-context'
 import { useTrackingChangesMode } from '@/shared/hooks/use-tracking-changes-mode'
+import { useFeatureFlag } from '@/shared/context/split-test-context'
 
-function ReviewModeSwitcher() {
+function ReviewModeDropdownItems() {
   const { t } = useTranslation()
   const user = useUserContext()
   const { saveTrackChangesForCurrentUser, saveTrackChanges } =
@@ -32,6 +33,108 @@ function ReviewModeSwitcher() {
   const showViewOption = permissionsLevel === 'readOnly'
   const view = useCodeMirrorViewContext()
 
+  return (
+    <>
+      <OLDropdownMenuItem
+        disabled={!write}
+        onClick={() => {
+          if (mode === 'edit') {
+            view.focus()
+            return
+          }
+          sendMB('editing-mode-change', {
+            role: permissionsLevel,
+            previousMode: mode,
+            newMode: 'edit',
+          })
+          if (user?.id) {
+            saveTrackChangesForCurrentUser(false)
+          } else {
+            saveTrackChanges({ on_for_guests: false })
+          }
+          view.focus()
+        }}
+        description={t('edit_content_directly')}
+        leadingIcon="edit"
+        active={write && mode === 'edit'}
+      >
+        {t('editing')}
+      </OLDropdownMenuItem>
+      <OLDropdownMenuItem
+        disabled={permissionsLevel === 'readOnly'}
+        onClick={() => {
+          if (mode === 'review') {
+            view.focus()
+            return
+          }
+          if (!features.trackChanges) {
+            setUpgradeTrackChangesModal({
+              show: true,
+              location: 'review-switcher',
+            })
+          } else {
+            sendMB('editing-mode-change', {
+              role: permissionsLevel,
+              previousMode: mode,
+              newMode: 'review',
+            })
+            if (user?.id) {
+              saveTrackChangesForCurrentUser(true)
+            } else {
+              saveTrackChanges({ on_for_guests: true })
+            }
+            view.focus()
+          }
+        }}
+        description={
+          permissionsLevel === 'review' && !trackedWrite
+            ? t('comment_only')
+            : t('edits_become_suggestions')
+        }
+        leadingIcon="rate_review"
+        active={trackedWrite && mode === 'review'}
+      >
+        {t('reviewing')}
+      </OLDropdownMenuItem>
+      {showViewOption && (
+        <OLDropdownMenuItem
+          description={t('can_view_content')}
+          leadingIcon="visibility"
+          active={mode === 'view'}
+        >
+          {t('viewing')}
+        </OLDropdownMenuItem>
+      )}
+    </>
+  )
+}
+
+function ReviewModeSwitcher() {
+  const isToolbarMigration = useFeatureFlag('writefull-toolbar-migration')
+
+  if (isToolbarMigration) {
+    return (
+      <Dropdown className="review-mode-switcher" align="end">
+        <DropdownToggle
+          as={ModeSwitcherToggleButton}
+          id="review-mode-switcher"
+        />
+        <DropdownMenu
+          flip={false}
+          popperConfig={{ strategy: 'fixed' }}
+          // renderOnMount ensures the menu is in the DOM on mount so Popper.js
+          // can calculate its position correctly when using strategy: 'fixed'
+          // inside the toolbar's stacking context
+          renderOnMount
+        >
+          <ReviewModeDropdownItems />
+        </DropdownMenu>
+      </Dropdown>
+    )
+  }
+
+  // Portal variant (default): sticky container keeps the button floating
+  // over the editor at top-right of the scroll area.
   return (
     <div className="review-mode-switcher-container">
       <Dropdown className="review-mode-switcher" align="end">
@@ -40,76 +143,7 @@ function ReviewModeSwitcher() {
           id="review-mode-switcher"
         />
         <DropdownMenu flip={false}>
-          <OLDropdownMenuItem
-            disabled={!write}
-            onClick={() => {
-              if (mode === 'edit') {
-                view.focus()
-                return
-              }
-              sendMB('editing-mode-change', {
-                role: permissionsLevel,
-                previousMode: mode,
-                newMode: 'edit',
-              })
-              if (user?.id) {
-                saveTrackChangesForCurrentUser(false)
-              } else {
-                saveTrackChanges({ on_for_guests: false })
-              }
-              view.focus()
-            }}
-            description={t('edit_content_directly')}
-            leadingIcon="edit"
-            active={write && mode === 'edit'}
-          >
-            {t('editing')}
-          </OLDropdownMenuItem>
-          <OLDropdownMenuItem
-            disabled={permissionsLevel === 'readOnly'}
-            onClick={() => {
-              if (mode === 'review') {
-                view.focus()
-                return
-              }
-              if (!features.trackChanges) {
-                setUpgradeTrackChangesModal({
-                  show: true,
-                  location: 'review-switcher',
-                })
-              } else {
-                sendMB('editing-mode-change', {
-                  role: permissionsLevel,
-                  previousMode: mode,
-                  newMode: 'review',
-                })
-                if (user?.id) {
-                  saveTrackChangesForCurrentUser(true)
-                } else {
-                  saveTrackChanges({ on_for_guests: true })
-                }
-                view.focus()
-              }
-            }}
-            description={
-              permissionsLevel === 'review' && !trackedWrite
-                ? t('comment_only')
-                : t('edits_become_suggestions')
-            }
-            leadingIcon="rate_review"
-            active={trackedWrite && mode === 'review'}
-          >
-            {t('reviewing')}
-          </OLDropdownMenuItem>
-          {showViewOption && (
-            <OLDropdownMenuItem
-              description={t('can_view_content')}
-              leadingIcon="visibility"
-              active={mode === 'view'}
-            >
-              {t('viewing')}
-            </OLDropdownMenuItem>
-          )}
+          <ReviewModeDropdownItems />
         </DropdownMenu>
       </Dropdown>
     </div>

+ 3 - 1
services/web/frontend/js/features/review-panel/components/review-panel-container.tsx

@@ -5,11 +5,13 @@ import ReviewPanel from './review-panel'
 import ReviewModeSwitcher from './review-mode-switcher'
 import useReviewPanelLayout from '../hooks/use-review-panel-layout'
 import { useLayoutContext } from '@/shared/context/layout-context'
+import { useFeatureFlag } from '@/shared/context/split-test-context'
 
 function ReviewPanelContainer() {
   const view = useCodeMirrorViewContext()
   const { showPanel, mini } = useReviewPanelLayout()
   const { focusMode } = useLayoutContext()
+  const isToolbarMigration = useFeatureFlag('writefull-toolbar-migration')
 
   if (!view) {
     return null
@@ -17,7 +19,7 @@ function ReviewPanelContainer() {
 
   return ReactDOM.createPortal(
     <>
-      {!focusMode && <ReviewModeSwitcher />}
+      {!focusMode && !isToolbarMigration && <ReviewModeSwitcher />}
       {showPanel && <ReviewPanel mini={mini} />}
     </>,
     view.scrollDOM

+ 11 - 0
services/web/frontend/js/features/source-editor/components/codemirror-toolbar.tsx

@@ -18,6 +18,7 @@ import useDropdown from '../../../shared/hooks/use-dropdown'
 import { getPanel } from '@codemirror/view'
 import { createToolbarPanel } from '../extensions/toolbar/toolbar-panel'
 import EditorSwitch from './editor-switch'
+import ReviewModeSwitcher from '@/features/review-panel/components/review-mode-switcher'
 import SwitchToPDFButton from './switch-to-pdf-button'
 import { DetacherSynctexControl } from '../../pdf-preview/components/detach-synctex-control'
 import DetachCompileButtonWrapper from '../../pdf-preview/components/detach-compile-button-wrapper'
@@ -33,6 +34,7 @@ import Breadcrumbs from '@/features/source-editor/extensions/breadcrumbs'
 import classNames from 'classnames'
 import { useUserSettingsContext } from '@/shared/context/user-settings-context'
 import { useFeatureFlag } from '@/shared/context/split-test-context'
+import { useProjectContext } from '@/shared/context/project-context'
 import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
 import { useLayoutContext } from '@/shared/context/layout-context'
 import ReviewPanelHeaderBuffer from '@/features/review-panel/components/review-panel-header-buffer'
@@ -69,6 +71,8 @@ const Toolbar = memo(function Toolbar() {
     userSettings: { breadcrumbs },
   } = useUserSettingsContext()
   const visualPreviewEnabled = useFeatureFlag('visual-preview')
+  const isToolbarMigration = useFeatureFlag('writefull-toolbar-migration')
+  const { features } = useProjectContext()
   const { focusMode } = useLayoutContext()
 
   const [overflowed, setOverflowed] = useState(false)
@@ -234,6 +238,13 @@ const Toolbar = memo(function Toolbar() {
 
           <div className="ol-cm-toolbar-button-group ol-cm-toolbar-end">
             {!visualPreviewEnabled && <EditorSwitch />}
+            {/* trackChangesVisible controls provider/UI availability; trackChanges
+                (checked inside the switcher) controls the actual feature entitlement.
+                Users with trackChangesVisible:true but trackChanges:false see the
+                switcher and get an upgrade modal when clicking "Reviewing". */}
+            {isToolbarMigration && features.trackChangesVisible && (
+              <ReviewModeSwitcher />
+            )}
             {sourceEditorToolbarEndButtons.map(
               ({ import: { default: Component }, path }) => (
                 <Component key={path} />

+ 71 - 0
services/web/frontend/stylesheets/pages/editor/review-panel.scss

@@ -11,6 +11,27 @@ $rp-type-blue: #6b7797;
   --review-panel-width: 230px;
   --review-panel-width-mini: 24px;
   --review-panel-color-scheme: dark;
+  --review-mode-switcher-reviewing-bg: var(--yellow-10);
+  --review-mode-switcher-reviewing-hover-bg: var(--yellow-20);
+  --review-mode-switcher-reviewing-color: var(--yellow-60);
+
+  @include theme('default') {
+    .ide-redesign-main {
+      --review-panel-bg-color: var(--bg-dark-secondary);
+      --review-panel-color: var(--content-primary-dark);
+      --review-panel-color-secondary: var(--content-secondary-dark);
+      --review-panel-header-bg-color: var(--bg-dark-primary);
+      --review-panel-footer-bg-color: var(--bg-dark-primary);
+      --review-panel-entry-bg-color: var(--bg-dark-primary);
+      --review-panel-empty-state-bg-color: var(--bg-dark-primary);
+      --review-panel-button-hover-bg-color: var(--bg-dark-tertiary);
+      --review-panel-border-color: var(--border-divider-dark);
+      --review-panel-color-scheme: dark;
+      --review-mode-switcher-reviewing-bg: var(--yellow-70);
+      --review-mode-switcher-reviewing-hover-bg: var(--yellow-60);
+      --review-mode-switcher-reviewing-color: var(--yellow-10);
+    }
+  }
 }
 
 @include theme('light') {
@@ -752,3 +773,53 @@ del.review-panel-content-highlight {
     }
   }
 }
+
+.ol-cm-toolbar {
+  .review-mode-switcher {
+    // In the toolbar the component is in normal flex flow — no absolute positioning.
+    position: static;
+
+    &:hover,
+    &:focus-within {
+      .review-mode-switcher-toggle-button.editing {
+        background-color: var(--review-panel-button-hover-bg-color);
+      }
+
+      .review-mode-switcher-toggle-button.reviewing {
+        background-color: var(--review-mode-switcher-reviewing-hover-bg);
+      }
+
+      .review-mode-switcher-toggle-button.viewing {
+        background-color: var(--blue-20);
+      }
+
+      // In the toolbar the label text does not expand on hover (would shift other buttons)
+      .review-mode-switcher-toggle-label {
+        display: none;
+      }
+    }
+  }
+
+  .review-mode-switcher-toggle-button {
+    border-radius: var(--border-radius-base);
+    padding: var(--spacing-01) var(--spacing-02);
+    gap: var(--spacing-01);
+    height: auto;
+    cursor: pointer;
+
+    &:focus-visible {
+      outline: 2px solid var(--content-primary);
+      outline-offset: 2px;
+    }
+
+    &.editing {
+      background-color: var(--toolbar-alt-bg-color);
+      color: var(--toolbar-btn-color);
+    }
+
+    &.reviewing {
+      background-color: var(--review-mode-switcher-reviewing-bg);
+      color: var(--review-mode-switcher-reviewing-color);
+    }
+  }
+}

+ 6 - 0
services/web/frontend/stylesheets/pages/editor/toolbar.scss

@@ -295,6 +295,12 @@
   z-index: 10; // Prevent track changes showing over toolbar
 }
 
+// Isolate secondary toolbar rows so they paint below the main toolbar (z-index: 10)
+// and its dropdowns, which would otherwise be obscured due to DOM order.
+.ol-cm-toolbar-wrapper > .writefull {
+  isolation: isolate;
+}
+
 /**************************************
 	Toggle Switch
 ***************************************/