Add toggle for workbench position (#30873)

GitOrigin-RevId: 622e7e8ad2b5493d4c55abc8392fb050a629b015
This commit is contained in:
Alf Eaton
2026-01-30 11:30:10 +00:00
committed by Copybot
parent db70d2db25
commit 46435ab8b4
9 changed files with 77 additions and 22 deletions

View File

@@ -1117,6 +1117,8 @@
"more_editor_toolbar_item": "",
"more_info": "",
"more_options": "",
"move_to_the_left": "",
"move_to_the_right": "",
"my_library": "",
"n_items": "",
"n_items_plural": "",

View File

@@ -10,7 +10,7 @@ export default function RailPanelHeader({
onClose,
}: {
title: React.ReactNode
actions?: React.ReactNode[]
actions?: React.ReactElement
onClose?: () => void
}) {
const { t } = useTranslation()

View File

@@ -1,7 +1,7 @@
import OLTooltip from '@/shared/components/ol/ol-tooltip'
import MaterialIcon from '@/shared/components/material-icon'
import classNames from 'classnames'
import { forwardRef, ReactElement } from 'react'
import { ComponentProps, forwardRef, ReactElement } from 'react'
import { NavLink } from 'react-bootstrap'
import { RailElement } from '../../utils/rail-types'
@@ -9,13 +9,12 @@ const RailTab = forwardRef<
HTMLAnchorElement,
{
icon: RailElement['icon']
eventKey: string
eventKey?: string
open: boolean
indicator?: ReactElement
title: string
disabled?: boolean
}
>(({ icon, eventKey, open, indicator, title, disabled = false }, ref) => {
} & ComponentProps<'button'>
>(({ icon, eventKey, className, open, indicator, title, ...props }, ref) => {
return (
<OLTooltip
id={`rail-tab-tooltip-${eventKey}`}
@@ -23,12 +22,12 @@ const RailTab = forwardRef<
overlayProps={{ delay: 0, placement: 'right' }}
>
<NavLink
{...props}
ref={ref}
eventKey={eventKey}
className={classNames('ide-rail-tab-link', {
className={classNames('ide-rail-tab-link', className, {
'open-rail': open,
})}
disabled={disabled}
>
<RailTabIcon icon={icon} title={title} open={open} />
{indicator}

View File

@@ -32,6 +32,7 @@ import EditorTourThemeTooltip from '../editor-tour/editor-tour-theme-tooltip'
import EditorTourGotQuestionsTooltip from '../editor-tour/editor-tour-got-questions'
import { shouldIncludeElement } from '../../utils/rail-utils'
import { useEditorContext } from '@/shared/context/editor-context'
import useEventListener from '@/shared/hooks/use-event-listener'
const moduleRailEntries = (
importOverleafModules('railEntries') as {
@@ -39,6 +40,7 @@ const moduleRailEntries = (
path: string
}[]
).map(({ import: { default: element } }) => element)
const moduleRailPopovers = (
importOverleafModules('railPopovers') as {
import: {
@@ -56,7 +58,8 @@ const moduleRailPopovers = (
export const RailLayout = () => {
const { sendEvent } = useEditorAnalytics()
const { t } = useTranslation()
const { selectedTab, openTab, isOpen, togglePane } = useRailContext()
const { selectedTab, openTab, isOpen, setIsOpen, togglePane, selectTab } =
useRailContext()
const { features } = useProjectContext()
const { isRestrictedTokenMember } = useEditorContext()
const gitBridgeEnabled = getMeta('ol-gitBridgeEnabled')
@@ -71,6 +74,23 @@ export const RailLayout = () => {
const fileTreeRef = useRef<HTMLAnchorElement>(null)
const settingsRef = useRef<HTMLButtonElement>(null)
useEventListener(
'ui:select-rail-tab',
useCallback(
(event: Event) => {
const {
detail: { tab, open },
} = event as CustomEvent<{
tab: RailTabKey
open: boolean
}>
selectTab(tab)
setIsOpen(open)
},
[selectTab, setIsOpen]
)
)
const railTabs: RailElement[] = useMemo(
() => [
{
@@ -253,18 +273,21 @@ export const RailLayout = () => {
<div className="ide-rail-tabs-wrapper" ref={tabWrapperRef}>
{tabsInRail
.filter(shouldIncludeElement)
.map(({ icon, key, indicator, title, disabled, ref }) => (
<RailTab
open={isOpen && selectedTab === key}
key={key}
eventKey={key}
icon={icon}
indicator={indicator}
title={title}
disabled={disabled}
ref={ref}
/>
))}
.map(({ icon, key, indicator, title, disabled, ref, tab }) => {
const Component = tab ?? RailTab
return (
<Component
open={isOpen && selectedTab === key}
key={key}
eventKey={key}
icon={icon}
indicator={indicator}
title={title}
disabled={disabled}
ref={ref}
/>
)
})}
<RailActionElement key="more-options" action={moreOptionsAction} />
</div>
<nav aria-label={t('help_editor_settings')}>

View File

@@ -41,6 +41,7 @@ const RailContext = createContext<
activeModal: RailModalKey | null
setActiveModal: Dispatch<SetStateAction<RailModalKey | null>>
openTab: (tab: RailTabKey) => void
selectTab: (tab: RailTabKey) => void
}
| undefined
>(undefined)
@@ -92,6 +93,13 @@ export const RailProvider: FC<React.PropsWithChildren> = ({ children }) => {
}
}, [isOpen, selectedTab])
const selectTab = useCallback(
(tab: RailTabKey) => {
setSelectedTab(tab)
},
[setSelectedTab]
)
const openTab = useCallback(
(tab: RailTabKey) => {
setSelectedTab(tab)
@@ -146,6 +154,7 @@ export const RailProvider: FC<React.PropsWithChildren> = ({ children }) => {
activeModal,
setActiveModal,
openTab,
selectTab,
}),
[
selectedTab,
@@ -160,6 +169,7 @@ export const RailProvider: FC<React.PropsWithChildren> = ({ children }) => {
activeModal,
setActiveModal,
openTab,
selectTab,
]
)

View File

@@ -1,6 +1,7 @@
import { AvailableUnfilledIcon } from '@/shared/components/material-icon'
import { RailTabKey } from '../contexts/rail-context'
import { FC, ReactElement } from 'react'
import RailTab from '@/features/ide-redesign/components/rail/rail-tab'
export type CustomRailTabIcon = FC<{ open: boolean; title: string }>
@@ -14,4 +15,5 @@ export type RailElement = {
disabled?: boolean
mountOnFirstLoad?: boolean
ref?: React.RefObject<HTMLAnchorElement>
tab?: typeof RailTab
}

View File

@@ -16,7 +16,7 @@ const ReviewPanelHeader: FC = () => {
{newEditor ? (
<RailPanelHeader
title={t('review')}
actions={[<ReviewPanelResolvedThreadsButton key="resolve-threads" />]}
actions={<ReviewPanelResolvedThreadsButton key="resolve-threads" />}
/>
) : (
<PanelHeading title={t('review')} handleClose={closeReviewPanel}>

View File

@@ -200,3 +200,20 @@ body {
background-color: black;
border-radius: var(--border-radius-base);
}
.separated-rail-tab {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-top: 8px;
isolation: isolate;
}
.separated-rail-tab::before {
display: inline-block;
content: '';
border-top: 1px solid var(--border-divider);
width: 100%;
transform: translateY(-12px);
}

View File

@@ -1440,6 +1440,8 @@
"more_project_collaborators": "<0>More</0> project <0>collaborators</0>",
"more_than_one_kind_of_snippet_was_requested": "The link to open this content on Overleaf included some invalid parameters. If this keeps happening for links on a particular site, please report this to them.",
"most_popular_uppercase": "Most popular",
"move_to_the_left": "Move to the left",
"move_to_the_right": "Move to the right",
"must_be_at_least_n_characters": "Must be at least __n__ characters. Avoid common passwords.",
"must_be_email_address": "Must be an email address.",
"must_be_purchased_online": "Must be purchased online",