mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-15 14:43:46 +01:00
Upgrade react-resizable-panels (#15998)
GitOrigin-RevId: af799f1a5b4945ad2acbb460806d559fae7416b9
This commit is contained in:
Generated
+8
-8
@@ -34095,9 +34095,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-resizable-panels": {
|
"node_modules/react-resizable-panels": {
|
||||||
"version": "0.0.55",
|
"version": "0.0.63",
|
||||||
"resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-0.0.55.tgz",
|
"resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-0.0.63.tgz",
|
||||||
"integrity": "sha512-J/LTFzUEjJiqwSjVh8gjUXkQDA8MRPjARASfn++d2+KOgA+9UcRYUfE3QBJixer2vkk+ffQ4cq3QzWzzHgqYpQ==",
|
"integrity": "sha512-AfA8b6kouhL4rBvgUGs17uzWVlYPaJIwwTCVeWRxNpUHJlCG1h9RIMlzA1849AZGsaNJO3j/SNdI5SS4GZDE3g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^16.14.0 || ^17.0.0 || ^18.0.0",
|
"react": "^16.14.0 || ^17.0.0 || ^18.0.0",
|
||||||
@@ -43709,7 +43709,7 @@
|
|||||||
"react-i18next": "^13.3.1",
|
"react-i18next": "^13.3.1",
|
||||||
"react-linkify": "^1.0.0-alpha",
|
"react-linkify": "^1.0.0-alpha",
|
||||||
"react-refresh": "^0.14.0",
|
"react-refresh": "^0.14.0",
|
||||||
"react-resizable-panels": "^0.0.55",
|
"react-resizable-panels": "^0.0.63",
|
||||||
"react2angular": "^4.0.6",
|
"react2angular": "^4.0.6",
|
||||||
"react2angular-shared-context": "^1.1.0",
|
"react2angular-shared-context": "^1.1.0",
|
||||||
"requirejs": "^2.3.6",
|
"requirejs": "^2.3.6",
|
||||||
@@ -52297,7 +52297,7 @@
|
|||||||
"react-i18next": "^13.3.1",
|
"react-i18next": "^13.3.1",
|
||||||
"react-linkify": "^1.0.0-alpha",
|
"react-linkify": "^1.0.0-alpha",
|
||||||
"react-refresh": "^0.14.0",
|
"react-refresh": "^0.14.0",
|
||||||
"react-resizable-panels": "^0.0.55",
|
"react-resizable-panels": "^0.0.63",
|
||||||
"react2angular": "^4.0.6",
|
"react2angular": "^4.0.6",
|
||||||
"react2angular-shared-context": "^1.1.0",
|
"react2angular-shared-context": "^1.1.0",
|
||||||
"recurly": "^4.0.0",
|
"recurly": "^4.0.0",
|
||||||
@@ -73465,9 +73465,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-resizable-panels": {
|
"react-resizable-panels": {
|
||||||
"version": "0.0.55",
|
"version": "0.0.63",
|
||||||
"resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-0.0.55.tgz",
|
"resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-0.0.63.tgz",
|
||||||
"integrity": "sha512-J/LTFzUEjJiqwSjVh8gjUXkQDA8MRPjARASfn++d2+KOgA+9UcRYUfE3QBJixer2vkk+ffQ4cq3QzWzzHgqYpQ==",
|
"integrity": "sha512-AfA8b6kouhL4rBvgUGs17uzWVlYPaJIwwTCVeWRxNpUHJlCG1h9RIMlzA1849AZGsaNJO3j/SNdI5SS4GZDE3g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,99 +1,103 @@
|
|||||||
import { ReactNode, useRef, useState } from 'react'
|
import React, { FC, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { Panel, PanelGroup } from 'react-resizable-panels'
|
||||||
import {
|
import NoSelectionPane from '@/features/ide-react/components/editor/no-selection-pane'
|
||||||
ImperativePanelHandle,
|
import FileView from '@/features/file-view/components/file-view'
|
||||||
Panel,
|
import { fileViewFile } from '@/features/ide-react/hooks/use-file-tree'
|
||||||
PanelGroup,
|
import MultipleSelectionPane from '@/features/ide-react/components/editor/multiple-selection-pane'
|
||||||
} from 'react-resizable-panels'
|
|
||||||
import { HorizontalResizeHandle } from '@/features/ide-react/components/resize/horizontal-resize-handle'
|
import { HorizontalResizeHandle } from '@/features/ide-react/components/resize/horizontal-resize-handle'
|
||||||
import { HorizontalToggler } from '@/features/ide-react/components/resize/horizontal-toggler'
|
import { HorizontalToggler } from '@/features/ide-react/components/resize/horizontal-toggler'
|
||||||
import useCollapsiblePanel from '@/features/ide-react/hooks/use-collapsible-panel'
|
|
||||||
import { useLayoutContext } from '@/shared/context/layout-context'
|
|
||||||
import PdfPreview from '@/features/pdf-preview/components/pdf-preview'
|
|
||||||
import { DefaultSynctexControl } from '@/features/pdf-preview/components/detach-synctex-control'
|
import { DefaultSynctexControl } from '@/features/pdf-preview/components/detach-synctex-control'
|
||||||
import classnames from 'classnames'
|
import PdfPreview from '@/features/pdf-preview/components/pdf-preview'
|
||||||
|
import { usePdfPane } from '@/features/ide-react/hooks/use-pdf-pane'
|
||||||
|
import { useLayoutContext } from '@/shared/context/layout-context'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import classNames from 'classnames'
|
||||||
|
|
||||||
export type EditorProps = {
|
export const EditorAndPdf: FC<{
|
||||||
editorContent: ReactNode
|
editorPane: React.ReactNode
|
||||||
}
|
selectedEntityCount: number
|
||||||
|
openEntity: any // TODO
|
||||||
export default function EditorAndPdf({ editorContent }: EditorProps) {
|
}> = ({ editorPane, selectedEntityCount, openEntity }) => {
|
||||||
const { t } = useTranslation()
|
|
||||||
const { view, pdfLayout, changeLayout, detachRole, reattach } =
|
|
||||||
useLayoutContext()
|
|
||||||
const [resizing, setResizing] = useState(false)
|
const [resizing, setResizing] = useState(false)
|
||||||
|
|
||||||
const pdfPanelRef = useRef<ImperativePanelHandle>(null)
|
const { t } = useTranslation()
|
||||||
const isDualPane = pdfLayout === 'sideBySide'
|
|
||||||
const editorIsVisible = isDualPane || view === 'editor' || view === 'file'
|
|
||||||
const pdfIsOpen = isDualPane || view === 'pdf'
|
|
||||||
|
|
||||||
useCollapsiblePanel(pdfIsOpen, pdfPanelRef)
|
const {
|
||||||
|
togglePdfPane,
|
||||||
|
handlePdfPaneExpand,
|
||||||
|
handlePdfPaneCollapse,
|
||||||
|
setPdfIsOpen,
|
||||||
|
pdfIsOpen,
|
||||||
|
pdfPanelRef,
|
||||||
|
} = usePdfPane()
|
||||||
|
|
||||||
const historyIsOpen = view === 'history'
|
const { view, pdfLayout } = useLayoutContext()
|
||||||
|
|
||||||
function setPdfIsOpen(isOpen: boolean) {
|
const editorIsOpen =
|
||||||
if (isOpen) {
|
view === 'editor' || view === 'file' || pdfLayout === 'sideBySide'
|
||||||
if (detachRole === 'detacher') {
|
|
||||||
reattach()
|
|
||||||
}
|
|
||||||
changeLayout('sideBySide')
|
|
||||||
} else {
|
|
||||||
changeLayout('flat', 'editor')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PanelGroup
|
<PanelGroup autoSaveId="ide-editor-pdf-layout" direction="horizontal">
|
||||||
autoSaveId="ide-react-editor-and-pdf-layout"
|
{/* main */}
|
||||||
direction="horizontal"
|
{editorIsOpen && (
|
||||||
className={classnames('ide-react-editor-and-pdf', {
|
<>
|
||||||
hide: historyIsOpen,
|
<Panel
|
||||||
'ide-react-editor-and-pdf-resizing': resizing,
|
id="panel-main"
|
||||||
})}
|
order={1}
|
||||||
>
|
defaultSizePercentage={50}
|
||||||
{editorIsVisible ? (
|
minSizePercentage={25}
|
||||||
<Panel
|
className={classNames('ide-react-panel', {
|
||||||
id="editor"
|
'ide-panel-group-resizing': resizing,
|
||||||
order={1}
|
})}
|
||||||
defaultSize={50}
|
>
|
||||||
className="ide-react-panel"
|
{selectedEntityCount === 0 && <NoSelectionPane />}
|
||||||
>
|
{selectedEntityCount === 1 && openEntity?.type === 'fileRef' && (
|
||||||
<div className="ide-react-editor-content full-size">
|
<FileView file={fileViewFile(openEntity.entity)} />
|
||||||
{editorContent}
|
)}
|
||||||
</div>
|
{selectedEntityCount === 1 && editorPane}
|
||||||
</Panel>
|
{selectedEntityCount > 1 && (
|
||||||
) : null}
|
<MultipleSelectionPane
|
||||||
<HorizontalResizeHandle
|
selectedEntityCount={selectedEntityCount}
|
||||||
resizable={isDualPane}
|
/>
|
||||||
onDoubleClick={() => setPdfIsOpen(!pdfIsOpen)}
|
)}
|
||||||
onDragging={setResizing}
|
</Panel>
|
||||||
>
|
|
||||||
<HorizontalToggler
|
<HorizontalResizeHandle
|
||||||
id="editor-pdf"
|
resizable={pdfLayout === 'sideBySide'}
|
||||||
togglerType="east"
|
onDoubleClick={togglePdfPane}
|
||||||
isOpen={pdfIsOpen}
|
onDragging={setResizing}
|
||||||
setIsOpen={setPdfIsOpen}
|
>
|
||||||
tooltipWhenOpen={t('tooltip_hide_pdf')}
|
<HorizontalToggler
|
||||||
tooltipWhenClosed={t('tooltip_show_pdf')}
|
id="editor-pdf"
|
||||||
/>
|
togglerType="east"
|
||||||
{isDualPane ? (
|
isOpen={pdfIsOpen}
|
||||||
<div className="synctex-controls">
|
setIsOpen={setPdfIsOpen}
|
||||||
<DefaultSynctexControl />
|
tooltipWhenOpen={t('tooltip_hide_pdf')}
|
||||||
</div>
|
tooltipWhenClosed={t('tooltip_show_pdf')}
|
||||||
) : null}
|
/>
|
||||||
</HorizontalResizeHandle>
|
|
||||||
|
{pdfLayout === 'sideBySide' && (
|
||||||
|
<div className="synctex-controls">
|
||||||
|
<DefaultSynctexControl />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</HorizontalResizeHandle>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* pdf */}
|
||||||
<Panel
|
<Panel
|
||||||
ref={pdfPanelRef}
|
ref={pdfPanelRef}
|
||||||
id="pdf"
|
id="panel-pdf"
|
||||||
order={2}
|
order={2}
|
||||||
defaultSize={50}
|
defaultSizePercentage={50}
|
||||||
minSize={5}
|
minSizePercentage={25}
|
||||||
collapsible
|
collapsible
|
||||||
onCollapse={collapsed => setPdfIsOpen(!collapsed)}
|
onCollapse={handlePdfPaneCollapse}
|
||||||
|
onExpand={handlePdfPaneExpand}
|
||||||
className="ide-react-panel"
|
className="ide-react-panel"
|
||||||
>
|
>
|
||||||
{pdfIsOpen || resizing ? <PdfPreview /> : null}
|
{pdfIsOpen && <PdfPreview />}
|
||||||
</Panel>
|
</Panel>
|
||||||
</PanelGroup>
|
</PanelGroup>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -27,11 +27,12 @@ export default function EditorSidebar({
|
|||||||
hidden: !shouldShow,
|
hidden: !shouldShow,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<PanelGroup
|
<PanelGroup autoSaveId="ide-editor-sidebar-layout" direction="vertical">
|
||||||
autoSaveId="ide-react-editor-sidebar-layout"
|
<Panel
|
||||||
direction="vertical"
|
defaultSizePercentage={75}
|
||||||
>
|
className="ide-react-file-tree-panel"
|
||||||
<Panel defaultSize={75} className="ide-react-file-tree-panel">
|
id="panel-file-tree"
|
||||||
|
>
|
||||||
<FileTree
|
<FileTree
|
||||||
onInit={onFileTreeInit}
|
onInit={onFileTreeInit}
|
||||||
onSelect={onFileTreeSelect}
|
onSelect={onFileTreeSelect}
|
||||||
@@ -39,7 +40,7 @@ export default function EditorSidebar({
|
|||||||
/>
|
/>
|
||||||
</Panel>
|
</Panel>
|
||||||
<VerticalResizeHandle />
|
<VerticalResizeHandle />
|
||||||
<Panel defaultSize={25}>
|
<Panel defaultSizePercentage={25} id="panel-outline">
|
||||||
<div className="outline-container" />
|
<div className="outline-container" />
|
||||||
</Panel>
|
</Panel>
|
||||||
</PanelGroup>
|
</PanelGroup>
|
||||||
|
|||||||
@@ -22,33 +22,38 @@ export const EditorPane: FC<{ show: boolean }> = ({ show }) => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PanelGroup
|
<div className="ide-react-editor-content full-size">
|
||||||
autoSaveId="ide-react-editor-and-symbol-palette-layout"
|
<PanelGroup
|
||||||
direction="vertical"
|
autoSaveId="ide-editor-layout"
|
||||||
units="pixels"
|
direction="vertical"
|
||||||
className={classNames({ hidden: !show })}
|
className={classNames({ hidden: !show })}
|
||||||
>
|
>
|
||||||
<Panel id="sourceEditor" order={1} className="ide-react-editor-panel">
|
<Panel
|
||||||
<SourceEditor />
|
id="panel-source-editor"
|
||||||
{isLoading && <LoadingPane />}
|
order={1}
|
||||||
</Panel>
|
className="ide-react-editor-panel"
|
||||||
|
>
|
||||||
|
<SourceEditor />
|
||||||
|
{isLoading && <LoadingPane />}
|
||||||
|
</Panel>
|
||||||
|
|
||||||
{editor.showSymbolPalette && (
|
{editor.showSymbolPalette && (
|
||||||
<>
|
<>
|
||||||
<VerticalResizeHandle id="editor-symbol-palette" />
|
<VerticalResizeHandle id="editor-symbol-palette" />
|
||||||
<Panel
|
<Panel
|
||||||
id="symbol-palette"
|
id="panel-symbol-palette"
|
||||||
order={2}
|
order={2}
|
||||||
defaultSize={250}
|
defaultSizePixels={250}
|
||||||
minSize={250}
|
minSizePixels={250}
|
||||||
maxSize={336}
|
maxSizePixels={336}
|
||||||
>
|
>
|
||||||
<Suspense fallback={<FullSizeLoadingSpinner delay={500} />}>
|
<Suspense fallback={<FullSizeLoadingSpinner delay={500} />}>
|
||||||
<SymbolPalettePane />
|
<SymbolPalettePane />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</Panel>
|
</Panel>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</PanelGroup>
|
</PanelGroup>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Alerts } from '@/features/ide-react/components/alerts/alerts'
|
import { Alerts } from '@/features/ide-react/components/alerts/alerts'
|
||||||
import MainLayout from '@/features/ide-react/components/layout/main-layout'
|
import { MainLayout } from '@/features/ide-react/components/layout/main-layout'
|
||||||
import EditorLeftMenu from '@/features/editor-left-menu/components/editor-left-menu'
|
import EditorLeftMenu from '@/features/editor-left-menu/components/editor-left-menu'
|
||||||
import { useLayoutEventTracking } from '@/features/ide-react/hooks/use-layout-event-tracking'
|
import { useLayoutEventTracking } from '@/features/ide-react/hooks/use-layout-event-tracking'
|
||||||
import useSocketListeners from '@/features/ide-react/hooks/use-socket-listeners'
|
import useSocketListeners from '@/features/ide-react/hooks/use-socket-listeners'
|
||||||
@@ -10,7 +10,7 @@ import { useHasLintingError } from '@/features/ide-react/hooks/use-has-linting-e
|
|||||||
import { useConnectionState } from '@/features/ide-react/hooks/use-connection-state'
|
import { useConnectionState } from '@/features/ide-react/hooks/use-connection-state'
|
||||||
|
|
||||||
export default function IdePage() {
|
export default function IdePage() {
|
||||||
useLayoutEventTracking() // send event when the layout changes
|
useLayoutEventTracking() // sent event when the layout changes
|
||||||
useSocketListeners() // listen for project-related websocket messages
|
useSocketListeners() // listen for project-related websocket messages
|
||||||
useEditingSessionHeartbeat() // send a batched event when user is active
|
useEditingSessionHeartbeat() // send a batched event when user is active
|
||||||
useRegisterUserActivity() // record activity and ensure connection when user is active
|
useRegisterUserActivity() // record activity and ensure connection when user is active
|
||||||
|
|||||||
@@ -1,62 +1,149 @@
|
|||||||
import { Panel, PanelGroup } from 'react-resizable-panels'
|
import { Panel, PanelGroup } from 'react-resizable-panels'
|
||||||
import { useState } from 'react'
|
import { FC } from 'react'
|
||||||
import { HorizontalResizeHandle } from '../resize/horizontal-resize-handle'
|
import { HorizontalResizeHandle } from '../resize/horizontal-resize-handle'
|
||||||
import useFixedSizeColumn from '@/features/ide-react/hooks/use-fixed-size-column'
|
|
||||||
import useCollapsiblePanel from '@/features/ide-react/hooks/use-collapsible-panel'
|
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { useLayoutContext } from '@/shared/context/layout-context'
|
import { useLayoutContext } from '@/shared/context/layout-context'
|
||||||
import EditorNavigationToolbar from '@/features/ide-react/components/editor-navigation-toolbar'
|
import EditorNavigationToolbar from '@/features/ide-react/components/editor-navigation-toolbar'
|
||||||
import ChatPane from '@/features/chat/components/chat-pane'
|
import ChatPane from '@/features/chat/components/chat-pane'
|
||||||
import { EditorAndSidebar } from '@/features/ide-react/components/editor-and-sidebar'
|
import { HorizontalToggler } from '@/features/ide-react/components/resize/horizontal-toggler'
|
||||||
|
import { HistorySidebar } from '@/features/ide-react/components/history-sidebar'
|
||||||
|
import { HistoryProvider } from '@/features/history/context/history-context'
|
||||||
|
import History from '@/features/ide-react/components/history'
|
||||||
|
import EditorSidebar from '@/features/ide-react/components/editor-sidebar'
|
||||||
|
import { EditorPane } from '@/features/ide-react/components/editor/editor-pane'
|
||||||
|
import { useFileTree } from '@/features/ide-react/hooks/use-file-tree'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useSidebarPane } from '@/features/ide-react/hooks/use-sidebar-pane'
|
||||||
|
import { useChatPane } from '@/features/ide-react/hooks/use-chat-pane'
|
||||||
|
import { EditorAndPdf } from '@/features/ide-react/components/editor-and-pdf'
|
||||||
|
|
||||||
const CHAT_DEFAULT_SIZE = 20
|
export const MainLayout: FC = () => {
|
||||||
|
const { view } = useLayoutContext()
|
||||||
|
|
||||||
// The main area below the header is split into two: the main content and chat.
|
const {
|
||||||
// The reason for not splitting the left column containing the file tree and
|
isOpen: sidebarIsOpen,
|
||||||
// outline here is that the history view has its own file tree, so it is more
|
setIsOpen: setSidebarIsOpen,
|
||||||
// convenient to replace the whole of the main content when in history view.
|
fixedPanelRef: sidebarPanelRef,
|
||||||
export default function MainLayout() {
|
handleLayout: handleSidebarLayout,
|
||||||
const { chatIsOpen } = useLayoutContext()
|
togglePane: toggleSidebar,
|
||||||
|
handlePaneExpand: handleSidebarExpand,
|
||||||
|
handlePaneCollapse: handleSidebarCollapse,
|
||||||
|
resizing: sidebarResizing,
|
||||||
|
setResizing: setSidebarResizing,
|
||||||
|
} = useSidebarPane()
|
||||||
|
|
||||||
const { fixedPanelRef: chatPanelRef, handleLayout } = useFixedSizeColumn(
|
const {
|
||||||
CHAT_DEFAULT_SIZE,
|
isOpen: chatIsOpen,
|
||||||
chatIsOpen
|
fixedPanelRef: chatPanelRef,
|
||||||
)
|
handleLayout: handleChatLayout,
|
||||||
|
resizing: chatResizing,
|
||||||
|
setResizing: setChatResizing,
|
||||||
|
} = useChatPane()
|
||||||
|
|
||||||
useCollapsiblePanel(chatIsOpen, chatPanelRef)
|
const {
|
||||||
|
selectedEntityCount,
|
||||||
|
openEntity,
|
||||||
|
openDocId,
|
||||||
|
handleFileTreeInit,
|
||||||
|
handleFileTreeSelect,
|
||||||
|
handleFileTreeDelete,
|
||||||
|
} = useFileTree()
|
||||||
|
|
||||||
const [resizing, setResizing] = useState(false)
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
// keep the editor pane open
|
||||||
|
const editorPane = openDocId ? (
|
||||||
|
<EditorPane
|
||||||
|
show={openEntity?.type === 'doc' && selectedEntityCount === 1}
|
||||||
|
/>
|
||||||
|
) : null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ide-react-main">
|
<div className="ide-react-main">
|
||||||
<EditorNavigationToolbar />
|
<EditorNavigationToolbar />
|
||||||
<div className="ide-react-body">
|
<div className="ide-react-body">
|
||||||
<PanelGroup
|
<PanelGroup
|
||||||
autoSaveId="ide-react-chat-layout"
|
autoSaveId="ide-outer-layout"
|
||||||
direction="horizontal"
|
direction="horizontal"
|
||||||
onLayout={handleLayout}
|
onLayout={handleSidebarLayout}
|
||||||
className={classNames({
|
className={classNames({
|
||||||
'ide-react-main-resizing': resizing,
|
'ide-panel-group-resizing': sidebarResizing || chatResizing,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Panel id="main" order={1}>
|
{/* sidebar */}
|
||||||
<EditorAndSidebar />
|
<Panel
|
||||||
|
ref={sidebarPanelRef}
|
||||||
|
id="panel-sidebar"
|
||||||
|
order={1}
|
||||||
|
defaultSizePixels={200}
|
||||||
|
minSizePixels={150}
|
||||||
|
collapsible
|
||||||
|
onCollapse={handleSidebarCollapse}
|
||||||
|
onExpand={handleSidebarExpand}
|
||||||
|
>
|
||||||
|
<EditorSidebar
|
||||||
|
shouldShow={view !== 'history'}
|
||||||
|
onFileTreeInit={handleFileTreeInit}
|
||||||
|
onFileTreeSelect={handleFileTreeSelect}
|
||||||
|
onFileTreeDelete={handleFileTreeDelete}
|
||||||
|
/>
|
||||||
|
{view === 'history' && <HistorySidebar />}
|
||||||
</Panel>
|
</Panel>
|
||||||
{chatIsOpen ? (
|
|
||||||
<>
|
<HorizontalResizeHandle
|
||||||
<HorizontalResizeHandle onDragging={setResizing} />
|
onDoubleClick={toggleSidebar}
|
||||||
<Panel
|
resizable={sidebarIsOpen}
|
||||||
ref={chatPanelRef}
|
onDragging={setSidebarResizing}
|
||||||
id="chat"
|
>
|
||||||
order={2}
|
<HorizontalToggler
|
||||||
defaultSize={CHAT_DEFAULT_SIZE}
|
id="panel-sidebar"
|
||||||
minSize={5}
|
togglerType="west"
|
||||||
collapsible
|
isOpen={sidebarIsOpen}
|
||||||
>
|
setIsOpen={setSidebarIsOpen}
|
||||||
<ChatPane />
|
tooltipWhenOpen={t('tooltip_hide_filetree')}
|
||||||
|
tooltipWhenClosed={t('tooltip_show_filetree')}
|
||||||
|
/>
|
||||||
|
</HorizontalResizeHandle>
|
||||||
|
|
||||||
|
<Panel id="panel-outer-main" order={2}>
|
||||||
|
<PanelGroup
|
||||||
|
autoSaveId="ide-inner-layout"
|
||||||
|
direction="horizontal"
|
||||||
|
onLayout={handleChatLayout}
|
||||||
|
>
|
||||||
|
<Panel className="ide-react-panel" id="panel-main" order={1}>
|
||||||
|
{view === 'history' ? (
|
||||||
|
<HistoryProvider>
|
||||||
|
<History />
|
||||||
|
</HistoryProvider>
|
||||||
|
) : (
|
||||||
|
<EditorAndPdf
|
||||||
|
editorPane={editorPane}
|
||||||
|
selectedEntityCount={selectedEntityCount}
|
||||||
|
openEntity={openEntity}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Panel>
|
</Panel>
|
||||||
</>
|
|
||||||
) : null}
|
{chatIsOpen && (
|
||||||
|
<>
|
||||||
|
<HorizontalResizeHandle onDragging={setChatResizing} />
|
||||||
|
|
||||||
|
{/* chat */}
|
||||||
|
<Panel
|
||||||
|
ref={chatPanelRef}
|
||||||
|
id="panel-chat"
|
||||||
|
order={2}
|
||||||
|
defaultSizePixels={200}
|
||||||
|
minSizePixels={150}
|
||||||
|
collapsible
|
||||||
|
>
|
||||||
|
<ChatPane />
|
||||||
|
</Panel>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</PanelGroup>
|
||||||
|
</Panel>
|
||||||
</PanelGroup>
|
</PanelGroup>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
-86
@@ -1,86 +0,0 @@
|
|||||||
import React, { ReactNode, useEffect, useState } from 'react'
|
|
||||||
import { Panel, PanelGroup } from 'react-resizable-panels'
|
|
||||||
import { HorizontalResizeHandle } from '@/features/ide-react/components/resize/horizontal-resize-handle'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import { HorizontalToggler } from '@/features/ide-react/components/resize/horizontal-toggler'
|
|
||||||
import useFixedSizeColumn from '@/features/ide-react/hooks/use-fixed-size-column'
|
|
||||||
import useCollapsiblePanel from '@/features/ide-react/hooks/use-collapsible-panel'
|
|
||||||
import classNames from 'classnames'
|
|
||||||
|
|
||||||
type TwoColumnMainContentProps = {
|
|
||||||
leftColumnId: string
|
|
||||||
leftColumnContent: ReactNode
|
|
||||||
leftColumnDefaultSize: number
|
|
||||||
setLeftColumnDefaultSize: React.Dispatch<React.SetStateAction<number>>
|
|
||||||
rightColumnContent: ReactNode
|
|
||||||
leftColumnIsOpen: boolean
|
|
||||||
setLeftColumnIsOpen: (
|
|
||||||
leftColumnIsOpen: TwoColumnMainContentProps['leftColumnIsOpen']
|
|
||||||
) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function TwoColumnMainContent({
|
|
||||||
leftColumnId,
|
|
||||||
leftColumnContent,
|
|
||||||
leftColumnDefaultSize,
|
|
||||||
setLeftColumnDefaultSize,
|
|
||||||
rightColumnContent,
|
|
||||||
leftColumnIsOpen,
|
|
||||||
setLeftColumnIsOpen,
|
|
||||||
}: TwoColumnMainContentProps) {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
|
|
||||||
const {
|
|
||||||
fixedPanelRef: leftColumnPanelRef,
|
|
||||||
fixedPanelWidthRef: leftColumnWidthRef,
|
|
||||||
handleLayout,
|
|
||||||
} = useFixedSizeColumn(leftColumnDefaultSize, leftColumnIsOpen)
|
|
||||||
|
|
||||||
useCollapsiblePanel(leftColumnIsOpen, leftColumnPanelRef)
|
|
||||||
|
|
||||||
const [resizing, setResizing] = useState(false)
|
|
||||||
|
|
||||||
// Update the left column default size on unmount rather than doing it on
|
|
||||||
// every resize, which causes ResizeObserver errors
|
|
||||||
useEffect(() => {
|
|
||||||
if (leftColumnWidthRef.current) {
|
|
||||||
setLeftColumnDefaultSize(leftColumnWidthRef.current.size)
|
|
||||||
}
|
|
||||||
}, [leftColumnWidthRef, setLeftColumnDefaultSize])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PanelGroup
|
|
||||||
autoSaveId="ide-react-main-content-layout"
|
|
||||||
direction="horizontal"
|
|
||||||
onLayout={handleLayout}
|
|
||||||
className={classNames({
|
|
||||||
'ide-react-main-content-resizing': resizing,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<Panel
|
|
||||||
ref={leftColumnPanelRef}
|
|
||||||
defaultSize={leftColumnDefaultSize}
|
|
||||||
minSize={5}
|
|
||||||
collapsible
|
|
||||||
onCollapse={collapsed => setLeftColumnIsOpen(!collapsed)}
|
|
||||||
>
|
|
||||||
{leftColumnContent}
|
|
||||||
</Panel>
|
|
||||||
<HorizontalResizeHandle
|
|
||||||
onDoubleClick={() => setLeftColumnIsOpen(!leftColumnIsOpen)}
|
|
||||||
resizable={leftColumnIsOpen}
|
|
||||||
onDragging={setResizing}
|
|
||||||
>
|
|
||||||
<HorizontalToggler
|
|
||||||
id={leftColumnId}
|
|
||||||
togglerType="west"
|
|
||||||
isOpen={leftColumnIsOpen}
|
|
||||||
setIsOpen={setLeftColumnIsOpen}
|
|
||||||
tooltipWhenOpen={t('tooltip_hide_filetree')}
|
|
||||||
tooltipWhenClosed={t('tooltip_show_filetree')}
|
|
||||||
/>
|
|
||||||
</HorizontalResizeHandle>
|
|
||||||
<Panel className="ide-react-panel">{rightColumnContent}</Panel>
|
|
||||||
</PanelGroup>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { useLayoutContext } from '@/shared/context/layout-context'
|
||||||
|
import useFixedSizeColumn from '@/features/ide-react/hooks/use-fixed-size-column'
|
||||||
|
import useCollapsiblePanel from '@/features/ide-react/hooks/use-collapsible-panel'
|
||||||
|
import { useState } from 'react'
|
||||||
|
|
||||||
|
export const useChatPane = () => {
|
||||||
|
const { chatIsOpen: isOpen } = useLayoutContext()
|
||||||
|
const [resizing, setResizing] = useState(false)
|
||||||
|
const { fixedPanelRef, handleLayout } = useFixedSizeColumn(isOpen)
|
||||||
|
useCollapsiblePanel(isOpen, fixedPanelRef)
|
||||||
|
|
||||||
|
return { isOpen, fixedPanelRef, handleLayout, resizing, setResizing }
|
||||||
|
}
|
||||||
+41
-137
@@ -1,60 +1,20 @@
|
|||||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
import { FileRef } from '../../../../../types/file-ref'
|
||||||
import TwoColumnMainContent from '@/features/ide-react/components/layout/two-column-main-content'
|
import { BinaryFile } from '@/features/file-view/types/binary-file'
|
||||||
import EditorSidebar from '@/features/ide-react/components/editor-sidebar'
|
import { useSelectFileTreeEntity } from '@/features/ide-react/hooks/use-select-file-tree-entity'
|
||||||
import History from '@/features/ide-react/components/history'
|
import useScopeValue from '@/shared/hooks/use-scope-value'
|
||||||
import { HistoryProvider } from '@/features/history/context/history-context'
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import { useProjectContext } from '@/shared/context/project-context'
|
|
||||||
import { useLayoutContext } from '@/shared/context/layout-context'
|
|
||||||
import {
|
import {
|
||||||
FileTreeDeleteHandler,
|
FileTreeDeleteHandler,
|
||||||
FileTreeDocumentFindResult,
|
FileTreeDocumentFindResult,
|
||||||
FileTreeFileRefFindResult,
|
FileTreeFileRefFindResult,
|
||||||
FileTreeFindResult,
|
FileTreeFindResult,
|
||||||
} from '@/features/ide-react/types/file-tree'
|
} from '@/features/ide-react/types/file-tree'
|
||||||
import FileView from '@/features/file-view/components/file-view'
|
import { debugConsole } from '@/utils/debugging'
|
||||||
import { FileRef } from '../../../../../types/file-ref'
|
import { useProjectContext } from '@/shared/context/project-context'
|
||||||
import { EditorPane } from '@/features/ide-react/components/editor/editor-pane'
|
|
||||||
import EditorAndPdf from '@/features/ide-react/components/editor-and-pdf'
|
|
||||||
import MultipleSelectionPane from '@/features/ide-react/components/editor/multiple-selection-pane'
|
|
||||||
import NoSelectionPane from '@/features/ide-react/components/editor/no-selection-pane'
|
|
||||||
import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context'
|
import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context'
|
||||||
import { useEditorManagerContext } from '@/features/ide-react/context/editor-manager-context'
|
import { useEditorManagerContext } from '@/features/ide-react/context/editor-manager-context'
|
||||||
import NoOpenDocPane from '@/features/ide-react/components/editor/no-open-doc-pane'
|
|
||||||
import { debugConsole } from '@/utils/debugging'
|
|
||||||
import { HistorySidebar } from '@/features/ide-react/components/history-sidebar'
|
|
||||||
import { BinaryFile } from '@/features/file-view/types/binary-file'
|
|
||||||
import useScopeValue from '@/shared/hooks/use-scope-value'
|
|
||||||
import { useSelectFileTreeEntity } from '@/features/ide-react/hooks/use-select-file-tree-entity'
|
|
||||||
|
|
||||||
function convertFileRefToBinaryFile(fileRef: FileRef): BinaryFile {
|
export const useFileTree = () => {
|
||||||
return {
|
|
||||||
_id: fileRef._id,
|
|
||||||
name: fileRef.name,
|
|
||||||
id: fileRef._id,
|
|
||||||
type: 'file',
|
|
||||||
selected: true,
|
|
||||||
linkedFileData: fileRef.linkedFileData,
|
|
||||||
created: fileRef.created ? new Date(fileRef.created) : new Date(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// `FileViewHeader`, which is TypeScript, expects a BinaryFile, which has a
|
|
||||||
// `created` property of type `Date`, while `TPRFileViewInfo`, written in JS,
|
|
||||||
// into which `FileViewHeader` passes its BinaryFile, expects a file object with
|
|
||||||
// `created` property of type `string`, which is a mismatch. `TPRFileViewInfo`
|
|
||||||
// is the only one making runtime complaints and it seems that other uses of
|
|
||||||
// `FileViewHeader` pass in a string for `created`, so that's what this function
|
|
||||||
// does too.
|
|
||||||
function fileViewFile(fileRef: FileRef) {
|
|
||||||
return {
|
|
||||||
...convertFileRefToBinaryFile(fileRef),
|
|
||||||
created: fileRef.created,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function EditorAndSidebar() {
|
|
||||||
const [leftColumnDefaultSize, setLeftColumnDefaultSize] = useState(20)
|
|
||||||
const [leftColumnIsOpen, setLeftColumnIsOpen] = useState(true)
|
|
||||||
const { rootDocId } = useProjectContext()
|
const { rootDocId } = useProjectContext()
|
||||||
const { eventEmitter } = useIdeReactContext()
|
const { eventEmitter } = useIdeReactContext()
|
||||||
const {
|
const {
|
||||||
@@ -62,7 +22,6 @@ export function EditorAndSidebar() {
|
|||||||
currentDocumentId: openDocId,
|
currentDocumentId: openDocId,
|
||||||
openInitialDoc,
|
openInitialDoc,
|
||||||
} = useEditorManagerContext()
|
} = useEditorManagerContext()
|
||||||
const { view } = useLayoutContext()
|
|
||||||
const { projectJoined } = useIdeReactContext()
|
const { projectJoined } = useIdeReactContext()
|
||||||
const { selectEntity } = useSelectFileTreeEntity()
|
const { selectEntity } = useSelectFileTreeEntity()
|
||||||
const [, setOpenFile] = useScopeValue<BinaryFile | null>('openFile')
|
const [, setOpenFile] = useScopeValue<BinaryFile | null>('openFile')
|
||||||
@@ -145,93 +104,38 @@ export function EditorAndSidebar() {
|
|||||||
}
|
}
|
||||||
}, [fileTreeReady, openInitialDoc, projectJoined, rootDocId])
|
}, [fileTreeReady, openInitialDoc, projectJoined, rootDocId])
|
||||||
|
|
||||||
// Keep the editor file tree around so that it is available and up to date when restoring a file
|
return {
|
||||||
const editorSidebar = (
|
selectedEntityCount,
|
||||||
<EditorSidebar
|
openEntity,
|
||||||
shouldShow={view !== 'history'}
|
openDocId,
|
||||||
onFileTreeInit={handleFileTreeInit}
|
handleFileTreeInit,
|
||||||
onFileTreeSelect={handleFileTreeSelect}
|
handleFileTreeSelect,
|
||||||
onFileTreeDelete={handleFileTreeDelete}
|
handleFileTreeDelete,
|
||||||
/>
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
if (view === 'history') {
|
function convertFileRefToBinaryFile(fileRef: FileRef): BinaryFile {
|
||||||
return (
|
return {
|
||||||
<TwoColumnMainContent
|
_id: fileRef._id,
|
||||||
leftColumnId="editor-left-column"
|
name: fileRef.name,
|
||||||
leftColumnContent={
|
id: fileRef._id,
|
||||||
<>
|
type: 'file',
|
||||||
{editorSidebar}
|
selected: true,
|
||||||
<HistorySidebar />
|
linkedFileData: fileRef.linkedFileData,
|
||||||
</>
|
created: fileRef.created ? new Date(fileRef.created) : new Date(),
|
||||||
}
|
}
|
||||||
leftColumnDefaultSize={leftColumnDefaultSize}
|
}
|
||||||
setLeftColumnDefaultSize={setLeftColumnDefaultSize}
|
|
||||||
rightColumnContent={
|
// `FileViewHeader`, which is TypeScript, expects a BinaryFile, which has a
|
||||||
<HistoryProvider>
|
// `created` property of type `Date`, while `TPRFileViewInfo`, written in JS,
|
||||||
<History />
|
// into which `FileViewHeader` passes its BinaryFile, expects a file object with
|
||||||
</HistoryProvider>
|
// `created` property of type `string`, which is a mismatch. `TPRFileViewInfo`
|
||||||
}
|
// is the only one making runtime complaints and it seems that other uses of
|
||||||
leftColumnIsOpen={leftColumnIsOpen}
|
// `FileViewHeader` pass in a string for `created`, so that's what this function
|
||||||
setLeftColumnIsOpen={setLeftColumnIsOpen}
|
// does too.
|
||||||
/>
|
export function fileViewFile(fileRef: FileRef) {
|
||||||
)
|
return {
|
||||||
|
...convertFileRefToBinaryFile(fileRef),
|
||||||
|
created: fileRef.created,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always have the editor mounted when not in history view, and hide and
|
|
||||||
// show it as necessary
|
|
||||||
const editorPane = (
|
|
||||||
<EditorPane
|
|
||||||
show={openEntity?.type === 'doc' && selectedEntityCount === 1}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
|
|
||||||
let rightColumnContent
|
|
||||||
|
|
||||||
if (openDocId === undefined) {
|
|
||||||
rightColumnContent = <NoOpenDocPane />
|
|
||||||
} else if (selectedEntityCount === 0) {
|
|
||||||
rightColumnContent = (
|
|
||||||
<>
|
|
||||||
{editorPane}
|
|
||||||
<NoSelectionPane />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
} else if (selectedEntityCount > 1) {
|
|
||||||
rightColumnContent = (
|
|
||||||
<EditorAndPdf
|
|
||||||
editorContent={
|
|
||||||
<>
|
|
||||||
{editorPane}
|
|
||||||
<MultipleSelectionPane selectedEntityCount={selectedEntityCount} />
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
} else if (openEntity) {
|
|
||||||
rightColumnContent = (
|
|
||||||
<EditorAndPdf
|
|
||||||
editorContent={
|
|
||||||
<>
|
|
||||||
{editorPane}
|
|
||||||
{openEntity.type !== 'doc' && (
|
|
||||||
<FileView file={fileViewFile(openEntity.entity)} />
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TwoColumnMainContent
|
|
||||||
leftColumnId="editor-left-column"
|
|
||||||
leftColumnContent={editorSidebar}
|
|
||||||
leftColumnDefaultSize={leftColumnDefaultSize}
|
|
||||||
setLeftColumnDefaultSize={setLeftColumnDefaultSize}
|
|
||||||
rightColumnContent={rightColumnContent}
|
|
||||||
leftColumnIsOpen={leftColumnIsOpen}
|
|
||||||
setLeftColumnIsOpen={setLeftColumnIsOpen}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
@@ -1,32 +1,21 @@
|
|||||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import { ImperativePanelHandle } from 'react-resizable-panels'
|
import {
|
||||||
import { PanelGroupOnLayout } from 'react-resizable-panels/src/types'
|
ImperativePanelHandle,
|
||||||
|
PanelGroupOnLayout,
|
||||||
|
} from 'react-resizable-panels'
|
||||||
|
|
||||||
export default function useFixedSizeColumn(
|
export default function useFixedSizeColumn(isOpen: boolean) {
|
||||||
defaultSize: number,
|
|
||||||
isOpen: boolean
|
|
||||||
) {
|
|
||||||
const fixedPanelRef = useRef<ImperativePanelHandle>(null)
|
const fixedPanelRef = useRef<ImperativePanelHandle>(null)
|
||||||
|
|
||||||
const fixedPanelWidthRef = useRef({ size: defaultSize, pixels: 0 })
|
const fixedPanelSizeRef = useRef<number>(0)
|
||||||
const [initialLayoutDone, setInitialLayoutDone] = useState(false)
|
const [initialLayoutDone, setInitialLayoutDone] = useState(false)
|
||||||
|
|
||||||
const measureFixedPanelSizePixels = useCallback(() => {
|
const handleLayout: PanelGroupOnLayout = useCallback(() => {
|
||||||
return fixedPanelRef.current?.getSize('pixels') || 0
|
if (fixedPanelRef.current) {
|
||||||
}, [fixedPanelRef])
|
fixedPanelSizeRef.current = fixedPanelRef.current.getSize().sizePixels
|
||||||
|
|
||||||
const handleLayout: PanelGroupOnLayout = useCallback(
|
|
||||||
sizes => {
|
|
||||||
// Measure the pixel width here because it's not always up to date in the
|
|
||||||
// panel's onResize
|
|
||||||
fixedPanelWidthRef.current = {
|
|
||||||
size: sizes[0],
|
|
||||||
pixels: measureFixedPanelSizePixels(),
|
|
||||||
}
|
|
||||||
setInitialLayoutDone(true)
|
setInitialLayoutDone(true)
|
||||||
},
|
}
|
||||||
[measureFixedPanelSizePixels]
|
}, [])
|
||||||
)
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
@@ -43,24 +32,26 @@ export default function useFixedSizeColumn(
|
|||||||
const fixedPanelElement = document.querySelector(
|
const fixedPanelElement = document.querySelector(
|
||||||
`[data-panel-id="${fixedPanelRef.current.getId()}"]`
|
`[data-panel-id="${fixedPanelRef.current.getId()}"]`
|
||||||
)
|
)
|
||||||
|
if (!fixedPanelElement) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const panelGroupElement = fixedPanelElement?.closest('[data-panel-group]')
|
const panelGroupElement = fixedPanelElement.closest('[data-panel-group]')
|
||||||
if (!panelGroupElement || !fixedPanelElement) {
|
if (!panelGroupElement) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const resizeObserver = new ResizeObserver(() => {
|
const resizeObserver = new ResizeObserver(() => {
|
||||||
fixedPanelRef.current?.resize(fixedPanelWidthRef.current.pixels, 'pixels')
|
// when the panel group resizes, set the size of this panel to the previous size, in pixels
|
||||||
|
fixedPanelRef.current?.resize({
|
||||||
|
sizePixels: fixedPanelSizeRef.current,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
resizeObserver.observe(panelGroupElement)
|
resizeObserver.observe(panelGroupElement)
|
||||||
|
|
||||||
return () => resizeObserver.unobserve(panelGroupElement)
|
return () => resizeObserver.unobserve(panelGroupElement)
|
||||||
}, [fixedPanelRef, measureFixedPanelSizePixels, initialLayoutDone, isOpen])
|
}, [fixedPanelRef, initialLayoutDone, isOpen])
|
||||||
|
|
||||||
return {
|
return { fixedPanelRef, handleLayout }
|
||||||
fixedPanelRef,
|
|
||||||
fixedPanelWidthRef,
|
|
||||||
handleLayout,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import { useCallback, useEffect, useRef } from 'react'
|
||||||
|
import { ImperativePanelHandle } from 'react-resizable-panels'
|
||||||
|
import useCollapsiblePanel from '@/features/ide-react/hooks/use-collapsible-panel'
|
||||||
|
import { useLayoutContext } from '@/shared/context/layout-context'
|
||||||
|
|
||||||
|
export const usePdfPane = () => {
|
||||||
|
const { view, pdfLayout, changeLayout, detachRole, reattach } =
|
||||||
|
useLayoutContext()
|
||||||
|
|
||||||
|
const pdfPanelRef = useRef<ImperativePanelHandle>(null)
|
||||||
|
const pdfIsOpen = pdfLayout === 'sideBySide' || view === 'pdf'
|
||||||
|
|
||||||
|
useCollapsiblePanel(pdfIsOpen, pdfPanelRef)
|
||||||
|
|
||||||
|
// close a detached PDF when the detacher PDF view opens
|
||||||
|
useEffect(() => {
|
||||||
|
if (pdfIsOpen && detachRole === 'detacher') {
|
||||||
|
reattach()
|
||||||
|
}
|
||||||
|
}, [detachRole, pdfIsOpen, reattach])
|
||||||
|
|
||||||
|
// triggered by a double-click on the resizer
|
||||||
|
const togglePdfPane = useCallback(() => {
|
||||||
|
if (pdfIsOpen) {
|
||||||
|
changeLayout('flat', 'editor')
|
||||||
|
} else {
|
||||||
|
changeLayout('sideBySide')
|
||||||
|
}
|
||||||
|
}, [changeLayout, pdfIsOpen])
|
||||||
|
|
||||||
|
// triggered by a click on the toggle button
|
||||||
|
const setPdfIsOpen = useCallback(
|
||||||
|
(value: boolean) => {
|
||||||
|
if (value) {
|
||||||
|
changeLayout('sideBySide')
|
||||||
|
} else {
|
||||||
|
changeLayout('flat', 'editor')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[changeLayout]
|
||||||
|
)
|
||||||
|
|
||||||
|
// triggered when the PDF pane becomes open
|
||||||
|
const handlePdfPaneExpand = useCallback(() => {
|
||||||
|
if (pdfLayout === 'flat' && view === 'editor') {
|
||||||
|
changeLayout('sideBySide', 'editor')
|
||||||
|
}
|
||||||
|
}, [changeLayout, pdfLayout, view])
|
||||||
|
|
||||||
|
// triggered when the PDF pane becomes closed (either by dragging or toggling)
|
||||||
|
const handlePdfPaneCollapse = useCallback(() => {
|
||||||
|
if (pdfLayout === 'sideBySide') {
|
||||||
|
changeLayout('flat', 'editor')
|
||||||
|
}
|
||||||
|
}, [changeLayout, pdfLayout])
|
||||||
|
|
||||||
|
return {
|
||||||
|
togglePdfPane,
|
||||||
|
handlePdfPaneExpand,
|
||||||
|
handlePdfPaneCollapse,
|
||||||
|
setPdfIsOpen,
|
||||||
|
pdfIsOpen,
|
||||||
|
pdfPanelRef,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { useCallback, useState } from 'react'
|
||||||
|
import useFixedSizeColumn from '@/features/ide-react/hooks/use-fixed-size-column'
|
||||||
|
import useCollapsiblePanel from '@/features/ide-react/hooks/use-collapsible-panel'
|
||||||
|
|
||||||
|
export const useSidebarPane = () => {
|
||||||
|
const [isOpen, setIsOpen] = useState(true)
|
||||||
|
const [resizing, setResizing] = useState(false)
|
||||||
|
const { fixedPanelRef, handleLayout } = useFixedSizeColumn(isOpen)
|
||||||
|
useCollapsiblePanel(isOpen, fixedPanelRef)
|
||||||
|
|
||||||
|
const togglePane = useCallback(() => {
|
||||||
|
setIsOpen(value => !value)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handlePaneExpand = useCallback(() => {
|
||||||
|
setIsOpen(true)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handlePaneCollapse = useCallback(() => {
|
||||||
|
setIsOpen(false)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return {
|
||||||
|
isOpen,
|
||||||
|
setIsOpen,
|
||||||
|
fixedPanelRef,
|
||||||
|
handleLayout,
|
||||||
|
togglePane,
|
||||||
|
handlePaneExpand,
|
||||||
|
handlePaneCollapse,
|
||||||
|
resizing,
|
||||||
|
setResizing,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -145,16 +145,12 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hide panel contents while resizing
|
.ide-panel-group-resizing {
|
||||||
.ide-react-main-resizing .ide-react-editor-and-pdf,
|
|
||||||
.ide-react-main-content-resizing .ide-react-editor-and-pdf,
|
|
||||||
.ide-react-editor-and-pdf-resizing .ide-react-editor-content,
|
|
||||||
.ide-react-editor-and-pdf-resizing .pdf {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ide-react-main-resizing,
|
|
||||||
.ide-react-main-content-resizing,
|
|
||||||
.ide-react-editor-and-pdf-resizing {
|
|
||||||
background-color: white;
|
background-color: white;
|
||||||
|
|
||||||
|
// Hide panel contents while resizing
|
||||||
|
.ide-react-editor-content,
|
||||||
|
.pdf {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -321,7 +321,7 @@
|
|||||||
"react-i18next": "^13.3.1",
|
"react-i18next": "^13.3.1",
|
||||||
"react-linkify": "^1.0.0-alpha",
|
"react-linkify": "^1.0.0-alpha",
|
||||||
"react-refresh": "^0.14.0",
|
"react-refresh": "^0.14.0",
|
||||||
"react-resizable-panels": "^0.0.55",
|
"react-resizable-panels": "^0.0.63",
|
||||||
"react2angular": "^4.0.6",
|
"react2angular": "^4.0.6",
|
||||||
"react2angular-shared-context": "^1.1.0",
|
"react2angular-shared-context": "^1.1.0",
|
||||||
"requirejs": "^2.3.6",
|
"requirejs": "^2.3.6",
|
||||||
|
|||||||
Reference in New Issue
Block a user