From da26648a9b787d221ba8817957e6507278322e17 Mon Sep 17 00:00:00 2001 From: cfngc4594 Date: Sun, 6 Apr 2025 18:16:43 +0800 Subject: [PATCH] feat(dockview): refactor component API and add customization options --- src/components/dockview.tsx | 128 ++++++++++++++++-------------------- 1 file changed, 58 insertions(+), 70 deletions(-) diff --git a/src/components/dockview.tsx b/src/components/dockview.tsx index e1840d5..69c7945 100644 --- a/src/components/dockview.tsx +++ b/src/components/dockview.tsx @@ -1,108 +1,96 @@ "use client"; -import { - BotIcon, - CircleCheckBigIcon, - FileTextIcon, - FlaskConicalIcon, - SquareCheckIcon, - SquarePenIcon, - TerminalIcon, -} from "lucide-react"; +import type { + AddPanelOptions, + DockviewApi, + DockviewReadyEvent, + IDockviewPanelHeaderProps, + IDockviewPanelProps, +} from "dockview"; import "@/styles/dockview.css"; -import { useEffect, useMemo, useState } from "react"; +import { useEffect, useState } from "react"; +import type { LucideIcon } from "lucide-react"; import { DockviewReact, themeAbyssSpaced } from "dockview"; -import type { AddPanelOptions, DockviewReadyEvent, DockviewApi } from "dockview"; - -const iconMap = { - FileTextIcon, - FlaskConicalIcon, - CircleCheckBigIcon, - SquarePenIcon, - SquareCheckIcon, - TerminalIcon, - BotIcon, -} as const; - -type IconKey = keyof typeof iconMap; interface DockviewProps { - options: (AddPanelOptions & { - node: React.ReactNode; - icon: IconKey; - })[]; storageKey: string; + options: AddPanelOptions[]; + components: Record>; + tabComponents?: Record>; + onApiReady?: (api: DockviewApi) => void; } -const DockView = ({ options, storageKey }: DockviewProps) => { +const DefaultTab = ( + props: IDockviewPanelHeaderProps<{ icon?: LucideIcon }> +) => { + const { icon: Icon } = props.params; + + return ( +
+ {Icon && ( +
+ ); +}; + +export default function DockView({ + storageKey, + options, + components, + tabComponents, + onApiReady, +}: DockviewProps) { const [api, setApi] = useState(); - const { components, tabComponents } = useMemo(() => { - const comps: Record React.ReactNode> = {}; - const tabs: Record React.ReactNode> = {}; - - options.forEach((option) => { - const { id, icon, node, title } = option; - - comps[id] = () => <>{node}; - - const Icon = iconMap[icon]; - tabs[id] = () => ( -
-
- ); - }); - - return { components: comps, tabComponents: tabs }; - }, [options]); - useEffect(() => { if (!api) return; const disposable = api.onDidLayoutChange(() => { - localStorage.setItem(storageKey, JSON.stringify(api.toJSON())); + const layout = api.toJSON(); + localStorage.setItem(storageKey, JSON.stringify(layout)); }); return () => disposable.dispose(); }, [api, storageKey]); - const handleReady = (event: DockviewReadyEvent) => { + const onReady = (event: DockviewReadyEvent) => { setApi(event.api); - const serializedLayout = localStorage.getItem(storageKey); + onApiReady?.(event.api); - const addDefaultPanels = () => { - options.forEach((option) => { - event.api.addPanel({ ...option }); - }); - }; + let success = false; + const serializedLayout = localStorage.getItem(storageKey); if (serializedLayout) { try { - event.api.fromJSON(JSON.parse(serializedLayout)); + const layout = JSON.parse(serializedLayout); + event.api.fromJSON(layout); + success = true; } catch (error) { - console.error("Failed to parse layout:", error); + console.error("Failed to load layout:", error); localStorage.removeItem(storageKey); - addDefaultPanels(); } - } else { - addDefaultPanels(); + } + + if (!success) { + options.forEach((option) => { + event.api.addPanel({ ...option }); + }); } }; return ( ); -}; - -export default DockView; +}