Merge pull request #23384 from overleaf/mj-ide-collapsible-rail

[web] Make panels collapsible in editor redesign

GitOrigin-RevId: bc979e3b6028666d3e0aa751e341d838615c1aac
This commit is contained in:
David
2025-02-07 09:06:22 +00:00
committed by Copybot
parent 37444c5eef
commit b5c11370d6
8 changed files with 138 additions and 16 deletions
@@ -1737,8 +1737,10 @@
"toolbar_undo": "",
"toolbar_undo_redo_actions": "",
"tooltip_hide_filetree": "",
"tooltip_hide_panel": "",
"tooltip_hide_pdf": "",
"tooltip_show_filetree": "",
"tooltip_show_panel": "",
"tooltip_show_pdf": "",
"total_due_today": "",
"total_per_month": "",
@@ -2,8 +2,11 @@ import { Panel, PanelGroup } from 'react-resizable-panels'
import { FileTree } from '@/features/ide-react/components/file-tree'
import { OutlineContainer } from '@/features/outline/components/outline-container'
import { VerticalResizeHandle } from '@/features/ide-react/components/resize/vertical-resize-handle'
import { useOutlinePane } from '@/features/ide-react/hooks/use-outline-pane'
function FileTreeOutlinePanel() {
const { outlineEnabled, outlinePanelRef } = useOutlinePane()
return (
<PanelGroup
autoSaveId="ide-redesign-file-tree-outline"
@@ -17,12 +20,18 @@ function FileTreeOutlinePanel() {
>
<FileTree />
</Panel>
<VerticalResizeHandle hitAreaMargins={{ coarse: 0, fine: 0 }} />
<VerticalResizeHandle
hitAreaMargins={{ coarse: 0, fine: 0 }}
disabled={!outlineEnabled}
/>
<Panel
defaultSize={50}
maxSize={75}
id="ide-redesign-file-outline"
order={2}
collapsible
ref={outlinePanelRef}
style={{ minHeight: 36 }} // keep the header visible
>
<OutlineContainer />
</Panel>
@@ -5,8 +5,20 @@ import PdfPreview from '@/features/pdf-preview/components/pdf-preview'
import { Editor } from './editor'
import { RailLayout } from './rail'
import { Toolbar } from './toolbar/toolbar'
import { HorizontalToggler } from '@/features/ide-react/components/resize/horizontal-toggler'
import { useTranslation } from 'react-i18next'
import { usePdfPane } from '../hooks/use-pdf-pane'
export default function MainLayout() {
const {
isOpen: isPdfOpen,
setIsOpen: setIsPdfOpen,
panelRef: pdfPanelRef,
handlePaneCollapse: handlePdfPaneCollapse,
handlePaneExpand: handlePdfPaneExpand,
togglePane: togglePdfPane,
} = usePdfPane()
const { t } = useTranslation()
return (
<div className="ide-redesign-main">
<Toolbar />
@@ -19,10 +31,6 @@ export default function MainLayout() {
})}
>
<RailLayout />
<HorizontalResizeHandle
resizable
hitAreaMargins={{ coarse: 0, fine: 0 }}
/>
<Panel id="ide-redesign-editor-panel" order={2}>
<div className="ide-redesign-editor-container">
<Editor />
@@ -30,12 +38,26 @@ export default function MainLayout() {
</Panel>
<HorizontalResizeHandle
resizable
onDoubleClick={togglePdfPane}
hitAreaMargins={{ coarse: 0, fine: 0 }}
/>
>
<HorizontalToggler
id="ide-redesign-pdf-panel"
togglerType="east"
isOpen={isPdfOpen}
setIsOpen={setIsPdfOpen}
tooltipWhenOpen={t('tooltip_hide_pdf')}
tooltipWhenClosed={t('tooltip_show_pdf')}
/>
</HorizontalResizeHandle>
<Panel
collapsible
className="ide-redesign-pdf-container"
id="ide-redesign-pdf-panel"
order={2}
ref={pdfPanelRef}
onExpand={handlePdfPaneExpand}
onCollapse={handlePdfPaneCollapse}
>
<PdfPreview />
</Panel>
@@ -10,6 +10,11 @@ import { RailTabKey, useRailTabContext } from '../contexts/rail-tab-context'
import FileTreeOutlinePanel from './file-tree-outline-panel'
import { ChatIndicator, ChatPane } from './chat'
import getMeta from '@/utils/meta'
import { HorizontalResizeHandle } from '@/features/ide-react/components/resize/horizontal-resize-handle'
import { HorizontalToggler } from '@/features/ide-react/components/resize/horizontal-toggler'
import { useRail } from '../hooks/use-rail'
import { useTranslation } from 'react-i18next'
import classNames from 'classnames'
type RailElement = {
icon: AvailableUnfilledIcon
@@ -59,6 +64,15 @@ const RAIL_TABS: RailElement[] = [
]
export const RailLayout = () => {
const { t } = useTranslation()
const {
isOpen,
setIsOpen,
panelRef,
handlePaneCollapse,
handlePaneExpand,
togglePane,
} = useRail()
const { selectedTab, setSelectedTab } = useRailTabContext()
const { setLeftMenuShown } = useLayoutContext()
@@ -74,16 +88,26 @@ export const RailLayout = () => {
[setLeftMenuShown]
)
const onTabSelect = useCallback(
(key: string | null) => {
if (key === selectedTab) {
togglePane()
} else {
// Change the selected tab and make sure it's open
setSelectedTab((key ?? 'file-tree') as RailTabKey)
setIsOpen(true)
}
},
[setSelectedTab, selectedTab, setIsOpen, togglePane]
)
return (
<TabContainer
mountOnEnter // Only render when necessary (so that we can lazy load tab content)
unmountOnExit={false} // TODO: Should we unmount the tabs when they're not used?
transition={false}
activeKey={selectedTab}
onSelect={useCallback(
key => setSelectedTab(key ?? undefined),
[setSelectedTab]
)}
onSelect={onTabSelect}
id="ide-rail-tabs"
>
<div className="ide-rail">
@@ -94,7 +118,7 @@ export const RailLayout = () => {
{RAIL_TABS.filter(({ hide }) => !hide).map(
({ icon, key, indicator }) => (
<RailTab
active={selectedTab === key}
open={isOpen && selectedTab === key}
key={key}
eventKey={key}
icon={icon}
@@ -114,6 +138,10 @@ export const RailLayout = () => {
defaultSize={15}
minSize={5}
maxSize={80}
ref={panelRef}
collapsible
onCollapse={handlePaneCollapse}
onExpand={handlePaneExpand}
>
<div className="ide-rail-content">
<Tab.Content>
@@ -125,6 +153,20 @@ export const RailLayout = () => {
</Tab.Content>
</div>
</Panel>
<HorizontalResizeHandle
resizable
hitAreaMargins={{ coarse: 0, fine: 0 }}
onDoubleClick={togglePane}
>
<HorizontalToggler
id="ide-redesign-sidebar-panel"
togglerType="west"
isOpen={isOpen}
setIsOpen={setIsOpen}
tooltipWhenOpen={t('tooltip_hide_panel')}
tooltipWhenClosed={t('tooltip_show_panel')}
/>
</HorizontalResizeHandle>
</TabContainer>
)
}
@@ -132,17 +174,22 @@ export const RailLayout = () => {
const RailTab = ({
icon,
eventKey,
active,
open,
indicator,
}: {
icon: AvailableUnfilledIcon
eventKey: string
active: boolean
open: boolean
indicator?: ReactElement
}) => {
return (
<NavLink eventKey={eventKey} className="ide-rail-tab-link">
{active ? (
<NavLink
eventKey={eventKey}
className={classNames('ide-rail-tab-link', {
'open-rail': open,
})}
>
{open ? (
<MaterialIcon className="ide-rail-tab-link-icon" type={icon} />
) : (
<MaterialIcon className="ide-rail-tab-link-icon" type={icon} unfilled />
@@ -0,0 +1,7 @@
import { useRail } from './use-rail'
export const usePdfPane = () => {
// FIXME: This is temporary, to avoid clashing with the existing usePdfPane
// which uses the layout context. That's the correct approach.
return useRail()
}
@@ -0,0 +1,33 @@
import { useCallback, useRef, useState } from 'react'
import useCollapsiblePanel from '@/features/ide-react/hooks/use-collapsible-panel'
import { ImperativePanelHandle } from 'react-resizable-panels'
export const useRail = () => {
const [isOpen, setIsOpen] = useState(true)
const [resizing, setResizing] = useState(false)
const panelRef = useRef<ImperativePanelHandle>(null)
useCollapsiblePanel(isOpen, panelRef)
const togglePane = useCallback(() => {
setIsOpen(value => !value)
}, [])
const handlePaneExpand = useCallback(() => {
setIsOpen(true)
}, [])
const handlePaneCollapse = useCallback(() => {
setIsOpen(false)
}, [])
return {
isOpen,
setIsOpen,
panelRef,
togglePane,
handlePaneExpand,
handlePaneCollapse,
resizing,
setResizing,
}
}
@@ -28,7 +28,7 @@
font-size: 20px;
}
&.active {
&.open-rail {
color: var(--ide-rail-color);
background-color: var(--ide-rail-link-active-background);
+2
View File
@@ -2265,8 +2265,10 @@
"toolbar_undo": "Undo",
"toolbar_undo_redo_actions": "Undo/Redo actions",
"tooltip_hide_filetree": "Click to hide the file tree",
"tooltip_hide_panel": "Click to hide the panel",
"tooltip_hide_pdf": "Click to hide the PDF",
"tooltip_show_filetree": "Click to show the file tree",
"tooltip_show_panel": "Click to show the panel",
"tooltip_show_pdf": "Click to show the PDF",
"top_pick": "Top pick",
"total": "Total",