Files
Entropyk/apps/web/src/components/palette/ComponentPalette.tsx
sepehr 3358b74342 Add diagram workbench UI with Modelica DoF coaching and ISO glyphs.
Ship the Next.js cycle editor with CAD chrome, technical HX symbols, Fixed/Free boundary guidance, and secondary water/air pressure drop support in the solver stack.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-17 22:46:46 +02:00

119 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useState } from "react";
import { ChevronDown, ChevronRight } from "lucide-react";
import { COMPONENTS, COMPONENT_CATEGORIES, type ComponentMeta } from "@/lib/componentMeta";
import { ComponentIcon } from "@/components/canvas/ComponentIcon";
import { hxFamily } from "@/lib/hxFamily";
export default function ComponentPalette() {
const [collapsed, setCollapsed] = useState<Record<string, boolean>>({
Advanced: true,
Generic: true,
});
const toggle = (cat: string) => setCollapsed((c) => ({ ...c, [cat]: !c[cat] }));
const onDragStart = (e: React.DragEvent, type: string) => {
e.dataTransfer.setData("application/entropyk-type", type);
e.dataTransfer.effectAllowed = "move";
};
return (
<aside className="flex h-full w-60 flex-col border-r border-[var(--line)] bg-[var(--panel)]">
<div className="flex h-9 items-center justify-between px-3">
<span className="eyebrow">Bibliothèque</span>
<span className="mono text-[10px] text-[var(--ink-faint)]">{COMPONENTS.length}</span>
</div>
<div className="h-px w-full bg-[var(--line)]" />
<div className="flex-1 overflow-y-auto py-1">
{COMPONENT_CATEGORIES.map((cat) => {
const items = COMPONENTS.filter((c) => c.category === cat);
if (items.length === 0) return null;
const isOpen = !collapsed[cat];
return (
<div key={cat} className="palette-cat">
<button
type="button"
onClick={() => toggle(cat)}
aria-expanded={isOpen}
className="palette-cat-toggle flex w-full items-center gap-1 px-2.5 py-1.5 text-left hover:bg-[var(--chrome-2)]"
>
{isOpen ? (
<ChevronDown size={12} className="text-[var(--ink-faint)]" />
) : (
<ChevronRight size={12} className="text-[var(--ink-faint)]" />
)}
<span className="eyebrow">{cat}</span>
<span className="mono ml-1 text-[9px] text-[var(--ink-faint)]">{items.length}</span>
{cat === "Advanced" && (
<span className="ml-auto text-[8px] font-medium text-[var(--ink-faint)]">
rare
</span>
)}
</button>
<div className={`palette-cat-body ${isOpen ? "is-open" : "is-closed"}`}>
<div className="palette-cat-inner pb-1">
{items.map((c, i) => (
<PaletteItem
key={c.type}
meta={c}
index={i}
onDragStart={onDragStart}
/>
))}
</div>
</div>
</div>
);
})}
</div>
<div className="h-px w-full bg-[var(--line)]" />
<p className="px-3 py-2 text-[10px] leading-relaxed text-[var(--ink-faint)]">
Glisse un composant sur le schéma. Relie les ports. Un pipe/gaine déposé sur une ligne
sinsère automatiquement (même milieu : vert / bleu / jaune).
</p>
</aside>
);
}
function PaletteItem({
meta,
index,
onDragStart,
}: {
meta: ComponentMeta;
index: number;
onDragStart: (e: React.DragEvent, type: string) => void;
}) {
const family = hxFamily(meta.type);
const color = family?.accent ?? meta.color;
return (
<div
draggable
onDragStart={(e) => onDragStart(e, meta.type)}
className="palette-item group mx-1.5 flex cursor-grab items-center gap-2.5 rounded-[2px] px-2 py-1.5 hover:bg-[var(--chrome-2)] active:cursor-grabbing"
style={{ ["--i" as string]: index }}
title={meta.help ?? meta.description}
>
<span className="grid h-9 w-9 flex-shrink-0 place-items-center">
<span style={{ width: 30, height: 30 }}>
<ComponentIcon type={meta.type} color={color} />
</span>
</span>
<span className="min-w-0 flex-1">
<span className="block truncate text-[12px] text-[var(--ink-dim)] group-hover:text-[var(--ink)]">
{meta.label}
</span>
{family && (
<span className="mono text-[8px] uppercase tracking-[0.08em] text-[var(--ink-faint)]">
{family.badge} · {family.label}
</span>
)}
</span>
</div>
);
}