@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.
Files changed (272) hide show
  1. package/README.md +75 -0
  2. package/lib/assets/ui.css +154 -0
  3. package/lib/index.cjs +183 -0
  4. package/lib/index.mjs +65 -0
  5. package/lib/index10.cjs +33 -0
  6. package/lib/index10.cjs.map +1 -0
  7. package/lib/index10.mjs +33 -0
  8. package/lib/index10.mjs.map +1 -0
  9. package/lib/index11.cjs +16 -0
  10. package/lib/index11.cjs.map +1 -0
  11. package/lib/index11.mjs +16 -0
  12. package/lib/index11.mjs.map +1 -0
  13. package/lib/index12.cjs +16 -0
  14. package/lib/index12.cjs.map +1 -0
  15. package/lib/index12.mjs +15 -0
  16. package/lib/index12.mjs.map +1 -0
  17. package/lib/index13.cjs +57 -0
  18. package/lib/index13.cjs.map +1 -0
  19. package/lib/index13.mjs +57 -0
  20. package/lib/index13.mjs.map +1 -0
  21. package/lib/index14.cjs +10 -0
  22. package/lib/index14.cjs.map +1 -0
  23. package/lib/index14.mjs +10 -0
  24. package/lib/index14.mjs.map +1 -0
  25. package/lib/index15.cjs +30 -0
  26. package/lib/index15.cjs.map +1 -0
  27. package/lib/index15.mjs +30 -0
  28. package/lib/index15.mjs.map +1 -0
  29. package/lib/index16.cjs +28 -0
  30. package/lib/index16.cjs.map +1 -0
  31. package/lib/index16.mjs +26 -0
  32. package/lib/index16.mjs.map +1 -0
  33. package/lib/index17.cjs +43 -0
  34. package/lib/index17.cjs.map +1 -0
  35. package/lib/index17.mjs +43 -0
  36. package/lib/index17.mjs.map +1 -0
  37. package/lib/index18.cjs +59 -0
  38. package/lib/index18.cjs.map +1 -0
  39. package/lib/index18.mjs +59 -0
  40. package/lib/index18.mjs.map +1 -0
  41. package/lib/index19.cjs +67 -0
  42. package/lib/index19.cjs.map +1 -0
  43. package/lib/index19.mjs +67 -0
  44. package/lib/index19.mjs.map +1 -0
  45. package/lib/index2.cjs +33 -0
  46. package/lib/index2.mjs +13 -0
  47. package/lib/index20.cjs +79 -0
  48. package/lib/index20.cjs.map +1 -0
  49. package/lib/index20.mjs +79 -0
  50. package/lib/index20.mjs.map +1 -0
  51. package/lib/index21.cjs +105 -0
  52. package/lib/index21.cjs.map +1 -0
  53. package/lib/index21.mjs +104 -0
  54. package/lib/index21.mjs.map +1 -0
  55. package/lib/index22.cjs +50 -0
  56. package/lib/index22.cjs.map +1 -0
  57. package/lib/index22.mjs +50 -0
  58. package/lib/index22.mjs.map +1 -0
  59. package/lib/index23.cjs +143 -0
  60. package/lib/index23.cjs.map +1 -0
  61. package/lib/index23.mjs +143 -0
  62. package/lib/index23.mjs.map +1 -0
  63. package/lib/index25.cjs +177 -0
  64. package/lib/index25.cjs.map +1 -0
  65. package/lib/index25.mjs +175 -0
  66. package/lib/index25.mjs.map +1 -0
  67. package/lib/index26.cjs +108 -0
  68. package/lib/index26.cjs.map +1 -0
  69. package/lib/index26.mjs +108 -0
  70. package/lib/index26.mjs.map +1 -0
  71. package/lib/index27.cjs +120 -0
  72. package/lib/index27.cjs.map +1 -0
  73. package/lib/index27.mjs +120 -0
  74. package/lib/index27.mjs.map +1 -0
  75. package/lib/index28.cjs +54 -0
  76. package/lib/index28.cjs.map +1 -0
  77. package/lib/index28.mjs +54 -0
  78. package/lib/index28.mjs.map +1 -0
  79. package/lib/index29.cjs +100 -0
  80. package/lib/index29.cjs.map +1 -0
  81. package/lib/index29.mjs +100 -0
  82. package/lib/index29.mjs.map +1 -0
  83. package/lib/index3.cjs +38 -0
  84. package/lib/index3.cjs.map +1 -0
  85. package/lib/index3.mjs +38 -0
  86. package/lib/index3.mjs.map +1 -0
  87. package/lib/index30.cjs +27 -0
  88. package/lib/index30.cjs.map +1 -0
  89. package/lib/index30.mjs +27 -0
  90. package/lib/index30.mjs.map +1 -0
  91. package/lib/index31.cjs +72 -0
  92. package/lib/index31.cjs.map +1 -0
  93. package/lib/index31.mjs +72 -0
  94. package/lib/index31.mjs.map +1 -0
  95. package/lib/index32.cjs +19 -0
  96. package/lib/index32.cjs.map +1 -0
  97. package/lib/index32.mjs +19 -0
  98. package/lib/index32.mjs.map +1 -0
  99. package/lib/index33.cjs +25 -0
  100. package/lib/index33.cjs.map +1 -0
  101. package/lib/index33.mjs +25 -0
  102. package/lib/index33.mjs.map +1 -0
  103. package/lib/index34.cjs +55 -0
  104. package/lib/index34.cjs.map +1 -0
  105. package/lib/index34.mjs +51 -0
  106. package/lib/index34.mjs.map +1 -0
  107. package/lib/index35.cjs +25 -0
  108. package/lib/index35.cjs.map +1 -0
  109. package/lib/index35.mjs +25 -0
  110. package/lib/index35.mjs.map +1 -0
  111. package/lib/index36.cjs +54 -0
  112. package/lib/index36.cjs.map +1 -0
  113. package/lib/index36.mjs +49 -0
  114. package/lib/index36.mjs.map +1 -0
  115. package/lib/index37.cjs +130 -0
  116. package/lib/index37.cjs.map +1 -0
  117. package/lib/index37.mjs +128 -0
  118. package/lib/index37.mjs.map +1 -0
  119. package/lib/index38.cjs +106 -0
  120. package/lib/index38.cjs.map +1 -0
  121. package/lib/index38.mjs +102 -0
  122. package/lib/index38.mjs.map +1 -0
  123. package/lib/index39.cjs +137 -0
  124. package/lib/index39.cjs.map +1 -0
  125. package/lib/index39.mjs +137 -0
  126. package/lib/index39.mjs.map +1 -0
  127. package/lib/index4.cjs +24 -0
  128. package/lib/index4.cjs.map +1 -0
  129. package/lib/index4.mjs +24 -0
  130. package/lib/index4.mjs.map +1 -0
  131. package/lib/index40.cjs +130 -0
  132. package/lib/index40.cjs.map +1 -0
  133. package/lib/index40.mjs +126 -0
  134. package/lib/index40.mjs.map +1 -0
  135. package/lib/index41.cjs +60 -0
  136. package/lib/index41.cjs.map +1 -0
  137. package/lib/index41.mjs +58 -0
  138. package/lib/index41.mjs.map +1 -0
  139. package/lib/index42.cjs +105 -0
  140. package/lib/index42.cjs.map +1 -0
  141. package/lib/index42.mjs +105 -0
  142. package/lib/index42.mjs.map +1 -0
  143. package/lib/index43.cjs +47 -0
  144. package/lib/index43.cjs.map +1 -0
  145. package/lib/index43.mjs +42 -0
  146. package/lib/index43.mjs.map +1 -0
  147. package/lib/index44.cjs +40 -0
  148. package/lib/index44.cjs.map +1 -0
  149. package/lib/index44.mjs +35 -0
  150. package/lib/index44.mjs.map +1 -0
  151. package/lib/index45.cjs +91 -0
  152. package/lib/index45.cjs.map +1 -0
  153. package/lib/index45.mjs +91 -0
  154. package/lib/index45.mjs.map +1 -0
  155. package/lib/index46.cjs +180 -0
  156. package/lib/index46.cjs.map +1 -0
  157. package/lib/index46.mjs +180 -0
  158. package/lib/index46.mjs.map +1 -0
  159. package/lib/index47.cjs +61 -0
  160. package/lib/index47.cjs.map +1 -0
  161. package/lib/index47.mjs +61 -0
  162. package/lib/index47.mjs.map +1 -0
  163. package/lib/index48.cjs +165 -0
  164. package/lib/index48.cjs.map +1 -0
  165. package/lib/index48.mjs +161 -0
  166. package/lib/index48.mjs.map +1 -0
  167. package/lib/index49.cjs +83 -0
  168. package/lib/index49.cjs.map +1 -0
  169. package/lib/index49.mjs +83 -0
  170. package/lib/index49.mjs.map +1 -0
  171. package/lib/index5.cjs +39 -0
  172. package/lib/index5.cjs.map +1 -0
  173. package/lib/index5.mjs +39 -0
  174. package/lib/index5.mjs.map +1 -0
  175. package/lib/index50.cjs +100 -0
  176. package/lib/index50.cjs.map +1 -0
  177. package/lib/index50.mjs +100 -0
  178. package/lib/index50.mjs.map +1 -0
  179. package/lib/index51.cjs +116 -0
  180. package/lib/index51.cjs.map +1 -0
  181. package/lib/index51.mjs +113 -0
  182. package/lib/index51.mjs.map +1 -0
  183. package/lib/index52.cjs +93 -0
  184. package/lib/index52.cjs.map +1 -0
  185. package/lib/index52.mjs +93 -0
  186. package/lib/index52.mjs.map +1 -0
  187. package/lib/index53.cjs +120 -0
  188. package/lib/index53.cjs.map +1 -0
  189. package/lib/index53.mjs +120 -0
  190. package/lib/index53.mjs.map +1 -0
  191. package/lib/index54.cjs +53 -0
  192. package/lib/index54.cjs.map +1 -0
  193. package/lib/index54.mjs +50 -0
  194. package/lib/index54.mjs.map +1 -0
  195. package/lib/index55.cjs +130 -0
  196. package/lib/index55.cjs.map +1 -0
  197. package/lib/index55.mjs +127 -0
  198. package/lib/index55.mjs.map +1 -0
  199. package/lib/index56.cjs +36 -0
  200. package/lib/index56.cjs.map +1 -0
  201. package/lib/index56.mjs +35 -0
  202. package/lib/index56.mjs.map +1 -0
  203. package/lib/index57.cjs +47 -0
  204. package/lib/index57.cjs.map +1 -0
  205. package/lib/index57.mjs +47 -0
  206. package/lib/index57.mjs.map +1 -0
  207. package/lib/index58.cjs +97 -0
  208. package/lib/index58.cjs.map +1 -0
  209. package/lib/index58.mjs +97 -0
  210. package/lib/index58.mjs.map +1 -0
  211. package/lib/index59.cjs +120 -0
  212. package/lib/index59.cjs.map +1 -0
  213. package/lib/index59.mjs +116 -0
  214. package/lib/index59.mjs.map +1 -0
  215. package/lib/index6.cjs +29 -0
  216. package/lib/index6.cjs.map +1 -0
  217. package/lib/index6.mjs +29 -0
  218. package/lib/index6.mjs.map +1 -0
  219. package/lib/index60.cjs +34 -0
  220. package/lib/index60.cjs.map +1 -0
  221. package/lib/index60.mjs +34 -0
  222. package/lib/index60.mjs.map +1 -0
  223. package/lib/index61.cjs +62 -0
  224. package/lib/index61.cjs.map +1 -0
  225. package/lib/index61.mjs +62 -0
  226. package/lib/index61.mjs.map +1 -0
  227. package/lib/index62.cjs +213 -0
  228. package/lib/index62.cjs.map +1 -0
  229. package/lib/index62.mjs +213 -0
  230. package/lib/index62.mjs.map +1 -0
  231. package/lib/index63.cjs +42 -0
  232. package/lib/index63.cjs.map +1 -0
  233. package/lib/index63.mjs +42 -0
  234. package/lib/index63.mjs.map +1 -0
  235. package/lib/index64.cjs +86 -0
  236. package/lib/index64.cjs.map +1 -0
  237. package/lib/index64.mjs +86 -0
  238. package/lib/index64.mjs.map +1 -0
  239. package/lib/index65.cjs +156 -0
  240. package/lib/index65.cjs.map +1 -0
  241. package/lib/index65.mjs +156 -0
  242. package/lib/index65.mjs.map +1 -0
  243. package/lib/index66.cjs +107 -0
  244. package/lib/index66.cjs.map +1 -0
  245. package/lib/index66.mjs +106 -0
  246. package/lib/index66.mjs.map +1 -0
  247. package/lib/index67.cjs +52 -0
  248. package/lib/index67.cjs.map +1 -0
  249. package/lib/index67.mjs +52 -0
  250. package/lib/index67.mjs.map +1 -0
  251. package/lib/index68.cjs +125 -0
  252. package/lib/index68.cjs.map +1 -0
  253. package/lib/index68.mjs +115 -0
  254. package/lib/index68.mjs.map +1 -0
  255. package/lib/index69.cjs +34 -0
  256. package/lib/index69.cjs.map +1 -0
  257. package/lib/index69.mjs +29 -0
  258. package/lib/index69.mjs.map +1 -0
  259. package/lib/index7.cjs +17 -0
  260. package/lib/index7.cjs.map +1 -0
  261. package/lib/index7.mjs +17 -0
  262. package/lib/index7.mjs.map +1 -0
  263. package/lib/index8.cjs +86 -0
  264. package/lib/index8.cjs.map +1 -0
  265. package/lib/index8.mjs +86 -0
  266. package/lib/index8.mjs.map +1 -0
  267. package/lib/index9.cjs +29 -0
  268. package/lib/index9.cjs.map +1 -0
  269. package/lib/index9.mjs +29 -0
  270. package/lib/index9.mjs.map +1 -0
  271. package/lib/types/index.d.ts +1 -0
  272. package/package.json +68 -0
@@ -0,0 +1,127 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { createContext, useContext, useId, useState } from "react";
3
+ import { ChevronDown } from "lucide-react";
4
+ //#region src/ui-library/gallery/accordion/Accordion.tsx
5
+ /**
6
+ * Accordion — Design System Component
7
+ *
8
+ * Disclosure/accordion pattern with support for single-open and
9
+ * multiple-open modes, controlled/uncontrolled state, and disabled items.
10
+ *
11
+ * WAI-ARIA Accordion pattern:
12
+ * - Trigger is a <button> with aria-expanded and aria-controls
13
+ * - Content panel has a stable id referenced by aria-controls
14
+ * - Disabled triggers have aria-disabled + disabled attribute
15
+ *
16
+ * Keyboard:
17
+ * - Tab moves between triggers
18
+ * - Enter / Space toggle the focused trigger
19
+ *
20
+ * Composition:
21
+ * <Accordion type="single" defaultValue="item-1">
22
+ * <AccordionItem value="item-1">
23
+ * <AccordionTrigger>Section heading</AccordionTrigger>
24
+ * <AccordionContent>Content here.</AccordionContent>
25
+ * </AccordionItem>
26
+ * </Accordion>
27
+ */
28
+ var AccordionContext = createContext(null);
29
+ function useAccordionContext() {
30
+ const ctx = useContext(AccordionContext);
31
+ if (!ctx) throw new Error("AccordionItem must be used inside <Accordion>");
32
+ return ctx;
33
+ }
34
+ var ItemContext = createContext(null);
35
+ function useItemContext() {
36
+ const ctx = useContext(ItemContext);
37
+ if (!ctx) throw new Error("AccordionTrigger/AccordionContent must be inside <AccordionItem>");
38
+ return ctx;
39
+ }
40
+ function Accordion({ type = "single", defaultValue, value: controlledValue, onValueChange, children, className = "" }) {
41
+ const isControlled = controlledValue !== void 0;
42
+ const [uncontrolled, setUncontrolled] = useState(() => {
43
+ if (!defaultValue) return /* @__PURE__ */ new Set();
44
+ return new Set(Array.isArray(defaultValue) ? defaultValue : [defaultValue]);
45
+ });
46
+ const openValues = isControlled ? new Set(Array.isArray(controlledValue) ? controlledValue : [controlledValue]) : uncontrolled;
47
+ const toggle = (val) => {
48
+ let next;
49
+ if (type === "single") next = openValues.has(val) ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([val]);
50
+ else {
51
+ next = new Set(openValues);
52
+ if (next.has(val)) next.delete(val);
53
+ else next.add(val);
54
+ }
55
+ if (!isControlled) setUncontrolled(next);
56
+ if (onValueChange) onValueChange(type === "single" ? next.values().next().value ?? "" : [...next]);
57
+ };
58
+ return /* @__PURE__ */ jsx(AccordionContext.Provider, {
59
+ value: {
60
+ type,
61
+ openValues,
62
+ toggle
63
+ },
64
+ children: /* @__PURE__ */ jsx("div", {
65
+ className: `divide-y divide-slate-200 rounded-xl border border-slate-200 ${className}`,
66
+ children
67
+ })
68
+ });
69
+ }
70
+ function AccordionItem({ value, disabled = false, children, className = "" }) {
71
+ const baseId = useId().replace(/:/g, "");
72
+ const panelId = `${baseId}-panel`;
73
+ const triggerId = `${baseId}-trigger`;
74
+ return /* @__PURE__ */ jsx(ItemContext.Provider, {
75
+ value: {
76
+ value,
77
+ disabled,
78
+ panelId,
79
+ triggerId
80
+ },
81
+ children: /* @__PURE__ */ jsx("div", {
82
+ className: `${className}`,
83
+ children
84
+ })
85
+ });
86
+ }
87
+ function AccordionTrigger({ children, className = "" }) {
88
+ const { openValues, toggle } = useAccordionContext();
89
+ const { value, disabled, panelId, triggerId } = useItemContext();
90
+ const isOpen = openValues.has(value);
91
+ return /* @__PURE__ */ jsxs("button", {
92
+ id: triggerId,
93
+ type: "button",
94
+ "aria-expanded": isOpen,
95
+ "aria-controls": panelId,
96
+ "aria-disabled": disabled || void 0,
97
+ disabled,
98
+ onClick: () => toggle(value),
99
+ className: [
100
+ "flex w-full items-center justify-between px-5 py-4 text-left text-sm font-semibold",
101
+ "transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-brand-blue/30",
102
+ disabled ? "cursor-not-allowed text-slate-300" : "text-slate-800 hover:bg-slate-50",
103
+ className
104
+ ].filter(Boolean).join(" "),
105
+ children: [/* @__PURE__ */ jsx("span", { children }), /* @__PURE__ */ jsx(ChevronDown, {
106
+ size: 16,
107
+ "aria-hidden": "true",
108
+ className: `shrink-0 text-slate-400 transition-transform duration-200 ${isOpen ? "rotate-180" : ""}`
109
+ })]
110
+ });
111
+ }
112
+ function AccordionContent({ children, className = "" }) {
113
+ const { openValues } = useAccordionContext();
114
+ const { value, panelId, triggerId } = useItemContext();
115
+ if (!openValues.has(value)) return null;
116
+ return /* @__PURE__ */ jsx("div", {
117
+ id: panelId,
118
+ role: "region",
119
+ "aria-labelledby": triggerId,
120
+ className: `border-t border-slate-100 px-5 py-4 text-sm font-medium text-slate-600 ${className}`,
121
+ children
122
+ });
123
+ }
124
+ //#endregion
125
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
126
+
127
+ //# sourceMappingURL=index55.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index55.mjs","names":[],"sources":["../src/ui-library/gallery/accordion/Accordion.tsx"],"sourcesContent":["/**\n * Accordion — Design System Component\n *\n * Disclosure/accordion pattern with support for single-open and\n * multiple-open modes, controlled/uncontrolled state, and disabled items.\n *\n * WAI-ARIA Accordion pattern:\n * - Trigger is a <button> with aria-expanded and aria-controls\n * - Content panel has a stable id referenced by aria-controls\n * - Disabled triggers have aria-disabled + disabled attribute\n *\n * Keyboard:\n * - Tab moves between triggers\n * - Enter / Space toggle the focused trigger\n *\n * Composition:\n * <Accordion type=\"single\" defaultValue=\"item-1\">\n * <AccordionItem value=\"item-1\">\n * <AccordionTrigger>Section heading</AccordionTrigger>\n * <AccordionContent>Content here.</AccordionContent>\n * </AccordionItem>\n * </Accordion>\n */\n\nimport { createContext, useContext, useId, useState, type ReactNode } from 'react';\nimport { ChevronDown } from 'lucide-react';\n\n// ── Context ───────────────────────────────────────────────────────────────────\n\ninterface AccordionContextValue {\n type: 'single' | 'multiple';\n openValues: Set<string>;\n toggle: (value: string) => void;\n}\n\nconst AccordionContext = createContext<AccordionContextValue | null>(null);\n\nfunction useAccordionContext() {\n const ctx = useContext(AccordionContext);\n if (!ctx) throw new Error('AccordionItem must be used inside <Accordion>');\n return ctx;\n}\n\ninterface ItemContextValue {\n value: string;\n disabled: boolean;\n panelId: string;\n triggerId: string;\n}\n\nconst ItemContext = createContext<ItemContextValue | null>(null);\n\nfunction useItemContext() {\n const ctx = useContext(ItemContext);\n if (!ctx) throw new Error('AccordionTrigger/AccordionContent must be inside <AccordionItem>');\n return ctx;\n}\n\n// ── Accordion (root) ──────────────────────────────────────────────────────────\n\nexport interface AccordionProps {\n /**\n * 'single' — only one item open at a time (default)\n * 'multiple' — any number of items can be open simultaneously\n */\n type?: 'single' | 'multiple';\n /** Uncontrolled: initial open value(s) */\n defaultValue?: string | string[];\n /** Controlled: open value(s) */\n value?: string | string[];\n /** Controlled: called when open values change */\n onValueChange?: (value: string | string[]) => void;\n children: ReactNode;\n className?: string;\n}\n\nexport function Accordion({\n type = 'single',\n defaultValue,\n value: controlledValue,\n onValueChange,\n children,\n className = '',\n}: AccordionProps) {\n const isControlled = controlledValue !== undefined;\n\n const [uncontrolled, setUncontrolled] = useState<Set<string>>(() => {\n if (!defaultValue) return new Set();\n return new Set(Array.isArray(defaultValue) ? defaultValue : [defaultValue]);\n });\n\n const openValues: Set<string> = isControlled\n ? new Set(Array.isArray(controlledValue) ? controlledValue : [controlledValue])\n : uncontrolled;\n\n const toggle = (val: string) => {\n let next: Set<string>;\n if (type === 'single') {\n next = openValues.has(val) ? new Set() : new Set([val]);\n } else {\n next = new Set(openValues);\n if (next.has(val)) next.delete(val); else next.add(val);\n }\n if (!isControlled) setUncontrolled(next);\n if (onValueChange) {\n onValueChange(type === 'single' ? (next.values().next().value ?? '') : [...next]);\n }\n };\n\n return (\n <AccordionContext.Provider value={{ type, openValues, toggle }}>\n <div className={`divide-y divide-slate-200 rounded-xl border border-slate-200 ${className}`}>\n {children}\n </div>\n </AccordionContext.Provider>\n );\n}\n\n// ── AccordionItem ─────────────────────────────────────────────────────────────\n\nexport interface AccordionItemProps {\n value: string;\n disabled?: boolean;\n children: ReactNode;\n className?: string;\n}\n\nexport function AccordionItem({ value, disabled = false, children, className = '' }: AccordionItemProps) {\n const baseId = useId().replace(/:/g, '');\n const panelId = `${baseId}-panel`;\n const triggerId = `${baseId}-trigger`;\n\n return (\n <ItemContext.Provider value={{ value, disabled, panelId, triggerId }}>\n <div className={`${className}`}>\n {children}\n </div>\n </ItemContext.Provider>\n );\n}\n\n// ── AccordionTrigger ──────────────────────────────────────────────────────────\n\nexport interface AccordionTriggerProps {\n children: ReactNode;\n className?: string;\n}\n\nexport function AccordionTrigger({ children, className = '' }: AccordionTriggerProps) {\n const { openValues, toggle } = useAccordionContext();\n const { value, disabled, panelId, triggerId } = useItemContext();\n const isOpen = openValues.has(value);\n\n return (\n <button\n id={triggerId}\n type=\"button\"\n aria-expanded={isOpen}\n aria-controls={panelId}\n aria-disabled={disabled || undefined}\n disabled={disabled}\n onClick={() => toggle(value)}\n className={[\n 'flex w-full items-center justify-between px-5 py-4 text-left text-sm font-semibold',\n 'transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-brand-blue/30',\n disabled\n ? 'cursor-not-allowed text-slate-300'\n : 'text-slate-800 hover:bg-slate-50',\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n >\n <span>{children}</span>\n <ChevronDown\n size={16}\n aria-hidden=\"true\"\n className={`shrink-0 text-slate-400 transition-transform duration-200 ${isOpen ? 'rotate-180' : ''}`}\n />\n </button>\n );\n}\n\n// ── AccordionContent ──────────────────────────────────────────────────────────\n\nexport interface AccordionContentProps {\n children: ReactNode;\n className?: string;\n}\n\nexport function AccordionContent({ children, className = '' }: AccordionContentProps) {\n const { openValues } = useAccordionContext();\n const { value, panelId, triggerId } = useItemContext();\n const isOpen = openValues.has(value);\n\n if (!isOpen) return null;\n\n return (\n <div\n id={panelId}\n role=\"region\"\n aria-labelledby={triggerId}\n className={`border-t border-slate-100 px-5 py-4 text-sm font-medium text-slate-600 ${className}`}\n >\n {children}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,IAAM,mBAAmB,cAA4C,IAAI;AAEzE,SAAS,sBAAsB;CAC7B,MAAM,MAAM,WAAW,gBAAgB;CACvC,IAAI,CAAC,KAAK,MAAM,IAAI,MAAM,+CAA+C;CACzE,OAAO;AACT;AASA,IAAM,cAAc,cAAuC,IAAI;AAE/D,SAAS,iBAAiB;CACxB,MAAM,MAAM,WAAW,WAAW;CAClC,IAAI,CAAC,KAAK,MAAM,IAAI,MAAM,kEAAkE;CAC5F,OAAO;AACT;AAoBA,SAAgB,UAAU,EACxB,OAAO,UACP,cACA,OAAO,iBACP,eACA,UACA,YAAY,MACK;CACjB,MAAM,eAAe,oBAAoB,KAAA;CAEzC,MAAM,CAAC,cAAc,mBAAmB,eAA4B;EAClE,IAAI,CAAC,cAAc,uBAAO,IAAI,IAAI;EAClC,OAAO,IAAI,IAAI,MAAM,QAAQ,YAAY,IAAI,eAAe,CAAC,YAAY,CAAC;CAC5E,CAAC;CAED,MAAM,aAA0B,eAC5B,IAAI,IAAI,MAAM,QAAQ,eAAe,IAAI,kBAAkB,CAAC,eAAe,CAAC,IAC5E;CAEJ,MAAM,UAAU,QAAgB;EAC9B,IAAI;EACJ,IAAI,SAAS,UACX,OAAO,WAAW,IAAI,GAAG,oBAAI,IAAI,IAAI,oBAAI,IAAI,IAAI,CAAC,GAAG,CAAC;OACjD;GACL,OAAO,IAAI,IAAI,UAAU;GACzB,IAAI,KAAK,IAAI,GAAG,GAAG,KAAK,OAAO,GAAG;QAAQ,KAAK,IAAI,GAAG;EACxD;EACA,IAAI,CAAC,cAAc,gBAAgB,IAAI;EACvC,IAAI,eACF,cAAc,SAAS,WAAY,KAAK,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,SAAS,KAAM,CAAC,GAAG,IAAI,CAAC;CAEpF;CAEA,OACE,oBAAC,iBAAiB,UAAlB;EAA2B,OAAO;GAAE;GAAM;GAAY;EAAO;EAC3D,UAAA,oBAAC,OAAD;GAAK,WAAW,gEAAgE;GAC7E;EACE,CAAA;CACoB,CAAA;AAE/B;AAWA,SAAgB,cAAc,EAAE,OAAO,WAAW,OAAO,UAAU,YAAY,MAA0B;CACvG,MAAM,SAAW,MAAM,CAAC,CAAC,QAAQ,MAAM,EAAE;CACzC,MAAM,UAAY,GAAG,OAAO;CAC5B,MAAM,YAAY,GAAG,OAAO;CAE5B,OACE,oBAAC,YAAY,UAAb;EAAsB,OAAO;GAAE;GAAO;GAAU;GAAS;EAAU;EACjE,UAAA,oBAAC,OAAD;GAAK,WAAW,GAAG;GAChB;EACE,CAAA;CACe,CAAA;AAE1B;AASA,SAAgB,iBAAiB,EAAE,UAAU,YAAY,MAA6B;CACpF,MAAM,EAAE,YAAY,WAAW,oBAAoB;CACnD,MAAM,EAAE,OAAO,UAAU,SAAS,cAAc,eAAe;CAC/D,MAAM,SAAS,WAAW,IAAI,KAAK;CAEnC,OACE,qBAAC,UAAD;EACE,IAAI;EACJ,MAAK;EACL,iBAAe;EACf,iBAAe;EACf,iBAAe,YAAY,KAAA;EACjB;EACV,eAAe,OAAO,KAAK;EAC3B,WAAW;GACT;GACA;GACA,WACI,sCACA;GACJ;EACF,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;EAjBb,UAAA,CAmBE,oBAAC,QAAD,EAAO,SAAe,CAAA,GACtB,oBAAC,aAAD;GACE,MAAM;GACN,eAAY;GACZ,WAAW,6DAA6D,SAAS,eAAe;EACjG,CAAA,CACK;;AAEZ;AASA,SAAgB,iBAAiB,EAAE,UAAU,YAAY,MAA6B;CACpF,MAAM,EAAE,eAAe,oBAAoB;CAC3C,MAAM,EAAE,OAAO,SAAS,cAAc,eAAe;CAGrD,IAAI,CAFW,WAAW,IAAI,KAEzB,GAAQ,OAAO;CAEpB,OACE,oBAAC,OAAD;EACE,IAAI;EACJ,MAAK;EACL,mBAAiB;EACjB,WAAW,0EAA0E;EAEpF;CACE,CAAA;AAET"}
@@ -0,0 +1,36 @@
1
+ let react_jsx_runtime = require("react/jsx-runtime");
2
+ //#region src/ui-library/gallery/description-list/DescriptionList.tsx
3
+ var LAYOUT_CLASS = {
4
+ vertical: "flex flex-col gap-4",
5
+ horizontal: "grid grid-cols-[minmax(8rem,14rem)_1fr] gap-x-6 gap-y-3",
6
+ responsive: "grid grid-cols-1 sm:grid-cols-[minmax(8rem,14rem)_1fr] gap-x-6 gap-y-3"
7
+ };
8
+ function DescriptionList({ layout = "vertical", children, className = "" }) {
9
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dl", {
10
+ className: `${LAYOUT_CLASS[layout]} ${className}`,
11
+ children
12
+ });
13
+ }
14
+ function DescriptionItem({ label, value, fullWidth = false, className = "" }) {
15
+ const isEmpty = value === void 0 || value === null || value === "";
16
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", {
17
+ className: [
18
+ "text-xs font-bold uppercase tracking-[0.12em] text-slate-500",
19
+ fullWidth ? "sm:col-span-2" : "",
20
+ className
21
+ ].filter(Boolean).join(" "),
22
+ children: label
23
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", {
24
+ className: [
25
+ "text-sm font-medium text-slate-800",
26
+ fullWidth ? "sm:col-span-2" : "",
27
+ isEmpty ? "text-slate-300" : ""
28
+ ].filter(Boolean).join(" "),
29
+ children: isEmpty ? "—" : value
30
+ })] });
31
+ }
32
+ //#endregion
33
+ exports.DescriptionItem = DescriptionItem;
34
+ exports.DescriptionList = DescriptionList;
35
+
36
+ //# sourceMappingURL=index56.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index56.cjs","names":[],"sources":["../src/ui-library/gallery/description-list/DescriptionList.tsx"],"sourcesContent":["/**\n * DescriptionList — Design System Component\n *\n * Read-only label/value display using semantic HTML:\n * <dl> — description list\n * <dt> — term (label)\n * <dd> — description (value)\n *\n * Two layouts:\n * 'vertical' — label stacked above value (default, compact)\n * 'horizontal' — label and value side by side in a grid\n *\n * Responsive variant: 'responsive' starts vertical on mobile and switches\n * to a 2-column horizontal grid at sm breakpoint.\n *\n * Usage:\n * <DescriptionList>\n * <DescriptionItem label=\"Full Name\" value=\"Juan dela Cruz\" />\n * <DescriptionItem label=\"Status\" value={<Badge>Active</Badge>} />\n * <DescriptionItem label=\"Notes\" /> // empty value → shows \"—\"\n * </DescriptionList>\n */\n\nimport type { ReactNode } from 'react';\n\nexport type DescriptionListLayout = 'vertical' | 'horizontal' | 'responsive';\n\nexport interface DescriptionListProps {\n layout?: DescriptionListLayout;\n children: ReactNode;\n className?: string;\n}\n\nexport interface DescriptionItemProps {\n label: string;\n value?: ReactNode;\n /** Span across full width (2 columns in horizontal layout) */\n fullWidth?: boolean;\n className?: string;\n}\n\nconst LAYOUT_CLASS: Record<DescriptionListLayout, string> = {\n vertical: 'flex flex-col gap-4',\n horizontal: 'grid grid-cols-[minmax(8rem,14rem)_1fr] gap-x-6 gap-y-3',\n responsive: 'grid grid-cols-1 sm:grid-cols-[minmax(8rem,14rem)_1fr] gap-x-6 gap-y-3',\n};\n\nexport function DescriptionList({\n layout = 'vertical',\n children,\n className = '',\n}: DescriptionListProps) {\n return (\n <dl className={`${LAYOUT_CLASS[layout]} ${className}`}>\n {children}\n </dl>\n );\n}\n\nexport function DescriptionItem({\n label,\n value,\n fullWidth = false,\n className = '',\n}: DescriptionItemProps) {\n const isEmpty = value === undefined || value === null || value === '';\n\n return (\n <>\n <dt\n className={[\n 'text-xs font-bold uppercase tracking-[0.12em] text-slate-500',\n fullWidth ? 'sm:col-span-2' : '',\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n >\n {label}\n </dt>\n <dd\n className={[\n 'text-sm font-medium text-slate-800',\n fullWidth ? 'sm:col-span-2' : '',\n isEmpty ? 'text-slate-300' : '',\n ]\n .filter(Boolean)\n .join(' ')}\n >\n {isEmpty ? '—' : value}\n </dd>\n </>\n );\n}\n"],"mappings":";;AAyCA,IAAM,eAAsD;CAC1D,UAAY;CACZ,YAAY;CACZ,YAAY;AACd;AAEA,SAAgB,gBAAgB,EAC9B,SAAS,YACT,UACA,YAAY,MACW;CACvB,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD;EAAI,WAAW,GAAG,aAAa,QAAQ,GAAG;EACvC;CACC,CAAA;AAER;AAEA,SAAgB,gBAAgB,EAC9B,OACA,OACA,YAAY,OACZ,YAAY,MACW;CACvB,MAAM,UAAU,UAAU,KAAA,KAAa,UAAU,QAAQ,UAAU;CAEnE,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD;EACE,WAAW;GACT;GACA,YAAY,kBAAkB;GAC9B;EACF,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;EAEV,UAAA;CACC,CAAA,GACJ,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD;EACE,WAAW;GACT;GACA,YAAY,kBAAkB;GAC9B,UAAU,mBAAmB;EAC/B,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;EAEV,UAAA,UAAU,MAAM;CACf,CAAA,CACJ,EAAA,CAAA;AAEN"}
@@ -0,0 +1,35 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ //#region src/ui-library/gallery/description-list/DescriptionList.tsx
3
+ var LAYOUT_CLASS = {
4
+ vertical: "flex flex-col gap-4",
5
+ horizontal: "grid grid-cols-[minmax(8rem,14rem)_1fr] gap-x-6 gap-y-3",
6
+ responsive: "grid grid-cols-1 sm:grid-cols-[minmax(8rem,14rem)_1fr] gap-x-6 gap-y-3"
7
+ };
8
+ function DescriptionList({ layout = "vertical", children, className = "" }) {
9
+ return /* @__PURE__ */ jsx("dl", {
10
+ className: `${LAYOUT_CLASS[layout]} ${className}`,
11
+ children
12
+ });
13
+ }
14
+ function DescriptionItem({ label, value, fullWidth = false, className = "" }) {
15
+ const isEmpty = value === void 0 || value === null || value === "";
16
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("dt", {
17
+ className: [
18
+ "text-xs font-bold uppercase tracking-[0.12em] text-slate-500",
19
+ fullWidth ? "sm:col-span-2" : "",
20
+ className
21
+ ].filter(Boolean).join(" "),
22
+ children: label
23
+ }), /* @__PURE__ */ jsx("dd", {
24
+ className: [
25
+ "text-sm font-medium text-slate-800",
26
+ fullWidth ? "sm:col-span-2" : "",
27
+ isEmpty ? "text-slate-300" : ""
28
+ ].filter(Boolean).join(" "),
29
+ children: isEmpty ? "—" : value
30
+ })] });
31
+ }
32
+ //#endregion
33
+ export { DescriptionItem, DescriptionList };
34
+
35
+ //# sourceMappingURL=index56.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index56.mjs","names":[],"sources":["../src/ui-library/gallery/description-list/DescriptionList.tsx"],"sourcesContent":["/**\n * DescriptionList — Design System Component\n *\n * Read-only label/value display using semantic HTML:\n * <dl> — description list\n * <dt> — term (label)\n * <dd> — description (value)\n *\n * Two layouts:\n * 'vertical' — label stacked above value (default, compact)\n * 'horizontal' — label and value side by side in a grid\n *\n * Responsive variant: 'responsive' starts vertical on mobile and switches\n * to a 2-column horizontal grid at sm breakpoint.\n *\n * Usage:\n * <DescriptionList>\n * <DescriptionItem label=\"Full Name\" value=\"Juan dela Cruz\" />\n * <DescriptionItem label=\"Status\" value={<Badge>Active</Badge>} />\n * <DescriptionItem label=\"Notes\" /> // empty value → shows \"—\"\n * </DescriptionList>\n */\n\nimport type { ReactNode } from 'react';\n\nexport type DescriptionListLayout = 'vertical' | 'horizontal' | 'responsive';\n\nexport interface DescriptionListProps {\n layout?: DescriptionListLayout;\n children: ReactNode;\n className?: string;\n}\n\nexport interface DescriptionItemProps {\n label: string;\n value?: ReactNode;\n /** Span across full width (2 columns in horizontal layout) */\n fullWidth?: boolean;\n className?: string;\n}\n\nconst LAYOUT_CLASS: Record<DescriptionListLayout, string> = {\n vertical: 'flex flex-col gap-4',\n horizontal: 'grid grid-cols-[minmax(8rem,14rem)_1fr] gap-x-6 gap-y-3',\n responsive: 'grid grid-cols-1 sm:grid-cols-[minmax(8rem,14rem)_1fr] gap-x-6 gap-y-3',\n};\n\nexport function DescriptionList({\n layout = 'vertical',\n children,\n className = '',\n}: DescriptionListProps) {\n return (\n <dl className={`${LAYOUT_CLASS[layout]} ${className}`}>\n {children}\n </dl>\n );\n}\n\nexport function DescriptionItem({\n label,\n value,\n fullWidth = false,\n className = '',\n}: DescriptionItemProps) {\n const isEmpty = value === undefined || value === null || value === '';\n\n return (\n <>\n <dt\n className={[\n 'text-xs font-bold uppercase tracking-[0.12em] text-slate-500',\n fullWidth ? 'sm:col-span-2' : '',\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n >\n {label}\n </dt>\n <dd\n className={[\n 'text-sm font-medium text-slate-800',\n fullWidth ? 'sm:col-span-2' : '',\n isEmpty ? 'text-slate-300' : '',\n ]\n .filter(Boolean)\n .join(' ')}\n >\n {isEmpty ? '—' : value}\n </dd>\n </>\n );\n}\n"],"mappings":";;AAyCA,IAAM,eAAsD;CAC1D,UAAY;CACZ,YAAY;CACZ,YAAY;AACd;AAEA,SAAgB,gBAAgB,EAC9B,SAAS,YACT,UACA,YAAY,MACW;CACvB,OACE,oBAAC,MAAD;EAAI,WAAW,GAAG,aAAa,QAAQ,GAAG;EACvC;CACC,CAAA;AAER;AAEA,SAAgB,gBAAgB,EAC9B,OACA,OACA,YAAY,OACZ,YAAY,MACW;CACvB,MAAM,UAAU,UAAU,KAAA,KAAa,UAAU,QAAQ,UAAU;CAEnE,OACE,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,MAAD;EACE,WAAW;GACT;GACA,YAAY,kBAAkB;GAC9B;EACF,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;EAEV,UAAA;CACC,CAAA,GACJ,oBAAC,MAAD;EACE,WAAW;GACT;GACA,YAAY,kBAAkB;GAC9B,UAAU,mBAAmB;EAC/B,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;EAEV,UAAA,UAAU,MAAM;CACf,CAAA,CACJ,EAAA,CAAA;AAEN"}
@@ -0,0 +1,47 @@
1
+ let react_jsx_runtime = require("react/jsx-runtime");
2
+ //#region src/ui-library/gallery/activity-feed/ActivityFeed.tsx
3
+ function ActivityFeed({ events, "aria-label": ariaLabel = "Activity feed", className = "" }) {
4
+ if (events.length === 0) return null;
5
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ol", {
6
+ "aria-label": ariaLabel,
7
+ className: `space-y-0 ${className}`,
8
+ children: events.map((event, i) => {
9
+ const isLast = i === events.length - 1;
10
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
11
+ className: "flex gap-3",
12
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13
+ className: "flex flex-col items-center",
14
+ "aria-hidden": "true",
15
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
16
+ className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-slate-100 text-xs font-bold text-slate-600",
17
+ children: event.avatar ?? event.actor.charAt(0).toUpperCase()
18
+ }), !isLast && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "mt-1 w-px flex-1 bg-slate-100 min-h-[1.5rem]" })]
19
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
20
+ className: `min-w-0 pb-4 ${isLast ? "" : ""}`,
21
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
22
+ className: "flex flex-wrap items-baseline gap-1.5",
23
+ children: [
24
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
25
+ className: "text-sm font-bold text-slate-800",
26
+ children: event.actor
27
+ }),
28
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
29
+ className: "text-sm font-medium text-slate-600",
30
+ children: event.action
31
+ }),
32
+ event.badge && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: event.badge })
33
+ ]
34
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("time", {
35
+ dateTime: event.dateTime ?? event.timestamp,
36
+ className: "mt-0.5 block text-[11px] font-medium text-slate-400",
37
+ children: event.timestamp
38
+ })]
39
+ })]
40
+ }, event.id);
41
+ })
42
+ });
43
+ }
44
+ //#endregion
45
+ exports.ActivityFeed = ActivityFeed;
46
+
47
+ //# sourceMappingURL=index57.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index57.cjs","names":[],"sources":["../src/ui-library/gallery/activity-feed/ActivityFeed.tsx"],"sourcesContent":["/**\n * ActivityFeed — Design System Component\n *\n * A vertical stream of activity events with avatar, actor, action,\n * timestamp, and optional metadata. No domain logic.\n *\n * Accessibility:\n * - <ol> with aria-label for the feed list\n * - Each item is an <li> with datetime on the timestamp\n * - Feed updates should use aria-live=\"polite\" on the container\n * when new events are streamed in\n */\n\nimport type { ReactNode } from 'react';\n\nexport interface ActivityEvent {\n id: string;\n /** Avatar node — use gallery Avatar or a simple initials tile */\n avatar?: ReactNode;\n /** Who performed the action */\n actor: string;\n /** Action description */\n action: ReactNode;\n /** ISO timestamp or display string */\n timestamp: string;\n /** Machine-readable ISO timestamp for <time datetime> */\n dateTime?: string;\n /** Optional badge or secondary label */\n badge?: ReactNode;\n}\n\nexport interface ActivityFeedProps {\n events: ActivityEvent[];\n /** Accessible label for the list. Default: \"Activity feed\" */\n 'aria-label'?: string;\n className?: string;\n}\n\nexport function ActivityFeed({\n events,\n 'aria-label': ariaLabel = 'Activity feed',\n className = '',\n}: ActivityFeedProps) {\n if (events.length === 0) return null;\n\n return (\n <ol aria-label={ariaLabel} className={`space-y-0 ${className}`}>\n {events.map((event, i) => {\n const isLast = i === events.length - 1;\n return (\n <li key={event.id} className=\"flex gap-3\">\n {/* Spine */}\n <div className=\"flex flex-col items-center\" aria-hidden=\"true\">\n <div className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-slate-100 text-xs font-bold text-slate-600\">\n {event.avatar ?? event.actor.charAt(0).toUpperCase()}\n </div>\n {!isLast && (\n <div className=\"mt-1 w-px flex-1 bg-slate-100 min-h-[1.5rem]\" />\n )}\n </div>\n\n {/* Content */}\n <div className={`min-w-0 pb-4 ${isLast ? '' : ''}`}>\n <div className=\"flex flex-wrap items-baseline gap-1.5\">\n <span className=\"text-sm font-bold text-slate-800\">{event.actor}</span>\n <span className=\"text-sm font-medium text-slate-600\">{event.action}</span>\n {event.badge && <span>{event.badge}</span>}\n </div>\n <time\n dateTime={event.dateTime ?? event.timestamp}\n className=\"mt-0.5 block text-[11px] font-medium text-slate-400\"\n >\n {event.timestamp}\n </time>\n </div>\n </li>\n );\n })}\n </ol>\n );\n}\n"],"mappings":";;AAsCA,SAAgB,aAAa,EAC3B,QACA,cAAc,YAAY,iBAC1B,YAAY,MACQ;CACpB,IAAI,OAAO,WAAW,GAAG,OAAO;CAEhC,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD;EAAI,cAAY;EAAW,WAAW,aAAa;EAChD,UAAA,OAAO,KAAK,OAAO,MAAM;GACxB,MAAM,SAAS,MAAM,OAAO,SAAS;GACrC,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,MAAD;IAAmB,WAAU;IAA7B,UAAA,CAEE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAA6B,eAAY;KAAxD,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAU;MACZ,UAAA,MAAM,UAAU,MAAM,MAAM,OAAO,CAAC,CAAC,CAAC,YAAY;KAChD,CAAA,GACJ,CAAC,UACA,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD,EAAK,WAAU,+CAAgD,CAAA,CAE9D;IAGL,CAAA,GAAA,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAW,gBAAgB,SAAS,KAAK;KAA9C,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,WAAU;MAAf,UAAA;OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAU;QAAoC,UAAA,MAAM;OAAY,CAAA;OACtE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAU;QAAsC,UAAA,MAAM;OAAa,CAAA;OACxE,MAAM,SAAS,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAA,UAAO,MAAM,MAAY,CAAA;MACtC;KACL,CAAA,GAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MACE,UAAU,MAAM,YAAY,MAAM;MAClC,WAAU;MAET,UAAA,MAAM;KACH,CAAA,CACH;IACH,CAAA,CAAA;GAzBK,GAAA,MAAM,EAyBX;EAER,CAAC;CACC,CAAA;AAER"}
@@ -0,0 +1,47 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ //#region src/ui-library/gallery/activity-feed/ActivityFeed.tsx
3
+ function ActivityFeed({ events, "aria-label": ariaLabel = "Activity feed", className = "" }) {
4
+ if (events.length === 0) return null;
5
+ return /* @__PURE__ */ jsx("ol", {
6
+ "aria-label": ariaLabel,
7
+ className: `space-y-0 ${className}`,
8
+ children: events.map((event, i) => {
9
+ const isLast = i === events.length - 1;
10
+ return /* @__PURE__ */ jsxs("li", {
11
+ className: "flex gap-3",
12
+ children: [/* @__PURE__ */ jsxs("div", {
13
+ className: "flex flex-col items-center",
14
+ "aria-hidden": "true",
15
+ children: [/* @__PURE__ */ jsx("div", {
16
+ className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-slate-100 text-xs font-bold text-slate-600",
17
+ children: event.avatar ?? event.actor.charAt(0).toUpperCase()
18
+ }), !isLast && /* @__PURE__ */ jsx("div", { className: "mt-1 w-px flex-1 bg-slate-100 min-h-[1.5rem]" })]
19
+ }), /* @__PURE__ */ jsxs("div", {
20
+ className: `min-w-0 pb-4 ${isLast ? "" : ""}`,
21
+ children: [/* @__PURE__ */ jsxs("div", {
22
+ className: "flex flex-wrap items-baseline gap-1.5",
23
+ children: [
24
+ /* @__PURE__ */ jsx("span", {
25
+ className: "text-sm font-bold text-slate-800",
26
+ children: event.actor
27
+ }),
28
+ /* @__PURE__ */ jsx("span", {
29
+ className: "text-sm font-medium text-slate-600",
30
+ children: event.action
31
+ }),
32
+ event.badge && /* @__PURE__ */ jsx("span", { children: event.badge })
33
+ ]
34
+ }), /* @__PURE__ */ jsx("time", {
35
+ dateTime: event.dateTime ?? event.timestamp,
36
+ className: "mt-0.5 block text-[11px] font-medium text-slate-400",
37
+ children: event.timestamp
38
+ })]
39
+ })]
40
+ }, event.id);
41
+ })
42
+ });
43
+ }
44
+ //#endregion
45
+ export { ActivityFeed };
46
+
47
+ //# sourceMappingURL=index57.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index57.mjs","names":[],"sources":["../src/ui-library/gallery/activity-feed/ActivityFeed.tsx"],"sourcesContent":["/**\n * ActivityFeed — Design System Component\n *\n * A vertical stream of activity events with avatar, actor, action,\n * timestamp, and optional metadata. No domain logic.\n *\n * Accessibility:\n * - <ol> with aria-label for the feed list\n * - Each item is an <li> with datetime on the timestamp\n * - Feed updates should use aria-live=\"polite\" on the container\n * when new events are streamed in\n */\n\nimport type { ReactNode } from 'react';\n\nexport interface ActivityEvent {\n id: string;\n /** Avatar node — use gallery Avatar or a simple initials tile */\n avatar?: ReactNode;\n /** Who performed the action */\n actor: string;\n /** Action description */\n action: ReactNode;\n /** ISO timestamp or display string */\n timestamp: string;\n /** Machine-readable ISO timestamp for <time datetime> */\n dateTime?: string;\n /** Optional badge or secondary label */\n badge?: ReactNode;\n}\n\nexport interface ActivityFeedProps {\n events: ActivityEvent[];\n /** Accessible label for the list. Default: \"Activity feed\" */\n 'aria-label'?: string;\n className?: string;\n}\n\nexport function ActivityFeed({\n events,\n 'aria-label': ariaLabel = 'Activity feed',\n className = '',\n}: ActivityFeedProps) {\n if (events.length === 0) return null;\n\n return (\n <ol aria-label={ariaLabel} className={`space-y-0 ${className}`}>\n {events.map((event, i) => {\n const isLast = i === events.length - 1;\n return (\n <li key={event.id} className=\"flex gap-3\">\n {/* Spine */}\n <div className=\"flex flex-col items-center\" aria-hidden=\"true\">\n <div className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-slate-100 text-xs font-bold text-slate-600\">\n {event.avatar ?? event.actor.charAt(0).toUpperCase()}\n </div>\n {!isLast && (\n <div className=\"mt-1 w-px flex-1 bg-slate-100 min-h-[1.5rem]\" />\n )}\n </div>\n\n {/* Content */}\n <div className={`min-w-0 pb-4 ${isLast ? '' : ''}`}>\n <div className=\"flex flex-wrap items-baseline gap-1.5\">\n <span className=\"text-sm font-bold text-slate-800\">{event.actor}</span>\n <span className=\"text-sm font-medium text-slate-600\">{event.action}</span>\n {event.badge && <span>{event.badge}</span>}\n </div>\n <time\n dateTime={event.dateTime ?? event.timestamp}\n className=\"mt-0.5 block text-[11px] font-medium text-slate-400\"\n >\n {event.timestamp}\n </time>\n </div>\n </li>\n );\n })}\n </ol>\n );\n}\n"],"mappings":";;AAsCA,SAAgB,aAAa,EAC3B,QACA,cAAc,YAAY,iBAC1B,YAAY,MACQ;CACpB,IAAI,OAAO,WAAW,GAAG,OAAO;CAEhC,OACE,oBAAC,MAAD;EAAI,cAAY;EAAW,WAAW,aAAa;EAChD,UAAA,OAAO,KAAK,OAAO,MAAM;GACxB,MAAM,SAAS,MAAM,OAAO,SAAS;GACrC,OACE,qBAAC,MAAD;IAAmB,WAAU;IAA7B,UAAA,CAEE,qBAAC,OAAD;KAAK,WAAU;KAA6B,eAAY;KAAxD,UAAA,CACE,oBAAC,OAAD;MAAK,WAAU;MACZ,UAAA,MAAM,UAAU,MAAM,MAAM,OAAO,CAAC,CAAC,CAAC,YAAY;KAChD,CAAA,GACJ,CAAC,UACA,oBAAC,OAAD,EAAK,WAAU,+CAAgD,CAAA,CAE9D;IAGL,CAAA,GAAA,qBAAC,OAAD;KAAK,WAAW,gBAAgB,SAAS,KAAK;KAA9C,UAAA,CACE,qBAAC,OAAD;MAAK,WAAU;MAAf,UAAA;OACE,oBAAC,QAAD;QAAM,WAAU;QAAoC,UAAA,MAAM;OAAY,CAAA;OACtE,oBAAC,QAAD;QAAM,WAAU;QAAsC,UAAA,MAAM;OAAa,CAAA;OACxE,MAAM,SAAS,oBAAC,QAAD,EAAA,UAAO,MAAM,MAAY,CAAA;MACtC;KACL,CAAA,GAAA,oBAAC,QAAD;MACE,UAAU,MAAM,YAAY,MAAM;MAClC,WAAU;MAET,UAAA,MAAM;KACH,CAAA,CACH;IACH,CAAA,CAAA;GAzBK,GAAA,MAAM,EAyBX;EAER,CAAC;CACC,CAAA;AAER"}
@@ -0,0 +1,97 @@
1
+ let react_jsx_runtime = require("react/jsx-runtime");
2
+ let lucide_react = require("lucide-react");
3
+ //#region src/ui-library/gallery/approval-timeline/ApprovalTimeline.tsx
4
+ var STATUS_ICON = {
5
+ approved: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Check, {
6
+ size: 12,
7
+ strokeWidth: 2.5
8
+ }),
9
+ verified: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ShieldCheck, {
10
+ size: 12,
11
+ strokeWidth: 2.5
12
+ }),
13
+ rejected: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.X, {
14
+ size: 12,
15
+ strokeWidth: 2.5
16
+ }),
17
+ cancelled: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Ban, {
18
+ size: 12,
19
+ strokeWidth: 2.5
20
+ }),
21
+ pending: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "block h-2 w-2 rounded-full bg-slate-400" }),
22
+ current: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Clock, {
23
+ size: 12,
24
+ strokeWidth: 2.5
25
+ })
26
+ };
27
+ var STATUS_NODE = {
28
+ approved: "bg-emerald-500 text-white",
29
+ verified: "bg-brand-blue text-white",
30
+ rejected: "bg-rose-500 text-white",
31
+ cancelled: "bg-slate-400 text-white",
32
+ pending: "bg-white border-2 border-slate-300 text-slate-400",
33
+ current: "bg-brand-blue text-white animate-pulse"
34
+ };
35
+ var STATUS_LABEL = {
36
+ approved: "Approved",
37
+ verified: "Verified",
38
+ rejected: "Rejected",
39
+ cancelled: "Cancelled",
40
+ pending: "Pending",
41
+ current: "In Review"
42
+ };
43
+ function ApprovalTimeline({ steps, "aria-label": ariaLabel = "Approval workflow", className = "" }) {
44
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ol", {
45
+ "aria-label": ariaLabel,
46
+ className: `space-y-0 ${className}`,
47
+ children: steps.map((step, i) => {
48
+ const isLast = i === steps.length - 1;
49
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
50
+ className: "flex gap-3",
51
+ "aria-label": `Step ${i + 1}: ${step.actor} — ${STATUS_LABEL[step.status]}`,
52
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
53
+ className: "flex flex-col items-center",
54
+ "aria-hidden": "true",
55
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
56
+ className: `flex h-7 w-7 shrink-0 items-center justify-center rounded-full ${STATUS_NODE[step.status]}`,
57
+ children: STATUS_ICON[step.status]
58
+ }), !isLast && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "mt-1 w-px flex-1 bg-slate-200 min-h-[1.5rem]" })]
59
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
60
+ className: "min-w-0 pb-5",
61
+ children: [
62
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
63
+ className: "flex items-baseline gap-2 flex-wrap",
64
+ children: [
65
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
66
+ className: "text-sm font-bold text-slate-800",
67
+ children: step.actor
68
+ }),
69
+ step.action && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
70
+ className: "text-xs font-medium text-slate-500",
71
+ children: step.action
72
+ }),
73
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
74
+ className: `text-[10px] font-bold uppercase tracking-wider px-1.5 py-0.5 rounded-full
75
+ ${step.status === "approved" || step.status === "verified" ? "bg-emerald-50 text-emerald-700" : step.status === "rejected" ? "bg-rose-50 text-rose-700" : step.status === "current" ? "bg-blue-50 text-brand-blue" : "bg-slate-100 text-slate-500"}`,
76
+ children: STATUS_LABEL[step.status]
77
+ })
78
+ ]
79
+ }),
80
+ step.timestamp && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
81
+ className: "mt-0.5 text-[11px] font-medium text-slate-400",
82
+ children: step.timestamp
83
+ }),
84
+ step.note && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
85
+ className: "mt-1 text-xs font-medium text-slate-500 bg-slate-50 rounded-lg px-3 py-2 border border-slate-100",
86
+ children: step.note
87
+ })
88
+ ]
89
+ })]
90
+ }, step.id);
91
+ })
92
+ });
93
+ }
94
+ //#endregion
95
+ exports.ApprovalTimeline = ApprovalTimeline;
96
+
97
+ //# sourceMappingURL=index58.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index58.cjs","names":[],"sources":["../src/ui-library/gallery/approval-timeline/ApprovalTimeline.tsx"],"sourcesContent":["/**\n * ApprovalTimeline — Design System Component\n *\n * A vertical workflow timeline showing approval steps with status,\n * actor, action, and timestamp. Generic — no domain logic.\n *\n * Based on the pattern in approvals/components/ApprovalTimeline.tsx\n * but extracted as a domain-free gallery primitive.\n *\n * Accessibility:\n * - <ol> with aria-label\n * - Each step is <li> with aria-label including status\n * - Status icons are aria-hidden; status text is visually present\n * - Active step is visually distinct (pulsing node)\n */\n\nimport type { ReactNode } from 'react';\nimport { Check, Clock, X, Ban, ShieldCheck } from 'lucide-react';\n\nexport type ApprovalStepStatus =\n | 'pending'\n | 'approved'\n | 'rejected'\n | 'cancelled'\n | 'verified'\n | 'current';\n\nexport interface ApprovalStep {\n id: string;\n actor: string;\n action?: string;\n status: ApprovalStepStatus;\n timestamp?: string;\n note?: string;\n}\n\nexport interface ApprovalTimelineProps {\n steps: ApprovalStep[];\n 'aria-label'?: string;\n className?: string;\n}\n\nconst STATUS_ICON: Record<ApprovalStepStatus, ReactNode> = {\n approved: <Check size={12} strokeWidth={2.5} />,\n verified: <ShieldCheck size={12} strokeWidth={2.5} />,\n rejected: <X size={12} strokeWidth={2.5} />,\n cancelled: <Ban size={12} strokeWidth={2.5} />,\n pending: <span className=\"block h-2 w-2 rounded-full bg-slate-400\" />,\n current: <Clock size={12} strokeWidth={2.5} />,\n};\n\nconst STATUS_NODE: Record<ApprovalStepStatus, string> = {\n approved: 'bg-emerald-500 text-white',\n verified: 'bg-brand-blue text-white',\n rejected: 'bg-rose-500 text-white',\n cancelled: 'bg-slate-400 text-white',\n pending: 'bg-white border-2 border-slate-300 text-slate-400',\n current: 'bg-brand-blue text-white animate-pulse',\n};\n\nconst STATUS_LABEL: Record<ApprovalStepStatus, string> = {\n approved: 'Approved',\n verified: 'Verified',\n rejected: 'Rejected',\n cancelled: 'Cancelled',\n pending: 'Pending',\n current: 'In Review',\n};\n\nexport function ApprovalTimeline({\n steps,\n 'aria-label': ariaLabel = 'Approval workflow',\n className = '',\n}: ApprovalTimelineProps) {\n return (\n <ol aria-label={ariaLabel} className={`space-y-0 ${className}`}>\n {steps.map((step, i) => {\n const isLast = i === steps.length - 1;\n return (\n <li\n key={step.id}\n className=\"flex gap-3\"\n aria-label={`Step ${i + 1}: ${step.actor} — ${STATUS_LABEL[step.status]}`}\n >\n {/* Spine */}\n <div className=\"flex flex-col items-center\" aria-hidden=\"true\">\n <div className={`flex h-7 w-7 shrink-0 items-center justify-center rounded-full ${STATUS_NODE[step.status]}`}>\n {STATUS_ICON[step.status]}\n </div>\n {!isLast && (\n <div className=\"mt-1 w-px flex-1 bg-slate-200 min-h-[1.5rem]\" />\n )}\n </div>\n\n {/* Content */}\n <div className=\"min-w-0 pb-5\">\n <div className=\"flex items-baseline gap-2 flex-wrap\">\n <span className=\"text-sm font-bold text-slate-800\">{step.actor}</span>\n {step.action && (\n <span className=\"text-xs font-medium text-slate-500\">{step.action}</span>\n )}\n <span className={`text-[10px] font-bold uppercase tracking-wider px-1.5 py-0.5 rounded-full\n ${step.status === 'approved' || step.status === 'verified' ? 'bg-emerald-50 text-emerald-700' :\n step.status === 'rejected' ? 'bg-rose-50 text-rose-700' :\n step.status === 'current' ? 'bg-blue-50 text-brand-blue' :\n 'bg-slate-100 text-slate-500'}`}>\n {STATUS_LABEL[step.status]}\n </span>\n </div>\n {step.timestamp && (\n <p className=\"mt-0.5 text-[11px] font-medium text-slate-400\">{step.timestamp}</p>\n )}\n {step.note && (\n <p className=\"mt-1 text-xs font-medium text-slate-500 bg-slate-50 rounded-lg px-3 py-2 border border-slate-100\">\n {step.note}\n </p>\n )}\n </div>\n </li>\n );\n })}\n </ol>\n );\n}\n"],"mappings":";;;AA0CA,IAAM,cAAqD;CACzD,UAAW,iBAAA,GAAA,kBAAA,IAAA,CAAC,aAAA,OAAD;EAAO,MAAM;EAAI,aAAa;CAAM,CAAA;CAC/C,UAAW,iBAAA,GAAA,kBAAA,IAAA,CAAC,aAAA,aAAD;EAAa,MAAM;EAAI,aAAa;CAAM,CAAA;CACrD,UAAW,iBAAA,GAAA,kBAAA,IAAA,CAAC,aAAA,GAAD;EAAG,MAAM;EAAI,aAAa;CAAM,CAAA;CAC3C,WAAW,iBAAA,GAAA,kBAAA,IAAA,CAAC,aAAA,KAAD;EAAK,MAAM;EAAI,aAAa;CAAM,CAAA;CAC7C,SAAW,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAM,WAAU,0CAA2C,CAAA;CACtE,SAAW,iBAAA,GAAA,kBAAA,IAAA,CAAC,aAAA,OAAD;EAAO,MAAM;EAAI,aAAa;CAAM,CAAA;AACjD;AAEA,IAAM,cAAkD;CACtD,UAAW;CACX,UAAW;CACX,UAAW;CACX,WAAW;CACX,SAAW;CACX,SAAW;AACb;AAEA,IAAM,eAAmD;CACvD,UAAW;CACX,UAAW;CACX,UAAW;CACX,WAAW;CACX,SAAW;CACX,SAAW;AACb;AAEA,SAAgB,iBAAiB,EAC/B,OACA,cAAc,YAAY,qBAC1B,YAAY,MACY;CACxB,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD;EAAI,cAAY;EAAW,WAAW,aAAa;EAChD,UAAA,MAAM,KAAK,MAAM,MAAM;GACtB,MAAM,SAAS,MAAM,MAAM,SAAS;GACpC,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,MAAD;IAEE,WAAU;IACV,cAAY,QAAQ,IAAI,EAAE,IAAI,KAAK,MAAM,KAAK,aAAa,KAAK;IAHlE,UAAA,CAME,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAA6B,eAAY;KAAxD,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAW,kEAAkE,YAAY,KAAK;MAChG,UAAA,YAAY,KAAK;KACf,CAAA,GACJ,CAAC,UACA,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD,EAAK,WAAU,+CAAgD,CAAA,CAE9D;IAGL,CAAA,GAAA,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAf,UAAA;MACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,WAAU;OAAf,UAAA;QACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;SAAM,WAAU;SAAoC,UAAA,KAAK;QAAY,CAAA;QACpE,KAAK,UACJ,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;SAAM,WAAU;SAAsC,UAAA,KAAK;QAAa,CAAA;QAE1E,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;SAAM,WAAW;oBACb,KAAK,WAAW,cAAc,KAAK,WAAW,aAAa,mCAC3D,KAAK,WAAW,aAAa,6BAC7B,KAAK,WAAW,YAAY,+BAC5B;SACD,UAAA,aAAa,KAAK;QACf,CAAA;OACH;;MACJ,KAAK,aACJ,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;OAAG,WAAU;OAAiD,UAAA,KAAK;MAAa,CAAA;MAEjF,KAAK,QACJ,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;OAAG,WAAU;OACV,UAAA,KAAK;MACL,CAAA;KAEF;IACH,CAAA,CAAA;GAtCG,GAAA,KAAK,EAsCR;EAER,CAAC;CACC,CAAA;AAER"}
@@ -0,0 +1,97 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Ban, Check, Clock, ShieldCheck, X } from "lucide-react";
3
+ //#region src/ui-library/gallery/approval-timeline/ApprovalTimeline.tsx
4
+ var STATUS_ICON = {
5
+ approved: /* @__PURE__ */ jsx(Check, {
6
+ size: 12,
7
+ strokeWidth: 2.5
8
+ }),
9
+ verified: /* @__PURE__ */ jsx(ShieldCheck, {
10
+ size: 12,
11
+ strokeWidth: 2.5
12
+ }),
13
+ rejected: /* @__PURE__ */ jsx(X, {
14
+ size: 12,
15
+ strokeWidth: 2.5
16
+ }),
17
+ cancelled: /* @__PURE__ */ jsx(Ban, {
18
+ size: 12,
19
+ strokeWidth: 2.5
20
+ }),
21
+ pending: /* @__PURE__ */ jsx("span", { className: "block h-2 w-2 rounded-full bg-slate-400" }),
22
+ current: /* @__PURE__ */ jsx(Clock, {
23
+ size: 12,
24
+ strokeWidth: 2.5
25
+ })
26
+ };
27
+ var STATUS_NODE = {
28
+ approved: "bg-emerald-500 text-white",
29
+ verified: "bg-brand-blue text-white",
30
+ rejected: "bg-rose-500 text-white",
31
+ cancelled: "bg-slate-400 text-white",
32
+ pending: "bg-white border-2 border-slate-300 text-slate-400",
33
+ current: "bg-brand-blue text-white animate-pulse"
34
+ };
35
+ var STATUS_LABEL = {
36
+ approved: "Approved",
37
+ verified: "Verified",
38
+ rejected: "Rejected",
39
+ cancelled: "Cancelled",
40
+ pending: "Pending",
41
+ current: "In Review"
42
+ };
43
+ function ApprovalTimeline({ steps, "aria-label": ariaLabel = "Approval workflow", className = "" }) {
44
+ return /* @__PURE__ */ jsx("ol", {
45
+ "aria-label": ariaLabel,
46
+ className: `space-y-0 ${className}`,
47
+ children: steps.map((step, i) => {
48
+ const isLast = i === steps.length - 1;
49
+ return /* @__PURE__ */ jsxs("li", {
50
+ className: "flex gap-3",
51
+ "aria-label": `Step ${i + 1}: ${step.actor} — ${STATUS_LABEL[step.status]}`,
52
+ children: [/* @__PURE__ */ jsxs("div", {
53
+ className: "flex flex-col items-center",
54
+ "aria-hidden": "true",
55
+ children: [/* @__PURE__ */ jsx("div", {
56
+ className: `flex h-7 w-7 shrink-0 items-center justify-center rounded-full ${STATUS_NODE[step.status]}`,
57
+ children: STATUS_ICON[step.status]
58
+ }), !isLast && /* @__PURE__ */ jsx("div", { className: "mt-1 w-px flex-1 bg-slate-200 min-h-[1.5rem]" })]
59
+ }), /* @__PURE__ */ jsxs("div", {
60
+ className: "min-w-0 pb-5",
61
+ children: [
62
+ /* @__PURE__ */ jsxs("div", {
63
+ className: "flex items-baseline gap-2 flex-wrap",
64
+ children: [
65
+ /* @__PURE__ */ jsx("span", {
66
+ className: "text-sm font-bold text-slate-800",
67
+ children: step.actor
68
+ }),
69
+ step.action && /* @__PURE__ */ jsx("span", {
70
+ className: "text-xs font-medium text-slate-500",
71
+ children: step.action
72
+ }),
73
+ /* @__PURE__ */ jsx("span", {
74
+ className: `text-[10px] font-bold uppercase tracking-wider px-1.5 py-0.5 rounded-full
75
+ ${step.status === "approved" || step.status === "verified" ? "bg-emerald-50 text-emerald-700" : step.status === "rejected" ? "bg-rose-50 text-rose-700" : step.status === "current" ? "bg-blue-50 text-brand-blue" : "bg-slate-100 text-slate-500"}`,
76
+ children: STATUS_LABEL[step.status]
77
+ })
78
+ ]
79
+ }),
80
+ step.timestamp && /* @__PURE__ */ jsx("p", {
81
+ className: "mt-0.5 text-[11px] font-medium text-slate-400",
82
+ children: step.timestamp
83
+ }),
84
+ step.note && /* @__PURE__ */ jsx("p", {
85
+ className: "mt-1 text-xs font-medium text-slate-500 bg-slate-50 rounded-lg px-3 py-2 border border-slate-100",
86
+ children: step.note
87
+ })
88
+ ]
89
+ })]
90
+ }, step.id);
91
+ })
92
+ });
93
+ }
94
+ //#endregion
95
+ export { ApprovalTimeline };
96
+
97
+ //# sourceMappingURL=index58.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index58.mjs","names":[],"sources":["../src/ui-library/gallery/approval-timeline/ApprovalTimeline.tsx"],"sourcesContent":["/**\n * ApprovalTimeline — Design System Component\n *\n * A vertical workflow timeline showing approval steps with status,\n * actor, action, and timestamp. Generic — no domain logic.\n *\n * Based on the pattern in approvals/components/ApprovalTimeline.tsx\n * but extracted as a domain-free gallery primitive.\n *\n * Accessibility:\n * - <ol> with aria-label\n * - Each step is <li> with aria-label including status\n * - Status icons are aria-hidden; status text is visually present\n * - Active step is visually distinct (pulsing node)\n */\n\nimport type { ReactNode } from 'react';\nimport { Check, Clock, X, Ban, ShieldCheck } from 'lucide-react';\n\nexport type ApprovalStepStatus =\n | 'pending'\n | 'approved'\n | 'rejected'\n | 'cancelled'\n | 'verified'\n | 'current';\n\nexport interface ApprovalStep {\n id: string;\n actor: string;\n action?: string;\n status: ApprovalStepStatus;\n timestamp?: string;\n note?: string;\n}\n\nexport interface ApprovalTimelineProps {\n steps: ApprovalStep[];\n 'aria-label'?: string;\n className?: string;\n}\n\nconst STATUS_ICON: Record<ApprovalStepStatus, ReactNode> = {\n approved: <Check size={12} strokeWidth={2.5} />,\n verified: <ShieldCheck size={12} strokeWidth={2.5} />,\n rejected: <X size={12} strokeWidth={2.5} />,\n cancelled: <Ban size={12} strokeWidth={2.5} />,\n pending: <span className=\"block h-2 w-2 rounded-full bg-slate-400\" />,\n current: <Clock size={12} strokeWidth={2.5} />,\n};\n\nconst STATUS_NODE: Record<ApprovalStepStatus, string> = {\n approved: 'bg-emerald-500 text-white',\n verified: 'bg-brand-blue text-white',\n rejected: 'bg-rose-500 text-white',\n cancelled: 'bg-slate-400 text-white',\n pending: 'bg-white border-2 border-slate-300 text-slate-400',\n current: 'bg-brand-blue text-white animate-pulse',\n};\n\nconst STATUS_LABEL: Record<ApprovalStepStatus, string> = {\n approved: 'Approved',\n verified: 'Verified',\n rejected: 'Rejected',\n cancelled: 'Cancelled',\n pending: 'Pending',\n current: 'In Review',\n};\n\nexport function ApprovalTimeline({\n steps,\n 'aria-label': ariaLabel = 'Approval workflow',\n className = '',\n}: ApprovalTimelineProps) {\n return (\n <ol aria-label={ariaLabel} className={`space-y-0 ${className}`}>\n {steps.map((step, i) => {\n const isLast = i === steps.length - 1;\n return (\n <li\n key={step.id}\n className=\"flex gap-3\"\n aria-label={`Step ${i + 1}: ${step.actor} — ${STATUS_LABEL[step.status]}`}\n >\n {/* Spine */}\n <div className=\"flex flex-col items-center\" aria-hidden=\"true\">\n <div className={`flex h-7 w-7 shrink-0 items-center justify-center rounded-full ${STATUS_NODE[step.status]}`}>\n {STATUS_ICON[step.status]}\n </div>\n {!isLast && (\n <div className=\"mt-1 w-px flex-1 bg-slate-200 min-h-[1.5rem]\" />\n )}\n </div>\n\n {/* Content */}\n <div className=\"min-w-0 pb-5\">\n <div className=\"flex items-baseline gap-2 flex-wrap\">\n <span className=\"text-sm font-bold text-slate-800\">{step.actor}</span>\n {step.action && (\n <span className=\"text-xs font-medium text-slate-500\">{step.action}</span>\n )}\n <span className={`text-[10px] font-bold uppercase tracking-wider px-1.5 py-0.5 rounded-full\n ${step.status === 'approved' || step.status === 'verified' ? 'bg-emerald-50 text-emerald-700' :\n step.status === 'rejected' ? 'bg-rose-50 text-rose-700' :\n step.status === 'current' ? 'bg-blue-50 text-brand-blue' :\n 'bg-slate-100 text-slate-500'}`}>\n {STATUS_LABEL[step.status]}\n </span>\n </div>\n {step.timestamp && (\n <p className=\"mt-0.5 text-[11px] font-medium text-slate-400\">{step.timestamp}</p>\n )}\n {step.note && (\n <p className=\"mt-1 text-xs font-medium text-slate-500 bg-slate-50 rounded-lg px-3 py-2 border border-slate-100\">\n {step.note}\n </p>\n )}\n </div>\n </li>\n );\n })}\n </ol>\n );\n}\n"],"mappings":";;;AA0CA,IAAM,cAAqD;CACzD,UAAW,oBAAC,OAAD;EAAO,MAAM;EAAI,aAAa;CAAM,CAAA;CAC/C,UAAW,oBAAC,aAAD;EAAa,MAAM;EAAI,aAAa;CAAM,CAAA;CACrD,UAAW,oBAAC,GAAD;EAAG,MAAM;EAAI,aAAa;CAAM,CAAA;CAC3C,WAAW,oBAAC,KAAD;EAAK,MAAM;EAAI,aAAa;CAAM,CAAA;CAC7C,SAAW,oBAAC,QAAD,EAAM,WAAU,0CAA2C,CAAA;CACtE,SAAW,oBAAC,OAAD;EAAO,MAAM;EAAI,aAAa;CAAM,CAAA;AACjD;AAEA,IAAM,cAAkD;CACtD,UAAW;CACX,UAAW;CACX,UAAW;CACX,WAAW;CACX,SAAW;CACX,SAAW;AACb;AAEA,IAAM,eAAmD;CACvD,UAAW;CACX,UAAW;CACX,UAAW;CACX,WAAW;CACX,SAAW;CACX,SAAW;AACb;AAEA,SAAgB,iBAAiB,EAC/B,OACA,cAAc,YAAY,qBAC1B,YAAY,MACY;CACxB,OACE,oBAAC,MAAD;EAAI,cAAY;EAAW,WAAW,aAAa;EAChD,UAAA,MAAM,KAAK,MAAM,MAAM;GACtB,MAAM,SAAS,MAAM,MAAM,SAAS;GACpC,OACE,qBAAC,MAAD;IAEE,WAAU;IACV,cAAY,QAAQ,IAAI,EAAE,IAAI,KAAK,MAAM,KAAK,aAAa,KAAK;IAHlE,UAAA,CAME,qBAAC,OAAD;KAAK,WAAU;KAA6B,eAAY;KAAxD,UAAA,CACE,oBAAC,OAAD;MAAK,WAAW,kEAAkE,YAAY,KAAK;MAChG,UAAA,YAAY,KAAK;KACf,CAAA,GACJ,CAAC,UACA,oBAAC,OAAD,EAAK,WAAU,+CAAgD,CAAA,CAE9D;IAGL,CAAA,GAAA,qBAAC,OAAD;KAAK,WAAU;KAAf,UAAA;MACE,qBAAC,OAAD;OAAK,WAAU;OAAf,UAAA;QACE,oBAAC,QAAD;SAAM,WAAU;SAAoC,UAAA,KAAK;QAAY,CAAA;QACpE,KAAK,UACJ,oBAAC,QAAD;SAAM,WAAU;SAAsC,UAAA,KAAK;QAAa,CAAA;QAE1E,oBAAC,QAAD;SAAM,WAAW;oBACb,KAAK,WAAW,cAAc,KAAK,WAAW,aAAa,mCAC3D,KAAK,WAAW,aAAa,6BAC7B,KAAK,WAAW,YAAY,+BAC5B;SACD,UAAA,aAAa,KAAK;QACf,CAAA;OACH;;MACJ,KAAK,aACJ,oBAAC,KAAD;OAAG,WAAU;OAAiD,UAAA,KAAK;MAAa,CAAA;MAEjF,KAAK,QACJ,oBAAC,KAAD;OAAG,WAAU;OACV,UAAA,KAAK;MACL,CAAA;KAEF;IACH,CAAA,CAAA;GAtCG,GAAA,KAAK,EAsCR;EAER,CAAC;CACC,CAAA;AAER"}