Files
siftlode/frontend/src/components/PanelGroup.tsx
T
peter c91caffa5e refactor(z-index): name the stacking layers as a token scale
Replace ~48 magic z-index values (z-10..z-[9999] + 2 inline zIndex) with named
tailwind zIndex tokens (base/menu/chrome/paneltab/panel/rail/overlay/popover/
tooltip/tuner). Values are UNCHANGED — a rename, not a renumber — so layering is
identical; the names give one source of truth for the stack.
2026-07-18 19:11:25 +02:00

85 lines
2.9 KiB
TypeScript

import { type ReactNode, type CSSProperties } from "react";
import { useTranslation } from "react-i18next";
import { ChevronDown, Eye, EyeOff, GripVertical } from "lucide-react";
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
// One collapsible "island" card inside a SidePanel: a titled glass-card with per-group collapse,
// and — in the panel's edit mode — a drag handle (reorder) and a hide toggle. Generalized from
// the feed sidebar's WidgetCard so all three panels share the same island look and behavior.
export default function PanelGroup({
id,
title,
editing,
collapsed,
hidden,
onToggleCollapse,
onToggleHidden,
children,
}: {
id: string;
title: string;
editing: boolean;
collapsed: boolean;
hidden: boolean;
onToggleCollapse: () => void;
onToggleHidden: () => void;
children: ReactNode;
}) {
const { t } = useTranslation();
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
id,
disabled: !editing,
});
const style: CSSProperties = { transform: CSS.Transform.toString(transform), transition };
const showBody = !editing && !collapsed;
return (
<div
ref={setNodeRef}
style={style}
className={`glass-card rounded-xl ${
isDragging ? "relative z-base shadow-2xl ring-1 ring-accent" : ""
} ${hidden && editing ? "opacity-50" : ""}`}
>
<div className="flex items-center gap-1.5 px-3 py-2">
{editing && (
<button
{...attributes}
{...listeners}
title={t("sidebar.dragToReorder")}
className="-ml-1 cursor-grab active:cursor-grabbing text-muted hover:text-fg touch-none"
>
<GripVertical className="w-4 h-4" />
</button>
)}
<button
onClick={editing ? undefined : onToggleCollapse}
disabled={editing}
className="flex-1 min-w-0 flex items-center text-left select-none text-xs uppercase tracking-wide text-muted enabled:hover:text-fg transition"
>
<span className="truncate">{title}</span>
</button>
{editing ? (
<button
onClick={onToggleHidden}
title={hidden ? t("sidebar.showWidget") : t("sidebar.hideWidget")}
className="text-muted hover:text-fg"
>
{hidden ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
</button>
) : (
<button
onClick={onToggleCollapse}
title={collapsed ? t("sidebar.expand") : t("sidebar.collapse")}
className="text-muted hover:text-fg"
>
<ChevronDown className={`w-4 h-4 transition-transform ${collapsed ? "-rotate-90" : ""}`} />
</button>
)}
</div>
{showBody && <div className="px-3 pb-3">{children}</div>}
</div>
);
}