refactor(components): migrate DockView to dynamic panel/tab components with store-based API exposure and autoAdd flag

This commit is contained in:
cfngc4594 2025-04-13 11:35:29 +08:00
parent 737965dc91
commit 56782b83fc

View File

@ -8,28 +8,46 @@ import type {
IDockviewPanelProps, IDockviewPanelProps,
} from "dockview"; } from "dockview";
import "@/styles/dockview.css"; import "@/styles/dockview.css";
import * as Icons from "lucide-react";
import { useEffect, useState } from "react";
import type { LucideIcon } from "lucide-react"; import type { LucideIcon } from "lucide-react";
import { useDockviewStore } from "@/stores/dockview";
import { useEffect, useMemo, useState } from "react";
import { DockviewReact, themeAbyssSpaced } from "dockview"; import { DockviewReact, themeAbyssSpaced } from "dockview";
interface PanelContent {
icon?: LucideIcon;
content?: React.ReactNode;
autoAdd?: boolean;
}
interface DockviewProps { interface DockviewProps {
storageKey: string; storageKey: string;
options: AddPanelOptions[]; options: AddPanelOptions<PanelContent>[];
components: Record<string, React.FunctionComponent<IDockviewPanelProps>>;
tabComponents?: Record<string, React.FunctionComponent<IDockviewPanelHeaderProps>>;
onApiReady?: (api: DockviewApi) => void;
} }
const DefaultTab = ( export default function DockView({ storageKey, options }: DockviewProps) {
props: IDockviewPanelHeaderProps<{ icon?: string }> const { setApi: _setApi } = useDockviewStore();
) => { const [api, setApi] = useState<DockviewApi>();
const { icon } = props.params;
const Icon =
icon && icon in Icons
? (Icons[icon as keyof typeof Icons] as LucideIcon)
: null;
const { components, tabComponents } = useMemo(() => {
const components: Record<
string,
React.FunctionComponent<IDockviewPanelProps<PanelContent>>
> = {};
const tabComponents: Record<
string,
React.FunctionComponent<IDockviewPanelHeaderProps<PanelContent>>
> = {};
options.forEach((option) => {
const { id, params } = option;
components[id] = () => {
const content = params?.content;
return <>{content}</>;
};
tabComponents[id] = (props) => {
const Icon = params?.icon;
return ( return (
<div className="flex items-center px-1 text-sm font-medium"> <div className="flex items-center px-1 text-sm font-medium">
{Icon && ( {Icon && (
@ -43,15 +61,10 @@ const DefaultTab = (
</div> </div>
); );
}; };
});
export default function DockView({ return { components, tabComponents };
storageKey, }, [options]);
options,
components,
tabComponents,
onApiReady,
}: DockviewProps) {
const [api, setApi] = useState<DockviewApi>();
useEffect(() => { useEffect(() => {
if (!api) return; if (!api) return;
@ -66,7 +79,7 @@ export default function DockView({
const onReady = (event: DockviewReadyEvent) => { const onReady = (event: DockviewReadyEvent) => {
setApi(event.api); setApi(event.api);
onApiReady?.(event.api); _setApi(event.api);
let success = false; let success = false;
const serializedLayout = localStorage.getItem(storageKey); const serializedLayout = localStorage.getItem(storageKey);
@ -84,6 +97,8 @@ export default function DockView({
if (!success) { if (!success) {
options.forEach((option) => { options.forEach((option) => {
const autoAdd = option.params?.autoAdd ?? true;
if (!autoAdd) return;
event.api.addPanel({ ...option }); event.api.addPanel({ ...option });
}); });
} }
@ -94,7 +109,6 @@ export default function DockView({
theme={themeAbyssSpaced} theme={themeAbyssSpaced}
onReady={onReady} onReady={onReady}
components={components} components={components}
defaultTabComponent={DefaultTab}
tabComponents={tabComponents} tabComponents={tabComponents}
/> />
); );