persistence in panels
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { useState, useCallback, useEffect } from 'react';
|
import { useState, useCallback, useEffect, useRef, type ComponentRef } from 'react';
|
||||||
import { flushSync } from 'react-dom';
|
import { flushSync } from 'react-dom';
|
||||||
import { ResizablePanel, ResizablePanelGroup, ResizableHandle } from '@/components/ui/resizable';
|
import { ResizablePanel, ResizablePanelGroup, ResizableHandle } from '@/components/ui/resizable';
|
||||||
import type { LayoutNode, WorkspaceState, EphemeralPanels, PanelComponents } from './types';
|
import type { LayoutNode, WorkspaceState, EphemeralPanels, PanelComponents } from './types';
|
||||||
@@ -111,43 +111,22 @@ export const WorkspaceView = ({ workspace, cwd = '~', root, initialFilePath, def
|
|||||||
return () => window.removeEventListener('keydown', onKeyDown);
|
return () => window.removeEventListener('keydown', onKeyDown);
|
||||||
}, [swapSourceId, dragSourceId, maximizedPanelId, setMaximizedAnimated]);
|
}, [swapSourceId, dragSourceId, maximizedPanelId, setMaximizedAnimated]);
|
||||||
|
|
||||||
|
const ephemeralPanelRef = useRef<ComponentRef<typeof ResizablePanel>>(null);
|
||||||
|
const isEphemeralOpen = !!ephemeral;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const panel = ephemeralPanelRef.current;
|
||||||
|
if (!panel) return;
|
||||||
|
|
||||||
|
if (isEphemeralOpen) {
|
||||||
|
panel.resize(100 - (ephemeral.defaultBaseSize ?? 40));
|
||||||
|
} else {
|
||||||
|
panel.collapse();
|
||||||
|
}
|
||||||
|
}, [isEphemeralOpen, ephemeral?.defaultBaseSize]);
|
||||||
|
|
||||||
if (!workspace.isLoaded) return null;
|
if (!workspace.isLoaded) return null;
|
||||||
|
|
||||||
const baseRenderer = (
|
|
||||||
<WorkspaceRenderer
|
|
||||||
layout={layout}
|
|
||||||
registry={registry}
|
|
||||||
components={components}
|
|
||||||
interactive
|
|
||||||
onSetApp={handleSetApp}
|
|
||||||
onSplit={handleSplit}
|
|
||||||
onRemove={handleRemove}
|
|
||||||
onResized={handleResized}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
const content = ephemeral ? (
|
|
||||||
<ResizablePanelGroup direction="horizontal" className="h-full w-full">
|
|
||||||
<ResizablePanel defaultSize={ephemeral.defaultBaseSize ?? 40} minSize={15}>
|
|
||||||
{baseRenderer}
|
|
||||||
</ResizablePanel>
|
|
||||||
<ResizableHandle className="bg-transparent after:bg-transparent" />
|
|
||||||
<ResizablePanel defaultSize={100 - (ephemeral.defaultBaseSize ?? 40)} minSize={15}>
|
|
||||||
<WorkspaceRenderer
|
|
||||||
layout={ephemeral.layout}
|
|
||||||
registry={registry}
|
|
||||||
components={ephemeral.components}
|
|
||||||
onSetApp={noop}
|
|
||||||
onSplit={noop}
|
|
||||||
onRemove={noop}
|
|
||||||
onResized={noop}
|
|
||||||
/>
|
|
||||||
</ResizablePanel>
|
|
||||||
</ResizablePanelGroup>
|
|
||||||
) : (
|
|
||||||
baseRenderer
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WorkspaceProvider
|
<WorkspaceProvider
|
||||||
value={{
|
value={{
|
||||||
@@ -167,7 +146,40 @@ export const WorkspaceView = ({ workspace, cwd = '~', root, initialFilePath, def
|
|||||||
transitioningPanelId,
|
transitioningPanelId,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{content}
|
<ResizablePanelGroup direction="horizontal" className="h-full w-full">
|
||||||
|
<ResizablePanel defaultSize={100} minSize={15}>
|
||||||
|
<WorkspaceRenderer
|
||||||
|
layout={layout}
|
||||||
|
registry={registry}
|
||||||
|
components={components}
|
||||||
|
interactive
|
||||||
|
onSetApp={handleSetApp}
|
||||||
|
onSplit={handleSplit}
|
||||||
|
onRemove={handleRemove}
|
||||||
|
onResized={handleResized}
|
||||||
|
/>
|
||||||
|
</ResizablePanel>
|
||||||
|
<ResizableHandle className="bg-transparent after:bg-transparent" disabled={!isEphemeralOpen} />
|
||||||
|
<ResizablePanel
|
||||||
|
ref={ephemeralPanelRef}
|
||||||
|
collapsible
|
||||||
|
collapsedSize={0}
|
||||||
|
defaultSize={0}
|
||||||
|
minSize={15}
|
||||||
|
>
|
||||||
|
{isEphemeralOpen ? (
|
||||||
|
<WorkspaceRenderer
|
||||||
|
layout={ephemeral.layout}
|
||||||
|
registry={registry}
|
||||||
|
components={ephemeral.components}
|
||||||
|
onSetApp={noop}
|
||||||
|
onSplit={noop}
|
||||||
|
onRemove={noop}
|
||||||
|
onResized={noop}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</ResizablePanel>
|
||||||
|
</ResizablePanelGroup>
|
||||||
</WorkspaceProvider>
|
</WorkspaceProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { useCallback, useMemo } from 'react';
|
||||||
import { useSearchParams } from 'react-router';
|
import { useSearchParams } from 'react-router';
|
||||||
import type { EphemeralPanels } from '../../components/Workspace';
|
import type { EphemeralPanels } from '../../components/Workspace';
|
||||||
import { FileViewerHeader, FileViewerBody } from '../../apps/FileViewer';
|
import { FileViewerHeader, FileViewerBody } from '../../apps/FileViewer';
|
||||||
@@ -17,18 +18,10 @@ export const useFileViewerPanels = (): EphemeralPanels | null => {
|
|||||||
? viewerWithEphemeralLayout
|
? viewerWithEphemeralLayout
|
||||||
: singleViewerLayout;
|
: singleViewerLayout;
|
||||||
|
|
||||||
const components = {
|
const onCloseViewer = useCallback(() => setSearchParams({}), [setSearchParams]);
|
||||||
'files-viewer': {
|
|
||||||
provider: ViewerProvider,
|
const onCloseEphemeral = useCallback(
|
||||||
header: FileViewerHeader,
|
() =>
|
||||||
component: FileViewerBody,
|
|
||||||
onClose: () => setSearchParams({}),
|
|
||||||
},
|
|
||||||
'files-ephemeral': {
|
|
||||||
provider: EphemeralProvider,
|
|
||||||
header: FileViewerHeader,
|
|
||||||
component: FileViewerBody,
|
|
||||||
onClose: () =>
|
|
||||||
setSearchParams((prev) => {
|
setSearchParams((prev) => {
|
||||||
const next = new URLSearchParams(prev);
|
const next = new URLSearchParams(prev);
|
||||||
next.delete('ephemeral');
|
next.delete('ephemeral');
|
||||||
@@ -38,12 +31,11 @@ export const useFileViewerPanels = (): EphemeralPanels | null => {
|
|||||||
next.delete('ephemeral2Auto');
|
next.delete('ephemeral2Auto');
|
||||||
return next;
|
return next;
|
||||||
}),
|
}),
|
||||||
},
|
[setSearchParams],
|
||||||
'files-ephemeral2': {
|
);
|
||||||
provider: Ephemeral2Provider,
|
|
||||||
header: FileViewerHeader,
|
const onCloseEphemeral2 = useCallback(
|
||||||
component: FileViewerBody,
|
() =>
|
||||||
onClose: () =>
|
|
||||||
setSearchParams((prev) => {
|
setSearchParams((prev) => {
|
||||||
const next = new URLSearchParams(prev);
|
const next = new URLSearchParams(prev);
|
||||||
next.delete('ephemeral2');
|
next.delete('ephemeral2');
|
||||||
@@ -51,8 +43,32 @@ export const useFileViewerPanels = (): EphemeralPanels | null => {
|
|||||||
next.delete('ephemeral2Auto');
|
next.delete('ephemeral2Auto');
|
||||||
return next;
|
return next;
|
||||||
}),
|
}),
|
||||||
|
[setSearchParams],
|
||||||
|
);
|
||||||
|
|
||||||
|
const components = useMemo(
|
||||||
|
() => ({
|
||||||
|
'files-viewer': {
|
||||||
|
provider: ViewerProvider,
|
||||||
|
header: FileViewerHeader,
|
||||||
|
component: FileViewerBody,
|
||||||
|
onClose: onCloseViewer,
|
||||||
},
|
},
|
||||||
};
|
'files-ephemeral': {
|
||||||
|
provider: EphemeralProvider,
|
||||||
|
header: FileViewerHeader,
|
||||||
|
component: FileViewerBody,
|
||||||
|
onClose: onCloseEphemeral,
|
||||||
|
},
|
||||||
|
'files-ephemeral2': {
|
||||||
|
provider: Ephemeral2Provider,
|
||||||
|
header: FileViewerHeader,
|
||||||
|
component: FileViewerBody,
|
||||||
|
onClose: onCloseEphemeral2,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
[onCloseViewer, onCloseEphemeral, onCloseEphemeral2],
|
||||||
|
);
|
||||||
|
|
||||||
if (!viewPath) return null;
|
if (!viewPath) return null;
|
||||||
return { layout, components, defaultBaseSize: 40 };
|
return { layout, components, defaultBaseSize: 40 };
|
||||||
|
|||||||
Reference in New Issue
Block a user