@schandlergarcia/sf-web-components 1.0.17 → 1.1.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/dist/components/library/cards/ActionList.js +1 -1
- package/dist/components/library/cards/ActionList.js.map +1 -1
- package/dist/components/library/cards/ListCard.js +15 -15
- package/dist/components/library/cards/TableCard.js +19 -19
- package/dist/components/library/cards/TableCard.js.map +1 -1
- package/dist/components/library/cards/WidgetCard.js +4 -4
- package/dist/components/library/charts/D3Chart.js.map +1 -1
- package/dist/components/library/chat/ChatToolCall.js +1 -1
- package/dist/components/library/chat/ChatToolCall.js.map +1 -1
- package/dist/components/library/data/DataModeProvider.js +9 -9
- package/dist/components/library/data/DataModeProvider.js.map +1 -1
- package/dist/components/library/forms/FormModal.js +1 -1
- package/dist/components/library/forms/FormModal.js.map +1 -1
- package/dist/components/library/index.d.ts +3 -2
- package/dist/components/library/theme/AppThemeProvider.js +4 -4
- package/dist/components/library/theme/AppThemeProvider.js.map +1 -1
- package/dist/components/library/ui/Avatar.js +8 -8
- package/dist/components/library/ui/Spinner.d.ts +8 -3
- package/dist/components/library/ui/UIButton.d.ts +11 -0
- package/dist/components/library/ui/UIButton.js +50 -0
- package/dist/components/library/ui/UIButton.js.map +1 -0
- package/dist/components/library/ui/UIInput.d.ts +5 -0
- package/dist/components/library/ui/UIInput.js +22 -0
- package/dist/components/library/ui/UIInput.js.map +1 -0
- package/dist/components/library/ui/spinner.js +57 -11
- package/dist/components/library/ui/spinner.js.map +1 -1
- package/dist/components/workspace/ComponentRegistry.js +6 -8
- package/dist/components/workspace/ComponentRegistry.js.map +1 -1
- package/dist/index.js +157 -268
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/postinstall.mjs +5 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionList.js","sources":["../../../../src/components/library/cards/ActionList.jsx"],"sourcesContent":["import React from \"react\";\nimport
|
|
1
|
+
{"version":3,"file":"ActionList.js","sources":["../../../../src/components/library/cards/ActionList.jsx"],"sourcesContent":["import React from \"react\";\nimport UIButton from \"../ui/UIButton\";\n\n/**\n * Row of action buttons — typically used at the bottom of a dashboard section.\n *\n * @param {{ label: string, [key]: any }[] | string[]} actions\n * @param {string} title\n * @param {Function} onAction Called with the action object/string when clicked\n */\nexport default function ActionList({\n actions = [],\n title,\n onAction,\n className = \"\",\n}) {\n return (\n <div className={`rounded-2xl border border-slate-200 bg-white p-4 dark:border-slate-800 dark:bg-slate-900 ${className}`}>\n {title && (\n <div className=\"mb-3 text-sm font-medium text-slate-900 dark:text-slate-50\">\n {title}\n </div>\n )}\n <div className=\"flex flex-wrap gap-2\">\n {actions.map((action, i) => (\n <UIButton\n key={i}\n size=\"sm\"\n variant={i === 0 ? \"primary\" : \"outline\"}\n onClick={() => onAction?.(action)}\n >\n {typeof action === \"string\" ? action : action.label}\n </UIButton>\n ))}\n </div>\n </div>\n );\n}\n"],"names":["ActionList","actions","title","onAction","className","jsxs","jsx","action","i","UIButton"],"mappings":";;;AAUA,SAAwBA,EAAW;AAAA,EACjC,SAAAC,IAAU,CAAA;AAAA,EACV,OAAAC;AAAA,EACA,UAAAC;AAAA,EACA,WAAAC,IAAY;AACd,GAAG;AACD,SACE,gBAAAC,EAAC,OAAA,EAAI,WAAW,4FAA4FD,CAAS,IAClH,UAAA;AAAA,IAAAF,KACC,gBAAAI,EAAC,OAAA,EAAI,WAAU,8DACZ,UAAAJ,GACH;AAAA,IAEF,gBAAAI,EAAC,SAAI,WAAU,wBACZ,YAAQ,IAAI,CAACC,GAAQC,MACpB,gBAAAF;AAAA,MAACG;AAAA,MAAA;AAAA,QAEC,MAAK;AAAA,QACL,SAASD,MAAM,IAAI,YAAY;AAAA,QAC/B,SAAS,MAAML,IAAWI,CAAM;AAAA,QAE/B,UAAA,OAAOA,KAAW,WAAWA,IAASA,EAAO;AAAA,MAAA;AAAA,MALzCC;AAAA,IAAA,CAOR,EAAA,CACH;AAAA,EAAA,GACF;AAEJ;"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx as e, jsxs as s } from "react/jsx-runtime";
|
|
2
|
-
import
|
|
2
|
+
import B from "react";
|
|
3
3
|
import w from "./BaseCard.js";
|
|
4
4
|
import i from "../ui/Text.js";
|
|
5
|
-
import
|
|
6
|
-
function
|
|
5
|
+
import E from "../ui/Chip.js";
|
|
6
|
+
function R(t) {
|
|
7
7
|
if (!t) return "";
|
|
8
8
|
try {
|
|
9
9
|
const a = t instanceof Date ? t : new Date(t);
|
|
@@ -12,7 +12,7 @@ function E(t) {
|
|
|
12
12
|
return String(t);
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
|
-
function
|
|
15
|
+
function _({ item: t }) {
|
|
16
16
|
const a = t?.avatar, d = t?.title ?? t?.name ?? "Item";
|
|
17
17
|
return a ? typeof a == "string" ? /* @__PURE__ */ e(
|
|
18
18
|
"img",
|
|
@@ -21,7 +21,7 @@ function R({ item: t }) {
|
|
|
21
21
|
alt: "",
|
|
22
22
|
className: "h-9 w-9 rounded-full border border-slate-200 object-cover dark:border-slate-800"
|
|
23
23
|
}
|
|
24
|
-
) :
|
|
24
|
+
) : B.isValidElement(a) ? /* @__PURE__ */ e("div", { className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-full border border-slate-200 bg-slate-50 dark:border-slate-800 dark:bg-slate-800", children: a }) : /* @__PURE__ */ e("div", { className: "h-9 w-9 rounded-full border border-slate-200 bg-slate-100 text-xs font-semibold text-slate-700 dark:border-slate-800 dark:bg-slate-800 dark:text-slate-200 grid place-items-center", children: String(d).slice(0, 2).toUpperCase() }) : null;
|
|
25
25
|
}
|
|
26
26
|
function K({
|
|
27
27
|
items: t = [],
|
|
@@ -60,7 +60,7 @@ function K({
|
|
|
60
60
|
...b
|
|
61
61
|
}
|
|
62
62
|
);
|
|
63
|
-
const
|
|
63
|
+
const D = C ? "py-2" : "py-3", I = h ? { maxHeight: h, overflowY: "auto" } : {}, V = L ? /* @__PURE__ */ e("div", { className: "mt-4 space-y-3", children: Array.from({ length: 4 }).map((r, l) => /* @__PURE__ */ s("div", { className: "flex items-center gap-3 rounded-xl border border-slate-200 bg-white p-3 dark:border-slate-800 dark:bg-slate-900", children: [
|
|
64
64
|
n ? /* @__PURE__ */ e("div", { className: "h-9 w-9 animate-pulse rounded-full bg-slate-200 dark:bg-slate-800" }) : null,
|
|
65
65
|
/* @__PURE__ */ s("div", { className: "flex-1 space-y-2", children: [
|
|
66
66
|
/* @__PURE__ */ e("div", { className: "h-4 w-1/3 animate-pulse rounded bg-slate-200 dark:bg-slate-800" }),
|
|
@@ -69,27 +69,27 @@ function K({
|
|
|
69
69
|
] }, l)) }) : t.length === 0 ? /* @__PURE__ */ s("div", { className: "mt-4 rounded-xl border border-dashed border-slate-300 bg-slate-50 p-6 text-center dark:border-slate-700 dark:bg-slate-950/30", children: [
|
|
70
70
|
u ? /* @__PURE__ */ e("div", { className: "mb-2", children: u }) : null,
|
|
71
71
|
/* @__PURE__ */ e(i, { size: "sm", muted: !0, children: T })
|
|
72
|
-
] }) : /* @__PURE__ */ e("div", { className: "mt-4 overflow-hidden rounded-xl border border-slate-200 bg-white dark:border-slate-800 dark:bg-slate-900", children: /* @__PURE__ */ e("ul", { className: U ? "divide-y divide-slate-200 dark:divide-slate-800" : "", style:
|
|
73
|
-
const
|
|
72
|
+
] }) : /* @__PURE__ */ e("div", { className: "mt-4 overflow-hidden rounded-xl border border-slate-200 bg-white dark:border-slate-800 dark:bg-slate-900", children: /* @__PURE__ */ e("ul", { className: U ? "divide-y divide-slate-200 dark:divide-slate-800" : "", style: I, children: t.map((r, l) => {
|
|
73
|
+
const Y = r?.id ?? l, A = r?.title ?? r?.name ?? "Item", f = r?.description, x = r?.status, v = r?.timestamp, p = r?.value, N = r?.unit, k = S ? z?.(r, l) : null;
|
|
74
74
|
return /* @__PURE__ */ s(
|
|
75
75
|
"li",
|
|
76
76
|
{
|
|
77
77
|
className: [
|
|
78
78
|
"group flex items-start justify-between gap-3 px-4",
|
|
79
|
-
|
|
79
|
+
D,
|
|
80
80
|
c ? "cursor-pointer hover:bg-slate-50 dark:hover:bg-slate-950/30" : ""
|
|
81
81
|
].filter(Boolean).join(" "),
|
|
82
82
|
onClick: () => c?.(r, l),
|
|
83
83
|
children: [
|
|
84
84
|
/* @__PURE__ */ s("div", { className: "flex min-w-0 items-start gap-3", children: [
|
|
85
|
-
n ? /* @__PURE__ */ e(
|
|
85
|
+
n ? /* @__PURE__ */ e(_, { item: r }) : null,
|
|
86
86
|
/* @__PURE__ */ s("div", { className: "min-w-0", children: [
|
|
87
87
|
/* @__PURE__ */ s("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
88
|
-
/* @__PURE__ */ e("div", { className: "text-sm font-semibold text-slate-900 dark:text-slate-50", children:
|
|
89
|
-
y && x ? /* @__PURE__ */ e(
|
|
88
|
+
/* @__PURE__ */ e("div", { className: "text-sm font-semibold text-slate-900 dark:text-slate-50", children: A }),
|
|
89
|
+
y && x ? /* @__PURE__ */ e(E, { children: String(x) }) : null
|
|
90
90
|
] }),
|
|
91
91
|
f ? /* @__PURE__ */ e("div", { className: "mt-1 text-sm text-slate-600 dark:text-slate-300", children: f }) : null,
|
|
92
|
-
j && v ? /* @__PURE__ */ e("div", { className: "mt-1 text-xs text-slate-500 dark:text-slate-400", children:
|
|
92
|
+
j && v ? /* @__PURE__ */ e("div", { className: "mt-1 text-xs text-slate-500 dark:text-slate-400", children: R(v) }) : null
|
|
93
93
|
] })
|
|
94
94
|
] }),
|
|
95
95
|
/* @__PURE__ */ s("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
@@ -101,10 +101,10 @@ function K({
|
|
|
101
101
|
] })
|
|
102
102
|
]
|
|
103
103
|
},
|
|
104
|
-
|
|
104
|
+
Y
|
|
105
105
|
);
|
|
106
106
|
}) }) });
|
|
107
|
-
return /* @__PURE__ */ e(w, { variant: "widget", header: g, body:
|
|
107
|
+
return /* @__PURE__ */ e(w, { variant: "widget", header: g, body: V, isLoading: !1, ...b });
|
|
108
108
|
}
|
|
109
109
|
export {
|
|
110
110
|
K as default
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsx as t, jsxs as d } from "react/jsx-runtime";
|
|
2
2
|
import i from "react";
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
3
|
+
import I from "./BaseCard.js";
|
|
4
|
+
import te from "../ui/UIInput.js";
|
|
5
|
+
import K from "../ui/UIButton.js";
|
|
6
6
|
import Q from "../ui/Text.js";
|
|
7
7
|
function re(o, s) {
|
|
8
8
|
if (s == null) return "";
|
|
@@ -40,23 +40,23 @@ function ce({
|
|
|
40
40
|
actions: F,
|
|
41
41
|
rowActions: h,
|
|
42
42
|
onRowSelect: R,
|
|
43
|
-
onSort:
|
|
44
|
-
onSearch:
|
|
45
|
-
loading:
|
|
43
|
+
onSort: _,
|
|
44
|
+
onSearch: G,
|
|
45
|
+
loading: H = !1,
|
|
46
46
|
error: P,
|
|
47
47
|
emptyMessage: J = "No results.",
|
|
48
48
|
simulateInitialLoad: B = !1,
|
|
49
49
|
minInitialDelayMs: L = 350,
|
|
50
|
-
maxInitialDelayMs:
|
|
51
|
-
|
|
50
|
+
maxInitialDelayMs: U = 900,
|
|
51
|
+
...$
|
|
52
52
|
}) {
|
|
53
53
|
const [N, O] = i.useState(""), [x, V] = i.useState(null), [k, W] = i.useState("asc"), [T, y] = i.useState(1), [X, Y] = i.useState(null), [Z, z] = i.useState(B);
|
|
54
54
|
i.useEffect(() => {
|
|
55
55
|
if (!B) return;
|
|
56
|
-
const e = Math.floor(Math.random() * (
|
|
56
|
+
const e = Math.floor(Math.random() * (U - L + 1)) + L, a = setTimeout(() => z(!1), e);
|
|
57
57
|
return () => clearTimeout(a);
|
|
58
|
-
}, [B, L,
|
|
59
|
-
const q =
|
|
58
|
+
}, [B, L, U]);
|
|
59
|
+
const q = H || Z, v = i.useMemo(() => {
|
|
60
60
|
if (!b || !N.trim()) return o;
|
|
61
61
|
const e = N.trim().toLowerCase();
|
|
62
62
|
return o.filter(
|
|
@@ -91,7 +91,7 @@ function ce({
|
|
|
91
91
|
{
|
|
92
92
|
value: N,
|
|
93
93
|
onChange: (e) => {
|
|
94
|
-
O(e.target.value), y(1),
|
|
94
|
+
O(e.target.value), y(1), G?.(e.target.value);
|
|
95
95
|
},
|
|
96
96
|
placeholder: "Search…",
|
|
97
97
|
"aria-label": "Search table"
|
|
@@ -102,16 +102,16 @@ function ce({
|
|
|
102
102
|
] });
|
|
103
103
|
if (P)
|
|
104
104
|
return /* @__PURE__ */ t(
|
|
105
|
-
|
|
105
|
+
I,
|
|
106
106
|
{
|
|
107
107
|
header: E,
|
|
108
108
|
body: /* @__PURE__ */ t("div", { className: "mt-4 rounded-xl border border-rose-200 bg-rose-50 p-4 text-sm text-rose-900 dark:border-rose-900/40 dark:bg-rose-950/30 dark:text-rose-100", children: String(P) }),
|
|
109
|
-
|
|
109
|
+
...$
|
|
110
110
|
}
|
|
111
111
|
);
|
|
112
112
|
const A = (e) => j && (e.sortable ?? !0);
|
|
113
113
|
return /* @__PURE__ */ t(
|
|
114
|
-
|
|
114
|
+
I,
|
|
115
115
|
{
|
|
116
116
|
variant: "table",
|
|
117
117
|
header: E,
|
|
@@ -135,7 +135,7 @@ function ce({
|
|
|
135
135
|
className: "inline-flex items-center gap-2 hover:text-slate-900 dark:hover:text-slate-50",
|
|
136
136
|
onClick: () => {
|
|
137
137
|
const n = a && k === "asc" ? "desc" : "asc";
|
|
138
|
-
V(e.key), W(n),
|
|
138
|
+
V(e.key), W(n), _?.({ key: e.key, direction: n });
|
|
139
139
|
},
|
|
140
140
|
children: [
|
|
141
141
|
/* @__PURE__ */ t("span", { children: e.label }),
|
|
@@ -206,7 +206,7 @@ function ce({
|
|
|
206
206
|
/* @__PURE__ */ t("div", { className: "text-xs text-slate-600 dark:text-slate-300", children: w === 0 ? "0 results" : `Showing ${(c - 1) * f + 1}-${Math.min(c * f, w)} of ${w}` }),
|
|
207
207
|
/* @__PURE__ */ d("div", { className: "flex items-center gap-2", children: [
|
|
208
208
|
/* @__PURE__ */ t(
|
|
209
|
-
|
|
209
|
+
K,
|
|
210
210
|
{
|
|
211
211
|
variant: "outline",
|
|
212
212
|
size: "sm",
|
|
@@ -222,7 +222,7 @@ function ce({
|
|
|
222
222
|
C
|
|
223
223
|
] }),
|
|
224
224
|
/* @__PURE__ */ t(
|
|
225
|
-
|
|
225
|
+
K,
|
|
226
226
|
{
|
|
227
227
|
variant: "outline",
|
|
228
228
|
size: "sm",
|
|
@@ -234,7 +234,7 @@ function ce({
|
|
|
234
234
|
] })
|
|
235
235
|
] }) : null
|
|
236
236
|
] }),
|
|
237
|
-
|
|
237
|
+
...$
|
|
238
238
|
}
|
|
239
239
|
);
|
|
240
240
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TableCard.js","sources":["../../../../src/components/library/cards/TableCard.jsx"],"sourcesContent":["import React from \"react\";\nimport BaseCard from \"./BaseCard\";\nimport { Input as UIInput } from \"../ui/input\";\nimport { Button as UIButton } from \"../ui/button\";\nimport UIText from \"../ui/Text\";\n\nfunction defaultTypeFormat(type, value) {\n if (value == null) return \"\";\n if (!type) return String(value);\n\n if (type === \"currency\") {\n const n = Number(value);\n if (Number.isFinite(n)) return n.toLocaleString(undefined, { style: \"currency\", currency: \"USD\" });\n }\n if (type === \"percentage\") {\n const n = Number(value);\n if (Number.isFinite(n)) return `${n}%`;\n }\n if (type === \"number\") {\n const n = Number(value);\n if (Number.isFinite(n)) return n.toLocaleString();\n }\n return String(value);\n}\n\nfunction stableSort(data, compare) {\n return data\n .map((item, idx) => ({ item, idx }))\n .sort((a, b) => {\n const c = compare(a.item, b.item);\n return c !== 0 ? c : a.idx - b.idx;\n })\n .map((x) => x.item);\n}\n\nexport default function TableCard({\n data = [],\n columns = [],\n title,\n subtitle,\n searchable = false,\n sortable = false,\n paginated = false,\n selectable = false,\n pageSize = 10,\n actions,\n rowActions,\n onRowSelect,\n onSort,\n onSearch,\n loading = false,\n error,\n emptyMessage = \"No results.\",\n simulateInitialLoad = false,\n minInitialDelayMs = 350,\n maxInitialDelayMs = 900,\n ...cardProps\n}) {\n const [query, setQuery] = React.useState(\"\");\n const [sortKey, setSortKey] = React.useState(null);\n const [sortDir, setSortDir] = React.useState(\"asc\");\n const [page, setPage] = React.useState(1);\n const [selectedId, setSelectedId] = React.useState(null);\n const [simLoading, setSimLoading] = React.useState(simulateInitialLoad);\n\n React.useEffect(() => {\n if (!simulateInitialLoad) return;\n const delay =\n Math.floor(Math.random() * (maxInitialDelayMs - minInitialDelayMs + 1)) + minInitialDelayMs;\n const t = setTimeout(() => setSimLoading(false), delay);\n return () => clearTimeout(t);\n }, [simulateInitialLoad, minInitialDelayMs, maxInitialDelayMs]);\n\n const effectiveLoading = loading || simLoading;\n\n const filtered = React.useMemo(() => {\n if (!searchable || !query.trim()) return data;\n const q = query.trim().toLowerCase();\n return data.filter((row) =>\n columns.some((col) => {\n const raw = row?.[col.key];\n if (raw == null) return false;\n return String(raw).toLowerCase().includes(q);\n })\n );\n }, [data, columns, query, searchable]);\n\n const sorted = React.useMemo(() => {\n if (!sortable || !sortKey) return filtered;\n const col = columns.find((c) => c.key === sortKey);\n if (!col) return filtered;\n const dir = sortDir === \"desc\" ? -1 : 1;\n return stableSort(filtered, (a, b) => {\n const av = a?.[sortKey];\n const bv = b?.[sortKey];\n if (av == null && bv == null) return 0;\n if (av == null) return -1 * dir;\n if (bv == null) return 1 * dir;\n if (typeof av === \"number\" && typeof bv === \"number\") return (av - bv) * dir;\n return String(av).localeCompare(String(bv)) * dir;\n });\n }, [filtered, sortable, sortKey, sortDir, columns]);\n\n const total = sorted.length;\n const totalPages = paginated ? Math.max(1, Math.ceil(total / pageSize)) : 1;\n const clampedPage = Math.min(page, totalPages);\n\n const pageData = React.useMemo(() => {\n if (!paginated) return sorted;\n const start = (clampedPage - 1) * pageSize;\n return sorted.slice(start, start + pageSize);\n }, [sorted, paginated, clampedPage, pageSize]);\n\n React.useEffect(() => {\n if (page !== clampedPage) setPage(clampedPage);\n }, [page, clampedPage]);\n\n const header = (\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between\">\n <div className=\"min-w-0\">\n {title ? (\n <UIText as=\"div\" size=\"sm\" weight=\"medium\">\n {title}\n </UIText>\n ) : null}\n {subtitle ? (\n <UIText as=\"div\" size=\"xs\" muted className=\"mt-1\">\n {subtitle}\n </UIText>\n ) : null}\n </div>\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-end\">\n {searchable ? (\n <div className=\"w-full sm:w-64\">\n <UIInput\n value={query}\n onChange={(e) => {\n setQuery(e.target.value);\n setPage(1);\n onSearch?.(e.target.value);\n }}\n placeholder=\"Search…\"\n aria-label=\"Search table\"\n />\n </div>\n ) : null}\n {actions ? <div className=\"flex items-center gap-2\">{actions}</div> : null}\n </div>\n </div>\n );\n\n if (error) {\n return (\n <BaseCard\n header={header}\n body={\n <div className=\"mt-4 rounded-xl border border-rose-200 bg-rose-50 p-4 text-sm text-rose-900 dark:border-rose-900/40 dark:bg-rose-950/30 dark:text-rose-100\">\n {String(error)}\n </div>\n }\n {...cardProps}\n />\n );\n }\n\n const canSort = (col) => sortable && (col.sortable ?? true);\n\n return (\n <BaseCard\n variant=\"table\"\n header={header}\n body={\n <div className=\"mt-4 overflow-hidden rounded-xl border border-slate-200 dark:border-slate-800\">\n <div className=\"w-full overflow-x-auto\">\n <table className=\"min-w-full border-separate border-spacing-0\">\n <thead className=\"bg-slate-50 dark:bg-slate-950/30\">\n <tr>\n {columns.map((col) => {\n const active = sortKey === col.key;\n return (\n <th\n key={col.key}\n scope=\"col\"\n className={[\n \"whitespace-nowrap border-b border-slate-200 px-4 py-3 text-left text-xs font-semibold text-slate-600 dark:border-slate-800 dark:text-slate-300\",\n col.className ?? \"\"\n ]\n .filter(Boolean)\n .join(\" \")}\n >\n {canSort(col) ? (\n <button\n type=\"button\"\n className=\"inline-flex items-center gap-2 hover:text-slate-900 dark:hover:text-slate-50\"\n onClick={() => {\n const nextDir = active && sortDir === \"asc\" ? \"desc\" : \"asc\";\n setSortKey(col.key);\n setSortDir(nextDir);\n onSort?.({ key: col.key, direction: nextDir });\n }}\n >\n <span>{col.label}</span>\n <span className=\"text-[10px] opacity-70\">\n {active ? (sortDir === \"asc\" ? \"▲\" : \"▼\") : \"↕\"}\n </span>\n </button>\n ) : (\n col.label\n )}\n </th>\n );\n })}\n {rowActions ? (\n <th className=\"border-b border-slate-200 px-4 py-3 text-right text-xs font-semibold text-slate-600 dark:border-slate-800 dark:text-slate-300\">\n Actions\n </th>\n ) : null}\n </tr>\n </thead>\n\n <tbody className=\"bg-white dark:bg-slate-900\">\n {effectiveLoading ? (\n Array.from({ length: Math.min(pageSize, 6) }).map((_, idx) => (\n <tr key={idx}>\n {columns.map((col) => (\n <td\n key={col.key}\n className=\"border-b border-slate-200 px-4 py-3 dark:border-slate-800\"\n >\n <div className=\"h-4 w-3/4 animate-pulse rounded bg-slate-200 dark:bg-slate-800\" />\n </td>\n ))}\n {rowActions ? (\n <td className=\"border-b border-slate-200 px-4 py-3 dark:border-slate-800\">\n <div className=\"h-4 w-10 animate-pulse rounded bg-slate-200 dark:bg-slate-800\" />\n </td>\n ) : null}\n </tr>\n ))\n ) : pageData.length === 0 ? (\n <tr>\n <td\n colSpan={columns.length + (rowActions ? 1 : 0)}\n className=\"px-4 py-10 text-center text-sm text-slate-600 dark:text-slate-300\"\n >\n {emptyMessage}\n </td>\n </tr>\n ) : (\n pageData.map((row, idx) => {\n const rowId = row?.id ?? idx;\n const selected = selectable && selectedId === rowId;\n return (\n <tr\n key={rowId}\n className={[\n \"group\",\n selectable ? \"cursor-pointer\" : \"\",\n selected ? \"bg-brand-50 dark:bg-brand-950/30\" : \"\"\n ]\n .filter(Boolean)\n .join(\" \")}\n onClick={() => {\n if (!selectable) return;\n setSelectedId(rowId);\n onRowSelect?.(row);\n }}\n >\n {columns.map((col) => {\n const raw = row?.[col.key];\n const content = col.render ? col.render(raw, row) : defaultTypeFormat(col.type, raw);\n return (\n <td\n key={col.key}\n className={[\n \"border-b border-slate-200 px-4 py-3 text-sm text-slate-700 dark:border-slate-800 dark:text-slate-200\",\n col.mono ? \"font-mono text-[13px]\" : \"\",\n col.className ?? \"\"\n ]\n .filter(Boolean)\n .join(\" \")}\n >\n {content}\n </td>\n );\n })}\n {rowActions ? (\n <td className=\"border-b border-slate-200 px-4 py-3 text-right text-sm dark:border-slate-800\">\n <div className=\"inline-flex items-center justify-end gap-2 opacity-100 sm:opacity-0 sm:group-hover:opacity-100\">\n {rowActions(row)}\n </div>\n </td>\n ) : null}\n </tr>\n );\n })\n )}\n </tbody>\n </table>\n </div>\n\n {paginated && !effectiveLoading ? (\n <div className=\"flex flex-col gap-2 border-t border-slate-200 bg-slate-50 px-4 py-3 dark:border-slate-800 dark:bg-slate-950/30 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"text-xs text-slate-600 dark:text-slate-300\">\n {total === 0 ? \"0 results\" : `Showing ${(clampedPage - 1) * pageSize + 1}-${Math.min(clampedPage * pageSize, total)} of ${total}`}\n </div>\n <div className=\"flex items-center gap-2\">\n <UIButton\n variant=\"outline\"\n size=\"sm\"\n disabled={clampedPage <= 1}\n onClick={() => setPage((p) => Math.max(1, p - 1))}\n >\n Prev\n </UIButton>\n <div className=\"text-xs text-slate-600 dark:text-slate-300\">\n Page {clampedPage} / {totalPages}\n </div>\n <UIButton\n variant=\"outline\"\n size=\"sm\"\n disabled={clampedPage >= totalPages}\n onClick={() => setPage((p) => Math.min(totalPages, p + 1))}\n >\n Next\n </UIButton>\n </div>\n </div>\n ) : null}\n </div>\n }\n {...cardProps}\n />\n );\n}\n\n\n"],"names":["defaultTypeFormat","type","value","n","stableSort","data","compare","item","idx","a","b","c","x","TableCard","columns","title","subtitle","searchable","sortable","paginated","selectable","pageSize","actions","rowActions","onRowSelect","onSort","onSearch","loading","error","emptyMessage","simulateInitialLoad","minInitialDelayMs","maxInitialDelayMs","cardProps","query","setQuery","React","sortKey","setSortKey","sortDir","setSortDir","page","setPage","selectedId","setSelectedId","simLoading","setSimLoading","delay","t","effectiveLoading","filtered","q","row","col","raw","sorted","dir","av","bv","total","totalPages","clampedPage","pageData","start","header","jsxs","jsx","UIText","UIInput","BaseCard","canSort","active","nextDir","_","rowId","content","UIButton","p"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TableCard.js","sources":["../../../../src/components/library/cards/TableCard.jsx"],"sourcesContent":["import React from \"react\";\nimport BaseCard from \"./BaseCard\";\nimport UIInput from \"../ui/UIInput\";\nimport UIButton from \"../ui/UIButton\";\nimport UIText from \"../ui/Text\";\n\nfunction defaultTypeFormat(type, value) {\n if (value == null) return \"\";\n if (!type) return String(value);\n\n if (type === \"currency\") {\n const n = Number(value);\n if (Number.isFinite(n)) return n.toLocaleString(undefined, { style: \"currency\", currency: \"USD\" });\n }\n if (type === \"percentage\") {\n const n = Number(value);\n if (Number.isFinite(n)) return `${n}%`;\n }\n if (type === \"number\") {\n const n = Number(value);\n if (Number.isFinite(n)) return n.toLocaleString();\n }\n return String(value);\n}\n\nfunction stableSort(data, compare) {\n return data\n .map((item, idx) => ({ item, idx }))\n .sort((a, b) => {\n const c = compare(a.item, b.item);\n return c !== 0 ? c : a.idx - b.idx;\n })\n .map((x) => x.item);\n}\n\nexport default function TableCard({\n data = [],\n columns = [],\n title,\n subtitle,\n searchable = false,\n sortable = false,\n paginated = false,\n selectable = false,\n pageSize = 10,\n actions,\n rowActions,\n onRowSelect,\n onSort,\n onSearch,\n loading = false,\n error,\n emptyMessage = \"No results.\",\n simulateInitialLoad = false,\n minInitialDelayMs = 350,\n maxInitialDelayMs = 900,\n ...cardProps\n}) {\n const [query, setQuery] = React.useState(\"\");\n const [sortKey, setSortKey] = React.useState(null);\n const [sortDir, setSortDir] = React.useState(\"asc\");\n const [page, setPage] = React.useState(1);\n const [selectedId, setSelectedId] = React.useState(null);\n const [simLoading, setSimLoading] = React.useState(simulateInitialLoad);\n\n React.useEffect(() => {\n if (!simulateInitialLoad) return;\n const delay =\n Math.floor(Math.random() * (maxInitialDelayMs - minInitialDelayMs + 1)) + minInitialDelayMs;\n const t = setTimeout(() => setSimLoading(false), delay);\n return () => clearTimeout(t);\n }, [simulateInitialLoad, minInitialDelayMs, maxInitialDelayMs]);\n\n const effectiveLoading = loading || simLoading;\n\n const filtered = React.useMemo(() => {\n if (!searchable || !query.trim()) return data;\n const q = query.trim().toLowerCase();\n return data.filter((row) =>\n columns.some((col) => {\n const raw = row?.[col.key];\n if (raw == null) return false;\n return String(raw).toLowerCase().includes(q);\n })\n );\n }, [data, columns, query, searchable]);\n\n const sorted = React.useMemo(() => {\n if (!sortable || !sortKey) return filtered;\n const col = columns.find((c) => c.key === sortKey);\n if (!col) return filtered;\n const dir = sortDir === \"desc\" ? -1 : 1;\n return stableSort(filtered, (a, b) => {\n const av = a?.[sortKey];\n const bv = b?.[sortKey];\n if (av == null && bv == null) return 0;\n if (av == null) return -1 * dir;\n if (bv == null) return 1 * dir;\n if (typeof av === \"number\" && typeof bv === \"number\") return (av - bv) * dir;\n return String(av).localeCompare(String(bv)) * dir;\n });\n }, [filtered, sortable, sortKey, sortDir, columns]);\n\n const total = sorted.length;\n const totalPages = paginated ? Math.max(1, Math.ceil(total / pageSize)) : 1;\n const clampedPage = Math.min(page, totalPages);\n\n const pageData = React.useMemo(() => {\n if (!paginated) return sorted;\n const start = (clampedPage - 1) * pageSize;\n return sorted.slice(start, start + pageSize);\n }, [sorted, paginated, clampedPage, pageSize]);\n\n React.useEffect(() => {\n if (page !== clampedPage) setPage(clampedPage);\n }, [page, clampedPage]);\n\n const header = (\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between\">\n <div className=\"min-w-0\">\n {title ? (\n <UIText as=\"div\" size=\"sm\" weight=\"medium\">\n {title}\n </UIText>\n ) : null}\n {subtitle ? (\n <UIText as=\"div\" size=\"xs\" muted className=\"mt-1\">\n {subtitle}\n </UIText>\n ) : null}\n </div>\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-end\">\n {searchable ? (\n <div className=\"w-full sm:w-64\">\n <UIInput\n value={query}\n onChange={(e) => {\n setQuery(e.target.value);\n setPage(1);\n onSearch?.(e.target.value);\n }}\n placeholder=\"Search…\"\n aria-label=\"Search table\"\n />\n </div>\n ) : null}\n {actions ? <div className=\"flex items-center gap-2\">{actions}</div> : null}\n </div>\n </div>\n );\n\n if (error) {\n return (\n <BaseCard\n header={header}\n body={\n <div className=\"mt-4 rounded-xl border border-rose-200 bg-rose-50 p-4 text-sm text-rose-900 dark:border-rose-900/40 dark:bg-rose-950/30 dark:text-rose-100\">\n {String(error)}\n </div>\n }\n {...cardProps}\n />\n );\n }\n\n const canSort = (col) => sortable && (col.sortable ?? true);\n\n return (\n <BaseCard\n variant=\"table\"\n header={header}\n body={\n <div className=\"mt-4 overflow-hidden rounded-xl border border-slate-200 dark:border-slate-800\">\n <div className=\"w-full overflow-x-auto\">\n <table className=\"min-w-full border-separate border-spacing-0\">\n <thead className=\"bg-slate-50 dark:bg-slate-950/30\">\n <tr>\n {columns.map((col) => {\n const active = sortKey === col.key;\n return (\n <th\n key={col.key}\n scope=\"col\"\n className={[\n \"whitespace-nowrap border-b border-slate-200 px-4 py-3 text-left text-xs font-semibold text-slate-600 dark:border-slate-800 dark:text-slate-300\",\n col.className ?? \"\"\n ]\n .filter(Boolean)\n .join(\" \")}\n >\n {canSort(col) ? (\n <button\n type=\"button\"\n className=\"inline-flex items-center gap-2 hover:text-slate-900 dark:hover:text-slate-50\"\n onClick={() => {\n const nextDir = active && sortDir === \"asc\" ? \"desc\" : \"asc\";\n setSortKey(col.key);\n setSortDir(nextDir);\n onSort?.({ key: col.key, direction: nextDir });\n }}\n >\n <span>{col.label}</span>\n <span className=\"text-[10px] opacity-70\">\n {active ? (sortDir === \"asc\" ? \"▲\" : \"▼\") : \"↕\"}\n </span>\n </button>\n ) : (\n col.label\n )}\n </th>\n );\n })}\n {rowActions ? (\n <th className=\"border-b border-slate-200 px-4 py-3 text-right text-xs font-semibold text-slate-600 dark:border-slate-800 dark:text-slate-300\">\n Actions\n </th>\n ) : null}\n </tr>\n </thead>\n\n <tbody className=\"bg-white dark:bg-slate-900\">\n {effectiveLoading ? (\n Array.from({ length: Math.min(pageSize, 6) }).map((_, idx) => (\n <tr key={idx}>\n {columns.map((col) => (\n <td\n key={col.key}\n className=\"border-b border-slate-200 px-4 py-3 dark:border-slate-800\"\n >\n <div className=\"h-4 w-3/4 animate-pulse rounded bg-slate-200 dark:bg-slate-800\" />\n </td>\n ))}\n {rowActions ? (\n <td className=\"border-b border-slate-200 px-4 py-3 dark:border-slate-800\">\n <div className=\"h-4 w-10 animate-pulse rounded bg-slate-200 dark:bg-slate-800\" />\n </td>\n ) : null}\n </tr>\n ))\n ) : pageData.length === 0 ? (\n <tr>\n <td\n colSpan={columns.length + (rowActions ? 1 : 0)}\n className=\"px-4 py-10 text-center text-sm text-slate-600 dark:text-slate-300\"\n >\n {emptyMessage}\n </td>\n </tr>\n ) : (\n pageData.map((row, idx) => {\n const rowId = row?.id ?? idx;\n const selected = selectable && selectedId === rowId;\n return (\n <tr\n key={rowId}\n className={[\n \"group\",\n selectable ? \"cursor-pointer\" : \"\",\n selected ? \"bg-brand-50 dark:bg-brand-950/30\" : \"\"\n ]\n .filter(Boolean)\n .join(\" \")}\n onClick={() => {\n if (!selectable) return;\n setSelectedId(rowId);\n onRowSelect?.(row);\n }}\n >\n {columns.map((col) => {\n const raw = row?.[col.key];\n const content = col.render ? col.render(raw, row) : defaultTypeFormat(col.type, raw);\n return (\n <td\n key={col.key}\n className={[\n \"border-b border-slate-200 px-4 py-3 text-sm text-slate-700 dark:border-slate-800 dark:text-slate-200\",\n col.mono ? \"font-mono text-[13px]\" : \"\",\n col.className ?? \"\"\n ]\n .filter(Boolean)\n .join(\" \")}\n >\n {content}\n </td>\n );\n })}\n {rowActions ? (\n <td className=\"border-b border-slate-200 px-4 py-3 text-right text-sm dark:border-slate-800\">\n <div className=\"inline-flex items-center justify-end gap-2 opacity-100 sm:opacity-0 sm:group-hover:opacity-100\">\n {rowActions(row)}\n </div>\n </td>\n ) : null}\n </tr>\n );\n })\n )}\n </tbody>\n </table>\n </div>\n\n {paginated && !effectiveLoading ? (\n <div className=\"flex flex-col gap-2 border-t border-slate-200 bg-slate-50 px-4 py-3 dark:border-slate-800 dark:bg-slate-950/30 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"text-xs text-slate-600 dark:text-slate-300\">\n {total === 0 ? \"0 results\" : `Showing ${(clampedPage - 1) * pageSize + 1}-${Math.min(clampedPage * pageSize, total)} of ${total}`}\n </div>\n <div className=\"flex items-center gap-2\">\n <UIButton\n variant=\"outline\"\n size=\"sm\"\n disabled={clampedPage <= 1}\n onClick={() => setPage((p) => Math.max(1, p - 1))}\n >\n Prev\n </UIButton>\n <div className=\"text-xs text-slate-600 dark:text-slate-300\">\n Page {clampedPage} / {totalPages}\n </div>\n <UIButton\n variant=\"outline\"\n size=\"sm\"\n disabled={clampedPage >= totalPages}\n onClick={() => setPage((p) => Math.min(totalPages, p + 1))}\n >\n Next\n </UIButton>\n </div>\n </div>\n ) : null}\n </div>\n }\n {...cardProps}\n />\n );\n}\n\n\n"],"names":["defaultTypeFormat","type","value","n","stableSort","data","compare","item","idx","a","b","c","x","TableCard","columns","title","subtitle","searchable","sortable","paginated","selectable","pageSize","actions","rowActions","onRowSelect","onSort","onSearch","loading","error","emptyMessage","simulateInitialLoad","minInitialDelayMs","maxInitialDelayMs","cardProps","query","setQuery","React","sortKey","setSortKey","sortDir","setSortDir","page","setPage","selectedId","setSelectedId","simLoading","setSimLoading","delay","t","effectiveLoading","filtered","q","row","col","raw","sorted","dir","av","bv","total","totalPages","clampedPage","pageData","start","header","jsxs","jsx","UIText","UIInput","BaseCard","canSort","active","nextDir","_","rowId","content","UIButton","p"],"mappings":"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"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { jsxs as s, jsx as e } from "react/jsx-runtime";
|
|
2
2
|
import x from "react";
|
|
3
3
|
import v from "./BaseCard.js";
|
|
4
|
-
import
|
|
5
|
-
function
|
|
4
|
+
import k from "../ui/Text.js";
|
|
5
|
+
function N({ title: d, actions: a, content: r, divided: l }) {
|
|
6
6
|
return /* @__PURE__ */ s("div", { className: l ? "border-t border-slate-200 pt-4 dark:border-slate-800" : "", children: [
|
|
7
7
|
d || a ? /* @__PURE__ */ s("div", { className: "mb-3 flex items-start justify-between gap-3", children: [
|
|
8
|
-
d ? /* @__PURE__ */ e(
|
|
8
|
+
d ? /* @__PURE__ */ e(k, { as: "div", size: "sm", weight: "medium", children: d }) : /* @__PURE__ */ e("div", {}),
|
|
9
9
|
a ? /* @__PURE__ */ e("div", { className: "shrink-0", children: a }) : null
|
|
10
10
|
] }) : null,
|
|
11
11
|
/* @__PURE__ */ e("div", { children: r })
|
|
@@ -41,7 +41,7 @@ function j({
|
|
|
41
41
|
/* @__PURE__ */ e("div", { className: "h-4 w-2/3 animate-pulse rounded bg-slate-200 dark:bg-slate-800" }),
|
|
42
42
|
/* @__PURE__ */ e("div", { className: "h-4 w-1/2 animate-pulse rounded bg-slate-200 dark:bg-slate-800" })
|
|
43
43
|
] }) : i ? a.length === 0 ? /* @__PURE__ */ e("div", { className: "rounded-xl border border-dashed border-slate-300 bg-slate-50 p-6 text-center text-sm text-slate-600 dark:border-slate-700 dark:bg-slate-950/30 dark:text-slate-300", children: b }) : a.map((t, n) => /* @__PURE__ */ e(
|
|
44
|
-
|
|
44
|
+
N,
|
|
45
45
|
{
|
|
46
46
|
title: t?.title,
|
|
47
47
|
actions: t?.actions,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"D3Chart.js","sources":["../../../../src/components/library/charts/D3Chart.jsx"],"sourcesContent":["import React from \"react\";\n\n/**\n * Minimal D3 chart host:\n * - Owns the <svg> element\n * - Computes responsive dimensions from container size\n * - Calls renderChart(svgEl, data, dims, options)\n */\nexport default function D3Chart({\n data,\n renderChart,\n options = {},\n width,\n height = 280,\n responsive = false,\n aspectRatio,\n className = \"\",\n style,\n containerStyle,\n svgStyle,\n loading = false,\n error,\n ariaLabel = \"Chart\"\n}) {\n const containerRef = React.useRef(null);\n const svgRef = React.useRef(null);\n const [containerWidth, setContainerWidth] = React.useState(null);\n\n React.useEffect(() => {\n if (!responsive) return;\n const el = containerRef.current;\n if (!el) return;\n\n const obs = new ResizeObserver((entries) => {\n const w = entries?.[0]?.contentRect?.width;\n if (typeof w === \"number\" && Number.isFinite(w)) setContainerWidth(w);\n });\n obs.observe(el);\n return () => obs.disconnect();\n }, [responsive]);\n\n const computedWidth = responsive ? containerWidth : width;\n const computedHeight = React.useMemo(() => {\n if (!responsive) return height;\n if (!containerWidth) return height;\n if (aspectRatio && Number.isFinite(aspectRatio) && aspectRatio > 0) return containerWidth / aspectRatio;\n return height;\n }, [responsive, containerWidth, height, aspectRatio]);\n\n React.useEffect(() => {\n if (loading || error) return;\n if (!renderChart) return;\n const svgEl = svgRef.current;\n if (!svgEl) return;\n\n const dims = {\n width: computedWidth ?? 0,\n height: computedHeight ?? 0\n };\n\n // Avoid calling renderChart before we have a measurable width in responsive mode\n if (responsive && (!dims.width || dims.width < 10)) return;\n if (!dims.height || dims.height < 10) return;\n\n renderChart(svgEl, data, dims, options);\n }, [data, renderChart, options, computedWidth, computedHeight, responsive, loading, error]);\n\n if (error) {\n return (\n <div\n ref={containerRef}\n className={[\"w-full rounded-xl border border-rose-200 bg-rose-50 p-4 text-sm text-rose-900 dark:border-rose-900/40 dark:bg-rose-950/30 dark:text-rose-100\", className]\n .filter(Boolean)\n .join(\" \")}\n style={containerStyle}\n >\n {String(error)}\n </div>\n );\n }\n\n return (\n <div\n ref={containerRef}\n className={[\"w-full\", className].filter(Boolean).join(\" \")}\n style={{ ...containerStyle, position: \"relative\" }}\n >\n {loading ? (\n <div className=\"h-full w-full rounded-xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-900\">\n <div className=\"space-y-3\">\n <div className=\"h-4 w-1/3 animate-pulse rounded bg-slate-200 dark:bg-slate-800\" />\n <div className=\"h-44 w-full animate-pulse rounded bg-slate-200 dark:bg-slate-800\" />\n </div>\n </div>\n ) : (\n <svg\n ref={svgRef}\n role=\"img\"\n aria-label={ariaLabel}\n width={computedWidth ?? width ?? \"100%\"}\n height={computedHeight}\n style={{ ...svgStyle, ...style, display: \"block\" }}\n />\n )}\n </div>\n );\n}\n\n\n"],"names":["D3Chart","data","renderChart","options","width","height","responsive","aspectRatio","className","style","containerStyle","svgStyle","loading","error","ariaLabel","containerRef","React","svgRef","containerWidth","setContainerWidth","el","obs","entries","w","computedWidth","computedHeight","svgEl","dims","jsx","jsxs"],"mappings":";;AAQA,SAAwBA,EAAQ;AAAA,EAC9B,MAAAC;AAAA,EACA,aAAAC;AAAA,EACA,SAAAC,IAAU,CAAA;AAAA,EACV,OAAAC;AAAA,EACA,QAAAC,IAAS;AAAA,EACT,YAAAC,IAAa;AAAA,EACb,aAAAC;AAAA,EACA,WAAAC,IAAY;AAAA,EACZ,OAAAC;AAAA,EACA,gBAAAC;AAAA,EACA,UAAAC;AAAA,EACA,SAAAC,IAAU;AAAA,EACV,OAAAC;AAAA,EACA,WAAAC,IAAY;AACd,GAAG;AACD,QAAMC,IAAeC,EAAM,OAAO,IAAI,GAChCC,IAASD,EAAM,OAAO,IAAI,GAC1B,CAACE,GAAgBC,CAAiB,IAAIH,EAAM,SAAS,IAAI;AAE/
|
|
1
|
+
{"version":3,"file":"D3Chart.js","sources":["../../../../src/components/library/charts/D3Chart.jsx"],"sourcesContent":["import React from \"react\";\n\n/**\n * Minimal D3 chart host:\n * - Owns the <svg> element\n * - Computes responsive dimensions from container size\n * - Calls renderChart(svgEl, data, dims, options)\n */\nexport default function D3Chart({\n data,\n renderChart,\n options = {},\n width,\n height = 280,\n responsive = false,\n aspectRatio,\n className = \"\",\n style,\n containerStyle,\n svgStyle,\n loading = false,\n error,\n ariaLabel = \"Chart\"\n}) {\n const containerRef = React.useRef(null);\n const svgRef = React.useRef(null);\n const [containerWidth, setContainerWidth] = React.useState(null);\n\n React.useEffect(() => {\n if (!responsive) return;\n const el = containerRef.current;\n if (!el) return;\n\n const obs = new ResizeObserver((entries) => {\n const w = entries?.[0]?.contentRect?.width;\n if (typeof w === \"number\" && Number.isFinite(w)) setContainerWidth(w);\n });\n obs.observe(el);\n return () => obs.disconnect();\n }, [responsive]);\n\n const computedWidth = responsive ? containerWidth : width;\n const computedHeight = React.useMemo(() => {\n if (!responsive) return height;\n if (!containerWidth) return height;\n if (aspectRatio && Number.isFinite(aspectRatio) && aspectRatio > 0) return containerWidth / aspectRatio;\n return height;\n }, [responsive, containerWidth, height, aspectRatio]);\n\n React.useEffect(() => {\n if (loading || error) return;\n if (!renderChart) return;\n const svgEl = svgRef.current;\n if (!svgEl) return;\n\n const dims = {\n width: computedWidth ?? 0,\n height: computedHeight ?? 0\n };\n\n // Avoid calling renderChart before we have a measurable width in responsive mode\n if (responsive && (!dims.width || dims.width < 10)) return;\n if (!dims.height || dims.height < 10) return;\n\n renderChart(svgEl, data, dims, options);\n }, [data, renderChart, options, computedWidth, computedHeight, responsive, loading, error]);\n\n if (error) {\n return (\n <div\n ref={containerRef}\n className={[\"w-full rounded-xl border border-rose-200 bg-rose-50 p-4 text-sm text-rose-900 dark:border-rose-900/40 dark:bg-rose-950/30 dark:text-rose-100\", className]\n .filter(Boolean)\n .join(\" \")}\n style={containerStyle}\n >\n {String(error)}\n </div>\n );\n }\n\n return (\n <div\n ref={containerRef}\n className={[\"w-full\", className].filter(Boolean).join(\" \")}\n style={{ ...containerStyle, position: \"relative\" }}\n >\n {loading ? (\n <div className=\"h-full w-full rounded-xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-900\">\n <div className=\"space-y-3\">\n <div className=\"h-4 w-1/3 animate-pulse rounded bg-slate-200 dark:bg-slate-800\" />\n <div className=\"h-44 w-full animate-pulse rounded bg-slate-200 dark:bg-slate-800\" />\n </div>\n </div>\n ) : (\n <svg\n ref={svgRef}\n role=\"img\"\n aria-label={ariaLabel}\n width={computedWidth ?? width ?? \"100%\"}\n height={computedHeight}\n style={{ ...svgStyle, ...style, display: \"block\" }}\n />\n )}\n </div>\n );\n}\n\n\n"],"names":["D3Chart","data","renderChart","options","width","height","responsive","aspectRatio","className","style","containerStyle","svgStyle","loading","error","ariaLabel","containerRef","React","svgRef","containerWidth","setContainerWidth","el","obs","entries","w","computedWidth","computedHeight","svgEl","dims","jsx","jsxs"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsxs as r, jsx as t } from "react/jsx-runtime";
|
|
2
2
|
import { useState as i } from "react";
|
|
3
|
-
import
|
|
3
|
+
import c from "../ui/Spinner.js";
|
|
4
4
|
import { WrenchScrewdriverIcon as o, ChevronDownIcon as m, XCircleIcon as x, CheckCircleIcon as b } from "@heroicons/react/24/outline";
|
|
5
5
|
const d = {
|
|
6
6
|
running: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatToolCall.js","sources":["../../../../src/components/library/chat/ChatToolCall.jsx"],"sourcesContent":["import React, { useState } from \"react\";\nimport
|
|
1
|
+
{"version":3,"file":"ChatToolCall.js","sources":["../../../../src/components/library/chat/ChatToolCall.jsx"],"sourcesContent":["import React, { useState } from \"react\";\nimport Spinner from \"../ui/Spinner\";\nimport {\n WrenchScrewdriverIcon,\n CheckCircleIcon,\n XCircleIcon,\n ChevronDownIcon,\n} from \"@heroicons/react/24/outline\";\n\nconst STATUS_CONFIG = {\n running: {\n icon: <Spinner size=\"xs\" tone=\"brand\" label=\"Running\" />,\n text: \"text-brand-600 dark:text-brand-400\",\n bg: \"bg-brand-50 border-brand-100 dark:bg-brand-950/20 dark:border-brand-900/30\",\n label: \"Running\",\n },\n complete: {\n icon: <CheckCircleIcon className=\"h-3.5 w-3.5 text-emerald-500\" />,\n text: \"text-emerald-700 dark:text-emerald-300\",\n bg: \"bg-emerald-50 border-emerald-100 dark:bg-emerald-950/20 dark:border-emerald-900/30\",\n label: \"Complete\",\n },\n error: {\n icon: <XCircleIcon className=\"h-3.5 w-3.5 text-red-500\" />,\n text: \"text-red-700 dark:text-red-300\",\n bg: \"bg-red-50 border-red-100 dark:bg-red-950/20 dark:border-red-900/30\",\n label: \"Failed\",\n },\n};\n\n/**\n * Displays an agent tool call / function execution step.\n *\n * @param {Object} toolCall — { id?, name, args?, status: \"running\"|\"complete\"|\"error\", result? }\n */\nexport default function ChatToolCall({ toolCall }) {\n const [expanded, setExpanded] = useState(false);\n const config = STATUS_CONFIG[toolCall.status] ?? STATUS_CONFIG.running;\n const hasDetails = toolCall.args || toolCall.result;\n\n return (\n <div\n className={[\n \"rounded-lg border text-xs\",\n config.bg,\n ].join(\" \")}\n >\n <button\n type=\"button\"\n onClick={() => hasDetails && setExpanded(!expanded)}\n className={[\n \"flex w-full items-center gap-2 px-3 py-1.5\",\n hasDetails ? \"cursor-pointer\" : \"cursor-default\",\n ].join(\" \")}\n >\n {config.icon}\n <WrenchScrewdriverIcon className=\"h-3 w-3 text-slate-400 dark:text-slate-500\" />\n <span className={[\"font-medium\", config.text].join(\" \")}>\n {toolCall.name}\n </span>\n <span className=\"text-slate-400 dark:text-slate-500\">\n — {config.label}\n </span>\n {hasDetails ? (\n <ChevronDownIcon\n className={[\n \"ml-auto h-3 w-3 text-slate-400 transition-transform dark:text-slate-500\",\n expanded ? \"rotate-180\" : \"\",\n ].join(\" \")}\n />\n ) : null}\n </button>\n\n {expanded && hasDetails ? (\n <div className=\"border-t border-inherit px-3 py-2 font-mono text-[11px] leading-relaxed text-slate-600 dark:text-slate-300\">\n {toolCall.args ? (\n <div className=\"mb-1\">\n <span className=\"font-sans font-semibold text-slate-500 dark:text-slate-400\">\n Args:{\" \"}\n </span>\n {typeof toolCall.args === \"string\"\n ? toolCall.args\n : JSON.stringify(toolCall.args, null, 2)}\n </div>\n ) : null}\n {toolCall.result ? (\n <div>\n <span className=\"font-sans font-semibold text-slate-500 dark:text-slate-400\">\n Result:{\" \"}\n </span>\n {typeof toolCall.result === \"string\"\n ? toolCall.result\n : JSON.stringify(toolCall.result, null, 2)}\n </div>\n ) : null}\n </div>\n ) : null}\n </div>\n );\n}\n"],"names":["STATUS_CONFIG","Spinner","jsx","CheckCircleIcon","XCircleIcon","ChatToolCall","toolCall","expanded","setExpanded","useState","config","hasDetails","jsxs","WrenchScrewdriverIcon","ChevronDownIcon"],"mappings":";;;;AASA,MAAMA,IAAgB;AAAA,EACpB,SAAS;AAAA,IACP,wBAAOC,GAAA,EAAQ,MAAK,MAAK,MAAK,SAAQ,OAAM,WAAU;AAAA,IACtD,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,OAAO;AAAA,EAAA;AAAA,EAET,UAAU;AAAA,IACR,MAAM,gBAAAC,EAACC,GAAA,EAAgB,WAAU,+BAAA,CAA+B;AAAA,IAChE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,OAAO;AAAA,EAAA;AAAA,EAET,OAAO;AAAA,IACL,MAAM,gBAAAD,EAACE,GAAA,EAAY,WAAU,2BAAA,CAA2B;AAAA,IACxD,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,OAAO;AAAA,EAAA;AAEX;AAOA,SAAwBC,EAAa,EAAE,UAAAC,KAAY;AACjD,QAAM,CAACC,GAAUC,CAAW,IAAIC,EAAS,EAAK,GACxCC,IAASV,EAAcM,EAAS,MAAM,KAAKN,EAAc,SACzDW,IAAaL,EAAS,QAAQA,EAAS;AAE7C,SACE,gBAAAM;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACAF,EAAO;AAAA,MAAA,EACP,KAAK,GAAG;AAAA,MAEV,UAAA;AAAA,QAAA,gBAAAE;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS,MAAMD,KAAcH,EAAY,CAACD,CAAQ;AAAA,YAClD,WAAW;AAAA,cACT;AAAA,cACAI,IAAa,mBAAmB;AAAA,YAAA,EAChC,KAAK,GAAG;AAAA,YAET,UAAA;AAAA,cAAAD,EAAO;AAAA,cACR,gBAAAR,EAACW,GAAA,EAAsB,WAAU,6CAAA,CAA6C;AAAA,cAC9E,gBAAAX,EAAC,QAAA,EAAK,WAAW,CAAC,eAAeQ,EAAO,IAAI,EAAE,KAAK,GAAG,GACnD,UAAAJ,EAAS,KAAA,CACZ;AAAA,cACA,gBAAAM,EAAC,QAAA,EAAK,WAAU,sCAAqC,UAAA;AAAA,gBAAA;AAAA,gBAChDF,EAAO;AAAA,cAAA,GACZ;AAAA,cACCC,IACC,gBAAAT;AAAA,gBAACY;AAAA,gBAAA;AAAA,kBACC,WAAW;AAAA,oBACT;AAAA,oBACAP,IAAW,eAAe;AAAA,kBAAA,EAC1B,KAAK,GAAG;AAAA,gBAAA;AAAA,cAAA,IAEV;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA;AAAA,QAGLA,KAAYI,IACX,gBAAAC,EAAC,OAAA,EAAI,WAAU,8GACZ,UAAA;AAAA,UAAAN,EAAS,OACR,gBAAAM,EAAC,OAAA,EAAI,WAAU,QACb,UAAA;AAAA,YAAA,gBAAAA,EAAC,QAAA,EAAK,WAAU,8DAA6D,UAAA;AAAA,cAAA;AAAA,cACrE;AAAA,YAAA,GACR;AAAA,YACC,OAAON,EAAS,QAAS,WACtBA,EAAS,OACT,KAAK,UAAUA,EAAS,MAAM,MAAM,CAAC;AAAA,UAAA,EAAA,CAC3C,IACE;AAAA,UACHA,EAAS,SACR,gBAAAM,EAAC,OAAA,EACC,UAAA;AAAA,YAAA,gBAAAA,EAAC,QAAA,EAAK,WAAU,8DAA6D,UAAA;AAAA,cAAA;AAAA,cACnE;AAAA,YAAA,GACV;AAAA,YACC,OAAON,EAAS,UAAW,WACxBA,EAAS,SACT,KAAK,UAAUA,EAAS,QAAQ,MAAM,CAAC;AAAA,UAAA,EAAA,CAC7C,IACE;AAAA,QAAA,EAAA,CACN,IACE;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGV;"}
|
|
@@ -9,15 +9,15 @@ const i = t.createContext({
|
|
|
9
9
|
setMode: () => {
|
|
10
10
|
}
|
|
11
11
|
}), l = "app-data-mode", c = ["sample", "live"];
|
|
12
|
-
function
|
|
12
|
+
function f() {
|
|
13
13
|
return t.useContext(i);
|
|
14
14
|
}
|
|
15
15
|
function v({ initialMode: n = "sample", children: r }) {
|
|
16
|
-
const [o,
|
|
16
|
+
const [o, s] = t.useState(n);
|
|
17
17
|
t.useEffect(() => {
|
|
18
18
|
try {
|
|
19
19
|
const e = window.localStorage.getItem(l);
|
|
20
|
-
c.includes(e) &&
|
|
20
|
+
c.includes(e) && s(e);
|
|
21
21
|
} catch {
|
|
22
22
|
}
|
|
23
23
|
}, []), t.useEffect(() => {
|
|
@@ -26,22 +26,22 @@ function v({ initialMode: n = "sample", children: r }) {
|
|
|
26
26
|
} catch {
|
|
27
27
|
}
|
|
28
28
|
}, [o]);
|
|
29
|
-
const
|
|
30
|
-
c.includes(e) &&
|
|
29
|
+
const a = t.useCallback((e) => {
|
|
30
|
+
c.includes(e) && s(e);
|
|
31
31
|
}, []), d = t.useMemo(
|
|
32
32
|
() => ({
|
|
33
33
|
mode: o,
|
|
34
34
|
isSample: o === "sample",
|
|
35
35
|
isLive: o === "live",
|
|
36
|
-
toggle: () =>
|
|
37
|
-
setMode:
|
|
36
|
+
toggle: () => s((e) => e === "sample" ? "live" : "sample"),
|
|
37
|
+
setMode: a
|
|
38
38
|
}),
|
|
39
|
-
[o,
|
|
39
|
+
[o, a]
|
|
40
40
|
);
|
|
41
41
|
return /* @__PURE__ */ m(i.Provider, { value: d, children: r });
|
|
42
42
|
}
|
|
43
43
|
export {
|
|
44
44
|
v as default,
|
|
45
|
-
|
|
45
|
+
f as useDataMode
|
|
46
46
|
};
|
|
47
47
|
//# sourceMappingURL=DataModeProvider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataModeProvider.js","sources":["../../../../src/components/library/data/DataModeProvider.jsx"],"sourcesContent":["import React from \"react\";\n\nconst DataModeContext = React.createContext({\n mode: \"sample\",\n isSample: true,\n isLive: false,\n toggle: () => {},\n setMode: () => {},\n});\n\nconst STORAGE_KEY = \"app-data-mode\";\nconst VALID_MODES = [\"sample\", \"live\"];\n\n/**\n * Read the current data mode from any component.\n *\n * @returns {{ mode: \"sample\"|\"live\", isSample: boolean, isLive: boolean, toggle: () => void, setMode: (mode) => void }}\n */\nexport function useDataMode() {\n return React.useContext(DataModeContext);\n}\n\n/**\n * Provides global data-mode state (sample vs live) to the component tree.\n * Persists to localStorage so the choice survives page reloads.\n *\n * Wrap once in _app.js alongside AppThemeProvider.\n */\nexport default function DataModeProvider({ initialMode = \"sample\", children }) {\n const [mode, setModeState] = React.useState(initialMode);\n\n React.useEffect(() => {\n try {\n const stored = window.localStorage.getItem(STORAGE_KEY);\n if (VALID_MODES.includes(stored)) setModeState(stored);\n } catch {\n // SSR or storage unavailable\n }\n }, []);\n\n React.useEffect(() => {\n try {\n window.localStorage.setItem(STORAGE_KEY, mode);\n } catch {\n // ignore\n }\n }, [mode]);\n\n const setMode = React.useCallback((m) => {\n if (VALID_MODES.includes(m)) setModeState(m);\n }, []);\n\n const value = React.useMemo(\n () => ({\n mode,\n isSample: mode === \"sample\",\n isLive: mode === \"live\",\n toggle: () => setModeState((m) => (m === \"sample\" ? \"live\" : \"sample\")),\n setMode,\n }),\n [mode, setMode]\n );\n\n return (\n <DataModeContext.Provider value={value}>{children}</DataModeContext.Provider>\n );\n}\n"],"names":["DataModeContext","React","STORAGE_KEY","VALID_MODES","useDataMode","DataModeProvider","initialMode","children","mode","setModeState","stored","setMode","m","value","jsx"],"mappings":";;AAEA,MAAMA,IAAkBC,EAAM,cAAc;AAAA,EAC1C,MAAM;AAAA,EACN,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,QAAQ,MAAM;AAAA,EAAC;AAAA,EACf,SAAS,MAAM;AAAA,EAAC;AAClB,CAAC,GAEKC,IAAc,iBACdC,IAAc,CAAC,UAAU,MAAM;AAO9B,SAASC,IAAc;AAC5B,SAAOH,EAAM,WAAWD,CAAe;AACzC;AAQA,SAAwBK,EAAiB,EAAE,aAAAC,IAAc,UAAU,UAAAC,KAAY;AAC7E,QAAM,CAACC,GAAMC,CAAY,IAAIR,EAAM,SAASK,CAAW;
|
|
1
|
+
{"version":3,"file":"DataModeProvider.js","sources":["../../../../src/components/library/data/DataModeProvider.jsx"],"sourcesContent":["import React from \"react\";\n\nconst DataModeContext = React.createContext({\n mode: \"sample\",\n isSample: true,\n isLive: false,\n toggle: () => {},\n setMode: () => {},\n});\n\nconst STORAGE_KEY = \"app-data-mode\";\nconst VALID_MODES = [\"sample\", \"live\"];\n\n/**\n * Read the current data mode from any component.\n *\n * @returns {{ mode: \"sample\"|\"live\", isSample: boolean, isLive: boolean, toggle: () => void, setMode: (mode) => void }}\n */\nexport function useDataMode() {\n return React.useContext(DataModeContext);\n}\n\n/**\n * Provides global data-mode state (sample vs live) to the component tree.\n * Persists to localStorage so the choice survives page reloads.\n *\n * Wrap once in _app.js alongside AppThemeProvider.\n */\nexport default function DataModeProvider({ initialMode = \"sample\", children }) {\n const [mode, setModeState] = React.useState(initialMode);\n\n React.useEffect(() => {\n try {\n const stored = window.localStorage.getItem(STORAGE_KEY);\n if (VALID_MODES.includes(stored)) setModeState(stored);\n } catch {\n // SSR or storage unavailable\n }\n }, []);\n\n React.useEffect(() => {\n try {\n window.localStorage.setItem(STORAGE_KEY, mode);\n } catch {\n // ignore\n }\n }, [mode]);\n\n const setMode = React.useCallback((m) => {\n if (VALID_MODES.includes(m)) setModeState(m);\n }, []);\n\n const value = React.useMemo(\n () => ({\n mode,\n isSample: mode === \"sample\",\n isLive: mode === \"live\",\n toggle: () => setModeState((m) => (m === \"sample\" ? \"live\" : \"sample\")),\n setMode,\n }),\n [mode, setMode]\n );\n\n return (\n <DataModeContext.Provider value={value}>{children}</DataModeContext.Provider>\n );\n}\n"],"names":["DataModeContext","React","STORAGE_KEY","VALID_MODES","useDataMode","DataModeProvider","initialMode","children","mode","setModeState","stored","setMode","m","value","jsx"],"mappings":";;AAEA,MAAMA,IAAkBC,EAAM,cAAc;AAAA,EAC1C,MAAM;AAAA,EACN,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,QAAQ,MAAM;AAAA,EAAC;AAAA,EACf,SAAS,MAAM;AAAA,EAAC;AAClB,CAAC,GAEKC,IAAc,iBACdC,IAAc,CAAC,UAAU,MAAM;AAO9B,SAASC,IAAc;AAC5B,SAAOH,EAAM,WAAWD,CAAe;AACzC;AAQA,SAAwBK,EAAiB,EAAE,aAAAC,IAAc,UAAU,UAAAC,KAAY;AAC7E,QAAM,CAACC,GAAMC,CAAY,IAAIR,EAAM,SAASK,CAAW;AAEvD,EAAAL,EAAM,UAAU,MAAM;AACpB,QAAI;AACF,YAAMS,IAAS,OAAO,aAAa,QAAQR,CAAW;AACtD,MAAIC,EAAY,SAASO,CAAM,OAAgBA,CAAM;AAAA,IACvD,QAAQ;AAAA,IAER;AAAA,EACF,GAAG,CAAA,CAAE,GAELT,EAAM,UAAU,MAAM;AACpB,QAAI;AACF,aAAO,aAAa,QAAQC,GAAaM,CAAI;AAAA,IAC/C,QAAQ;AAAA,IAER;AAAA,EACF,GAAG,CAACA,CAAI,CAAC;AAET,QAAMG,IAAUV,EAAM,YAAY,CAACW,MAAM;AACvC,IAAIT,EAAY,SAASS,CAAC,OAAgBA,CAAC;AAAA,EAC7C,GAAG,CAAA,CAAE,GAECC,IAAQZ,EAAM;AAAA,IAClB,OAAO;AAAA,MACL,MAAAO;AAAA,MACA,UAAUA,MAAS;AAAA,MACnB,QAAQA,MAAS;AAAA,MACjB,QAAQ,MAAMC,EAAa,CAACG,MAAOA,MAAM,WAAW,SAAS,QAAS;AAAA,MACtE,SAAAD;AAAA,IAAA;AAAA,IAEF,CAACH,GAAMG,CAAO;AAAA,EAAA;AAGhB,SACE,gBAAAG,EAACd,EAAgB,UAAhB,EAAyB,OAAAa,GAAe,UAAAN,EAAA,CAAS;AAEtD;"}
|
|
@@ -3,7 +3,7 @@ import { useEffect as m, useCallback as k } from "react";
|
|
|
3
3
|
import { createPortal as w } from "react-dom";
|
|
4
4
|
import { AnimatePresence as N, motion as c } from "framer-motion";
|
|
5
5
|
import { XMarkIcon as S } from "@heroicons/react/24/outline";
|
|
6
|
-
import
|
|
6
|
+
import E from "../ui/Spinner.js";
|
|
7
7
|
import A from "./FormRenderer.js";
|
|
8
8
|
import j from "./useFormState.js";
|
|
9
9
|
const F = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormModal.js","sources":["../../../../src/components/library/forms/FormModal.jsx"],"sourcesContent":["import React, { useEffect, useCallback } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { motion, AnimatePresence } from \"framer-motion\";\nimport { XMarkIcon } from \"@heroicons/react/24/outline\";\nimport { Spinner } from \"../ui/spinner\";\nimport FormRenderer from \"./FormRenderer\";\nimport useFormState from \"./useFormState\";\n\nconst OVERLAY_VARIANTS = {\n hidden: { opacity: 0 },\n visible: { opacity: 1 },\n};\n\nconst PANEL_VARIANTS = {\n hidden: { opacity: 0, y: 24, scale: 0.97 },\n visible: { opacity: 1, y: 0, scale: 1, transition: { type: \"spring\", damping: 25, stiffness: 350 } },\n exit: { opacity: 0, y: 16, scale: 0.97, transition: { duration: 0.15 } },\n};\n\n/**\n * Size → max-width mapping.\n */\nconst SIZE_CLASSES = {\n sm: \"max-w-md\",\n md: \"max-w-xl\",\n lg: \"max-w-2xl\",\n xl: \"max-w-4xl\",\n};\n\n/**\n * Modal dialog for creating or editing records.\n * Wraps FormRenderer + useFormState in an animated overlay.\n *\n * @param {boolean} isOpen — whether the modal is visible\n * @param {Function} onClose — close handler\n * @param {string} title — modal title (e.g. \"Edit Service\", \"New Incident\")\n * @param {string} subtitle — optional subtitle\n * @param {Array} sections — form schema sections\n * @param {Object} initialValues — prefill for editing (empty = create mode)\n * @param {Function} onSubmit — async (values) => void, called on valid submit\n * @param {string} submitLabel — submit button text (default: \"Save\")\n * @param {string} cancelLabel — cancel button text (default: \"Cancel\")\n * @param {string} size — \"sm\" | \"md\" | \"lg\" | \"xl\" (default: \"lg\")\n * @param {boolean} destructive — if true, submit button is red (for delete confirmations)\n */\nexport default function FormModal({\n isOpen = false,\n onClose,\n title,\n subtitle,\n sections = [],\n initialValues = {},\n onSubmit,\n submitLabel = \"Save\",\n cancelLabel = \"Cancel\",\n size = \"lg\",\n destructive = false,\n minSubmitMs,\n}) {\n const form = useFormState({\n initialValues,\n sections,\n minSubmitMs,\n onSubmit: async (vals) => {\n await onSubmit?.(vals);\n onClose?.();\n },\n });\n\n useEffect(() => {\n if (isOpen) form.reset();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isOpen]);\n\n const onKeyDown = useCallback(\n (e) => {\n if (e.key === \"Escape\") onClose?.();\n },\n [onClose]\n );\n\n useEffect(() => {\n if (!isOpen) return;\n document.addEventListener(\"keydown\", onKeyDown);\n document.body.style.overflow = \"hidden\";\n return () => {\n document.removeEventListener(\"keydown\", onKeyDown);\n document.body.style.overflow = \"\";\n };\n }, [isOpen, onKeyDown]);\n\n if (typeof document === \"undefined\") return null;\n\n return createPortal(\n <AnimatePresence>\n {isOpen ? (\n <div className=\"fixed inset-0 z-50 flex items-start justify-center px-4 pt-[10vh] sm:pt-[12vh]\">\n {/* Backdrop */}\n <motion.div\n key=\"overlay\"\n className=\"fixed inset-0 bg-black/40 backdrop-blur-sm\"\n variants={OVERLAY_VARIANTS}\n initial=\"hidden\"\n animate=\"visible\"\n exit=\"hidden\"\n transition={{ duration: 0.2 }}\n onClick={onClose}\n aria-hidden=\"true\"\n />\n\n {/* Panel */}\n <motion.div\n key=\"panel\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={title}\n variants={PANEL_VARIANTS}\n initial=\"hidden\"\n animate=\"visible\"\n exit=\"exit\"\n className={[\n \"relative z-10 flex w-full flex-col rounded-xl border border-slate-200 bg-white shadow-xl dark:border-slate-800 dark:bg-slate-900\",\n SIZE_CLASSES[size] ?? SIZE_CLASSES.lg,\n \"max-h-[80vh]\",\n ].join(\" \")}\n >\n {/* Header */}\n <div className=\"flex items-start justify-between border-b border-slate-100 px-6 py-4 dark:border-slate-800\">\n <div>\n <h2 className=\"text-base font-semibold text-slate-900 dark:text-slate-50\">\n {title}\n </h2>\n {subtitle ? (\n <p className=\"mt-0.5 text-sm text-slate-500 dark:text-slate-400\">{subtitle}</p>\n ) : null}\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-lg p-1.5 text-slate-400 transition hover:bg-slate-100 hover:text-slate-600 dark:text-slate-500 dark:hover:bg-slate-800 dark:hover:text-slate-300\"\n aria-label=\"Close\"\n >\n <XMarkIcon className=\"h-5 w-5\" />\n </button>\n </div>\n\n {/* Body — scrollable */}\n <div className=\"flex-1 overflow-y-auto px-6 py-5\">\n <FormRenderer\n sections={sections}\n values={form.values}\n errors={form.errors}\n touched={form.touched}\n onFieldChange={form.setValue}\n onFieldBlur={form.setTouched}\n formError={form.errors._form}\n />\n </div>\n\n {/* Footer */}\n <div className=\"flex items-center justify-between border-t border-slate-100 px-6 py-4 dark:border-slate-800\">\n <div className=\"text-xs text-slate-400 dark:text-slate-500\">\n {form.isDirty ? \"Unsaved changes\" : \"\\u00A0\"}\n </div>\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={onClose}\n className=\"inline-flex h-9 items-center rounded-lg border border-slate-200 bg-white px-4 text-sm font-medium text-slate-700 shadow-sm transition hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-200 dark:hover:bg-slate-700\"\n >\n {cancelLabel}\n </button>\n <button\n type=\"button\"\n onClick={form.handleSubmit}\n disabled={form.isSubmitting}\n className={[\n \"inline-flex h-9 items-center gap-2 rounded-lg border border-transparent px-4 text-sm font-medium text-white shadow-sm transition\",\n \"focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-900\",\n \"disabled:cursor-not-allowed disabled:opacity-60\",\n destructive\n ? \"bg-red-600 hover:bg-red-500 focus-visible:ring-red-500 dark:bg-red-600 dark:hover:bg-red-500\"\n : \"bg-brand-600 hover:bg-brand-500 focus-visible:ring-brand-500 dark:bg-brand-500 dark:hover:bg-brand-400\",\n ].join(\" \")}\n >\n {form.isSubmitting ? (\n <>\n <Spinner size=\"sm\" tone=\"white\" label=\"Submitting\" />\n Saving…\n </>\n ) : submitLabel}\n </button>\n </div>\n </div>\n </motion.div>\n </div>\n ) : null}\n </AnimatePresence>,\n document.body\n );\n}\n"],"names":["OVERLAY_VARIANTS","PANEL_VARIANTS","SIZE_CLASSES","FormModal","isOpen","onClose","title","subtitle","sections","initialValues","onSubmit","submitLabel","cancelLabel","size","destructive","minSubmitMs","form","useFormState","vals","useEffect","onKeyDown","useCallback","e","createPortal","AnimatePresence","jsxs","jsx","motion","XMarkIcon","FormRenderer","Fragment","Spinner"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"FormModal.js","sources":["../../../../src/components/library/forms/FormModal.jsx"],"sourcesContent":["import React, { useEffect, useCallback } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { motion, AnimatePresence } from \"framer-motion\";\nimport { XMarkIcon } from \"@heroicons/react/24/outline\";\nimport Spinner from \"../ui/Spinner\";\nimport FormRenderer from \"./FormRenderer\";\nimport useFormState from \"./useFormState\";\n\nconst OVERLAY_VARIANTS = {\n hidden: { opacity: 0 },\n visible: { opacity: 1 },\n};\n\nconst PANEL_VARIANTS = {\n hidden: { opacity: 0, y: 24, scale: 0.97 },\n visible: { opacity: 1, y: 0, scale: 1, transition: { type: \"spring\", damping: 25, stiffness: 350 } },\n exit: { opacity: 0, y: 16, scale: 0.97, transition: { duration: 0.15 } },\n};\n\n/**\n * Size → max-width mapping.\n */\nconst SIZE_CLASSES = {\n sm: \"max-w-md\",\n md: \"max-w-xl\",\n lg: \"max-w-2xl\",\n xl: \"max-w-4xl\",\n};\n\n/**\n * Modal dialog for creating or editing records.\n * Wraps FormRenderer + useFormState in an animated overlay.\n *\n * @param {boolean} isOpen — whether the modal is visible\n * @param {Function} onClose — close handler\n * @param {string} title — modal title (e.g. \"Edit Service\", \"New Incident\")\n * @param {string} subtitle — optional subtitle\n * @param {Array} sections — form schema sections\n * @param {Object} initialValues — prefill for editing (empty = create mode)\n * @param {Function} onSubmit — async (values) => void, called on valid submit\n * @param {string} submitLabel — submit button text (default: \"Save\")\n * @param {string} cancelLabel — cancel button text (default: \"Cancel\")\n * @param {string} size — \"sm\" | \"md\" | \"lg\" | \"xl\" (default: \"lg\")\n * @param {boolean} destructive — if true, submit button is red (for delete confirmations)\n */\nexport default function FormModal({\n isOpen = false,\n onClose,\n title,\n subtitle,\n sections = [],\n initialValues = {},\n onSubmit,\n submitLabel = \"Save\",\n cancelLabel = \"Cancel\",\n size = \"lg\",\n destructive = false,\n minSubmitMs,\n}) {\n const form = useFormState({\n initialValues,\n sections,\n minSubmitMs,\n onSubmit: async (vals) => {\n await onSubmit?.(vals);\n onClose?.();\n },\n });\n\n useEffect(() => {\n if (isOpen) form.reset();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isOpen]);\n\n const onKeyDown = useCallback(\n (e) => {\n if (e.key === \"Escape\") onClose?.();\n },\n [onClose]\n );\n\n useEffect(() => {\n if (!isOpen) return;\n document.addEventListener(\"keydown\", onKeyDown);\n document.body.style.overflow = \"hidden\";\n return () => {\n document.removeEventListener(\"keydown\", onKeyDown);\n document.body.style.overflow = \"\";\n };\n }, [isOpen, onKeyDown]);\n\n if (typeof document === \"undefined\") return null;\n\n return createPortal(\n <AnimatePresence>\n {isOpen ? (\n <div className=\"fixed inset-0 z-50 flex items-start justify-center px-4 pt-[10vh] sm:pt-[12vh]\">\n {/* Backdrop */}\n <motion.div\n key=\"overlay\"\n className=\"fixed inset-0 bg-black/40 backdrop-blur-sm\"\n variants={OVERLAY_VARIANTS}\n initial=\"hidden\"\n animate=\"visible\"\n exit=\"hidden\"\n transition={{ duration: 0.2 }}\n onClick={onClose}\n aria-hidden=\"true\"\n />\n\n {/* Panel */}\n <motion.div\n key=\"panel\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={title}\n variants={PANEL_VARIANTS}\n initial=\"hidden\"\n animate=\"visible\"\n exit=\"exit\"\n className={[\n \"relative z-10 flex w-full flex-col rounded-xl border border-slate-200 bg-white shadow-xl dark:border-slate-800 dark:bg-slate-900\",\n SIZE_CLASSES[size] ?? SIZE_CLASSES.lg,\n \"max-h-[80vh]\",\n ].join(\" \")}\n >\n {/* Header */}\n <div className=\"flex items-start justify-between border-b border-slate-100 px-6 py-4 dark:border-slate-800\">\n <div>\n <h2 className=\"text-base font-semibold text-slate-900 dark:text-slate-50\">\n {title}\n </h2>\n {subtitle ? (\n <p className=\"mt-0.5 text-sm text-slate-500 dark:text-slate-400\">{subtitle}</p>\n ) : null}\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-lg p-1.5 text-slate-400 transition hover:bg-slate-100 hover:text-slate-600 dark:text-slate-500 dark:hover:bg-slate-800 dark:hover:text-slate-300\"\n aria-label=\"Close\"\n >\n <XMarkIcon className=\"h-5 w-5\" />\n </button>\n </div>\n\n {/* Body — scrollable */}\n <div className=\"flex-1 overflow-y-auto px-6 py-5\">\n <FormRenderer\n sections={sections}\n values={form.values}\n errors={form.errors}\n touched={form.touched}\n onFieldChange={form.setValue}\n onFieldBlur={form.setTouched}\n formError={form.errors._form}\n />\n </div>\n\n {/* Footer */}\n <div className=\"flex items-center justify-between border-t border-slate-100 px-6 py-4 dark:border-slate-800\">\n <div className=\"text-xs text-slate-400 dark:text-slate-500\">\n {form.isDirty ? \"Unsaved changes\" : \"\\u00A0\"}\n </div>\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={onClose}\n className=\"inline-flex h-9 items-center rounded-lg border border-slate-200 bg-white px-4 text-sm font-medium text-slate-700 shadow-sm transition hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-200 dark:hover:bg-slate-700\"\n >\n {cancelLabel}\n </button>\n <button\n type=\"button\"\n onClick={form.handleSubmit}\n disabled={form.isSubmitting}\n className={[\n \"inline-flex h-9 items-center gap-2 rounded-lg border border-transparent px-4 text-sm font-medium text-white shadow-sm transition\",\n \"focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-900\",\n \"disabled:cursor-not-allowed disabled:opacity-60\",\n destructive\n ? \"bg-red-600 hover:bg-red-500 focus-visible:ring-red-500 dark:bg-red-600 dark:hover:bg-red-500\"\n : \"bg-brand-600 hover:bg-brand-500 focus-visible:ring-brand-500 dark:bg-brand-500 dark:hover:bg-brand-400\",\n ].join(\" \")}\n >\n {form.isSubmitting ? (\n <>\n <Spinner size=\"sm\" tone=\"white\" label=\"Submitting\" />\n Saving…\n </>\n ) : submitLabel}\n </button>\n </div>\n </div>\n </motion.div>\n </div>\n ) : null}\n </AnimatePresence>,\n document.body\n );\n}\n"],"names":["OVERLAY_VARIANTS","PANEL_VARIANTS","SIZE_CLASSES","FormModal","isOpen","onClose","title","subtitle","sections","initialValues","onSubmit","submitLabel","cancelLabel","size","destructive","minSubmitMs","form","useFormState","vals","useEffect","onKeyDown","useCallback","e","createPortal","AnimatePresence","jsxs","jsx","motion","XMarkIcon","FormRenderer","Fragment","Spinner"],"mappings":"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"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
export { default as UIButton } from "./ui/UIButton";
|
|
2
|
+
export { default as UIInput } from "./ui/UIInput";
|
|
1
3
|
export { default as UIText } from "./ui/Text";
|
|
2
4
|
export { default as UIContainer } from "./ui/Container";
|
|
3
5
|
export { default as UIChip } from "./ui/Chip";
|
|
4
6
|
export { default as Avatar } from "./ui/Avatar";
|
|
5
7
|
export { default as EmptyState } from "./ui/EmptyState";
|
|
6
|
-
export
|
|
8
|
+
export { default as Spinner } from "./ui/Spinner";
|
|
7
9
|
export { default as BaseCard } from "./cards/BaseCard";
|
|
8
10
|
export { default as ChartCard } from "./cards/ChartCard";
|
|
9
11
|
export { default as ListCard } from "./cards/ListCard";
|
|
@@ -47,7 +49,6 @@ export { default as HeroUIMeter } from "./heroui/Meter";
|
|
|
47
49
|
export { default as HeroUISkeleton } from "./heroui/Skeleton";
|
|
48
50
|
export { default as HeroUISelect } from "./heroui/Select";
|
|
49
51
|
export { default as AppThemeProvider, useThemeMode } from "./theme/AppThemeProvider";
|
|
50
|
-
export { Button as UIButton, Input as UIInput, Card as UICard, Spinner } from "./ui/shadcn-exports";
|
|
51
52
|
export { FormModal, FormRenderer, FormSection, FormField, useFormState } from "./forms";
|
|
52
53
|
export { FilterBar, SearchFilter, SelectFilter, ToggleFilter } from "./filters";
|
|
53
54
|
export { default as DataModeProvider, useDataMode } from "./data/DataModeProvider";
|