mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-12 13:13:45 +01:00
* Rename dropdown primitives to OL-prefixed components * Migrate all consumers to OL-prefixed dropdown components * Migrate subscription dropdown shells to OL components * Update pdf-preview story to OL-prefixed dropdown components * Migrate navbar and spelling dropdowns to OL components * Move dropdown primitives to ol/ol-dropdown-menu, remove BS5 import aliases * Rename dropdown prop types to OL-prefixed, clean up BS5 import aliases * Widen OLDropdownItem/Toggle/Menu props to include AriaAttributes and data-* GitOrigin-RevId: 0babce46867849b3f176bac8cf1652dc76fd34da
235 lines
7.1 KiB
TypeScript
235 lines
7.1 KiB
TypeScript
import { forwardRef, memo, MouseEventHandler } from 'react'
|
|
import {
|
|
OLDropdown,
|
|
OLDropdownMenu,
|
|
OLDropdownToggle,
|
|
} from '@/shared/components/ol/ol-dropdown-menu'
|
|
import DropdownMenuItem from '@/shared/components/dropdown/dropdown-menu-item'
|
|
import MaterialIcon from '@/shared/components/material-icon'
|
|
import classNames from 'classnames'
|
|
import { useTrackChangesStateActionsContext } from '../context/track-changes-state-context'
|
|
import { useUserContext } from '@/shared/context/user-context'
|
|
import { useTranslation } from 'react-i18next'
|
|
import { usePermissionsContext } from '@/features/ide-react/context/permissions-context'
|
|
import usePersistedState from '@/shared/hooks/use-persisted-state'
|
|
import { sendMB } from '@/infrastructure/event-tracking'
|
|
import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context'
|
|
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 ReviewModeDropdownItems() {
|
|
const { t } = useTranslation()
|
|
const user = useUserContext()
|
|
const { saveTrackChangesForCurrentUser, saveTrackChanges } =
|
|
useTrackChangesStateActionsContext()
|
|
const mode = useTrackingChangesMode()
|
|
const { permissionsLevel } = useIdeReactContext()
|
|
const { write, trackedWrite } = usePermissionsContext()
|
|
const { features } = useProjectContext()
|
|
const { setUpgradeTrackChangesModal } = useEditorContext()
|
|
const showViewOption = permissionsLevel === 'readOnly'
|
|
const view = useCodeMirrorViewContext()
|
|
|
|
return (
|
|
<>
|
|
<DropdownMenuItem
|
|
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')}
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem
|
|
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')}
|
|
</DropdownMenuItem>
|
|
{showViewOption && (
|
|
<DropdownMenuItem
|
|
description={t('can_view_content')}
|
|
leadingIcon="visibility"
|
|
active={mode === 'view'}
|
|
>
|
|
{t('viewing')}
|
|
</DropdownMenuItem>
|
|
)}
|
|
</>
|
|
)
|
|
}
|
|
|
|
function ReviewModeSwitcher() {
|
|
const isToolbarMigration = useFeatureFlag('writefull-toolbar-migration')
|
|
|
|
if (isToolbarMigration) {
|
|
return (
|
|
<OLDropdown className="review-mode-switcher" align="end">
|
|
<OLDropdownToggle
|
|
as={ModeSwitcherToggleButton}
|
|
id="review-mode-switcher"
|
|
/>
|
|
<OLDropdownMenu
|
|
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 />
|
|
</OLDropdownMenu>
|
|
</OLDropdown>
|
|
)
|
|
}
|
|
|
|
// 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">
|
|
<OLDropdown className="review-mode-switcher" align="end">
|
|
<OLDropdownToggle
|
|
as={ModeSwitcherToggleButton}
|
|
id="review-mode-switcher"
|
|
/>
|
|
<OLDropdownMenu flip={false}>
|
|
<ReviewModeDropdownItems />
|
|
</OLDropdownMenu>
|
|
</OLDropdown>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
const ModeSwitcherToggleButton = forwardRef<
|
|
HTMLButtonElement,
|
|
{ onClick: MouseEventHandler<HTMLButtonElement>; 'aria-expanded': boolean }
|
|
>(({ onClick, 'aria-expanded': ariaExpanded }, ref) => {
|
|
const { t } = useTranslation()
|
|
const mode = useTrackingChangesMode()
|
|
|
|
if (mode === 'edit') {
|
|
return (
|
|
<ModeSwitcherToggleButtonContent
|
|
ref={ref}
|
|
onClick={onClick}
|
|
className="editing"
|
|
iconType="edit"
|
|
label={t('editing')}
|
|
ariaExpanded={ariaExpanded}
|
|
/>
|
|
)
|
|
} else if (mode === 'review') {
|
|
return (
|
|
<ModeSwitcherToggleButtonContent
|
|
ref={ref}
|
|
onClick={onClick}
|
|
className="reviewing"
|
|
iconType="rate_review"
|
|
label={t('reviewing')}
|
|
ariaExpanded={ariaExpanded}
|
|
/>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<ModeSwitcherToggleButtonContent
|
|
ref={ref}
|
|
onClick={onClick}
|
|
className="viewing"
|
|
iconType="visibility"
|
|
label={t('viewing')}
|
|
ariaExpanded={ariaExpanded}
|
|
/>
|
|
)
|
|
})
|
|
|
|
const ModeSwitcherToggleButtonContent = forwardRef<
|
|
HTMLButtonElement,
|
|
{
|
|
onClick: MouseEventHandler<HTMLButtonElement>
|
|
className: string
|
|
iconType: string
|
|
label: string
|
|
ariaExpanded: boolean
|
|
}
|
|
>(({ onClick, className, iconType, label, ariaExpanded }, ref) => {
|
|
const [isFirstTimeUsed, setIsFirstTimeUsed] = usePersistedState(
|
|
`modeSwitcherFirstTimeUsed`,
|
|
true
|
|
)
|
|
|
|
return (
|
|
<button
|
|
className={classNames('review-mode-switcher-toggle-button', className, {
|
|
'review-mode-switcher-toggle-button-expanded': isFirstTimeUsed,
|
|
})}
|
|
ref={ref}
|
|
onClick={event => {
|
|
setIsFirstTimeUsed(false)
|
|
onClick(event)
|
|
}}
|
|
aria-expanded={ariaExpanded}
|
|
aria-label={label}
|
|
>
|
|
<MaterialIcon type={iconType} />
|
|
<div className="review-mode-switcher-toggle-label">{label}</div>
|
|
<MaterialIcon type="keyboard_arrow_down" />
|
|
</button>
|
|
)
|
|
})
|
|
|
|
ModeSwitcherToggleButton.displayName = 'ModeSwitcherToggleButton'
|
|
ModeSwitcherToggleButtonContent.displayName = 'ModeSwitcherToggleButtonContent'
|
|
|
|
export default memo(ReviewModeSwitcher)
|