0a5bd4e47d
Build and Deploy Verso / deploy (push) Has been cancelled
- Mobile vertical layout: add key= based on direction so react-resizable-panels remounts cleanly when switching between horizontal and vertical; also use isVertical (not just pdfLayout) for the autoSaveId to avoid restoring a mismatched layout from localStorage - Language picker: replace stopPropagation pattern with a containment check on the document click handler — more robust on React pages where Bootstrap JS or React's event delegation can interfere with stopPropagation - Presentation download dropdown: use popperConfig strategy:'fixed' so the menu escapes overflow:hidden table cells; remove forced drop='up' and let Popper choose; defer URL.revokeObjectURL by 10 s to give the browser time to start the download before the blob URL is released Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
180 lines
6.4 KiB
TypeScript
180 lines
6.4 KiB
TypeScript
import { Panel, PanelGroup } from 'react-resizable-panels'
|
|
import classNames from 'classnames'
|
|
import { HorizontalResizeHandle } from '@/features/ide-react/components/resize/horizontal-resize-handle'
|
|
import { VerticalResizeHandle } from '@/features/ide-react/components/resize/vertical-resize-handle'
|
|
import PdfPreview from '@/features/pdf-preview/components/pdf-preview'
|
|
import { RailLayout } from '../rail/rail'
|
|
import { Toolbar } from '../toolbar/toolbar'
|
|
import { HorizontalToggler } from '@/features/ide-react/components/resize/horizontal-toggler'
|
|
import { useTranslation } from 'react-i18next'
|
|
import { usePdfPane } from '@/features/ide-react/hooks/use-pdf-pane'
|
|
import { useLayoutContext } from '@/shared/context/layout-context'
|
|
import { ElementType, useEffect, useState } from 'react'
|
|
import EditorPanel from '../editor/editor-panel'
|
|
import { useRailContext } from '../../context/rail-context'
|
|
import HistoryContainer from '@/features/ide-react/components/history-container'
|
|
import { DefaultSynctexControl } from '@/features/pdf-preview/components/detach-synctex-control'
|
|
import importOverleafModules from '../../../../../macros/import-overleaf-module.macro'
|
|
|
|
const mainEditorLayoutPanels: Array<{
|
|
import: { default: ElementType }
|
|
path: string
|
|
}> = importOverleafModules('mainEditorLayoutPanels')
|
|
|
|
const mainEditorLayoutModalsModules: Array<{
|
|
import: { default: ElementType }
|
|
path: string
|
|
}> = importOverleafModules('mainEditorLayoutModals')
|
|
|
|
// Bootstrap md breakpoint — below this we stack panels vertically on mobile
|
|
const MOBILE_MQ = '(max-width: 767px)'
|
|
|
|
export default function MainLayout() {
|
|
const [resizing, setResizing] = useState(false)
|
|
const { resizing: railResizing } = useRailContext()
|
|
const {
|
|
togglePdfPane,
|
|
handlePdfPaneExpand,
|
|
handlePdfPaneCollapse,
|
|
setPdfIsOpen: setIsPdfOpen,
|
|
pdfIsOpen: isPdfOpen,
|
|
pdfPanelRef,
|
|
} = usePdfPane()
|
|
const { view, pdfLayout } = useLayoutContext()
|
|
|
|
const [isMobile, setIsMobile] = useState(
|
|
() => window.matchMedia(MOBILE_MQ).matches
|
|
)
|
|
useEffect(() => {
|
|
const mq = window.matchMedia(MOBILE_MQ)
|
|
const handler = (e: MediaQueryListEvent) => setIsMobile(e.matches)
|
|
mq.addEventListener('change', handler)
|
|
return () => mq.removeEventListener('change', handler)
|
|
}, [])
|
|
|
|
// verticalSplit is always vertical; sideBySide becomes vertical on mobile
|
|
const isVertical =
|
|
pdfLayout === 'verticalSplit' ||
|
|
(pdfLayout === 'sideBySide' && isMobile)
|
|
|
|
const editorIsOpen =
|
|
view === 'editor' ||
|
|
view === 'file' ||
|
|
pdfLayout === 'sideBySide' ||
|
|
pdfLayout === 'verticalSplit'
|
|
|
|
const { t } = useTranslation()
|
|
|
|
return (
|
|
<div className="ide-redesign-main">
|
|
<Toolbar />
|
|
<div className="ide-redesign-body">
|
|
<PanelGroup
|
|
autoSaveId="ide-redesign-outer-layout"
|
|
direction="horizontal"
|
|
className={classNames('ide-redesign-inner', {
|
|
'ide-panel-group-resizing': resizing || railResizing,
|
|
})}
|
|
>
|
|
<RailLayout />
|
|
<Panel id="ide-redesign-editor-and-pdf-panel" order={2}>
|
|
<HistoryContainer />
|
|
<PanelGroup
|
|
key={isVertical ? 'vertical' : 'horizontal'}
|
|
autoSaveId={
|
|
isVertical
|
|
? 'ide-redesign-editor-and-pdf-panel-group-vertical'
|
|
: 'ide-redesign-editor-and-pdf-panel-group'
|
|
}
|
|
direction={isVertical ? 'vertical' : 'horizontal'}
|
|
className={classNames({
|
|
hidden: view === 'history',
|
|
})}
|
|
>
|
|
<Panel
|
|
id="ide-redesign-editor-panel"
|
|
order={1}
|
|
className={classNames({
|
|
hidden: !editorIsOpen || view === 'history',
|
|
})}
|
|
minSize={5}
|
|
defaultSize={50}
|
|
tagName="section"
|
|
aria-label={t('editor')}
|
|
>
|
|
<div className="ide-redesign-editor-container">
|
|
<EditorPanel />
|
|
</div>
|
|
</Panel>
|
|
{isVertical ? (
|
|
<VerticalResizeHandle
|
|
onDragging={setResizing}
|
|
className={classNames({
|
|
hidden: !editorIsOpen,
|
|
})}
|
|
/>
|
|
) : (
|
|
<HorizontalResizeHandle
|
|
resizable={pdfLayout === 'sideBySide'}
|
|
onDragging={setResizing}
|
|
onDoubleClick={togglePdfPane}
|
|
hitAreaMargins={{ coarse: 0, fine: 0 }}
|
|
className={classNames({
|
|
hidden: !editorIsOpen,
|
|
})}
|
|
>
|
|
<HorizontalToggler
|
|
id="ide-redesign-pdf-panel"
|
|
togglerType="east"
|
|
isOpen={isPdfOpen}
|
|
setIsOpen={setIsPdfOpen}
|
|
tooltipWhenOpen={t('tooltip_hide_pdf')}
|
|
tooltipWhenClosed={t('tooltip_show_pdf')}
|
|
/>
|
|
{pdfLayout === 'sideBySide' && (
|
|
<div className="synctex-controls">
|
|
<DefaultSynctexControl />
|
|
</div>
|
|
)}
|
|
</HorizontalResizeHandle>
|
|
)}
|
|
<Panel
|
|
collapsible
|
|
className={classNames('ide-redesign-pdf-container', {
|
|
hidden: view === 'history',
|
|
})}
|
|
id="ide-redesign-pdf-panel"
|
|
order={2}
|
|
defaultSize={50}
|
|
minSize={5}
|
|
ref={pdfPanelRef}
|
|
onExpand={handlePdfPaneExpand}
|
|
onCollapse={handlePdfPaneCollapse}
|
|
tagName="section"
|
|
aria-label={t('pdf_preview')}
|
|
>
|
|
<PdfPreview />
|
|
{pdfLayout === 'flat' && view === 'pdf' && (
|
|
<div className="synctex-controls" hidden>
|
|
<DefaultSynctexControl />
|
|
</div>
|
|
)}
|
|
</Panel>
|
|
</PanelGroup>
|
|
</Panel>
|
|
{mainEditorLayoutPanels.map(
|
|
({ import: { default: Component }, path }, i) => {
|
|
return <Component key={path} order={i + 3} />
|
|
}
|
|
)}
|
|
</PanelGroup>
|
|
</div>
|
|
{mainEditorLayoutModalsModules.map(
|
|
({ import: { default: Component }, path }) => (
|
|
<Component key={path} />
|
|
)
|
|
)}
|
|
</div>
|
|
)
|
|
}
|