/* eslint-disable react-refresh/only-export-components */
import { Children, Fragment, isValidElement, useRef } from "react";
import type React from "react";
import {
ArrowLeft,
Columns3,
Grid3X3,
List,
Rows3,
Search,
X,
} from "lucide-react";
import { useTranslation } from "react-i18next";
import { cn } from "@/lib/utils";
import { Separator } from "@/components/separator";
import { DocsLink } from "@/components/docs-link";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/select";
/** One spacing rhythm for every panel and tab. */
export const PANEL = {
body: "p-2.5",
gap: "gap-2",
band: "px-3 py-2",
cardHead: "px-3 py-2.5",
header: "h-12.5",
} as const;
export type PanelViewMode = "grid" | "list";
export type PanelDensity = "comfortable" | "compact";
/** Cards per row. "auto" fits as many as the pane has room for. */
export type GridSize = "auto" | "1" | "2" | "3" | "4" | "5" | "6";
export const GRID_SIZES: readonly GridSize[] = [
"auto",
"1",
"2",
"3",
"4",
"5",
"6",
];
// Sized off the pane's own width, not the window. Cards keep a min width,
// so narrow panes drop columns on their own.
export function gridSizeStyle(
size: GridSize,
minCardWidth = 320,
gap = "0.5rem",
): React.CSSProperties {
if (size === "auto") {
return {
gridTemplateColumns: `repeat(auto-fill, minmax(min(100%, ${minCardWidth}px), 1fr))`,
};
}
const n = Number(size);
return {
gridTemplateColumns: `repeat(auto-fill, minmax(max(min(100%, 220px), calc((100% - ${n - 1} * ${gap}) / ${n} - 0.5px)), 1fr))`,
};
}
/** The header, toolbar band and body every tab and panel draws. */
export function PanelShell({
chrome = true,
icon,
title,
status,
leading,
actions,
toolbar,
tabs,
footer,
scroll = true,
docs,
className,
children,
}: {
/** False inside a sidebar, which draws its own header. */
chrome?: boolean;
icon?: React.ReactNode;
title?: React.ReactNode;
/** Short caption beside the title, like "12 of 18 running". */
status?: React.ReactNode;
/** Sits before the title, usually a BackButton. */
leading?: React.ReactNode;
actions?: React.ReactNode;
toolbar?: React.ReactNode;
/** A full-bleed tab strip in place of a toolbar. */
tabs?: React.ReactNode;
/** A bar pinned under the body, like Cancel and Save. */
footer?: React.ReactNode;
/** False when the body scrolls itself. */
scroll?: boolean;
/** Docs URL for this screen, shown as a book icon in the header. */
docs?: string | null;
className?: string;
children: React.ReactNode;
}) {
const body = (