@diwauhris/ui 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +75 -0
- package/lib/assets/ui.css +154 -0
- package/lib/index.cjs +183 -0
- package/lib/index.mjs +65 -0
- package/lib/index10.cjs +33 -0
- package/lib/index10.cjs.map +1 -0
- package/lib/index10.mjs +33 -0
- package/lib/index10.mjs.map +1 -0
- package/lib/index11.cjs +16 -0
- package/lib/index11.cjs.map +1 -0
- package/lib/index11.mjs +16 -0
- package/lib/index11.mjs.map +1 -0
- package/lib/index12.cjs +16 -0
- package/lib/index12.cjs.map +1 -0
- package/lib/index12.mjs +15 -0
- package/lib/index12.mjs.map +1 -0
- package/lib/index13.cjs +57 -0
- package/lib/index13.cjs.map +1 -0
- package/lib/index13.mjs +57 -0
- package/lib/index13.mjs.map +1 -0
- package/lib/index14.cjs +10 -0
- package/lib/index14.cjs.map +1 -0
- package/lib/index14.mjs +10 -0
- package/lib/index14.mjs.map +1 -0
- package/lib/index15.cjs +30 -0
- package/lib/index15.cjs.map +1 -0
- package/lib/index15.mjs +30 -0
- package/lib/index15.mjs.map +1 -0
- package/lib/index16.cjs +28 -0
- package/lib/index16.cjs.map +1 -0
- package/lib/index16.mjs +26 -0
- package/lib/index16.mjs.map +1 -0
- package/lib/index17.cjs +43 -0
- package/lib/index17.cjs.map +1 -0
- package/lib/index17.mjs +43 -0
- package/lib/index17.mjs.map +1 -0
- package/lib/index18.cjs +59 -0
- package/lib/index18.cjs.map +1 -0
- package/lib/index18.mjs +59 -0
- package/lib/index18.mjs.map +1 -0
- package/lib/index19.cjs +67 -0
- package/lib/index19.cjs.map +1 -0
- package/lib/index19.mjs +67 -0
- package/lib/index19.mjs.map +1 -0
- package/lib/index2.cjs +33 -0
- package/lib/index2.mjs +13 -0
- package/lib/index20.cjs +79 -0
- package/lib/index20.cjs.map +1 -0
- package/lib/index20.mjs +79 -0
- package/lib/index20.mjs.map +1 -0
- package/lib/index21.cjs +105 -0
- package/lib/index21.cjs.map +1 -0
- package/lib/index21.mjs +104 -0
- package/lib/index21.mjs.map +1 -0
- package/lib/index22.cjs +50 -0
- package/lib/index22.cjs.map +1 -0
- package/lib/index22.mjs +50 -0
- package/lib/index22.mjs.map +1 -0
- package/lib/index23.cjs +143 -0
- package/lib/index23.cjs.map +1 -0
- package/lib/index23.mjs +143 -0
- package/lib/index23.mjs.map +1 -0
- package/lib/index25.cjs +177 -0
- package/lib/index25.cjs.map +1 -0
- package/lib/index25.mjs +175 -0
- package/lib/index25.mjs.map +1 -0
- package/lib/index26.cjs +108 -0
- package/lib/index26.cjs.map +1 -0
- package/lib/index26.mjs +108 -0
- package/lib/index26.mjs.map +1 -0
- package/lib/index27.cjs +120 -0
- package/lib/index27.cjs.map +1 -0
- package/lib/index27.mjs +120 -0
- package/lib/index27.mjs.map +1 -0
- package/lib/index28.cjs +54 -0
- package/lib/index28.cjs.map +1 -0
- package/lib/index28.mjs +54 -0
- package/lib/index28.mjs.map +1 -0
- package/lib/index29.cjs +100 -0
- package/lib/index29.cjs.map +1 -0
- package/lib/index29.mjs +100 -0
- package/lib/index29.mjs.map +1 -0
- package/lib/index3.cjs +38 -0
- package/lib/index3.cjs.map +1 -0
- package/lib/index3.mjs +38 -0
- package/lib/index3.mjs.map +1 -0
- package/lib/index30.cjs +27 -0
- package/lib/index30.cjs.map +1 -0
- package/lib/index30.mjs +27 -0
- package/lib/index30.mjs.map +1 -0
- package/lib/index31.cjs +72 -0
- package/lib/index31.cjs.map +1 -0
- package/lib/index31.mjs +72 -0
- package/lib/index31.mjs.map +1 -0
- package/lib/index32.cjs +19 -0
- package/lib/index32.cjs.map +1 -0
- package/lib/index32.mjs +19 -0
- package/lib/index32.mjs.map +1 -0
- package/lib/index33.cjs +25 -0
- package/lib/index33.cjs.map +1 -0
- package/lib/index33.mjs +25 -0
- package/lib/index33.mjs.map +1 -0
- package/lib/index34.cjs +55 -0
- package/lib/index34.cjs.map +1 -0
- package/lib/index34.mjs +51 -0
- package/lib/index34.mjs.map +1 -0
- package/lib/index35.cjs +25 -0
- package/lib/index35.cjs.map +1 -0
- package/lib/index35.mjs +25 -0
- package/lib/index35.mjs.map +1 -0
- package/lib/index36.cjs +54 -0
- package/lib/index36.cjs.map +1 -0
- package/lib/index36.mjs +49 -0
- package/lib/index36.mjs.map +1 -0
- package/lib/index37.cjs +130 -0
- package/lib/index37.cjs.map +1 -0
- package/lib/index37.mjs +128 -0
- package/lib/index37.mjs.map +1 -0
- package/lib/index38.cjs +106 -0
- package/lib/index38.cjs.map +1 -0
- package/lib/index38.mjs +102 -0
- package/lib/index38.mjs.map +1 -0
- package/lib/index39.cjs +137 -0
- package/lib/index39.cjs.map +1 -0
- package/lib/index39.mjs +137 -0
- package/lib/index39.mjs.map +1 -0
- package/lib/index4.cjs +24 -0
- package/lib/index4.cjs.map +1 -0
- package/lib/index4.mjs +24 -0
- package/lib/index4.mjs.map +1 -0
- package/lib/index40.cjs +130 -0
- package/lib/index40.cjs.map +1 -0
- package/lib/index40.mjs +126 -0
- package/lib/index40.mjs.map +1 -0
- package/lib/index41.cjs +60 -0
- package/lib/index41.cjs.map +1 -0
- package/lib/index41.mjs +58 -0
- package/lib/index41.mjs.map +1 -0
- package/lib/index42.cjs +105 -0
- package/lib/index42.cjs.map +1 -0
- package/lib/index42.mjs +105 -0
- package/lib/index42.mjs.map +1 -0
- package/lib/index43.cjs +47 -0
- package/lib/index43.cjs.map +1 -0
- package/lib/index43.mjs +42 -0
- package/lib/index43.mjs.map +1 -0
- package/lib/index44.cjs +40 -0
- package/lib/index44.cjs.map +1 -0
- package/lib/index44.mjs +35 -0
- package/lib/index44.mjs.map +1 -0
- package/lib/index45.cjs +91 -0
- package/lib/index45.cjs.map +1 -0
- package/lib/index45.mjs +91 -0
- package/lib/index45.mjs.map +1 -0
- package/lib/index46.cjs +180 -0
- package/lib/index46.cjs.map +1 -0
- package/lib/index46.mjs +180 -0
- package/lib/index46.mjs.map +1 -0
- package/lib/index47.cjs +61 -0
- package/lib/index47.cjs.map +1 -0
- package/lib/index47.mjs +61 -0
- package/lib/index47.mjs.map +1 -0
- package/lib/index48.cjs +165 -0
- package/lib/index48.cjs.map +1 -0
- package/lib/index48.mjs +161 -0
- package/lib/index48.mjs.map +1 -0
- package/lib/index49.cjs +83 -0
- package/lib/index49.cjs.map +1 -0
- package/lib/index49.mjs +83 -0
- package/lib/index49.mjs.map +1 -0
- package/lib/index5.cjs +39 -0
- package/lib/index5.cjs.map +1 -0
- package/lib/index5.mjs +39 -0
- package/lib/index5.mjs.map +1 -0
- package/lib/index50.cjs +100 -0
- package/lib/index50.cjs.map +1 -0
- package/lib/index50.mjs +100 -0
- package/lib/index50.mjs.map +1 -0
- package/lib/index51.cjs +116 -0
- package/lib/index51.cjs.map +1 -0
- package/lib/index51.mjs +113 -0
- package/lib/index51.mjs.map +1 -0
- package/lib/index52.cjs +93 -0
- package/lib/index52.cjs.map +1 -0
- package/lib/index52.mjs +93 -0
- package/lib/index52.mjs.map +1 -0
- package/lib/index53.cjs +120 -0
- package/lib/index53.cjs.map +1 -0
- package/lib/index53.mjs +120 -0
- package/lib/index53.mjs.map +1 -0
- package/lib/index54.cjs +53 -0
- package/lib/index54.cjs.map +1 -0
- package/lib/index54.mjs +50 -0
- package/lib/index54.mjs.map +1 -0
- package/lib/index55.cjs +130 -0
- package/lib/index55.cjs.map +1 -0
- package/lib/index55.mjs +127 -0
- package/lib/index55.mjs.map +1 -0
- package/lib/index56.cjs +36 -0
- package/lib/index56.cjs.map +1 -0
- package/lib/index56.mjs +35 -0
- package/lib/index56.mjs.map +1 -0
- package/lib/index57.cjs +47 -0
- package/lib/index57.cjs.map +1 -0
- package/lib/index57.mjs +47 -0
- package/lib/index57.mjs.map +1 -0
- package/lib/index58.cjs +97 -0
- package/lib/index58.cjs.map +1 -0
- package/lib/index58.mjs +97 -0
- package/lib/index58.mjs.map +1 -0
- package/lib/index59.cjs +120 -0
- package/lib/index59.cjs.map +1 -0
- package/lib/index59.mjs +116 -0
- package/lib/index59.mjs.map +1 -0
- package/lib/index6.cjs +29 -0
- package/lib/index6.cjs.map +1 -0
- package/lib/index6.mjs +29 -0
- package/lib/index6.mjs.map +1 -0
- package/lib/index60.cjs +34 -0
- package/lib/index60.cjs.map +1 -0
- package/lib/index60.mjs +34 -0
- package/lib/index60.mjs.map +1 -0
- package/lib/index61.cjs +62 -0
- package/lib/index61.cjs.map +1 -0
- package/lib/index61.mjs +62 -0
- package/lib/index61.mjs.map +1 -0
- package/lib/index62.cjs +213 -0
- package/lib/index62.cjs.map +1 -0
- package/lib/index62.mjs +213 -0
- package/lib/index62.mjs.map +1 -0
- package/lib/index63.cjs +42 -0
- package/lib/index63.cjs.map +1 -0
- package/lib/index63.mjs +42 -0
- package/lib/index63.mjs.map +1 -0
- package/lib/index64.cjs +86 -0
- package/lib/index64.cjs.map +1 -0
- package/lib/index64.mjs +86 -0
- package/lib/index64.mjs.map +1 -0
- package/lib/index65.cjs +156 -0
- package/lib/index65.cjs.map +1 -0
- package/lib/index65.mjs +156 -0
- package/lib/index65.mjs.map +1 -0
- package/lib/index66.cjs +107 -0
- package/lib/index66.cjs.map +1 -0
- package/lib/index66.mjs +106 -0
- package/lib/index66.mjs.map +1 -0
- package/lib/index67.cjs +52 -0
- package/lib/index67.cjs.map +1 -0
- package/lib/index67.mjs +52 -0
- package/lib/index67.mjs.map +1 -0
- package/lib/index68.cjs +125 -0
- package/lib/index68.cjs.map +1 -0
- package/lib/index68.mjs +115 -0
- package/lib/index68.mjs.map +1 -0
- package/lib/index69.cjs +34 -0
- package/lib/index69.cjs.map +1 -0
- package/lib/index69.mjs +29 -0
- package/lib/index69.mjs.map +1 -0
- package/lib/index7.cjs +17 -0
- package/lib/index7.cjs.map +1 -0
- package/lib/index7.mjs +17 -0
- package/lib/index7.mjs.map +1 -0
- package/lib/index8.cjs +86 -0
- package/lib/index8.cjs.map +1 -0
- package/lib/index8.mjs +86 -0
- package/lib/index8.mjs.map +1 -0
- package/lib/index9.cjs +29 -0
- package/lib/index9.cjs.map +1 -0
- package/lib/index9.mjs +29 -0
- package/lib/index9.mjs.map +1 -0
- package/lib/types/index.d.ts +1 -0
- package/package.json +68 -0
package/lib/index62.cjs
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
2
|
+
let react = require("react");
|
|
3
|
+
let lucide_react = require("lucide-react");
|
|
4
|
+
//#region src/ui-library/gallery/tree-view/TreeView.tsx
|
|
5
|
+
/**
|
|
6
|
+
* TreeView — Design System Component
|
|
7
|
+
*
|
|
8
|
+
* Hierarchical tree with expand/collapse, selection, and keyboard navigation.
|
|
9
|
+
*
|
|
10
|
+
* WAI-ARIA Tree pattern:
|
|
11
|
+
* role="tree" on the root <ul>
|
|
12
|
+
* role="treeitem" + aria-expanded + aria-selected on each item
|
|
13
|
+
* role="group" on nested <ul> children
|
|
14
|
+
*
|
|
15
|
+
* Keyboard (WAI-ARIA authoring guide):
|
|
16
|
+
* ArrowDown — next visible item
|
|
17
|
+
* ArrowUp — previous visible item
|
|
18
|
+
* ArrowRight — expand collapsed node, or move to first child
|
|
19
|
+
* ArrowLeft — collapse expanded node, or move to parent
|
|
20
|
+
* Home — first item in tree
|
|
21
|
+
* End — last visible item in tree
|
|
22
|
+
* Enter — select item
|
|
23
|
+
* Space — select item
|
|
24
|
+
*
|
|
25
|
+
* Data model:
|
|
26
|
+
* const items: TreeItem[] = [
|
|
27
|
+
* { id: '1', label: 'Parent', children: [
|
|
28
|
+
* { id: '1.1', label: 'Child' },
|
|
29
|
+
* ]},
|
|
30
|
+
* ];
|
|
31
|
+
*
|
|
32
|
+
* <TreeView
|
|
33
|
+
* items={items}
|
|
34
|
+
* selectedId={selected}
|
|
35
|
+
* onSelect={setSelected}
|
|
36
|
+
* />
|
|
37
|
+
*/
|
|
38
|
+
function flattenVisible(items, expanded) {
|
|
39
|
+
const result = [];
|
|
40
|
+
for (const item of items) {
|
|
41
|
+
result.push(item);
|
|
42
|
+
if (item.children && expanded.has(item.id)) result.push(...flattenVisible(item.children, expanded));
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
function findParent(items, id) {
|
|
47
|
+
for (const item of items) if (item.children) {
|
|
48
|
+
if (item.children.some((c) => c.id === id)) return item;
|
|
49
|
+
const found = findParent(item.children, id);
|
|
50
|
+
if (found) return found;
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
function TreeNode({ item, depth, selectedId, expanded, toggleExpand, onSelect, baseId }) {
|
|
55
|
+
const hasChildren = Boolean(item.children?.length);
|
|
56
|
+
const isExpanded = expanded.has(item.id);
|
|
57
|
+
const isSelected = item.id === selectedId;
|
|
58
|
+
const handleClick = () => {
|
|
59
|
+
if (item.disabled) return;
|
|
60
|
+
if (hasChildren) toggleExpand(item.id);
|
|
61
|
+
onSelect(item.id);
|
|
62
|
+
};
|
|
63
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
64
|
+
id: `${baseId}-${item.id}`,
|
|
65
|
+
role: "treeitem",
|
|
66
|
+
"aria-expanded": hasChildren ? isExpanded : void 0,
|
|
67
|
+
"aria-selected": isSelected,
|
|
68
|
+
"aria-disabled": item.disabled || void 0,
|
|
69
|
+
"aria-level": depth,
|
|
70
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
71
|
+
type: "button",
|
|
72
|
+
onClick: handleClick,
|
|
73
|
+
disabled: item.disabled,
|
|
74
|
+
style: { paddingLeft: `${(depth - 1) * 16 + 8}px` },
|
|
75
|
+
className: [
|
|
76
|
+
"flex w-full items-center gap-1.5 rounded-lg py-1.5 pr-3 text-sm font-medium transition",
|
|
77
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30",
|
|
78
|
+
isSelected ? "bg-blue-50 font-semibold text-brand-blue" : item.disabled ? "cursor-not-allowed text-slate-300" : "text-slate-700 hover:bg-slate-100"
|
|
79
|
+
].filter(Boolean).join(" "),
|
|
80
|
+
children: [
|
|
81
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
82
|
+
className: `flex h-4 w-4 shrink-0 items-center justify-center transition-transform ${hasChildren ? "" : "invisible"} ${isExpanded ? "rotate-90" : ""}`,
|
|
83
|
+
"aria-hidden": "true",
|
|
84
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronRight, { size: 12 })
|
|
85
|
+
}),
|
|
86
|
+
item.icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
87
|
+
className: "shrink-0 text-slate-400",
|
|
88
|
+
"aria-hidden": "true",
|
|
89
|
+
children: item.icon
|
|
90
|
+
}),
|
|
91
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
92
|
+
className: "truncate",
|
|
93
|
+
children: item.label
|
|
94
|
+
})
|
|
95
|
+
]
|
|
96
|
+
}), hasChildren && isExpanded && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
97
|
+
role: "group",
|
|
98
|
+
children: item.children.map((child) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TreeNode, {
|
|
99
|
+
item: child,
|
|
100
|
+
depth: depth + 1,
|
|
101
|
+
selectedId,
|
|
102
|
+
expanded,
|
|
103
|
+
toggleExpand,
|
|
104
|
+
onSelect,
|
|
105
|
+
baseId
|
|
106
|
+
}, child.id))
|
|
107
|
+
})]
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function TreeView({ items, selectedId, onSelect, "aria-label": ariaLabel, className = "" }) {
|
|
111
|
+
const baseId = (0, react.useId)().replace(/:/g, "");
|
|
112
|
+
const [expanded, setExpanded] = (0, react.useState)(() => {
|
|
113
|
+
const s = /* @__PURE__ */ new Set();
|
|
114
|
+
items.forEach((i) => {
|
|
115
|
+
if (i.children?.length) s.add(i.id);
|
|
116
|
+
});
|
|
117
|
+
return s;
|
|
118
|
+
});
|
|
119
|
+
const treeRef = (0, react.useRef)(null);
|
|
120
|
+
const toggleExpand = (0, react.useCallback)((id) => {
|
|
121
|
+
setExpanded((prev) => {
|
|
122
|
+
const next = new Set(prev);
|
|
123
|
+
if (next.has(id)) next.delete(id);
|
|
124
|
+
else next.add(id);
|
|
125
|
+
return next;
|
|
126
|
+
});
|
|
127
|
+
}, []);
|
|
128
|
+
const handleSelect = (0, react.useCallback)((id) => {
|
|
129
|
+
onSelect?.(id);
|
|
130
|
+
}, [onSelect]);
|
|
131
|
+
const handleKeyDown = (e) => {
|
|
132
|
+
const visible = flattenVisible(items, expanded);
|
|
133
|
+
if (visible.length === 0) return;
|
|
134
|
+
const currentId = selectedId ?? visible[0]?.id;
|
|
135
|
+
const currentIdx = visible.findIndex((v) => v.id === currentId);
|
|
136
|
+
const current = visible[currentIdx];
|
|
137
|
+
const focusItem = (id) => {
|
|
138
|
+
handleSelect(id);
|
|
139
|
+
document.getElementById(`${baseId}-${id}`)?.querySelector("button")?.focus();
|
|
140
|
+
};
|
|
141
|
+
switch (e.key) {
|
|
142
|
+
case "ArrowDown": {
|
|
143
|
+
e.preventDefault();
|
|
144
|
+
const next = visible[currentIdx + 1];
|
|
145
|
+
if (next && !next.disabled) focusItem(next.id);
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
case "ArrowUp": {
|
|
149
|
+
e.preventDefault();
|
|
150
|
+
let prev = visible[currentIdx - 1];
|
|
151
|
+
while (prev && prev.disabled) prev = visible[visible.indexOf(prev) - 1];
|
|
152
|
+
if (prev) focusItem(prev.id);
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
case "ArrowRight":
|
|
156
|
+
e.preventDefault();
|
|
157
|
+
if (current?.children?.length && !expanded.has(current.id)) toggleExpand(current.id);
|
|
158
|
+
else {
|
|
159
|
+
const child = current?.children?.[0];
|
|
160
|
+
if (child && !child.disabled) focusItem(child.id);
|
|
161
|
+
}
|
|
162
|
+
break;
|
|
163
|
+
case "ArrowLeft":
|
|
164
|
+
e.preventDefault();
|
|
165
|
+
if (current?.children?.length && expanded.has(current.id)) toggleExpand(current.id);
|
|
166
|
+
else {
|
|
167
|
+
const parent = findParent(items, current?.id ?? "");
|
|
168
|
+
if (parent) focusItem(parent.id);
|
|
169
|
+
}
|
|
170
|
+
break;
|
|
171
|
+
case "Home": {
|
|
172
|
+
e.preventDefault();
|
|
173
|
+
const first = visible.find((v) => !v.disabled);
|
|
174
|
+
if (first) focusItem(first.id);
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
case "End": {
|
|
178
|
+
e.preventDefault();
|
|
179
|
+
const last = [...visible].reverse().find((v) => !v.disabled);
|
|
180
|
+
if (last) focusItem(last.id);
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
case "Enter":
|
|
184
|
+
case " ":
|
|
185
|
+
e.preventDefault();
|
|
186
|
+
if (current && !current.disabled) {
|
|
187
|
+
if (current.children?.length) toggleExpand(current.id);
|
|
188
|
+
handleSelect(current.id);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
193
|
+
ref: treeRef,
|
|
194
|
+
role: "tree",
|
|
195
|
+
"aria-label": ariaLabel,
|
|
196
|
+
onKeyDown: handleKeyDown,
|
|
197
|
+
className: `flex flex-col gap-0.5 outline-none ${className}`,
|
|
198
|
+
tabIndex: 0,
|
|
199
|
+
children: items.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TreeNode, {
|
|
200
|
+
item,
|
|
201
|
+
depth: 1,
|
|
202
|
+
selectedId,
|
|
203
|
+
expanded,
|
|
204
|
+
toggleExpand,
|
|
205
|
+
onSelect: handleSelect,
|
|
206
|
+
baseId
|
|
207
|
+
}, item.id))
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
//#endregion
|
|
211
|
+
exports.TreeView = TreeView;
|
|
212
|
+
|
|
213
|
+
//# sourceMappingURL=index62.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index62.cjs","names":[],"sources":["../src/ui-library/gallery/tree-view/TreeView.tsx"],"sourcesContent":["/**\n * TreeView — Design System Component\n *\n * Hierarchical tree with expand/collapse, selection, and keyboard navigation.\n *\n * WAI-ARIA Tree pattern:\n * role=\"tree\" on the root <ul>\n * role=\"treeitem\" + aria-expanded + aria-selected on each item\n * role=\"group\" on nested <ul> children\n *\n * Keyboard (WAI-ARIA authoring guide):\n * ArrowDown — next visible item\n * ArrowUp — previous visible item\n * ArrowRight — expand collapsed node, or move to first child\n * ArrowLeft — collapse expanded node, or move to parent\n * Home — first item in tree\n * End — last visible item in tree\n * Enter — select item\n * Space — select item\n *\n * Data model:\n * const items: TreeItem[] = [\n * { id: '1', label: 'Parent', children: [\n * { id: '1.1', label: 'Child' },\n * ]},\n * ];\n *\n * <TreeView\n * items={items}\n * selectedId={selected}\n * onSelect={setSelected}\n * />\n */\n\nimport { useCallback, useId, useRef, useState, type ReactNode } from 'react';\nimport { ChevronRight } from 'lucide-react';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface TreeItem {\n id: string;\n label: string;\n icon?: ReactNode;\n disabled?: boolean;\n children?: TreeItem[];\n}\n\nexport interface TreeViewProps {\n items: TreeItem[];\n /** Currently selected item id */\n selectedId?: string;\n /** Called when an item is selected */\n onSelect?: (id: string) => void;\n /** Optional accessible label for the tree */\n 'aria-label'?: string;\n className?: string;\n}\n\n// ── Flatten visible items for keyboard navigation ─────────────────────────────\n\nfunction flattenVisible(items: TreeItem[], expanded: Set<string>): TreeItem[] {\n const result: TreeItem[] = [];\n for (const item of items) {\n result.push(item);\n if (item.children && expanded.has(item.id)) {\n result.push(...flattenVisible(item.children, expanded));\n }\n }\n return result;\n}\n\n// ── Find parent of a given id ─────────────────────────────────────────────────\n\nfunction findParent(items: TreeItem[], id: string): TreeItem | null {\n for (const item of items) {\n if (item.children) {\n if (item.children.some((c) => c.id === id)) return item;\n const found = findParent(item.children, id);\n if (found) return found;\n }\n }\n return null;\n}\n\n// ── TreeNode (recursive) ──────────────────────────────────────────────────────\n\ninterface TreeNodeProps {\n item: TreeItem;\n depth: number;\n selectedId?: string;\n expanded: Set<string>;\n toggleExpand: (id: string) => void;\n onSelect: (id: string) => void;\n baseId: string;\n}\n\nfunction TreeNode({ item, depth, selectedId, expanded, toggleExpand, onSelect, baseId }: TreeNodeProps) {\n const hasChildren = Boolean(item.children?.length);\n const isExpanded = expanded.has(item.id);\n const isSelected = item.id === selectedId;\n\n const handleClick = () => {\n if (item.disabled) return;\n if (hasChildren) toggleExpand(item.id);\n onSelect(item.id);\n };\n\n return (\n <li\n id={`${baseId}-${item.id}`}\n role=\"treeitem\"\n aria-expanded={hasChildren ? isExpanded : undefined}\n aria-selected={isSelected}\n aria-disabled={item.disabled || undefined}\n aria-level={depth}\n >\n <button\n type=\"button\"\n onClick={handleClick}\n disabled={item.disabled}\n style={{ paddingLeft: `${(depth - 1) * 16 + 8}px` }}\n className={[\n 'flex w-full items-center gap-1.5 rounded-lg py-1.5 pr-3 text-sm font-medium transition',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30',\n isSelected\n ? 'bg-blue-50 font-semibold text-brand-blue'\n : item.disabled\n ? 'cursor-not-allowed text-slate-300'\n : 'text-slate-700 hover:bg-slate-100',\n ].filter(Boolean).join(' ')}\n >\n {/* Expand/collapse chevron */}\n <span\n className={`flex h-4 w-4 shrink-0 items-center justify-center transition-transform ${\n hasChildren ? '' : 'invisible'\n } ${isExpanded ? 'rotate-90' : ''}`}\n aria-hidden=\"true\"\n >\n <ChevronRight size={12} />\n </span>\n\n {/* Icon */}\n {item.icon && (\n <span className=\"shrink-0 text-slate-400\" aria-hidden=\"true\">\n {item.icon}\n </span>\n )}\n\n {/* Label */}\n <span className=\"truncate\">{item.label}</span>\n </button>\n\n {/* Children */}\n {hasChildren && isExpanded && (\n <ul role=\"group\">\n {item.children!.map((child) => (\n <TreeNode\n key={child.id}\n item={child}\n depth={depth + 1}\n selectedId={selectedId}\n expanded={expanded}\n toggleExpand={toggleExpand}\n onSelect={onSelect}\n baseId={baseId}\n />\n ))}\n </ul>\n )}\n </li>\n );\n}\n\n// ── TreeView (root) ───────────────────────────────────────────────────────────\n\nexport function TreeView({\n items,\n selectedId,\n onSelect,\n 'aria-label': ariaLabel,\n className = '',\n}: TreeViewProps) {\n const baseId = useId().replace(/:/g, '');\n const [expanded, setExpanded] = useState<Set<string>>(() => {\n // Start with top-level nodes expanded\n const s = new Set<string>();\n items.forEach((i) => { if (i.children?.length) s.add(i.id); });\n return s;\n });\n const treeRef = useRef<HTMLUListElement>(null);\n\n const toggleExpand = useCallback((id: string) => {\n setExpanded((prev) => {\n const next = new Set(prev);\n if (next.has(id)) next.delete(id); else next.add(id);\n return next;\n });\n }, []);\n\n const handleSelect = useCallback((id: string) => {\n onSelect?.(id);\n }, [onSelect]);\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLUListElement>) => {\n const visible = flattenVisible(items, expanded);\n if (visible.length === 0) return;\n\n const currentId = selectedId ?? visible[0]?.id;\n const currentIdx = visible.findIndex((v) => v.id === currentId);\n const current = visible[currentIdx];\n\n const focusItem = (id: string) => {\n handleSelect(id);\n document.getElementById(`${baseId}-${id}`)?.querySelector('button')?.focus();\n };\n\n switch (e.key) {\n case 'ArrowDown': {\n e.preventDefault();\n const next = visible[currentIdx + 1];\n if (next && !next.disabled) focusItem(next.id);\n break;\n }\n case 'ArrowUp': {\n e.preventDefault();\n let prev = visible[currentIdx - 1];\n while (prev && prev.disabled) {\n const idx = visible.indexOf(prev);\n prev = visible[idx - 1];\n }\n if (prev) focusItem(prev.id);\n break;\n }\n case 'ArrowRight': {\n e.preventDefault();\n if (current?.children?.length && !expanded.has(current.id)) {\n toggleExpand(current.id);\n } else {\n const child = current?.children?.[0];\n if (child && !child.disabled) focusItem(child.id);\n }\n break;\n }\n case 'ArrowLeft': {\n e.preventDefault();\n if (current?.children?.length && expanded.has(current.id)) {\n toggleExpand(current.id);\n } else {\n const parent = findParent(items, current?.id ?? '');\n if (parent) focusItem(parent.id);\n }\n break;\n }\n case 'Home': {\n e.preventDefault();\n const first = visible.find((v) => !v.disabled);\n if (first) focusItem(first.id);\n break;\n }\n case 'End': {\n e.preventDefault();\n const last = [...visible].reverse().find((v) => !v.disabled);\n if (last) focusItem(last.id);\n break;\n }\n case 'Enter':\n case ' ': {\n e.preventDefault();\n if (current && !current.disabled) {\n if (current.children?.length) toggleExpand(current.id);\n handleSelect(current.id);\n }\n break;\n }\n }\n };\n\n return (\n <ul\n ref={treeRef}\n role=\"tree\"\n aria-label={ariaLabel}\n onKeyDown={handleKeyDown}\n className={`flex flex-col gap-0.5 outline-none ${className}`}\n tabIndex={0}\n >\n {items.map((item) => (\n <TreeNode\n key={item.id}\n item={item}\n depth={1}\n selectedId={selectedId}\n expanded={expanded}\n toggleExpand={toggleExpand}\n onSelect={handleSelect}\n baseId={baseId}\n />\n ))}\n </ul>\n );\n}\n"],"mappings":"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"}
|
package/lib/index62.mjs
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useId, useRef, useState } from "react";
|
|
3
|
+
import { ChevronRight } from "lucide-react";
|
|
4
|
+
//#region src/ui-library/gallery/tree-view/TreeView.tsx
|
|
5
|
+
/**
|
|
6
|
+
* TreeView — Design System Component
|
|
7
|
+
*
|
|
8
|
+
* Hierarchical tree with expand/collapse, selection, and keyboard navigation.
|
|
9
|
+
*
|
|
10
|
+
* WAI-ARIA Tree pattern:
|
|
11
|
+
* role="tree" on the root <ul>
|
|
12
|
+
* role="treeitem" + aria-expanded + aria-selected on each item
|
|
13
|
+
* role="group" on nested <ul> children
|
|
14
|
+
*
|
|
15
|
+
* Keyboard (WAI-ARIA authoring guide):
|
|
16
|
+
* ArrowDown — next visible item
|
|
17
|
+
* ArrowUp — previous visible item
|
|
18
|
+
* ArrowRight — expand collapsed node, or move to first child
|
|
19
|
+
* ArrowLeft — collapse expanded node, or move to parent
|
|
20
|
+
* Home — first item in tree
|
|
21
|
+
* End — last visible item in tree
|
|
22
|
+
* Enter — select item
|
|
23
|
+
* Space — select item
|
|
24
|
+
*
|
|
25
|
+
* Data model:
|
|
26
|
+
* const items: TreeItem[] = [
|
|
27
|
+
* { id: '1', label: 'Parent', children: [
|
|
28
|
+
* { id: '1.1', label: 'Child' },
|
|
29
|
+
* ]},
|
|
30
|
+
* ];
|
|
31
|
+
*
|
|
32
|
+
* <TreeView
|
|
33
|
+
* items={items}
|
|
34
|
+
* selectedId={selected}
|
|
35
|
+
* onSelect={setSelected}
|
|
36
|
+
* />
|
|
37
|
+
*/
|
|
38
|
+
function flattenVisible(items, expanded) {
|
|
39
|
+
const result = [];
|
|
40
|
+
for (const item of items) {
|
|
41
|
+
result.push(item);
|
|
42
|
+
if (item.children && expanded.has(item.id)) result.push(...flattenVisible(item.children, expanded));
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
function findParent(items, id) {
|
|
47
|
+
for (const item of items) if (item.children) {
|
|
48
|
+
if (item.children.some((c) => c.id === id)) return item;
|
|
49
|
+
const found = findParent(item.children, id);
|
|
50
|
+
if (found) return found;
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
function TreeNode({ item, depth, selectedId, expanded, toggleExpand, onSelect, baseId }) {
|
|
55
|
+
const hasChildren = Boolean(item.children?.length);
|
|
56
|
+
const isExpanded = expanded.has(item.id);
|
|
57
|
+
const isSelected = item.id === selectedId;
|
|
58
|
+
const handleClick = () => {
|
|
59
|
+
if (item.disabled) return;
|
|
60
|
+
if (hasChildren) toggleExpand(item.id);
|
|
61
|
+
onSelect(item.id);
|
|
62
|
+
};
|
|
63
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
64
|
+
id: `${baseId}-${item.id}`,
|
|
65
|
+
role: "treeitem",
|
|
66
|
+
"aria-expanded": hasChildren ? isExpanded : void 0,
|
|
67
|
+
"aria-selected": isSelected,
|
|
68
|
+
"aria-disabled": item.disabled || void 0,
|
|
69
|
+
"aria-level": depth,
|
|
70
|
+
children: [/* @__PURE__ */ jsxs("button", {
|
|
71
|
+
type: "button",
|
|
72
|
+
onClick: handleClick,
|
|
73
|
+
disabled: item.disabled,
|
|
74
|
+
style: { paddingLeft: `${(depth - 1) * 16 + 8}px` },
|
|
75
|
+
className: [
|
|
76
|
+
"flex w-full items-center gap-1.5 rounded-lg py-1.5 pr-3 text-sm font-medium transition",
|
|
77
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30",
|
|
78
|
+
isSelected ? "bg-blue-50 font-semibold text-brand-blue" : item.disabled ? "cursor-not-allowed text-slate-300" : "text-slate-700 hover:bg-slate-100"
|
|
79
|
+
].filter(Boolean).join(" "),
|
|
80
|
+
children: [
|
|
81
|
+
/* @__PURE__ */ jsx("span", {
|
|
82
|
+
className: `flex h-4 w-4 shrink-0 items-center justify-center transition-transform ${hasChildren ? "" : "invisible"} ${isExpanded ? "rotate-90" : ""}`,
|
|
83
|
+
"aria-hidden": "true",
|
|
84
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { size: 12 })
|
|
85
|
+
}),
|
|
86
|
+
item.icon && /* @__PURE__ */ jsx("span", {
|
|
87
|
+
className: "shrink-0 text-slate-400",
|
|
88
|
+
"aria-hidden": "true",
|
|
89
|
+
children: item.icon
|
|
90
|
+
}),
|
|
91
|
+
/* @__PURE__ */ jsx("span", {
|
|
92
|
+
className: "truncate",
|
|
93
|
+
children: item.label
|
|
94
|
+
})
|
|
95
|
+
]
|
|
96
|
+
}), hasChildren && isExpanded && /* @__PURE__ */ jsx("ul", {
|
|
97
|
+
role: "group",
|
|
98
|
+
children: item.children.map((child) => /* @__PURE__ */ jsx(TreeNode, {
|
|
99
|
+
item: child,
|
|
100
|
+
depth: depth + 1,
|
|
101
|
+
selectedId,
|
|
102
|
+
expanded,
|
|
103
|
+
toggleExpand,
|
|
104
|
+
onSelect,
|
|
105
|
+
baseId
|
|
106
|
+
}, child.id))
|
|
107
|
+
})]
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function TreeView({ items, selectedId, onSelect, "aria-label": ariaLabel, className = "" }) {
|
|
111
|
+
const baseId = useId().replace(/:/g, "");
|
|
112
|
+
const [expanded, setExpanded] = useState(() => {
|
|
113
|
+
const s = /* @__PURE__ */ new Set();
|
|
114
|
+
items.forEach((i) => {
|
|
115
|
+
if (i.children?.length) s.add(i.id);
|
|
116
|
+
});
|
|
117
|
+
return s;
|
|
118
|
+
});
|
|
119
|
+
const treeRef = useRef(null);
|
|
120
|
+
const toggleExpand = useCallback((id) => {
|
|
121
|
+
setExpanded((prev) => {
|
|
122
|
+
const next = new Set(prev);
|
|
123
|
+
if (next.has(id)) next.delete(id);
|
|
124
|
+
else next.add(id);
|
|
125
|
+
return next;
|
|
126
|
+
});
|
|
127
|
+
}, []);
|
|
128
|
+
const handleSelect = useCallback((id) => {
|
|
129
|
+
onSelect?.(id);
|
|
130
|
+
}, [onSelect]);
|
|
131
|
+
const handleKeyDown = (e) => {
|
|
132
|
+
const visible = flattenVisible(items, expanded);
|
|
133
|
+
if (visible.length === 0) return;
|
|
134
|
+
const currentId = selectedId ?? visible[0]?.id;
|
|
135
|
+
const currentIdx = visible.findIndex((v) => v.id === currentId);
|
|
136
|
+
const current = visible[currentIdx];
|
|
137
|
+
const focusItem = (id) => {
|
|
138
|
+
handleSelect(id);
|
|
139
|
+
document.getElementById(`${baseId}-${id}`)?.querySelector("button")?.focus();
|
|
140
|
+
};
|
|
141
|
+
switch (e.key) {
|
|
142
|
+
case "ArrowDown": {
|
|
143
|
+
e.preventDefault();
|
|
144
|
+
const next = visible[currentIdx + 1];
|
|
145
|
+
if (next && !next.disabled) focusItem(next.id);
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
case "ArrowUp": {
|
|
149
|
+
e.preventDefault();
|
|
150
|
+
let prev = visible[currentIdx - 1];
|
|
151
|
+
while (prev && prev.disabled) prev = visible[visible.indexOf(prev) - 1];
|
|
152
|
+
if (prev) focusItem(prev.id);
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
case "ArrowRight":
|
|
156
|
+
e.preventDefault();
|
|
157
|
+
if (current?.children?.length && !expanded.has(current.id)) toggleExpand(current.id);
|
|
158
|
+
else {
|
|
159
|
+
const child = current?.children?.[0];
|
|
160
|
+
if (child && !child.disabled) focusItem(child.id);
|
|
161
|
+
}
|
|
162
|
+
break;
|
|
163
|
+
case "ArrowLeft":
|
|
164
|
+
e.preventDefault();
|
|
165
|
+
if (current?.children?.length && expanded.has(current.id)) toggleExpand(current.id);
|
|
166
|
+
else {
|
|
167
|
+
const parent = findParent(items, current?.id ?? "");
|
|
168
|
+
if (parent) focusItem(parent.id);
|
|
169
|
+
}
|
|
170
|
+
break;
|
|
171
|
+
case "Home": {
|
|
172
|
+
e.preventDefault();
|
|
173
|
+
const first = visible.find((v) => !v.disabled);
|
|
174
|
+
if (first) focusItem(first.id);
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
case "End": {
|
|
178
|
+
e.preventDefault();
|
|
179
|
+
const last = [...visible].reverse().find((v) => !v.disabled);
|
|
180
|
+
if (last) focusItem(last.id);
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
case "Enter":
|
|
184
|
+
case " ":
|
|
185
|
+
e.preventDefault();
|
|
186
|
+
if (current && !current.disabled) {
|
|
187
|
+
if (current.children?.length) toggleExpand(current.id);
|
|
188
|
+
handleSelect(current.id);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
return /* @__PURE__ */ jsx("ul", {
|
|
193
|
+
ref: treeRef,
|
|
194
|
+
role: "tree",
|
|
195
|
+
"aria-label": ariaLabel,
|
|
196
|
+
onKeyDown: handleKeyDown,
|
|
197
|
+
className: `flex flex-col gap-0.5 outline-none ${className}`,
|
|
198
|
+
tabIndex: 0,
|
|
199
|
+
children: items.map((item) => /* @__PURE__ */ jsx(TreeNode, {
|
|
200
|
+
item,
|
|
201
|
+
depth: 1,
|
|
202
|
+
selectedId,
|
|
203
|
+
expanded,
|
|
204
|
+
toggleExpand,
|
|
205
|
+
onSelect: handleSelect,
|
|
206
|
+
baseId
|
|
207
|
+
}, item.id))
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
//#endregion
|
|
211
|
+
export { TreeView };
|
|
212
|
+
|
|
213
|
+
//# sourceMappingURL=index62.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index62.mjs","names":[],"sources":["../src/ui-library/gallery/tree-view/TreeView.tsx"],"sourcesContent":["/**\n * TreeView — Design System Component\n *\n * Hierarchical tree with expand/collapse, selection, and keyboard navigation.\n *\n * WAI-ARIA Tree pattern:\n * role=\"tree\" on the root <ul>\n * role=\"treeitem\" + aria-expanded + aria-selected on each item\n * role=\"group\" on nested <ul> children\n *\n * Keyboard (WAI-ARIA authoring guide):\n * ArrowDown — next visible item\n * ArrowUp — previous visible item\n * ArrowRight — expand collapsed node, or move to first child\n * ArrowLeft — collapse expanded node, or move to parent\n * Home — first item in tree\n * End — last visible item in tree\n * Enter — select item\n * Space — select item\n *\n * Data model:\n * const items: TreeItem[] = [\n * { id: '1', label: 'Parent', children: [\n * { id: '1.1', label: 'Child' },\n * ]},\n * ];\n *\n * <TreeView\n * items={items}\n * selectedId={selected}\n * onSelect={setSelected}\n * />\n */\n\nimport { useCallback, useId, useRef, useState, type ReactNode } from 'react';\nimport { ChevronRight } from 'lucide-react';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface TreeItem {\n id: string;\n label: string;\n icon?: ReactNode;\n disabled?: boolean;\n children?: TreeItem[];\n}\n\nexport interface TreeViewProps {\n items: TreeItem[];\n /** Currently selected item id */\n selectedId?: string;\n /** Called when an item is selected */\n onSelect?: (id: string) => void;\n /** Optional accessible label for the tree */\n 'aria-label'?: string;\n className?: string;\n}\n\n// ── Flatten visible items for keyboard navigation ─────────────────────────────\n\nfunction flattenVisible(items: TreeItem[], expanded: Set<string>): TreeItem[] {\n const result: TreeItem[] = [];\n for (const item of items) {\n result.push(item);\n if (item.children && expanded.has(item.id)) {\n result.push(...flattenVisible(item.children, expanded));\n }\n }\n return result;\n}\n\n// ── Find parent of a given id ─────────────────────────────────────────────────\n\nfunction findParent(items: TreeItem[], id: string): TreeItem | null {\n for (const item of items) {\n if (item.children) {\n if (item.children.some((c) => c.id === id)) return item;\n const found = findParent(item.children, id);\n if (found) return found;\n }\n }\n return null;\n}\n\n// ── TreeNode (recursive) ──────────────────────────────────────────────────────\n\ninterface TreeNodeProps {\n item: TreeItem;\n depth: number;\n selectedId?: string;\n expanded: Set<string>;\n toggleExpand: (id: string) => void;\n onSelect: (id: string) => void;\n baseId: string;\n}\n\nfunction TreeNode({ item, depth, selectedId, expanded, toggleExpand, onSelect, baseId }: TreeNodeProps) {\n const hasChildren = Boolean(item.children?.length);\n const isExpanded = expanded.has(item.id);\n const isSelected = item.id === selectedId;\n\n const handleClick = () => {\n if (item.disabled) return;\n if (hasChildren) toggleExpand(item.id);\n onSelect(item.id);\n };\n\n return (\n <li\n id={`${baseId}-${item.id}`}\n role=\"treeitem\"\n aria-expanded={hasChildren ? isExpanded : undefined}\n aria-selected={isSelected}\n aria-disabled={item.disabled || undefined}\n aria-level={depth}\n >\n <button\n type=\"button\"\n onClick={handleClick}\n disabled={item.disabled}\n style={{ paddingLeft: `${(depth - 1) * 16 + 8}px` }}\n className={[\n 'flex w-full items-center gap-1.5 rounded-lg py-1.5 pr-3 text-sm font-medium transition',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30',\n isSelected\n ? 'bg-blue-50 font-semibold text-brand-blue'\n : item.disabled\n ? 'cursor-not-allowed text-slate-300'\n : 'text-slate-700 hover:bg-slate-100',\n ].filter(Boolean).join(' ')}\n >\n {/* Expand/collapse chevron */}\n <span\n className={`flex h-4 w-4 shrink-0 items-center justify-center transition-transform ${\n hasChildren ? '' : 'invisible'\n } ${isExpanded ? 'rotate-90' : ''}`}\n aria-hidden=\"true\"\n >\n <ChevronRight size={12} />\n </span>\n\n {/* Icon */}\n {item.icon && (\n <span className=\"shrink-0 text-slate-400\" aria-hidden=\"true\">\n {item.icon}\n </span>\n )}\n\n {/* Label */}\n <span className=\"truncate\">{item.label}</span>\n </button>\n\n {/* Children */}\n {hasChildren && isExpanded && (\n <ul role=\"group\">\n {item.children!.map((child) => (\n <TreeNode\n key={child.id}\n item={child}\n depth={depth + 1}\n selectedId={selectedId}\n expanded={expanded}\n toggleExpand={toggleExpand}\n onSelect={onSelect}\n baseId={baseId}\n />\n ))}\n </ul>\n )}\n </li>\n );\n}\n\n// ── TreeView (root) ───────────────────────────────────────────────────────────\n\nexport function TreeView({\n items,\n selectedId,\n onSelect,\n 'aria-label': ariaLabel,\n className = '',\n}: TreeViewProps) {\n const baseId = useId().replace(/:/g, '');\n const [expanded, setExpanded] = useState<Set<string>>(() => {\n // Start with top-level nodes expanded\n const s = new Set<string>();\n items.forEach((i) => { if (i.children?.length) s.add(i.id); });\n return s;\n });\n const treeRef = useRef<HTMLUListElement>(null);\n\n const toggleExpand = useCallback((id: string) => {\n setExpanded((prev) => {\n const next = new Set(prev);\n if (next.has(id)) next.delete(id); else next.add(id);\n return next;\n });\n }, []);\n\n const handleSelect = useCallback((id: string) => {\n onSelect?.(id);\n }, [onSelect]);\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLUListElement>) => {\n const visible = flattenVisible(items, expanded);\n if (visible.length === 0) return;\n\n const currentId = selectedId ?? visible[0]?.id;\n const currentIdx = visible.findIndex((v) => v.id === currentId);\n const current = visible[currentIdx];\n\n const focusItem = (id: string) => {\n handleSelect(id);\n document.getElementById(`${baseId}-${id}`)?.querySelector('button')?.focus();\n };\n\n switch (e.key) {\n case 'ArrowDown': {\n e.preventDefault();\n const next = visible[currentIdx + 1];\n if (next && !next.disabled) focusItem(next.id);\n break;\n }\n case 'ArrowUp': {\n e.preventDefault();\n let prev = visible[currentIdx - 1];\n while (prev && prev.disabled) {\n const idx = visible.indexOf(prev);\n prev = visible[idx - 1];\n }\n if (prev) focusItem(prev.id);\n break;\n }\n case 'ArrowRight': {\n e.preventDefault();\n if (current?.children?.length && !expanded.has(current.id)) {\n toggleExpand(current.id);\n } else {\n const child = current?.children?.[0];\n if (child && !child.disabled) focusItem(child.id);\n }\n break;\n }\n case 'ArrowLeft': {\n e.preventDefault();\n if (current?.children?.length && expanded.has(current.id)) {\n toggleExpand(current.id);\n } else {\n const parent = findParent(items, current?.id ?? '');\n if (parent) focusItem(parent.id);\n }\n break;\n }\n case 'Home': {\n e.preventDefault();\n const first = visible.find((v) => !v.disabled);\n if (first) focusItem(first.id);\n break;\n }\n case 'End': {\n e.preventDefault();\n const last = [...visible].reverse().find((v) => !v.disabled);\n if (last) focusItem(last.id);\n break;\n }\n case 'Enter':\n case ' ': {\n e.preventDefault();\n if (current && !current.disabled) {\n if (current.children?.length) toggleExpand(current.id);\n handleSelect(current.id);\n }\n break;\n }\n }\n };\n\n return (\n <ul\n ref={treeRef}\n role=\"tree\"\n aria-label={ariaLabel}\n onKeyDown={handleKeyDown}\n className={`flex flex-col gap-0.5 outline-none ${className}`}\n tabIndex={0}\n >\n {items.map((item) => (\n <TreeNode\n key={item.id}\n item={item}\n depth={1}\n selectedId={selectedId}\n expanded={expanded}\n toggleExpand={toggleExpand}\n onSelect={handleSelect}\n baseId={baseId}\n />\n ))}\n </ul>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4DA,SAAS,eAAe,OAAmB,UAAmC;CAC5E,MAAM,SAAqB,CAAC;CAC5B,KAAK,MAAM,QAAQ,OAAO;EACxB,OAAO,KAAK,IAAI;EAChB,IAAI,KAAK,YAAY,SAAS,IAAI,KAAK,EAAE,GACvC,OAAO,KAAK,GAAG,eAAe,KAAK,UAAU,QAAQ,CAAC;CAE1D;CACA,OAAO;AACT;AAIA,SAAS,WAAW,OAAmB,IAA6B;CAClE,KAAK,MAAM,QAAQ,OACjB,IAAI,KAAK,UAAU;EACjB,IAAI,KAAK,SAAS,MAAM,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO;EACnD,MAAM,QAAQ,WAAW,KAAK,UAAU,EAAE;EAC1C,IAAI,OAAO,OAAO;CACpB;CAEF,OAAO;AACT;AAcA,SAAS,SAAS,EAAE,MAAM,OAAO,YAAY,UAAU,cAAc,UAAU,UAAyB;CACtG,MAAM,cAAc,QAAQ,KAAK,UAAU,MAAM;CACjD,MAAM,aAAa,SAAS,IAAI,KAAK,EAAE;CACvC,MAAM,aAAa,KAAK,OAAO;CAE/B,MAAM,oBAAoB;EACxB,IAAI,KAAK,UAAU;EACnB,IAAI,aAAa,aAAa,KAAK,EAAE;EACrC,SAAS,KAAK,EAAE;CAClB;CAEA,OACE,qBAAC,MAAD;EACE,IAAI,GAAG,OAAO,GAAG,KAAK;EACtB,MAAK;EACL,iBAAe,cAAc,aAAa,KAAA;EAC1C,iBAAe;EACf,iBAAe,KAAK,YAAY,KAAA;EAChC,cAAY;EANd,UAAA,CAQE,qBAAC,UAAD;GACE,MAAK;GACL,SAAS;GACT,UAAU,KAAK;GACf,OAAO,EAAE,aAAa,IAAI,QAAQ,KAAK,KAAK,EAAE,IAAI;GAClD,WAAW;IACT;IACA;IACA,aACI,6CACA,KAAK,WACH,sCACA;GACR,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;GAb5B,UAAA;IAgBE,oBAAC,QAAD;KACE,WAAW,0EACT,cAAc,KAAK,YACpB,GAAG,aAAa,cAAc;KAC/B,eAAY;KAEZ,UAAA,oBAAC,cAAD,EAAc,MAAM,GAAK,CAAA;IACrB,CAAA;IAGL,KAAK,QACJ,oBAAC,QAAD;KAAM,WAAU;KAA0B,eAAY;KACnD,UAAA,KAAK;IACF,CAAA;IAIR,oBAAC,QAAD;KAAM,WAAU;KAAY,UAAA,KAAK;IAAY,CAAA;GACvC;EAGP,CAAA,GAAA,eAAe,cACd,oBAAC,MAAD;GAAI,MAAK;GACN,UAAA,KAAK,SAAU,KAAK,UACnB,oBAAC,UAAD;IAEE,MAAM;IACN,OAAO,QAAQ;IACH;IACF;IACI;IACJ;IACF;GACT,GARM,MAAM,EAQZ,CACF;EACC,CAAA,CAEJ;;AAER;AAIA,SAAgB,SAAS,EACvB,OACA,YACA,UACA,cAAc,WACd,YAAY,MACI;CAChB,MAAM,SAAS,MAAM,CAAC,CAAC,QAAQ,MAAM,EAAE;CACvC,MAAM,CAAC,UAAU,eAAe,eAA4B;EAE1D,MAAM,oBAAI,IAAI,IAAY;EAC1B,MAAM,SAAS,MAAM;GAAE,IAAI,EAAE,UAAU,QAAQ,EAAE,IAAI,EAAE,EAAE;EAAG,CAAC;EAC7D,OAAO;CACT,CAAC;CACD,MAAM,UAAU,OAAyB,IAAI;CAE7C,MAAM,eAAe,aAAa,OAAe;EAC/C,aAAa,SAAS;GACpB,MAAM,OAAO,IAAI,IAAI,IAAI;GACzB,IAAI,KAAK,IAAI,EAAE,GAAG,KAAK,OAAO,EAAE;QAAQ,KAAK,IAAI,EAAE;GACnD,OAAO;EACT,CAAC;CACH,GAAG,CAAC,CAAC;CAEL,MAAM,eAAe,aAAa,OAAe;EAC/C,WAAW,EAAE;CACf,GAAG,CAAC,QAAQ,CAAC;CAEb,MAAM,iBAAiB,MAA6C;EAClE,MAAM,UAAU,eAAe,OAAO,QAAQ;EAC9C,IAAI,QAAQ,WAAW,GAAG;EAE1B,MAAM,YAAY,cAAc,QAAQ,EAAE,EAAE;EAC5C,MAAM,aAAa,QAAQ,WAAW,MAAM,EAAE,OAAO,SAAS;EAC9D,MAAM,UAAU,QAAQ;EAExB,MAAM,aAAa,OAAe;GAChC,aAAa,EAAE;GACf,SAAS,eAAe,GAAG,OAAO,GAAG,IAAI,CAAC,EAAE,cAAc,QAAQ,CAAC,EAAE,MAAM;EAC7E;EAEA,QAAQ,EAAE,KAAV;GACE,KAAK,aAAa;IAChB,EAAE,eAAe;IACjB,MAAM,OAAO,QAAQ,aAAa;IAClC,IAAI,QAAQ,CAAC,KAAK,UAAU,UAAU,KAAK,EAAE;IAC7C;GACF;GACA,KAAK,WAAW;IACd,EAAE,eAAe;IACjB,IAAI,OAAO,QAAQ,aAAa;IAChC,OAAO,QAAQ,KAAK,UAElB,OAAO,QADK,QAAQ,QAAQ,IACb,IAAM;IAEvB,IAAI,MAAM,UAAU,KAAK,EAAE;IAC3B;GACF;GACA,KAAK;IACH,EAAE,eAAe;IACjB,IAAI,SAAS,UAAU,UAAU,CAAC,SAAS,IAAI,QAAQ,EAAE,GACvD,aAAa,QAAQ,EAAE;SAClB;KACL,MAAM,QAAQ,SAAS,WAAW;KAClC,IAAI,SAAS,CAAC,MAAM,UAAU,UAAU,MAAM,EAAE;IAClD;IACA;GAEF,KAAK;IACH,EAAE,eAAe;IACjB,IAAI,SAAS,UAAU,UAAU,SAAS,IAAI,QAAQ,EAAE,GACtD,aAAa,QAAQ,EAAE;SAClB;KACL,MAAM,SAAS,WAAW,OAAO,SAAS,MAAM,EAAE;KAClD,IAAI,QAAQ,UAAU,OAAO,EAAE;IACjC;IACA;GAEF,KAAK,QAAQ;IACX,EAAE,eAAe;IACjB,MAAM,QAAQ,QAAQ,MAAM,MAAM,CAAC,EAAE,QAAQ;IAC7C,IAAI,OAAO,UAAU,MAAM,EAAE;IAC7B;GACF;GACA,KAAK,OAAO;IACV,EAAE,eAAe;IACjB,MAAM,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM,MAAM,CAAC,EAAE,QAAQ;IAC3D,IAAI,MAAM,UAAU,KAAK,EAAE;IAC3B;GACF;GACA,KAAK;GACL,KAAK;IACH,EAAE,eAAe;IACjB,IAAI,WAAW,CAAC,QAAQ,UAAU;KAChC,IAAI,QAAQ,UAAU,QAAQ,aAAa,QAAQ,EAAE;KACrD,aAAa,QAAQ,EAAE;IACzB;EAGJ;CACF;CAEA,OACE,oBAAC,MAAD;EACE,KAAK;EACL,MAAK;EACL,cAAY;EACZ,WAAW;EACX,WAAW,sCAAsC;EACjD,UAAU;EAET,UAAA,MAAM,KAAK,SACV,oBAAC,UAAD;GAEQ;GACN,OAAO;GACK;GACF;GACI;GACd,UAAU;GACF;EACT,GARM,KAAK,EAQX,CACF;CACC,CAAA;AAER"}
|
package/lib/index63.cjs
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
2
|
+
//#region src/ui-library/gallery/kanban/Kanban.tsx
|
|
3
|
+
function Kanban({ columns, className = "" }) {
|
|
4
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5
|
+
className: `w-full flex gap-4 overflow-x-auto pb-2 ${className}`,
|
|
6
|
+
children: columns.map((col) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
7
|
+
"aria-label": `${col.label} column, ${col.cards.length} cards`,
|
|
8
|
+
className: "flex w-64 shrink-0 flex-col rounded-2xl border border-slate-200 bg-slate-50/60 overflow-hidden",
|
|
9
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10
|
+
className: "flex items-center justify-between px-4 py-3",
|
|
11
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
12
|
+
className: "flex items-center gap-2",
|
|
13
|
+
children: [col.accentClass && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
14
|
+
className: `h-2.5 w-2.5 rounded-full ${col.accentClass}`,
|
|
15
|
+
"aria-hidden": "true"
|
|
16
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
17
|
+
className: "text-xs font-bold uppercase tracking-widest text-slate-600",
|
|
18
|
+
children: col.label
|
|
19
|
+
})]
|
|
20
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
21
|
+
"aria-label": `${col.cards.length} cards`,
|
|
22
|
+
className: "rounded-full bg-slate-200 px-2 py-0.5 text-[10px] font-bold text-slate-500",
|
|
23
|
+
children: col.cards.length
|
|
24
|
+
})]
|
|
25
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("ul", {
|
|
26
|
+
className: "flex flex-col gap-2 px-3 pb-3",
|
|
27
|
+
"aria-label": `${col.label} cards`,
|
|
28
|
+
children: [col.cards.map((card) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
29
|
+
className: "rounded-xl border border-slate-200 bg-white p-3 shadow-sm",
|
|
30
|
+
children: card.content
|
|
31
|
+
}) }, card.id)), col.cards.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
32
|
+
className: "flex items-center justify-center rounded-xl border-2 border-dashed border-slate-200 p-4 text-xs font-medium text-slate-400",
|
|
33
|
+
children: "No cards"
|
|
34
|
+
}) })]
|
|
35
|
+
})]
|
|
36
|
+
}, col.id))
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
exports.Kanban = Kanban;
|
|
41
|
+
|
|
42
|
+
//# sourceMappingURL=index63.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index63.cjs","names":[],"sources":["../src/ui-library/gallery/kanban/Kanban.tsx"],"sourcesContent":["/**\n * Kanban — Design System Component\n *\n * A static column-based board for visualizing workflow states.\n * Cards are rendered by the caller; this component provides the\n * column layout and card slot structure.\n *\n * No drag-and-drop: this is a gallery demonstration component.\n * For drag-and-drop in production, add @dnd-kit or similar.\n *\n * Accessibility:\n * - Each column is a <section> with aria-label\n * - Card count announced via aria-label on the count badge\n * - Card slots render as <li> items in a <ul>\n */\n\nimport type { ReactNode } from 'react';\n\nexport interface KanbanColumn {\n id: string;\n label: string;\n /** Color class for the column header accent. E.g. \"bg-amber-400\" */\n accentClass?: string;\n cards: KanbanCard[];\n}\n\nexport interface KanbanCard {\n id: string;\n content: ReactNode;\n}\n\nexport interface KanbanProps {\n columns: KanbanColumn[];\n /** Additional class on the root element. */\n className?: string;\n}\n\nexport function Kanban({ columns, className = '' }: KanbanProps) {\n return (\n <div className={`w-full flex gap-4 overflow-x-auto pb-2 ${className}`}>\n {columns.map((col) => (\n <section\n key={col.id}\n aria-label={`${col.label} column, ${col.cards.length} cards`}\n className=\"flex w-64 shrink-0 flex-col rounded-2xl border border-slate-200 bg-slate-50/60 overflow-hidden\"\n >\n {/* Column header */}\n <div className=\"flex items-center justify-between px-4 py-3\">\n <div className=\"flex items-center gap-2\">\n {col.accentClass && (\n <span className={`h-2.5 w-2.5 rounded-full ${col.accentClass}`} aria-hidden=\"true\" />\n )}\n <h3 className=\"text-xs font-bold uppercase tracking-widest text-slate-600\">\n {col.label}\n </h3>\n </div>\n <span\n aria-label={`${col.cards.length} cards`}\n className=\"rounded-full bg-slate-200 px-2 py-0.5 text-[10px] font-bold text-slate-500\"\n >\n {col.cards.length}\n </span>\n </div>\n\n {/* Cards */}\n <ul className=\"flex flex-col gap-2 px-3 pb-3\" aria-label={`${col.label} cards`}>\n {col.cards.map((card) => (\n <li key={card.id}>\n <div className=\"rounded-xl border border-slate-200 bg-white p-3 shadow-sm\">\n {card.content}\n </div>\n </li>\n ))}\n {col.cards.length === 0 && (\n <li>\n <div className=\"flex items-center justify-center rounded-xl border-2 border-dashed border-slate-200 p-4 text-xs font-medium text-slate-400\">\n No cards\n </div>\n </li>\n )}\n </ul>\n </section>\n ))}\n </div>\n );\n}\n"],"mappings":";;AAqCA,SAAgB,OAAO,EAAE,SAAS,YAAY,MAAmB;CAC/D,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;EAAK,WAAW,0CAA0C;EACvD,UAAA,QAAQ,KAAK,QACZ,iBAAA,GAAA,kBAAA,KAAA,CAAC,WAAD;GAEE,cAAY,GAAG,IAAI,MAAM,WAAW,IAAI,MAAM,OAAO;GACrD,WAAU;GAHZ,UAAA,CAME,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACG,IAAI,eACH,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,WAAW,4BAA4B,IAAI;MAAe,eAAY;KAAQ,CAAA,GAEtF,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD;MAAI,WAAU;MACX,UAAA,IAAI;KACH,CAAA,CACD;IACL,CAAA,GAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;KACE,cAAY,GAAG,IAAI,MAAM,OAAO;KAChC,WAAU;KAET,UAAA,IAAI,MAAM;IACP,CAAA,CACH;GAGL,CAAA,GAAA,iBAAA,GAAA,kBAAA,KAAA,CAAC,MAAD;IAAI,WAAU;IAAgC,cAAY,GAAG,IAAI,MAAM;IAAvE,UAAA,CACG,IAAI,MAAM,KAAK,SACd,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD,EAAA,UACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;KAAK,WAAU;KACZ,UAAA,KAAK;IACH,CAAA,EACH,GAJK,KAAK,EAIV,CACL,GACA,IAAI,MAAM,WAAW,KACpB,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD,EAAA,UACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;KAAK,WAAU;KAA6H,UAAA;IAEvI,CAAA,EACH,CAAA,CAEJ;GACG,CAAA,CAAA;EAvCF,GAAA,IAAI,EAuCF,CACV;CACE,CAAA;AAET"}
|
package/lib/index63.mjs
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
//#region src/ui-library/gallery/kanban/Kanban.tsx
|
|
3
|
+
function Kanban({ columns, className = "" }) {
|
|
4
|
+
return /* @__PURE__ */ jsx("div", {
|
|
5
|
+
className: `w-full flex gap-4 overflow-x-auto pb-2 ${className}`,
|
|
6
|
+
children: columns.map((col) => /* @__PURE__ */ jsxs("section", {
|
|
7
|
+
"aria-label": `${col.label} column, ${col.cards.length} cards`,
|
|
8
|
+
className: "flex w-64 shrink-0 flex-col rounded-2xl border border-slate-200 bg-slate-50/60 overflow-hidden",
|
|
9
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
10
|
+
className: "flex items-center justify-between px-4 py-3",
|
|
11
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
12
|
+
className: "flex items-center gap-2",
|
|
13
|
+
children: [col.accentClass && /* @__PURE__ */ jsx("span", {
|
|
14
|
+
className: `h-2.5 w-2.5 rounded-full ${col.accentClass}`,
|
|
15
|
+
"aria-hidden": "true"
|
|
16
|
+
}), /* @__PURE__ */ jsx("h3", {
|
|
17
|
+
className: "text-xs font-bold uppercase tracking-widest text-slate-600",
|
|
18
|
+
children: col.label
|
|
19
|
+
})]
|
|
20
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
21
|
+
"aria-label": `${col.cards.length} cards`,
|
|
22
|
+
className: "rounded-full bg-slate-200 px-2 py-0.5 text-[10px] font-bold text-slate-500",
|
|
23
|
+
children: col.cards.length
|
|
24
|
+
})]
|
|
25
|
+
}), /* @__PURE__ */ jsxs("ul", {
|
|
26
|
+
className: "flex flex-col gap-2 px-3 pb-3",
|
|
27
|
+
"aria-label": `${col.label} cards`,
|
|
28
|
+
children: [col.cards.map((card) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("div", {
|
|
29
|
+
className: "rounded-xl border border-slate-200 bg-white p-3 shadow-sm",
|
|
30
|
+
children: card.content
|
|
31
|
+
}) }, card.id)), col.cards.length === 0 && /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("div", {
|
|
32
|
+
className: "flex items-center justify-center rounded-xl border-2 border-dashed border-slate-200 p-4 text-xs font-medium text-slate-400",
|
|
33
|
+
children: "No cards"
|
|
34
|
+
}) })]
|
|
35
|
+
})]
|
|
36
|
+
}, col.id))
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
export { Kanban };
|
|
41
|
+
|
|
42
|
+
//# sourceMappingURL=index63.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index63.mjs","names":[],"sources":["../src/ui-library/gallery/kanban/Kanban.tsx"],"sourcesContent":["/**\n * Kanban — Design System Component\n *\n * A static column-based board for visualizing workflow states.\n * Cards are rendered by the caller; this component provides the\n * column layout and card slot structure.\n *\n * No drag-and-drop: this is a gallery demonstration component.\n * For drag-and-drop in production, add @dnd-kit or similar.\n *\n * Accessibility:\n * - Each column is a <section> with aria-label\n * - Card count announced via aria-label on the count badge\n * - Card slots render as <li> items in a <ul>\n */\n\nimport type { ReactNode } from 'react';\n\nexport interface KanbanColumn {\n id: string;\n label: string;\n /** Color class for the column header accent. E.g. \"bg-amber-400\" */\n accentClass?: string;\n cards: KanbanCard[];\n}\n\nexport interface KanbanCard {\n id: string;\n content: ReactNode;\n}\n\nexport interface KanbanProps {\n columns: KanbanColumn[];\n /** Additional class on the root element. */\n className?: string;\n}\n\nexport function Kanban({ columns, className = '' }: KanbanProps) {\n return (\n <div className={`w-full flex gap-4 overflow-x-auto pb-2 ${className}`}>\n {columns.map((col) => (\n <section\n key={col.id}\n aria-label={`${col.label} column, ${col.cards.length} cards`}\n className=\"flex w-64 shrink-0 flex-col rounded-2xl border border-slate-200 bg-slate-50/60 overflow-hidden\"\n >\n {/* Column header */}\n <div className=\"flex items-center justify-between px-4 py-3\">\n <div className=\"flex items-center gap-2\">\n {col.accentClass && (\n <span className={`h-2.5 w-2.5 rounded-full ${col.accentClass}`} aria-hidden=\"true\" />\n )}\n <h3 className=\"text-xs font-bold uppercase tracking-widest text-slate-600\">\n {col.label}\n </h3>\n </div>\n <span\n aria-label={`${col.cards.length} cards`}\n className=\"rounded-full bg-slate-200 px-2 py-0.5 text-[10px] font-bold text-slate-500\"\n >\n {col.cards.length}\n </span>\n </div>\n\n {/* Cards */}\n <ul className=\"flex flex-col gap-2 px-3 pb-3\" aria-label={`${col.label} cards`}>\n {col.cards.map((card) => (\n <li key={card.id}>\n <div className=\"rounded-xl border border-slate-200 bg-white p-3 shadow-sm\">\n {card.content}\n </div>\n </li>\n ))}\n {col.cards.length === 0 && (\n <li>\n <div className=\"flex items-center justify-center rounded-xl border-2 border-dashed border-slate-200 p-4 text-xs font-medium text-slate-400\">\n No cards\n </div>\n </li>\n )}\n </ul>\n </section>\n ))}\n </div>\n );\n}\n"],"mappings":";;AAqCA,SAAgB,OAAO,EAAE,SAAS,YAAY,MAAmB;CAC/D,OACE,oBAAC,OAAD;EAAK,WAAW,0CAA0C;EACvD,UAAA,QAAQ,KAAK,QACZ,qBAAC,WAAD;GAEE,cAAY,GAAG,IAAI,MAAM,WAAW,IAAI,MAAM,OAAO;GACrD,WAAU;GAHZ,UAAA,CAME,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,qBAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACG,IAAI,eACH,oBAAC,QAAD;MAAM,WAAW,4BAA4B,IAAI;MAAe,eAAY;KAAQ,CAAA,GAEtF,oBAAC,MAAD;MAAI,WAAU;MACX,UAAA,IAAI;KACH,CAAA,CACD;IACL,CAAA,GAAA,oBAAC,QAAD;KACE,cAAY,GAAG,IAAI,MAAM,OAAO;KAChC,WAAU;KAET,UAAA,IAAI,MAAM;IACP,CAAA,CACH;GAGL,CAAA,GAAA,qBAAC,MAAD;IAAI,WAAU;IAAgC,cAAY,GAAG,IAAI,MAAM;IAAvE,UAAA,CACG,IAAI,MAAM,KAAK,SACd,oBAAC,MAAD,EAAA,UACE,oBAAC,OAAD;KAAK,WAAU;KACZ,UAAA,KAAK;IACH,CAAA,EACH,GAJK,KAAK,EAIV,CACL,GACA,IAAI,MAAM,WAAW,KACpB,oBAAC,MAAD,EAAA,UACE,oBAAC,OAAD;KAAK,WAAU;KAA6H,UAAA;IAEvI,CAAA,EACH,CAAA,CAEJ;GACG,CAAA,CAAA;EAvCF,GAAA,IAAI,EAuCF,CACV;CACE,CAAA;AAET"}
|