feat(devtool): 可拖动面板支持记住上次面板尺寸

This commit is contained in:
XcantloadX 2025-02-05 10:39:08 +08:00
parent 62e3ae044f
commit 2e6e9a9367
4 changed files with 66 additions and 5 deletions

View File

@ -1,6 +1,7 @@
import React, { useState, useEffect, Children } from 'react';
import styled from '@emotion/styled';
import { useResizePanel } from '../hooks/useResizePanel';
import { SplitableSizesStorage } from '../utils/storageUtils';
interface SplitableProps {
children: React.ReactNode;
@ -12,8 +13,13 @@ interface SplitableProps {
onCollapsedChange?: (collapsed: boolean) => void;
/** 是否显示折叠按钮 */
collapseButton?: boolean;
/** 每个面板的默认宽度null 表示不设置默认宽度 */
/** 每个面板的默认宽度null 表示不设置默认宽度。优先级低于 memorizeSizesKey 记住的大小 */
defaultSize?: (number | null)[];
/**
* localStorage
* defaultSize使
*/
memorizeSizesKey?: string;
}
const Container = styled.div<{ $vertical?: boolean }>`
@ -110,17 +116,31 @@ export const Splitable: React.FC<SplitableProps> = ({
defaultCollapsed = false,
onCollapsedChange,
collapseButton = false,
defaultSize
defaultSize,
memorizeSizesKey
}) => {
const childrenArray = Children.toArray(children);
const [collapsed, setCollapsed] = useState(defaultCollapsed);
const [panelSizes, setPanelSizes] = useState<number[]>(() => {
if (memorizeSizesKey) {
const memorizedSizes = SplitableSizesStorage.loadSizes(memorizeSizesKey);
if (memorizedSizes && memorizedSizes.length === childrenArray.length) {
return memorizedSizes;
}
}
if (defaultSize) {
return defaultSize.map(size => size ?? 400);
}
return Array(childrenArray.length).fill(400);
});
// 当面板大小改变时保存到 localStorage
useEffect(() => {
if (memorizeSizesKey) {
SplitableSizesStorage.saveSizes(memorizeSizesKey, panelSizes);
}
}, [memorizeSizesKey, panelSizes]);
// 为每个可调整大小的面板创建一个 useResizePanel 实例
const resizePanels = childrenArray.map((_, index) => {
if (index === 0) return null; // 第一个面板不需要调整大小

View File

@ -1470,6 +1470,7 @@ function VSToolBarDemo(): JSX.Element {
function SplitableDemo(): JSX.Element {
const [direction, setDirection] = useState<'horizontal' | 'vertical'>('horizontal');
const [panelCount, setPanelCount] = useState(2);
const [useMemory, setUseMemory] = useState(true);
const panels = Array.from({ length: panelCount }, (_, i) => (
<div
@ -1502,6 +1503,9 @@ function SplitableDemo(): JSX.Element {
<Button onClick={() => setPanelCount(c => Math.max(c - 1, 2))}>
</Button>
<Button onClick={() => setUseMemory(m => !m)}>
{useMemory ? '禁用大小记忆' : '启用大小记忆'}
</Button>
</ControlPanel>
<div style={{
height: '500px',
@ -1509,7 +1513,10 @@ function SplitableDemo(): JSX.Element {
borderRadius: '4px',
overflow: 'hidden'
}}>
<Splitable vertical={direction === 'vertical'}>
<Splitable
vertical={direction === 'vertical'}
memorizeSizesKey={useMemory ? `demo-splitable-${direction}-${panelCount}` : undefined}
>
{panels}
</Splitable>
</div>
@ -1526,6 +1533,13 @@ function SplitableDemo(): JSX.Element {
<li>/</li>
<li></li>
<li></li>
<li></li>
<ul>
<li></li>
<li>使</li>
<li>"禁用大小记忆"</li>
<li> localStorage </li>
</ul>
</ul>
</div>
</div>

View File

@ -693,7 +693,7 @@ const ScriptRecorder: React.FC = () => {
/>
)}
<div css={css`height: 100%; margin-top: 0;`}>
<Splitable>
<Splitable memorizeSizesKey='ScriptRecorder.ImageViewer'>
<ImageViewerWrapper>
<ImageEditor
enableMask
@ -710,7 +710,7 @@ const ScriptRecorder: React.FC = () => {
code={code}
client={client}
/>
<Splitable vertical defaultSize={[null, 200]}>
<Splitable vertical defaultSize={[null, 200]} memorizeSizesKey='ScriptRecorder.CodeEditor'>
<AceEditor
ref={editorRef}
mode="python"

View File

@ -89,3 +89,30 @@ export class ScriptRecorderStorage {
}
}
}
export class SplitableSizesStorage {
private static readonly STORAGE_KEY = 'splitable';
static saveSizes(key: string, sizes: number[]): void {
const data = this.loadAllSizes();
data[key] = sizes;
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(data));
}
static loadSizes(key: string): number[] | null {
const data = this.loadAllSizes();
return data[key] || null;
}
private static loadAllSizes(): Record<string, number[]> {
const dataStr = localStorage.getItem(this.STORAGE_KEY);
if (!dataStr) return {};
try {
return JSON.parse(dataStr);
} catch (e) {
console.error('Failed to parse splitable sizes data:', e);
return {};
}
}
}