Files
kandev/apps/web/lib/layout/use-default-layout.ts
2026-02-19 22:18:01 +00:00

78 lines
2.5 KiB
TypeScript

"use client";
import { useCallback, useMemo } from "react";
import { getLocalStorage, setLocalStorage } from "@/lib/local-storage";
import type { Layout } from "react-resizable-panels";
const MIN_PANEL_PERCENT = 5;
const buildDefaultLayout = (ids: string[], base: Record<string, number>): Layout => {
// For unknown panel IDs, assign the average of known values so they get reasonable space
const knownValues = ids.map((id) => base[id]).filter((v): v is number => v != null && v > 0);
const fallback =
knownValues.length > 0 ? knownValues.reduce((a, b) => a + b, 0) / knownValues.length : 50;
const raw = ids.map((id) => base[id] ?? fallback);
const total = raw.reduce((sum, value) => sum + value, 0) || 1;
const normalized = raw.map((value) => (value / total) * 100);
const result: Layout = {};
ids.forEach((id, index) => {
result[id] = normalized[index];
});
return result;
};
const isLayoutValid = (layout: Layout, panelIds: string[]): boolean => {
return panelIds.every((id) => typeof layout[id] === "number" && layout[id] >= MIN_PANEL_PERCENT);
};
const cookieKeyForId = (id: string) => `layout:${encodeURIComponent(id)}`;
type UseDefaultLayoutParams = {
id: string;
panelIds?: string[];
baseLayout?: Record<string, number>;
serverDefaultLayout?: Layout;
};
export function useDefaultLayout({
id,
panelIds,
baseLayout,
serverDefaultLayout,
}: UseDefaultLayoutParams) {
const defaultLayout = useMemo(() => {
const stored = getLocalStorage(id, null as unknown as Layout | null);
if (stored && typeof stored === "object" && !Array.isArray(stored)) {
if (!panelIds || isLayoutValid(stored, panelIds)) {
return stored;
}
}
if (serverDefaultLayout && typeof serverDefaultLayout === "object") {
if (!panelIds || isLayoutValid(serverDefaultLayout, panelIds)) {
return serverDefaultLayout;
}
}
if (panelIds && baseLayout) {
return buildDefaultLayout(panelIds, baseLayout);
}
return undefined;
}, [id, panelIds, baseLayout, serverDefaultLayout]);
const onLayoutChanged = useCallback(
(layout: Layout) => {
if (panelIds && !panelIds.every((panelId) => typeof layout[panelId] === "number")) return;
setLocalStorage(id, layout);
if (typeof document !== "undefined") {
document.cookie = `${cookieKeyForId(id)}=${encodeURIComponent(
JSON.stringify(layout),
)}; path=/;`;
}
},
[id, panelIds],
);
return { defaultLayout, onLayoutChanged };
}