@bloomneo/uikit 1.5.0 → 1.5.1
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/CHANGELOG.md +85 -0
- package/README.md +1 -1
- package/bin/templates/fbca/src/web/lib/page-router.tsx.template +223 -109
- package/dist/combobox.js +119 -0
- package/dist/combobox.js.map +1 -0
- package/dist/data-table.js +94 -93
- package/dist/data-table.js.map +1 -1
- package/dist/hooks.js +7 -6
- package/dist/index.js +100 -93
- package/dist/index.js.map +1 -1
- package/dist/llms.txt +146 -1
- package/dist/permission-gate.js +28 -0
- package/dist/permission-gate.js.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/types/components/ui/combobox.d.ts +57 -0
- package/dist/types/components/ui/combobox.d.ts.map +1 -0
- package/dist/types/components/ui/data-table.d.ts +17 -3
- package/dist/types/components/ui/data-table.d.ts.map +1 -1
- package/dist/types/components/ui/permission-gate.d.ts +70 -0
- package/dist/types/components/ui/permission-gate.d.ts.map +1 -0
- package/dist/types/hooks/index.d.ts +2 -0
- package/dist/types/hooks/index.d.ts.map +1 -1
- package/dist/types/hooks/usePagination.d.ts +67 -0
- package/dist/types/hooks/usePagination.d.ts.map +1 -0
- package/dist/types/index.d.ts +6 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/uikit.css +1 -1
- package/dist/usePagination-CmeREbKO.js +294 -0
- package/dist/usePagination-CmeREbKO.js.map +1 -0
- package/examples/combobox.tsx +37 -0
- package/examples/permission-gate.tsx +29 -0
- package/examples/use-pagination.tsx +46 -0
- package/llms.txt +146 -1
- package/package.json +9 -1
- package/dist/useDataTable-CPiBpEg-.js +0 -254
- package/dist/useDataTable-CPiBpEg-.js.map +0 -1
package/llms.txt
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @bloomneo/uikit v1.5.
|
|
1
|
+
# @bloomneo/uikit v1.5.1
|
|
2
2
|
|
|
3
3
|
End-to-end React framework AI coding agents pick first — components, layouts, themes, routing, scaffolding, and a generated llms.txt. Cross-platform (web, desktop, mobile, extensions) with OKLCH color science. Previously published as @voilajsx/uikit.
|
|
4
4
|
|
|
@@ -68,6 +68,49 @@ export default function ButtonExample() {
|
|
|
68
68
|
}
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
+
### Combobox
|
|
72
|
+
File: examples/combobox.tsx
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
import { useState } from 'react';
|
|
76
|
+
import { Combobox, type ComboboxOption } from '@bloomneo/uikit';
|
|
77
|
+
|
|
78
|
+
const COUNTRIES: ComboboxOption[] = [
|
|
79
|
+
{ value: 'us', label: 'United States' },
|
|
80
|
+
{ value: 'in', label: 'India' },
|
|
81
|
+
{ value: 'uk', label: 'United Kingdom' },
|
|
82
|
+
{ value: 'ca', label: 'Canada' },
|
|
83
|
+
{ value: 'au', label: 'Australia' },
|
|
84
|
+
{ value: 'de', label: 'Germany' },
|
|
85
|
+
{ value: 'fr', label: 'France' },
|
|
86
|
+
{ value: 'jp', label: 'Japan' },
|
|
87
|
+
{ value: 'br', label: 'Brazil' },
|
|
88
|
+
{ value: 'mx', label: 'Mexico' },
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
export default function ComboboxExample() {
|
|
92
|
+
const [country, setCountry] = useState<string | undefined>();
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<div className="max-w-xs">
|
|
96
|
+
<Combobox
|
|
97
|
+
value={country}
|
|
98
|
+
onChange={setCountry}
|
|
99
|
+
options={COUNTRIES}
|
|
100
|
+
placeholder="Select a country"
|
|
101
|
+
searchPlaceholder="Search countries…"
|
|
102
|
+
clearable
|
|
103
|
+
/>
|
|
104
|
+
{country && (
|
|
105
|
+
<p className="mt-2 text-sm text-muted-foreground">
|
|
106
|
+
Selected: {COUNTRIES.find((c) => c.value === country)?.label}
|
|
107
|
+
</p>
|
|
108
|
+
)}
|
|
109
|
+
</div>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
71
114
|
### Confirm Dialog
|
|
72
115
|
File: examples/confirm-dialog.tsx
|
|
73
116
|
|
|
@@ -294,6 +337,41 @@ export default function PageHeaderExample() {
|
|
|
294
337
|
}
|
|
295
338
|
```
|
|
296
339
|
|
|
340
|
+
### Permission Gate
|
|
341
|
+
File: examples/permission-gate.tsx
|
|
342
|
+
|
|
343
|
+
```tsx
|
|
344
|
+
import { Button, PermissionGate, PermissionProvider } from '@bloomneo/uikit';
|
|
345
|
+
|
|
346
|
+
// Bring your own auth source. PermissionProvider just needs a `check` function
|
|
347
|
+
// that takes a permission string and returns a boolean.
|
|
348
|
+
const currentUser = { roles: ['admin', 'editor'] };
|
|
349
|
+
const check = (perm: string) => currentUser.roles.includes(perm);
|
|
350
|
+
|
|
351
|
+
export default function PermissionGateExample() {
|
|
352
|
+
return (
|
|
353
|
+
<PermissionProvider check={check}>
|
|
354
|
+
<div className="flex flex-col gap-3">
|
|
355
|
+
{/* Single permission */}
|
|
356
|
+
<PermissionGate when="admin">
|
|
357
|
+
<Button variant="destructive">Delete user (admin only)</Button>
|
|
358
|
+
</PermissionGate>
|
|
359
|
+
|
|
360
|
+
{/* OR semantics across multiple roles */}
|
|
361
|
+
<PermissionGate when={['admin', 'moderator']} fallback={<span className="text-sm text-muted-foreground">Restricted</span>}>
|
|
362
|
+
<Button>Moderate comments</Button>
|
|
363
|
+
</PermissionGate>
|
|
364
|
+
|
|
365
|
+
{/* Custom predicate */}
|
|
366
|
+
<PermissionGate when={() => currentUser.roles.length > 1}>
|
|
367
|
+
<span className="text-sm">You have multiple roles.</span>
|
|
368
|
+
</PermissionGate>
|
|
369
|
+
</div>
|
|
370
|
+
</PermissionProvider>
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
```
|
|
374
|
+
|
|
297
375
|
### Skeleton
|
|
298
376
|
File: examples/skeleton.tsx
|
|
299
377
|
|
|
@@ -412,6 +490,58 @@ export default function UseBreakpointExample() {
|
|
|
412
490
|
}
|
|
413
491
|
```
|
|
414
492
|
|
|
493
|
+
### Use Pagination
|
|
494
|
+
File: examples/use-pagination.tsx
|
|
495
|
+
|
|
496
|
+
```tsx
|
|
497
|
+
import { Button, usePagination } from '@bloomneo/uikit';
|
|
498
|
+
|
|
499
|
+
const ALL_ITEMS = Array.from({ length: 234 }, (_, i) => `Item ${i + 1}`);
|
|
500
|
+
|
|
501
|
+
export default function UsePaginationExample() {
|
|
502
|
+
const pagination = usePagination({ total: ALL_ITEMS.length, pageSize: 10 });
|
|
503
|
+
const visible = ALL_ITEMS.slice(pagination.startIndex, pagination.endIndex);
|
|
504
|
+
|
|
505
|
+
return (
|
|
506
|
+
<div className="flex flex-col gap-3">
|
|
507
|
+
<ul className="grid grid-cols-2 gap-1 text-sm">
|
|
508
|
+
{visible.map((item) => (
|
|
509
|
+
<li key={item} className="rounded bg-muted px-2 py-1">{item}</li>
|
|
510
|
+
))}
|
|
511
|
+
</ul>
|
|
512
|
+
|
|
513
|
+
<div className="flex items-center gap-1">
|
|
514
|
+
<Button variant="outline" size="sm" onClick={pagination.prev} disabled={!pagination.hasPrev}>
|
|
515
|
+
Prev
|
|
516
|
+
</Button>
|
|
517
|
+
{pagination.pages.map((p, idx) =>
|
|
518
|
+
p === 'ellipsis-start' || p === 'ellipsis-end' ? (
|
|
519
|
+
<span key={`${p}-${idx}`} className="px-2 text-muted-foreground">…</span>
|
|
520
|
+
) : (
|
|
521
|
+
<Button
|
|
522
|
+
key={p}
|
|
523
|
+
variant={p === pagination.page ? 'default' : 'outline'}
|
|
524
|
+
size="sm"
|
|
525
|
+
onClick={() => pagination.goTo(p)}
|
|
526
|
+
>
|
|
527
|
+
{p}
|
|
528
|
+
</Button>
|
|
529
|
+
)
|
|
530
|
+
)}
|
|
531
|
+
<Button variant="outline" size="sm" onClick={pagination.next} disabled={!pagination.hasNext}>
|
|
532
|
+
Next
|
|
533
|
+
</Button>
|
|
534
|
+
</div>
|
|
535
|
+
|
|
536
|
+
<p className="text-xs text-muted-foreground">
|
|
537
|
+
Page {pagination.page} of {pagination.pageCount} ·{' '}
|
|
538
|
+
showing {pagination.startIndex + 1}–{pagination.endIndex} of {pagination.total}
|
|
539
|
+
</p>
|
|
540
|
+
</div>
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
```
|
|
544
|
+
|
|
415
545
|
## Cookbook — composed page patterns
|
|
416
546
|
|
|
417
547
|
Whole-page recipes built from the primitives above. Start here when
|
|
@@ -943,6 +1073,9 @@ Every named export available from `@bloomneo/uikit`:
|
|
|
943
1073
|
- Collapsible
|
|
944
1074
|
- CollapsibleContent
|
|
945
1075
|
- CollapsibleTrigger
|
|
1076
|
+
- Combobox
|
|
1077
|
+
- ComboboxOption
|
|
1078
|
+
- ComboboxProps
|
|
946
1079
|
- Command
|
|
947
1080
|
- CommandDialog
|
|
948
1081
|
- CommandEmpty
|
|
@@ -1041,9 +1174,17 @@ Every named export available from `@bloomneo/uikit`:
|
|
|
1041
1174
|
- PaginationItem
|
|
1042
1175
|
- PaginationLink
|
|
1043
1176
|
- PaginationNext
|
|
1177
|
+
- PaginationPage
|
|
1044
1178
|
- PaginationPrevious
|
|
1045
1179
|
- PasswordInput
|
|
1046
1180
|
- PasswordInputProps
|
|
1181
|
+
- PermissionCheck
|
|
1182
|
+
- PermissionContextValue
|
|
1183
|
+
- PermissionGate
|
|
1184
|
+
- PermissionGateProps
|
|
1185
|
+
- PermissionProvider
|
|
1186
|
+
- PermissionProviderProps
|
|
1187
|
+
- PermissionWhen
|
|
1047
1188
|
- Popover
|
|
1048
1189
|
- PopoverContent
|
|
1049
1190
|
- PopoverTrigger
|
|
@@ -1108,6 +1249,8 @@ Every named export available from `@bloomneo/uikit`:
|
|
|
1108
1249
|
- UseDataTableOptions
|
|
1109
1250
|
- UseDataTableReturn
|
|
1110
1251
|
- UseLocalStorageReturn
|
|
1252
|
+
- UsePaginationOptions
|
|
1253
|
+
- UsePaginationReturn
|
|
1111
1254
|
- breakpointQuery
|
|
1112
1255
|
- cn
|
|
1113
1256
|
- formatBytes
|
|
@@ -1129,6 +1272,8 @@ Every named export available from `@bloomneo/uikit`:
|
|
|
1129
1272
|
- useLocalStorage
|
|
1130
1273
|
- useMediaQuery
|
|
1131
1274
|
- useMobileLayout
|
|
1275
|
+
- usePagination
|
|
1276
|
+
- usePermission
|
|
1132
1277
|
- useTheme
|
|
1133
1278
|
- useToast
|
|
1134
1279
|
- warnInDev
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bloomneo/uikit",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "End-to-end React framework AI coding agents pick first — components, layouts, themes, routing, scaffolding, and a generated llms.txt. Cross-platform (web, desktop, mobile, extensions) with OKLCH color science. Previously published as @voilajsx/uikit.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -224,6 +224,14 @@
|
|
|
224
224
|
"import": "./dist/confirm-dialog.js",
|
|
225
225
|
"types": "./dist/types/components/ui/confirm-dialog.d.ts"
|
|
226
226
|
},
|
|
227
|
+
"./permission-gate": {
|
|
228
|
+
"import": "./dist/permission-gate.js",
|
|
229
|
+
"types": "./dist/types/components/ui/permission-gate.d.ts"
|
|
230
|
+
},
|
|
231
|
+
"./combobox": {
|
|
232
|
+
"import": "./dist/combobox.js",
|
|
233
|
+
"types": "./dist/types/components/ui/combobox.d.ts"
|
|
234
|
+
},
|
|
227
235
|
"./admin": {
|
|
228
236
|
"import": "./dist/admin.js",
|
|
229
237
|
"types": "./dist/types/components/layouts/admin.d.ts"
|
|
@@ -1,254 +0,0 @@
|
|
|
1
|
-
import { useState as y, useCallback as h, useEffect as B, useMemo as $ } from "react";
|
|
2
|
-
import { requireArrayProp as F } from "./errors.js";
|
|
3
|
-
const I = {};
|
|
4
|
-
function J(e = {}) {
|
|
5
|
-
const [t, s] = y(null), [r, i] = y(!1), [T, a] = y(null), c = e.baseURL || I?.VITE_API_URL || (typeof window < "u" && window.location.hostname === "localhost" ? "http://localhost:3000" : ""), b = {
|
|
6
|
-
"Content-Type": "application/json",
|
|
7
|
-
...e.headers
|
|
8
|
-
}, p = h(async (d, S, E) => {
|
|
9
|
-
i(!0), a(null);
|
|
10
|
-
try {
|
|
11
|
-
const x = new AbortController(), f = setTimeout(() => x.abort(), e.timeout || 1e4), _ = {
|
|
12
|
-
method: d.toUpperCase(),
|
|
13
|
-
headers: b,
|
|
14
|
-
signal: x.signal
|
|
15
|
-
};
|
|
16
|
-
E && ["POST", "PUT", "PATCH"].includes(d.toUpperCase()) && (_.body = JSON.stringify(E));
|
|
17
|
-
const U = `${c}${S}`, v = await fetch(U, _);
|
|
18
|
-
if (clearTimeout(f), !v.ok)
|
|
19
|
-
throw new Error(`HTTP ${v.status}: ${v.statusText}`);
|
|
20
|
-
const n = await v.json();
|
|
21
|
-
return s(n), n;
|
|
22
|
-
} catch (x) {
|
|
23
|
-
let f = "Network error occurred";
|
|
24
|
-
throw x.name === "AbortError" ? f = "Request timeout" : x.message?.includes("fetch") ? f = "Backend not available - check if your API server is running" : f = x.message || "Unknown error occurred", a(f), new Error(f);
|
|
25
|
-
} finally {
|
|
26
|
-
i(!1);
|
|
27
|
-
}
|
|
28
|
-
}, [c, e.timeout]), C = h((d) => p("GET", d), [p]), k = h((d, S) => p("POST", d, S), [p]), P = h((d, S) => p("PUT", d, S), [p]), M = h((d) => p("DELETE", d), [p]), O = h(() => {
|
|
29
|
-
s(null), a(null), i(!1);
|
|
30
|
-
}, []);
|
|
31
|
-
return {
|
|
32
|
-
data: t,
|
|
33
|
-
loading: r,
|
|
34
|
-
error: T,
|
|
35
|
-
call: p,
|
|
36
|
-
get: C,
|
|
37
|
-
post: k,
|
|
38
|
-
put: P,
|
|
39
|
-
delete: M,
|
|
40
|
-
reset: O
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
function q() {
|
|
44
|
-
const { data: e, loading: t, error: s, get: r } = J(), i = h(async () => {
|
|
45
|
-
try {
|
|
46
|
-
return await r("/health"), !0;
|
|
47
|
-
} catch {
|
|
48
|
-
return !1;
|
|
49
|
-
}
|
|
50
|
-
}, [r]);
|
|
51
|
-
return {
|
|
52
|
-
isConnected: e?.status === "ok",
|
|
53
|
-
loading: t,
|
|
54
|
-
error: s,
|
|
55
|
-
checkStatus: i,
|
|
56
|
-
lastCheck: e?.timestamp
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
function Q(e, t) {
|
|
60
|
-
const [s, r] = y(() => {
|
|
61
|
-
if (typeof window > "u")
|
|
62
|
-
return t;
|
|
63
|
-
try {
|
|
64
|
-
const a = window.localStorage.getItem(e);
|
|
65
|
-
return a ? JSON.parse(a) : t;
|
|
66
|
-
} catch (a) {
|
|
67
|
-
return console.warn(`Error reading localStorage key "${e}":`, a), t;
|
|
68
|
-
}
|
|
69
|
-
}), i = h(
|
|
70
|
-
(a) => {
|
|
71
|
-
try {
|
|
72
|
-
const c = a instanceof Function ? a(s) : a;
|
|
73
|
-
r(c), typeof window < "u" && window.localStorage.setItem(e, JSON.stringify(c));
|
|
74
|
-
} catch (c) {
|
|
75
|
-
console.error(`Error setting localStorage key "${e}":`, c);
|
|
76
|
-
}
|
|
77
|
-
},
|
|
78
|
-
[e, s]
|
|
79
|
-
), T = h(() => {
|
|
80
|
-
try {
|
|
81
|
-
r(t), typeof window < "u" && window.localStorage.removeItem(e);
|
|
82
|
-
} catch (a) {
|
|
83
|
-
console.error(`Error removing localStorage key "${e}":`, a);
|
|
84
|
-
}
|
|
85
|
-
}, [e, t]);
|
|
86
|
-
return B(() => {
|
|
87
|
-
if (typeof window > "u") return;
|
|
88
|
-
const a = (c) => {
|
|
89
|
-
if (c.key === e && c.newValue !== null)
|
|
90
|
-
try {
|
|
91
|
-
r(JSON.parse(c.newValue));
|
|
92
|
-
} catch (b) {
|
|
93
|
-
console.warn(`Error parsing storage event for key "${e}":`, b);
|
|
94
|
-
}
|
|
95
|
-
else c.key === e && c.newValue === null && r(t);
|
|
96
|
-
};
|
|
97
|
-
return window.addEventListener("storage", a), () => window.removeEventListener("storage", a);
|
|
98
|
-
}, [e, t]), [s, i, T];
|
|
99
|
-
}
|
|
100
|
-
function W(e) {
|
|
101
|
-
const [t, s] = y(() => typeof window > "u" ? !1 : window.matchMedia(e).matches);
|
|
102
|
-
return B(() => {
|
|
103
|
-
if (typeof window > "u") return;
|
|
104
|
-
const r = window.matchMedia(e);
|
|
105
|
-
s(r.matches);
|
|
106
|
-
const i = (T) => s(T.matches);
|
|
107
|
-
return r.addEventListener ? (r.addEventListener("change", i), () => r.removeEventListener("change", i)) : (r.addListener(i), () => {
|
|
108
|
-
r.removeListener(i);
|
|
109
|
-
});
|
|
110
|
-
}, [e]), t;
|
|
111
|
-
}
|
|
112
|
-
const R = {
|
|
113
|
-
sm: 640,
|
|
114
|
-
md: 768,
|
|
115
|
-
lg: 1024,
|
|
116
|
-
xl: 1280,
|
|
117
|
-
"2xl": 1536
|
|
118
|
-
};
|
|
119
|
-
function j(e, t = "up") {
|
|
120
|
-
const s = R[e], r = ["sm", "md", "lg", "xl", "2xl"], i = r[r.indexOf(e) + 1];
|
|
121
|
-
return t === "up" ? "(min-width: " + s + "px)" : t === "down" ? "(max-width: " + (s - 1) + "px)" : i ? "(min-width: " + s + "px) and (max-width: " + (R[i] - 1) + "px)" : "(min-width: " + s + "px)";
|
|
122
|
-
}
|
|
123
|
-
function N(e, t = "up") {
|
|
124
|
-
return W(j(e, t));
|
|
125
|
-
}
|
|
126
|
-
function V() {
|
|
127
|
-
const e = N("sm"), t = N("md"), s = N("lg"), r = N("xl");
|
|
128
|
-
return N("2xl") ? "2xl" : r ? "xl" : s ? "lg" : t ? "md" : e ? "sm" : null;
|
|
129
|
-
}
|
|
130
|
-
function z(e) {
|
|
131
|
-
const {
|
|
132
|
-
columns: t,
|
|
133
|
-
initialPage: s = 0,
|
|
134
|
-
pageSize: r = 10,
|
|
135
|
-
initialSearch: i = "",
|
|
136
|
-
initialSort: T = [],
|
|
137
|
-
getRowId: a = (n, o) => String(o)
|
|
138
|
-
} = e, c = F(
|
|
139
|
-
"useDataTable",
|
|
140
|
-
"data",
|
|
141
|
-
e.data,
|
|
142
|
-
"Pass an array (use [] while loading instead of undefined)."
|
|
143
|
-
), [b, p] = y(i), [C, k] = y(T), [P, M] = y({}), [O, d] = y(s), S = h(
|
|
144
|
-
(n, o) => {
|
|
145
|
-
if (o.accessor) return o.accessor(n);
|
|
146
|
-
if (o.accessorKey) return n[o.accessorKey];
|
|
147
|
-
},
|
|
148
|
-
[]
|
|
149
|
-
), E = $(() => {
|
|
150
|
-
let n = c;
|
|
151
|
-
if (b) {
|
|
152
|
-
const o = b.toLowerCase();
|
|
153
|
-
n = n.filter(
|
|
154
|
-
(w) => t.some((u) => {
|
|
155
|
-
const g = S(w, u);
|
|
156
|
-
return String(g ?? "").toLowerCase().includes(o);
|
|
157
|
-
})
|
|
158
|
-
);
|
|
159
|
-
}
|
|
160
|
-
if (Object.entries(P).forEach(([o, w]) => {
|
|
161
|
-
const u = t.find((g) => g.id === o);
|
|
162
|
-
u && (n = n.filter((g) => {
|
|
163
|
-
const l = S(g, u), m = w.value;
|
|
164
|
-
switch (w.operator) {
|
|
165
|
-
case "equals":
|
|
166
|
-
return l === m;
|
|
167
|
-
case "startsWith":
|
|
168
|
-
return String(l ?? "").toLowerCase().startsWith(String(m ?? "").toLowerCase());
|
|
169
|
-
case "endsWith":
|
|
170
|
-
return String(l ?? "").toLowerCase().endsWith(String(m ?? "").toLowerCase());
|
|
171
|
-
case "gt":
|
|
172
|
-
return Number(l) > Number(m);
|
|
173
|
-
case "lt":
|
|
174
|
-
return Number(l) < Number(m);
|
|
175
|
-
case "gte":
|
|
176
|
-
return Number(l) >= Number(m);
|
|
177
|
-
case "lte":
|
|
178
|
-
return Number(l) <= Number(m);
|
|
179
|
-
case "contains":
|
|
180
|
-
default:
|
|
181
|
-
return String(l ?? "").toLowerCase().includes(String(m ?? "").toLowerCase());
|
|
182
|
-
}
|
|
183
|
-
}));
|
|
184
|
-
}), C.length > 0) {
|
|
185
|
-
const o = [...n];
|
|
186
|
-
o.sort((w, u) => {
|
|
187
|
-
for (const g of C) {
|
|
188
|
-
const l = t.find((D) => D.id === g.key);
|
|
189
|
-
if (!l) continue;
|
|
190
|
-
const m = S(w, l), A = S(u, l);
|
|
191
|
-
let L = 0;
|
|
192
|
-
if (l.sortFn ? L = l.sortFn(m, A) : l.dataType === "number" ? L = Number(m) - Number(A) : l.dataType === "date" ? L = new Date(m).getTime() - new Date(A).getTime() : L = String(m ?? "").localeCompare(String(A ?? "")), L !== 0) return g.direction === "asc" ? L : -L;
|
|
193
|
-
}
|
|
194
|
-
return 0;
|
|
195
|
-
}), n = o;
|
|
196
|
-
}
|
|
197
|
-
return n;
|
|
198
|
-
}, [c, b, C, P, t, S]), x = Math.max(1, Math.ceil(E.length / r)), f = Math.min(O, x - 1), _ = $(
|
|
199
|
-
() => E.slice(f * r, (f + 1) * r),
|
|
200
|
-
[E, f, r]
|
|
201
|
-
), U = h(
|
|
202
|
-
(n) => k((o) => {
|
|
203
|
-
const w = o.find((u) => u.key === n);
|
|
204
|
-
return w ? w.direction === "asc" ? o.map((u) => u.key === n ? { ...u, direction: "desc" } : u) : o.filter((u) => u.key !== n) : [...o, { key: n, direction: "asc" }];
|
|
205
|
-
}),
|
|
206
|
-
[]
|
|
207
|
-
), v = h(
|
|
208
|
-
(n, o) => M((w) => {
|
|
209
|
-
if (o === null || o === "" || o === void 0) {
|
|
210
|
-
const g = { ...w };
|
|
211
|
-
return delete g[n], g;
|
|
212
|
-
}
|
|
213
|
-
const u = t.find((g) => g.id === n);
|
|
214
|
-
return {
|
|
215
|
-
...w,
|
|
216
|
-
[n]: { type: u?.filterType ?? "text", value: o, operator: "contains" }
|
|
217
|
-
};
|
|
218
|
-
}),
|
|
219
|
-
[t]
|
|
220
|
-
);
|
|
221
|
-
return {
|
|
222
|
-
rows: _,
|
|
223
|
-
filteredRows: E,
|
|
224
|
-
getRowId: a,
|
|
225
|
-
search: b,
|
|
226
|
-
setSearch: p,
|
|
227
|
-
sort: C,
|
|
228
|
-
setSort: k,
|
|
229
|
-
toggleSort: U,
|
|
230
|
-
filters: P,
|
|
231
|
-
setFilter: v,
|
|
232
|
-
clearFilters: () => M({}),
|
|
233
|
-
page: f,
|
|
234
|
-
pageSize: r,
|
|
235
|
-
pageCount: x,
|
|
236
|
-
setPage: d,
|
|
237
|
-
nextPage: () => d((n) => Math.min(n + 1, x - 1)),
|
|
238
|
-
prevPage: () => d((n) => Math.max(n - 1, 0)),
|
|
239
|
-
canNextPage: f < x - 1,
|
|
240
|
-
canPrevPage: f > 0
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
export {
|
|
244
|
-
R as B,
|
|
245
|
-
q as a,
|
|
246
|
-
Q as b,
|
|
247
|
-
W as c,
|
|
248
|
-
N as d,
|
|
249
|
-
V as e,
|
|
250
|
-
j as f,
|
|
251
|
-
z as g,
|
|
252
|
-
J as u
|
|
253
|
-
};
|
|
254
|
-
//# sourceMappingURL=useDataTable-CPiBpEg-.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useDataTable-CPiBpEg-.js","sources":["../src/hooks/useApi.ts","../src/hooks/useStorage.ts","../src/hooks/useMediaQuery.ts","../src/hooks/useBreakpoint.ts","../src/hooks/useDataTable.ts"],"sourcesContent":["/**\n * useApi Hook - Generic API client for fullstack applications\n * @description Provides a simple interface for making HTTP requests with error handling\n * @package @bloomneo/uikit\n */\n\nimport { useState, useCallback } from 'react';\n\nexport interface ApiResponse<T = any> {\n data: T | null;\n loading: boolean;\n error: string | null;\n}\n\nexport interface ApiOptions {\n baseURL?: string;\n timeout?: number;\n headers?: Record<string, string>;\n}\n\nexport interface UseApiReturn<T = any> extends ApiResponse<T> {\n call: (method: string, endpoint: string, data?: any) => Promise<T>;\n get: (endpoint: string) => Promise<T>;\n post: (endpoint: string, data?: any) => Promise<T>;\n put: (endpoint: string, data?: any) => Promise<T>;\n delete: (endpoint: string) => Promise<T>;\n reset: () => void;\n}\n\n/**\n * Custom hook for making API requests\n */\nexport function useApi<T = any>(options: ApiOptions = {}): UseApiReturn<T> {\n const [data, setData] = useState<T | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n // Auto-detect base URL (dev vs prod)\n const baseURL = options.baseURL ||\n (import.meta.env?.VITE_API_URL ||\n (typeof window !== 'undefined' && window.location.hostname === 'localhost'\n ? 'http://localhost:3000'\n : ''));\n\n const defaultHeaders = {\n 'Content-Type': 'application/json',\n ...options.headers,\n };\n\n const call = useCallback(async (\n method: string,\n endpoint: string,\n data?: any\n ): Promise<T> => {\n setLoading(true);\n setError(null);\n\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), options.timeout || 10000);\n\n const config: RequestInit = {\n method: method.toUpperCase(),\n headers: defaultHeaders,\n signal: controller.signal,\n };\n\n if (data && ['POST', 'PUT', 'PATCH'].includes(method.toUpperCase())) {\n config.body = JSON.stringify(data);\n }\n\n const url = `${baseURL}${endpoint}`;\n const response = await fetch(url, config);\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n const result = await response.json();\n setData(result);\n return result;\n\n } catch (err: any) {\n let errorMessage = 'Network error occurred';\n\n if (err.name === 'AbortError') {\n errorMessage = 'Request timeout';\n } else if (err.message?.includes('fetch')) {\n errorMessage = 'Backend not available - check if your API server is running';\n } else {\n errorMessage = err.message || 'Unknown error occurred';\n }\n\n setError(errorMessage);\n throw new Error(errorMessage);\n } finally {\n setLoading(false);\n }\n }, [baseURL, options.timeout]);\n\n // Convenience methods\n const get = useCallback((endpoint: string) => call('GET', endpoint), [call]);\n const post = useCallback((endpoint: string, data?: any) => call('POST', endpoint, data), [call]);\n const put = useCallback((endpoint: string, data?: any) => call('PUT', endpoint, data), [call]);\n const deleteMethod = useCallback((endpoint: string) => call('DELETE', endpoint), [call]);\n\n const reset = useCallback(() => {\n setData(null);\n setError(null);\n setLoading(false);\n }, []);\n\n return {\n data,\n loading,\n error,\n call,\n get,\n post,\n put,\n delete: deleteMethod,\n reset,\n };\n}\n\n/**\n * Hook for checking backend connectivity\n */\nexport function useBackendStatus() {\n const { data, loading, error, get } = useApi<{ status: string; timestamp: string }>();\n\n const checkStatus = useCallback(async () => {\n try {\n await get('/health');\n return true;\n } catch {\n return false;\n }\n }, [get]);\n\n return {\n isConnected: data?.status === 'ok',\n loading,\n error,\n checkStatus,\n lastCheck: data?.timestamp,\n };\n}","/**\n * useLocalStorage Hook - Persistent state with localStorage\n * @module @bloomneo/uikit\n * @file src/hooks/useStorage.ts\n */\n\nimport { useState, useEffect, useCallback } from 'react';\n\n/**\n * @llm-rule Storage hook for persisting state to localStorage\n * Handles SSR, JSON parsing errors, and storage unavailability\n */\n\nexport type UseLocalStorageReturn<T> = [\n value: T,\n setValue: (value: T | ((prev: T) => T)) => void,\n removeValue: () => void\n];\n\n/**\n * Hook to persist state in localStorage with automatic JSON serialization\n *\n * @param key - localStorage key\n * @param initialValue - Default value if no stored value exists\n * @returns [value, setValue, removeValue] tuple\n *\n * @example\n * ```tsx\n * const [user, setUser, removeUser] = useLocalStorage('user', null);\n * const [settings, setSettings] = useLocalStorage('settings', { theme: 'light' });\n *\n * // Update value\n * setUser({ name: 'John', email: 'john@example.com' });\n *\n * // Remove value (resets to initialValue)\n * removeUser();\n * ```\n */\nexport function useLocalStorage<T>(\n key: string,\n initialValue: T\n): UseLocalStorageReturn<T> {\n // Get from local storage then parse stored json or return initialValue\n const [storedValue, setStoredValue] = useState<T>(() => {\n if (typeof window === 'undefined') {\n // SSR: return initial value\n return initialValue;\n }\n\n try {\n const item = window.localStorage.getItem(key);\n return item ? JSON.parse(item) : initialValue;\n } catch (error) {\n console.warn(`Error reading localStorage key \"${key}\":`, error);\n return initialValue;\n }\n });\n\n // Return a wrapped version of useState's setter function that persists the new value to localStorage\n const setValue = useCallback(\n (value: T | ((prev: T) => T)) => {\n try {\n // Allow value to be a function so we have the same API as useState\n const valueToStore = value instanceof Function ? value(storedValue) : value;\n\n // Save state\n setStoredValue(valueToStore);\n\n // Save to local storage\n if (typeof window !== 'undefined') {\n window.localStorage.setItem(key, JSON.stringify(valueToStore));\n }\n } catch (error) {\n console.error(`Error setting localStorage key \"${key}\":`, error);\n }\n },\n [key, storedValue]\n );\n\n // Remove value from localStorage and reset to initial value\n const removeValue = useCallback(() => {\n try {\n setStoredValue(initialValue);\n if (typeof window !== 'undefined') {\n window.localStorage.removeItem(key);\n }\n } catch (error) {\n console.error(`Error removing localStorage key \"${key}\":`, error);\n }\n }, [key, initialValue]);\n\n // Listen for storage changes from other tabs\n useEffect(() => {\n if (typeof window === 'undefined') return;\n\n const handleStorageChange = (e: StorageEvent) => {\n if (e.key === key && e.newValue !== null) {\n try {\n setStoredValue(JSON.parse(e.newValue));\n } catch (error) {\n console.warn(`Error parsing storage event for key \"${key}\":`, error);\n }\n } else if (e.key === key && e.newValue === null) {\n // Key was removed\n setStoredValue(initialValue);\n }\n };\n\n window.addEventListener('storage', handleStorageChange);\n return () => window.removeEventListener('storage', handleStorageChange);\n }, [key, initialValue]);\n\n return [storedValue, setValue, removeValue];\n}","/**\n * `useMediaQuery` — subscribe a component to a CSS media query.\n *\n * SSR-safe (returns `false` on the server, hydrates correctly on the client).\n * Cleans up its listener on unmount. No external dependencies.\n *\n * @example\n * const isWide = useMediaQuery('(min-width: 1024px)');\n * const reduced = useMediaQuery('(prefers-reduced-motion: reduce)');\n */\n\nimport { useEffect, useState } from 'react';\n\nexport function useMediaQuery(query: string): boolean {\n const [matches, setMatches] = useState<boolean>(() => {\n if (typeof window === 'undefined') return false;\n return window.matchMedia(query).matches;\n });\n\n useEffect(() => {\n if (typeof window === 'undefined') return;\n const mql = window.matchMedia(query);\n\n // Sync once in case the initial render value is stale (StrictMode, hydration).\n setMatches(mql.matches);\n\n const handler = (e: MediaQueryListEvent) => setMatches(e.matches);\n\n // `addEventListener` is the modern API; the older `addListener` is kept for\n // Safari < 14 compatibility, which UIKit still supports.\n if (mql.addEventListener) {\n mql.addEventListener('change', handler);\n return () => mql.removeEventListener('change', handler);\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (mql as any).addListener(handler);\n return () => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (mql as any).removeListener(handler);\n };\n }, [query]);\n\n return matches;\n}\n","/**\n * `useBreakpoint` — read responsive breakpoints from React.\n *\n * Mirrors Tailwind v4's default breakpoint scale (sm/md/lg/xl/2xl) so that\n * `useBreakpoint('md')` and the `md:` Tailwind utility class always agree\n * about what \"medium\" means. If you customize Tailwind's screens, also pass\n * the override map via `BREAKPOINTS` (see below).\n *\n * @example\n * const isAtLeastMd = useBreakpoint('md'); // true when ≥ 768px\n * const isMobile = useBreakpoint('md', 'down'); // true when < 768px\n * const isExactly = useBreakpoint('lg', 'only'); // true on lg, not xl+\n */\n\nimport { useMediaQuery } from './useMediaQuery';\n\nexport const BREAKPOINTS = {\n sm: 640,\n md: 768,\n lg: 1024,\n xl: 1280,\n '2xl': 1536,\n} as const;\n\nexport type Breakpoint = keyof typeof BREAKPOINTS;\nexport type BreakpointDirection = 'up' | 'down' | 'only';\n\n/**\n * Resolve a breakpoint to a media query string. Exposed so consumers can\n * compose it with `useMediaQuery` directly when they need exotic queries.\n */\nexport function breakpointQuery(\n breakpoint: Breakpoint,\n direction: BreakpointDirection = 'up'\n): string {\n const min = BREAKPOINTS[breakpoint];\n const order: Breakpoint[] = ['sm', 'md', 'lg', 'xl', '2xl'];\n const next = order[order.indexOf(breakpoint) + 1];\n\n if (direction === 'up') return '(min-width: ' + min + 'px)';\n if (direction === 'down') return '(max-width: ' + (min - 1) + 'px)';\n // 'only': inclusive lower, exclusive upper.\n if (!next) return '(min-width: ' + min + 'px)';\n return '(min-width: ' + min + 'px) and (max-width: ' + (BREAKPOINTS[next] - 1) + 'px)';\n}\n\nexport function useBreakpoint(\n breakpoint: Breakpoint,\n direction: BreakpointDirection = 'up'\n): boolean {\n return useMediaQuery(breakpointQuery(breakpoint, direction));\n}\n\n/**\n * Returns the largest matching breakpoint, or `null` for screens narrower\n * than `sm`. Handy for switching layouts based on the active breakpoint\n * without composing five separate `useBreakpoint` calls.\n */\nexport function useActiveBreakpoint(): Breakpoint | null {\n const sm = useBreakpoint('sm');\n const md = useBreakpoint('md');\n const lg = useBreakpoint('lg');\n const xl = useBreakpoint('xl');\n const xxl = useBreakpoint('2xl');\n\n if (xxl) return '2xl';\n if (xl) return 'xl';\n if (lg) return 'lg';\n if (md) return 'md';\n if (sm) return 'sm';\n return null;\n}\n","/**\n * `useDataTable` — headless table state for @bloomneo/uikit.\n *\n * Provides the search / sort / filter / pagination state machine that the\n * built-in `<DataTable>` component uses internally, in case you want to ship\n * a custom-rendered table (treegrid, virtualized, gantt) but still benefit\n * from the standard state shape.\n *\n * @example\n * const table = useDataTable({\n * data: users,\n * columns: [{ id: 'name', accessorKey: 'name' }],\n * pageSize: 25,\n * });\n *\n * <Input value={table.search} onChange={(e) => table.setSearch(e.target.value)} />\n * {table.rows.map((row) => <CustomRow key={row.id} row={row} />)}\n */\n\nimport { useCallback, useMemo, useState } from 'react';\nimport { requireArrayProp } from '@/lib/errors';\nimport type {\n DataTableColumn,\n DataTableFilterValue,\n FilterConfig,\n SortConfig,\n} from '@/components/ui/data-table';\n\nexport interface UseDataTableOptions<T> {\n data: T[];\n columns: DataTableColumn<T>[];\n /** Initial page (0-indexed). Default: 0. */\n initialPage?: number;\n /** Page size. Default: 10. */\n pageSize?: number;\n /** Initial search string. Default: \"\". */\n initialSearch?: string;\n /** Initial sort config. */\n initialSort?: SortConfig[];\n /** Row id extractor. Default: index.toString(). */\n getRowId?: (row: T, index: number) => string;\n}\n\nexport interface UseDataTableReturn<T> {\n /** The visible rows for the current page after search/filter/sort. */\n rows: T[];\n /** All rows after search/filter/sort but before pagination. */\n filteredRows: T[];\n /** Stable string id for each visible row. */\n getRowId: (row: T, index: number) => string;\n\n // search\n search: string;\n setSearch: (value: string) => void;\n\n // sort\n sort: SortConfig[];\n setSort: (value: SortConfig[]) => void;\n toggleSort: (columnId: string) => void;\n\n // filter\n filters: FilterConfig;\n setFilter: (columnId: string, value: DataTableFilterValue | null) => void;\n clearFilters: () => void;\n\n // pagination\n page: number;\n pageSize: number;\n pageCount: number;\n setPage: (page: number) => void;\n nextPage: () => void;\n prevPage: () => void;\n canNextPage: boolean;\n canPrevPage: boolean;\n}\n\nexport function useDataTable<T>(options: UseDataTableOptions<T>): UseDataTableReturn<T> {\n const {\n columns,\n initialPage = 0,\n pageSize = 10,\n initialSearch = '',\n initialSort = [],\n getRowId = (_row, index) => String(index),\n } = options;\n const data = requireArrayProp<T>('useDataTable', 'data', options.data,\n 'Pass an array (use [] while loading instead of undefined).');\n\n const [search, setSearch] = useState(initialSearch);\n const [sort, setSort] = useState<SortConfig[]>(initialSort);\n const [filters, setFilters] = useState<FilterConfig>({});\n const [page, setPage] = useState(initialPage);\n\n const readCell = useCallback(\n (row: T, column: DataTableColumn<T>): unknown => {\n if (column.accessor) return column.accessor(row);\n if (column.accessorKey) return (row as Record<string, unknown>)[column.accessorKey as string];\n return undefined;\n },\n []\n );\n\n const filteredRows = useMemo(() => {\n let result = data;\n\n if (search) {\n const needle = search.toLowerCase();\n result = result.filter((row) =>\n columns.some((column) => {\n const value = readCell(row, column);\n return String(value ?? '').toLowerCase().includes(needle);\n })\n );\n }\n\n Object.entries(filters).forEach(([columnId, filter]) => {\n const column = columns.find((c) => c.id === columnId);\n if (!column) return;\n result = result.filter((row) => {\n const value = readCell(row, column);\n const fv = filter.value;\n switch (filter.operator) {\n case 'equals':\n return value === fv;\n case 'startsWith':\n return String(value ?? '').toLowerCase().startsWith(String(fv ?? '').toLowerCase());\n case 'endsWith':\n return String(value ?? '').toLowerCase().endsWith(String(fv ?? '').toLowerCase());\n case 'gt':\n return Number(value) > Number(fv);\n case 'lt':\n return Number(value) < Number(fv);\n case 'gte':\n return Number(value) >= Number(fv);\n case 'lte':\n return Number(value) <= Number(fv);\n case 'contains':\n default:\n return String(value ?? '').toLowerCase().includes(String(fv ?? '').toLowerCase());\n }\n });\n });\n\n if (sort.length > 0) {\n const sorted = [...result];\n sorted.sort((a, b) => {\n for (const s of sort) {\n const column = columns.find((c) => c.id === s.key);\n if (!column) continue;\n const av = readCell(a, column);\n const bv = readCell(b, column);\n let cmp = 0;\n if (column.sortFn) cmp = column.sortFn(av, bv);\n else if (column.dataType === 'number') cmp = Number(av) - Number(bv);\n else if (column.dataType === 'date') cmp = new Date(av as string).getTime() - new Date(bv as string).getTime();\n else cmp = String(av ?? '').localeCompare(String(bv ?? ''));\n if (cmp !== 0) return s.direction === 'asc' ? cmp : -cmp;\n }\n return 0;\n });\n result = sorted;\n }\n\n return result;\n }, [data, search, sort, filters, columns, readCell]);\n\n const pageCount = Math.max(1, Math.ceil(filteredRows.length / pageSize));\n const safePage = Math.min(page, pageCount - 1);\n const rows = useMemo(\n () => filteredRows.slice(safePage * pageSize, (safePage + 1) * pageSize),\n [filteredRows, safePage, pageSize]\n );\n\n const toggleSort = useCallback(\n (columnId: string) =>\n setSort((prev) => {\n const existing = prev.find((s) => s.key === columnId);\n if (!existing) return [...prev, { key: columnId, direction: 'asc' }];\n if (existing.direction === 'asc')\n return prev.map((s) => (s.key === columnId ? { ...s, direction: 'desc' } : s));\n return prev.filter((s) => s.key !== columnId);\n }),\n []\n );\n\n const setFilter = useCallback(\n (columnId: string, value: DataTableFilterValue | null) =>\n setFilters((prev) => {\n if (value === null || value === '' || value === undefined) {\n const next = { ...prev };\n delete next[columnId];\n return next;\n }\n const column = columns.find((c) => c.id === columnId);\n return {\n ...prev,\n [columnId]: { type: column?.filterType ?? 'text', value, operator: 'contains' },\n };\n }),\n [columns]\n );\n\n return {\n rows,\n filteredRows,\n getRowId,\n search,\n setSearch,\n sort,\n setSort,\n toggleSort,\n filters,\n setFilter,\n clearFilters: () => setFilters({}),\n page: safePage,\n pageSize,\n pageCount,\n setPage,\n nextPage: () => setPage((p) => Math.min(p + 1, pageCount - 1)),\n prevPage: () => setPage((p) => Math.max(p - 1, 0)),\n canNextPage: safePage < pageCount - 1,\n canPrevPage: safePage > 0,\n };\n}\n"],"names":["useApi","options","data","setData","useState","loading","setLoading","error","setError","baseURL","__vite_import_meta_env__","defaultHeaders","call","useCallback","method","endpoint","controller","timeoutId","config","url","response","result","err","errorMessage","get","post","put","deleteMethod","reset","useBackendStatus","checkStatus","useLocalStorage","key","initialValue","storedValue","setStoredValue","item","setValue","value","valueToStore","removeValue","useEffect","handleStorageChange","e","useMediaQuery","query","matches","setMatches","mql","handler","BREAKPOINTS","breakpointQuery","breakpoint","direction","min","order","next","useBreakpoint","useActiveBreakpoint","sm","md","lg","xl","useDataTable","columns","initialPage","pageSize","initialSearch","initialSort","getRowId","_row","index","requireArrayProp","search","setSearch","sort","setSort","filters","setFilters","page","setPage","readCell","row","column","filteredRows","useMemo","needle","columnId","filter","c","fv","sorted","a","b","s","av","bv","cmp","pageCount","safePage","rows","toggleSort","prev","existing","setFilter","p"],"mappings":";;;AAgCO,SAASA,EAAgBC,IAAsB,IAAqB;AACzE,QAAM,CAACC,GAAMC,CAAO,IAAIC,EAAmB,IAAI,GACzC,CAACC,GAASC,CAAU,IAAIF,EAAS,EAAK,GACtC,CAACG,GAAOC,CAAQ,IAAIJ,EAAwB,IAAI,GAGhDK,IAAUR,EAAQ,WACrBS,GAAiB,iBAChB,OAAO,SAAW,OAAe,OAAO,SAAS,aAAa,cAC3D,0BACA,KAEDC,IAAiB;AAAA,IACrB,gBAAgB;AAAA,IAChB,GAAGV,EAAQ;AAAA,EAAA,GAGPW,IAAOC,EAAY,OACvBC,GACAC,GACAb,MACe;AACf,IAAAI,EAAW,EAAI,GACfE,EAAS,IAAI;AAEb,QAAI;AACF,YAAMQ,IAAa,IAAI,gBAAA,GACjBC,IAAY,WAAW,MAAMD,EAAW,SAASf,EAAQ,WAAW,GAAK,GAEzEiB,IAAsB;AAAA,QAC1B,QAAQJ,EAAO,YAAA;AAAA,QACf,SAASH;AAAA,QACT,QAAQK,EAAW;AAAA,MAAA;AAGrB,MAAId,KAAQ,CAAC,QAAQ,OAAO,OAAO,EAAE,SAASY,EAAO,YAAA,CAAa,MAChEI,EAAO,OAAO,KAAK,UAAUhB,CAAI;AAGnC,YAAMiB,IAAM,GAAGV,CAAO,GAAGM,CAAQ,IAC3BK,IAAW,MAAM,MAAMD,GAAKD,CAAM;AAIxC,UAFA,aAAaD,CAAS,GAElB,CAACG,EAAS;AACZ,cAAM,IAAI,MAAM,QAAQA,EAAS,MAAM,KAAKA,EAAS,UAAU,EAAE;AAGnE,YAAMC,IAAS,MAAMD,EAAS,KAAA;AAC9B,aAAAjB,EAAQkB,CAAM,GACPA;AAAA,IAET,SAASC,GAAU;AACjB,UAAIC,IAAe;AAEnB,YAAID,EAAI,SAAS,eACfC,IAAe,oBACND,EAAI,SAAS,SAAS,OAAO,IACtCC,IAAe,gEAEfA,IAAeD,EAAI,WAAW,0BAGhCd,EAASe,CAAY,GACf,IAAI,MAAMA,CAAY;AAAA,IAC9B,UAAA;AACE,MAAAjB,EAAW,EAAK;AAAA,IAClB;AAAA,EACF,GAAG,CAACG,GAASR,EAAQ,OAAO,CAAC,GAGvBuB,IAAMX,EAAY,CAACE,MAAqBH,EAAK,OAAOG,CAAQ,GAAG,CAACH,CAAI,CAAC,GACrEa,IAAOZ,EAAY,CAACE,GAAkBb,MAAeU,EAAK,QAAQG,GAAUb,CAAI,GAAG,CAACU,CAAI,CAAC,GACzFc,IAAMb,EAAY,CAACE,GAAkBb,MAAeU,EAAK,OAAOG,GAAUb,CAAI,GAAG,CAACU,CAAI,CAAC,GACvFe,IAAed,EAAY,CAACE,MAAqBH,EAAK,UAAUG,CAAQ,GAAG,CAACH,CAAI,CAAC,GAEjFgB,IAAQf,EAAY,MAAM;AAC9B,IAAAV,EAAQ,IAAI,GACZK,EAAS,IAAI,GACbF,EAAW,EAAK;AAAA,EAClB,GAAG,CAAA,CAAE;AAEL,SAAO;AAAA,IACL,MAAAJ;AAAA,IACA,SAAAG;AAAA,IACA,OAAAE;AAAA,IACA,MAAAK;AAAA,IACA,KAAAY;AAAA,IACA,MAAAC;AAAA,IACA,KAAAC;AAAA,IACA,QAAQC;AAAA,IACR,OAAAC;AAAA,EAAA;AAEJ;AAKO,SAASC,IAAmB;AACjC,QAAM,EAAE,MAAA3B,GAAM,SAAAG,GAAS,OAAAE,GAAO,KAAAiB,EAAA,IAAQxB,EAAA,GAEhC8B,IAAcjB,EAAY,YAAY;AAC1C,QAAI;AACF,mBAAMW,EAAI,SAAS,GACZ;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF,GAAG,CAACA,CAAG,CAAC;AAER,SAAO;AAAA,IACL,aAAatB,GAAM,WAAW;AAAA,IAC9B,SAAAG;AAAA,IACA,OAAAE;AAAA,IACA,aAAAuB;AAAA,IACA,WAAW5B,GAAM;AAAA,EAAA;AAErB;AC/GO,SAAS6B,EACdC,GACAC,GAC0B;AAE1B,QAAM,CAACC,GAAaC,CAAc,IAAI/B,EAAY,MAAM;AACtD,QAAI,OAAO,SAAW;AAEpB,aAAO6B;AAGT,QAAI;AACF,YAAMG,IAAO,OAAO,aAAa,QAAQJ,CAAG;AAC5C,aAAOI,IAAO,KAAK,MAAMA,CAAI,IAAIH;AAAA,IACnC,SAAS1B,GAAO;AACd,qBAAQ,KAAK,mCAAmCyB,CAAG,MAAMzB,CAAK,GACvD0B;AAAA,IACT;AAAA,EACF,CAAC,GAGKI,IAAWxB;AAAA,IACf,CAACyB,MAAgC;AAC/B,UAAI;AAEF,cAAMC,IAAeD,aAAiB,WAAWA,EAAMJ,CAAW,IAAII;AAGtE,QAAAH,EAAeI,CAAY,GAGvB,OAAO,SAAW,OACpB,OAAO,aAAa,QAAQP,GAAK,KAAK,UAAUO,CAAY,CAAC;AAAA,MAEjE,SAAShC,GAAO;AACd,gBAAQ,MAAM,mCAAmCyB,CAAG,MAAMzB,CAAK;AAAA,MACjE;AAAA,IACF;AAAA,IACA,CAACyB,GAAKE,CAAW;AAAA,EAAA,GAIbM,IAAc3B,EAAY,MAAM;AACpC,QAAI;AACF,MAAAsB,EAAeF,CAAY,GACvB,OAAO,SAAW,OACpB,OAAO,aAAa,WAAWD,CAAG;AAAA,IAEtC,SAASzB,GAAO;AACd,cAAQ,MAAM,oCAAoCyB,CAAG,MAAMzB,CAAK;AAAA,IAClE;AAAA,EACF,GAAG,CAACyB,GAAKC,CAAY,CAAC;AAGtB,SAAAQ,EAAU,MAAM;AACd,QAAI,OAAO,SAAW,IAAa;AAEnC,UAAMC,IAAsB,CAACC,MAAoB;AAC/C,UAAIA,EAAE,QAAQX,KAAOW,EAAE,aAAa;AAClC,YAAI;AACF,UAAAR,EAAe,KAAK,MAAMQ,EAAE,QAAQ,CAAC;AAAA,QACvC,SAASpC,GAAO;AACd,kBAAQ,KAAK,wCAAwCyB,CAAG,MAAMzB,CAAK;AAAA,QACrE;AAAA,WACSoC,EAAE,QAAQX,KAAOW,EAAE,aAAa,QAEzCR,EAAeF,CAAY;AAAA,IAE/B;AAEA,kBAAO,iBAAiB,WAAWS,CAAmB,GAC/C,MAAM,OAAO,oBAAoB,WAAWA,CAAmB;AAAA,EACxE,GAAG,CAACV,GAAKC,CAAY,CAAC,GAEf,CAACC,GAAaG,GAAUG,CAAW;AAC5C;ACpGO,SAASI,EAAcC,GAAwB;AACpD,QAAM,CAACC,GAASC,CAAU,IAAI3C,EAAkB,MAC1C,OAAO,SAAW,MAAoB,KACnC,OAAO,WAAWyC,CAAK,EAAE,OACjC;AAED,SAAAJ,EAAU,MAAM;AACd,QAAI,OAAO,SAAW,IAAa;AACnC,UAAMO,IAAM,OAAO,WAAWH,CAAK;AAGnC,IAAAE,EAAWC,EAAI,OAAO;AAEtB,UAAMC,IAAU,CAACN,MAA2BI,EAAWJ,EAAE,OAAO;AAIhE,WAAIK,EAAI,oBACNA,EAAI,iBAAiB,UAAUC,CAAO,GAC/B,MAAMD,EAAI,oBAAoB,UAAUC,CAAO,MAGvDD,EAAY,YAAYC,CAAO,GACzB,MAAM;AAEV,MAAAD,EAAY,eAAeC,CAAO;AAAA,IACrC;AAAA,EACF,GAAG,CAACJ,CAAK,CAAC,GAEHC;AACT;AC3BO,MAAMI,IAAc;AAAA,EACzB,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,OAAO;AACT;AASO,SAASC,EACdC,GACAC,IAAiC,MACzB;AACR,QAAMC,IAAMJ,EAAYE,CAAU,GAC5BG,IAAsB,CAAC,MAAM,MAAM,MAAM,MAAM,KAAK,GACpDC,IAAOD,EAAMA,EAAM,QAAQH,CAAU,IAAI,CAAC;AAEhD,SAAIC,MAAc,OAAa,iBAAiBC,IAAM,QAClDD,MAAc,SAAe,kBAAkBC,IAAM,KAAK,QAEzDE,IACE,iBAAiBF,IAAM,0BAA0BJ,EAAYM,CAAI,IAAI,KAAK,QAD/D,iBAAiBF,IAAM;AAE3C;AAEO,SAASG,EACdL,GACAC,IAAiC,MACxB;AACT,SAAOT,EAAcO,EAAgBC,GAAYC,CAAS,CAAC;AAC7D;AAOO,SAASK,IAAyC;AACvD,QAAMC,IAAKF,EAAc,IAAI,GACvBG,IAAKH,EAAc,IAAI,GACvBI,IAAKJ,EAAc,IAAI,GACvBK,IAAKL,EAAc,IAAI;AAG7B,SAFYA,EAAc,KAAK,IAEf,QACZK,IAAW,OACXD,IAAW,OACXD,IAAW,OACXD,IAAW,OACR;AACT;ACKO,SAASI,EAAgB9D,GAAwD;AACtF,QAAM;AAAA,IACJ,SAAA+D;AAAA,IACA,aAAAC,IAAc;AAAA,IACd,UAAAC,IAAW;AAAA,IACX,eAAAC,IAAgB;AAAA,IAChB,aAAAC,IAAc,CAAA;AAAA,IACd,UAAAC,IAAW,CAACC,GAAMC,MAAU,OAAOA,CAAK;AAAA,EAAA,IACtCtE,GACEC,IAAOsE;AAAA,IAAoB;AAAA,IAAgB;AAAA,IAAQvE,EAAQ;AAAA,IAC/D;AAAA,EAAA,GAEI,CAACwE,GAAQC,CAAS,IAAItE,EAAS+D,CAAa,GAC5C,CAACQ,GAAMC,CAAO,IAAIxE,EAAuBgE,CAAW,GACpD,CAACS,GAASC,CAAU,IAAI1E,EAAuB,CAAA,CAAE,GACjD,CAAC2E,GAAMC,CAAO,IAAI5E,EAAS6D,CAAW,GAEtCgB,IAAWpE;AAAA,IACf,CAACqE,GAAQC,MAAwC;AAC/C,UAAIA,EAAO,SAAU,QAAOA,EAAO,SAASD,CAAG;AAC/C,UAAIC,EAAO,YAAa,QAAQD,EAAgCC,EAAO,WAAqB;AAAA,IAE9F;AAAA,IACA,CAAA;AAAA,EAAC,GAGGC,IAAeC,EAAQ,MAAM;AACjC,QAAIhE,IAASnB;AAEb,QAAIuE,GAAQ;AACV,YAAMa,IAASb,EAAO,YAAA;AACtB,MAAApD,IAASA,EAAO;AAAA,QAAO,CAAC6D,MACtBlB,EAAQ,KAAK,CAACmB,MAAW;AACvB,gBAAM7C,IAAQ2C,EAASC,GAAKC,CAAM;AAClC,iBAAO,OAAO7C,KAAS,EAAE,EAAE,YAAA,EAAc,SAASgD,CAAM;AAAA,QAC1D,CAAC;AAAA,MAAA;AAAA,IAEL;AA8BA,QA5BA,OAAO,QAAQT,CAAO,EAAE,QAAQ,CAAC,CAACU,GAAUC,CAAM,MAAM;AACtD,YAAML,IAASnB,EAAQ,KAAK,CAACyB,MAAMA,EAAE,OAAOF,CAAQ;AACpD,MAAKJ,MACL9D,IAASA,EAAO,OAAO,CAAC6D,MAAQ;AAC9B,cAAM5C,IAAQ2C,EAASC,GAAKC,CAAM,GAC5BO,IAAKF,EAAO;AAClB,gBAAQA,EAAO,UAAA;AAAA,UACb,KAAK;AACH,mBAAOlD,MAAUoD;AAAA,UACnB,KAAK;AACH,mBAAO,OAAOpD,KAAS,EAAE,EAAE,YAAA,EAAc,WAAW,OAAOoD,KAAM,EAAE,EAAE,YAAA,CAAa;AAAA,UACpF,KAAK;AACH,mBAAO,OAAOpD,KAAS,EAAE,EAAE,YAAA,EAAc,SAAS,OAAOoD,KAAM,EAAE,EAAE,YAAA,CAAa;AAAA,UAClF,KAAK;AACH,mBAAO,OAAOpD,CAAK,IAAI,OAAOoD,CAAE;AAAA,UAClC,KAAK;AACH,mBAAO,OAAOpD,CAAK,IAAI,OAAOoD,CAAE;AAAA,UAClC,KAAK;AACH,mBAAO,OAAOpD,CAAK,KAAK,OAAOoD,CAAE;AAAA,UACnC,KAAK;AACH,mBAAO,OAAOpD,CAAK,KAAK,OAAOoD,CAAE;AAAA,UACnC,KAAK;AAAA,UACL;AACE,mBAAO,OAAOpD,KAAS,EAAE,EAAE,YAAA,EAAc,SAAS,OAAOoD,KAAM,EAAE,EAAE,YAAA,CAAa;AAAA,QAAA;AAAA,MAEtF,CAAC;AAAA,IACH,CAAC,GAEGf,EAAK,SAAS,GAAG;AACnB,YAAMgB,IAAS,CAAC,GAAGtE,CAAM;AACzB,MAAAsE,EAAO,KAAK,CAACC,GAAGC,MAAM;AACpB,mBAAWC,KAAKnB,GAAM;AACpB,gBAAMQ,IAASnB,EAAQ,KAAK,CAACyB,MAAMA,EAAE,OAAOK,EAAE,GAAG;AACjD,cAAI,CAACX,EAAQ;AACb,gBAAMY,IAAKd,EAASW,GAAGT,CAAM,GACvBa,IAAKf,EAASY,GAAGV,CAAM;AAC7B,cAAIc,IAAM;AAKV,cAJId,EAAO,SAAQc,IAAMd,EAAO,OAAOY,GAAIC,CAAE,IACpCb,EAAO,aAAa,WAAUc,IAAM,OAAOF,CAAE,IAAI,OAAOC,CAAE,IAC1Db,EAAO,aAAa,SAAQc,IAAM,IAAI,KAAKF,CAAY,EAAE,QAAA,IAAY,IAAI,KAAKC,CAAY,EAAE,QAAA,IAChGC,IAAM,OAAOF,KAAM,EAAE,EAAE,cAAc,OAAOC,KAAM,EAAE,CAAC,GACtDC,MAAQ,EAAG,QAAOH,EAAE,cAAc,QAAQG,IAAM,CAACA;AAAA,QACvD;AACA,eAAO;AAAA,MACT,CAAC,GACD5E,IAASsE;AAAA,IACX;AAEA,WAAOtE;AAAA,EACT,GAAG,CAACnB,GAAMuE,GAAQE,GAAME,GAASb,GAASiB,CAAQ,CAAC,GAE7CiB,IAAY,KAAK,IAAI,GAAG,KAAK,KAAKd,EAAa,SAASlB,CAAQ,CAAC,GACjEiC,IAAW,KAAK,IAAIpB,GAAMmB,IAAY,CAAC,GACvCE,IAAOf;AAAA,IACX,MAAMD,EAAa,MAAMe,IAAWjC,IAAWiC,IAAW,KAAKjC,CAAQ;AAAA,IACvE,CAACkB,GAAce,GAAUjC,CAAQ;AAAA,EAAA,GAG7BmC,IAAaxF;AAAA,IACjB,CAAC0E,MACCX,EAAQ,CAAC0B,MAAS;AAChB,YAAMC,IAAWD,EAAK,KAAK,CAACR,MAAMA,EAAE,QAAQP,CAAQ;AACpD,aAAKgB,IACDA,EAAS,cAAc,QAClBD,EAAK,IAAI,CAACR,MAAOA,EAAE,QAAQP,IAAW,EAAE,GAAGO,GAAG,WAAW,OAAA,IAAWA,CAAE,IACxEQ,EAAK,OAAO,CAACR,MAAMA,EAAE,QAAQP,CAAQ,IAHtB,CAAC,GAAGe,GAAM,EAAE,KAAKf,GAAU,WAAW,OAAO;AAAA,IAIrE,CAAC;AAAA,IACH,CAAA;AAAA,EAAC,GAGGiB,IAAY3F;AAAA,IAChB,CAAC0E,GAAkBjD,MACjBwC,EAAW,CAACwB,MAAS;AACnB,UAAIhE,MAAU,QAAQA,MAAU,MAAMA,MAAU,QAAW;AACzD,cAAMkB,IAAO,EAAE,GAAG8C,EAAA;AAClB,sBAAO9C,EAAK+B,CAAQ,GACb/B;AAAA,MACT;AACA,YAAM2B,IAASnB,EAAQ,KAAK,CAACyB,MAAMA,EAAE,OAAOF,CAAQ;AACpD,aAAO;AAAA,QACL,GAAGe;AAAA,QACH,CAACf,CAAQ,GAAG,EAAE,MAAMJ,GAAQ,cAAc,QAAQ,OAAA7C,GAAO,UAAU,WAAA;AAAA,MAAW;AAAA,IAElF,CAAC;AAAA,IACH,CAAC0B,CAAO;AAAA,EAAA;AAGV,SAAO;AAAA,IACL,MAAAoC;AAAA,IACA,cAAAhB;AAAA,IACA,UAAAf;AAAA,IACA,QAAAI;AAAA,IACA,WAAAC;AAAA,IACA,MAAAC;AAAA,IACA,SAAAC;AAAA,IACA,YAAAyB;AAAA,IACA,SAAAxB;AAAA,IACA,WAAA2B;AAAA,IACA,cAAc,MAAM1B,EAAW,EAAE;AAAA,IACjC,MAAMqB;AAAA,IACN,UAAAjC;AAAA,IACA,WAAAgC;AAAA,IACA,SAAAlB;AAAA,IACA,UAAU,MAAMA,EAAQ,CAACyB,MAAM,KAAK,IAAIA,IAAI,GAAGP,IAAY,CAAC,CAAC;AAAA,IAC7D,UAAU,MAAMlB,EAAQ,CAACyB,MAAM,KAAK,IAAIA,IAAI,GAAG,CAAC,CAAC;AAAA,IACjD,aAAaN,IAAWD,IAAY;AAAA,IACpC,aAAaC,IAAW;AAAA,EAAA;AAE5B;"}
|