mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-12 21:23:45 +01:00
[web] Add a focus mode to the editor (#32245)
* Basic focus mode implementation * Exit button * Toggle between editor pdf mode * Keep the rail and file tree mounted for editor tabs to work properly * Hide Switch to editor button in focus mode * Show shortcuts in layout menu * Gate the feature behind a focus-mode feature flag * Add split test badges in the focus mode dropdown item and top bar * Add analytics tracking for focusMode changes * Cleanup leftover IdeView value * Allow passing focusMode value in layout test provider * Remove redundant listener for focus mode shortcut * Update focus mode and full project search listeners to use the event key * Post-rebase cleanup --------- Co-authored-by: Anna Fields <acfields11@gmail.com> GitOrigin-RevId: 207f8d136fa6662558c474cca4a867a9842191e7
This commit is contained in:
committed by
Copybot
co-authored by
Anna Fields
parent
757735b075
commit
c8a3bdd13f
+40
-2
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
DropdownItem,
|
||||
DropdownHeader,
|
||||
DropdownDivider,
|
||||
} from '@/shared/components/dropdown/dropdown-menu'
|
||||
import {
|
||||
IdeLayout,
|
||||
@@ -14,8 +15,16 @@ import OLSpinner from '@/shared/components/ol/ol-spinner'
|
||||
import { isMac } from '@/shared/utils/os'
|
||||
import { Shortcut } from '@/shared/components/shortcut'
|
||||
import classNames from 'classnames'
|
||||
import { useEditorAnalytics } from '@/shared/hooks/use-editor-analytics'
|
||||
import { useFeatureFlag } from '@/shared/context/split-test-context'
|
||||
import SplitTestBadge from '@/shared/components/split-test-badge'
|
||||
|
||||
type LayoutOption = 'sideBySide' | 'editorOnly' | 'pdfOnly' | 'detachedPdf'
|
||||
type LayoutOption =
|
||||
| 'sideBySide'
|
||||
| 'editorOnly'
|
||||
| 'pdfOnly'
|
||||
| 'detachedPdf'
|
||||
| 'focusMode'
|
||||
|
||||
const getActiveLayoutOption = ({
|
||||
pdfLayout,
|
||||
@@ -45,7 +54,6 @@ const getActiveLayoutOption = ({
|
||||
if (pdfLayout === 'sideBySide') {
|
||||
return 'sideBySide'
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -93,12 +101,14 @@ const shortcuts: Record<LayoutOption, string[] | null> = isMac
|
||||
pdfOnly: ['⌃', '⌘', '→'],
|
||||
sideBySide: ['⌃', '⌘', '↓'],
|
||||
detachedPdf: ['⌃', '⌘', '↑'],
|
||||
focusMode: ['⌘', '⇧', 'M'],
|
||||
}
|
||||
: {
|
||||
editorOnly: null,
|
||||
pdfOnly: null,
|
||||
sideBySide: null,
|
||||
detachedPdf: null,
|
||||
focusMode: ['⌃', '⇧', 'M'],
|
||||
}
|
||||
|
||||
export default function ChangeLayoutOptions() {
|
||||
@@ -109,9 +119,13 @@ export default function ChangeLayoutOptions() {
|
||||
pdfLayout,
|
||||
handleChangeLayout,
|
||||
handleDetach,
|
||||
focusMode,
|
||||
setFocusMode,
|
||||
} = useLayoutContext()
|
||||
|
||||
const { t } = useTranslation()
|
||||
const { sendEvent } = useEditorAnalytics()
|
||||
const focusModeEnabled = useFeatureFlag('focus-mode')
|
||||
|
||||
const detachable = 'BroadcastChannel' in window
|
||||
|
||||
@@ -168,6 +182,30 @@ export default function ChangeLayoutOptions() {
|
||||
>
|
||||
{t('open_pdf_in_separate_tab')}
|
||||
</LayoutDropdownItem>
|
||||
{focusModeEnabled && (
|
||||
<>
|
||||
<DropdownDivider />
|
||||
<LayoutDropdownItem
|
||||
onClick={() => {
|
||||
setFocusMode(!focusMode)
|
||||
sendEvent('focus-mode-toggle', { focusMode: !focusMode })
|
||||
}}
|
||||
active={focusMode}
|
||||
leadingIcon="self_improvement"
|
||||
trailingIcon={
|
||||
<span className="d-flex align-items-center gap-2">
|
||||
<SplitTestBadge
|
||||
splitTestName="focus-mode"
|
||||
displayOnVariants={['enabled']}
|
||||
/>
|
||||
{shortcuts.focusMode && <Shortcut keys={shortcuts.focusMode} />}
|
||||
</span>
|
||||
}
|
||||
>
|
||||
{t('focus_mode')}
|
||||
</LayoutDropdownItem>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -16,12 +16,16 @@ import UpgradeButton from './upgrade-button'
|
||||
import getMeta from '@/utils/meta'
|
||||
import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context'
|
||||
import { useFeatureFlag } from '@/shared/context/split-test-context'
|
||||
import OLIconButton from '@/shared/components/ol/ol-icon-button'
|
||||
import OLTooltip from '@/shared/components/ol/ol-tooltip'
|
||||
import SplitTestBadge from '@/shared/components/split-test-badge'
|
||||
|
||||
const [publishModalModules] = importOverleafModules('publishModal')
|
||||
const SubmitProjectButton = publishModalModules?.import.NewPublishToolbarButton
|
||||
|
||||
export const Toolbar = () => {
|
||||
const { view, restoreView } = useLayoutContext()
|
||||
const { view, restoreView, focusMode, setFocusMode, pdfLayout, setView } =
|
||||
useLayoutContext()
|
||||
const { cobranding, isRestrictedTokenMember } = useEditorContext()
|
||||
const { permissionsLevel } = useIdeReactContext()
|
||||
const showUpgradePrompt = getMeta('ol-showUpgradePrompt')
|
||||
@@ -38,6 +42,72 @@ export const Toolbar = () => {
|
||||
restoreView()
|
||||
}, [restoreView])
|
||||
|
||||
const handleExitFocusMode = useCallback(() => {
|
||||
setFocusMode(false)
|
||||
eventTracking.sendMB('focus-mode-exit')
|
||||
}, [setFocusMode])
|
||||
|
||||
const handleSwitchView = useCallback(() => {
|
||||
const newView = view === 'pdf' ? 'editor' : 'pdf'
|
||||
setView(newView)
|
||||
eventTracking.sendMB('focus-mode-switch-view', { view: newView })
|
||||
}, [view, setView])
|
||||
|
||||
if (focusMode) {
|
||||
const showViewSwitcher = pdfLayout === 'flat'
|
||||
const switchTooltip =
|
||||
view === 'pdf' ? t('switch_to_editor') : t('switch_to_pdf')
|
||||
const switchIcon = view === 'pdf' ? 'edit' : 'picture_as_pdf'
|
||||
|
||||
return (
|
||||
<nav className="ide-redesign-toolbar" aria-label={t('project_actions')}>
|
||||
<div className="ide-redesign-toolbar-menu">
|
||||
<ToolbarLogos cobranding={cobranding} />
|
||||
</div>
|
||||
<ToolbarProjectTitle />
|
||||
<div className="ide-redesign-toolbar-actions">
|
||||
<div className="ide-redesign-toolbar-button-container">
|
||||
<SplitTestBadge
|
||||
splitTestName="focus-mode"
|
||||
displayOnVariants={['enabled']}
|
||||
/>
|
||||
</div>
|
||||
{showViewSwitcher && (
|
||||
<div className="ide-redesign-toolbar-button-container">
|
||||
<OLTooltip
|
||||
id="tooltip-switch-view"
|
||||
description={switchTooltip}
|
||||
overlayProps={{ delay: 0, placement: 'bottom' }}
|
||||
>
|
||||
<OLIconButton
|
||||
icon={switchIcon}
|
||||
className="ide-redesign-toolbar-button-subdued ide-redesign-toolbar-button-icon"
|
||||
onClick={handleSwitchView}
|
||||
accessibilityLabel={switchTooltip}
|
||||
/>
|
||||
</OLTooltip>
|
||||
</div>
|
||||
)}
|
||||
<ChangeLayoutButton />
|
||||
<div className="ide-redesign-toolbar-button-container">
|
||||
<OLTooltip
|
||||
id="tooltip-exit-focus-mode"
|
||||
description={t('exit_focus_mode')}
|
||||
overlayProps={{ delay: 0, placement: 'bottom' }}
|
||||
>
|
||||
<OLIconButton
|
||||
icon="close"
|
||||
className="ide-redesign-toolbar-button-subdued ide-redesign-toolbar-button-icon"
|
||||
onClick={handleExitFocusMode}
|
||||
accessibilityLabel={t('exit_focus_mode')}
|
||||
/>
|
||||
</OLTooltip>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
if (view === 'history') {
|
||||
return (
|
||||
<nav className="ide-redesign-toolbar" aria-label={t('project_actions')}>
|
||||
|
||||
Reference in New Issue
Block a user