@alepha/ui 0.11.9 → 0.11.10

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/index.cjs ADDED
@@ -0,0 +1,2444 @@
1
+ const require_AlephaMantineProvider = require('./AlephaMantineProvider-DlOEv9f0.cjs');
2
+ let __alepha_core = require("@alepha/core");
3
+ let __alepha_react_form = require("@alepha/react-form");
4
+ let __alepha_react_head = require("@alepha/react-head");
5
+ let __alepha_react_i18n = require("@alepha/react-i18n");
6
+ let __alepha_react = require("@alepha/react");
7
+ let __mantine_core = require("@mantine/core");
8
+ let __mantine_modals = require("@mantine/modals");
9
+ let __tabler_icons_react = require("@tabler/icons-react");
10
+ let react = require("react");
11
+ let react_jsx_runtime = require("react/jsx-runtime");
12
+ let __mantine_spotlight = require("@mantine/spotlight");
13
+ let __mantine_dates = require("@mantine/dates");
14
+ let __alepha_postgres = require("@alepha/postgres");
15
+ let __alepha_datetime = require("@alepha/datetime");
16
+ let __mantine_hooks = require("@mantine/hooks");
17
+
18
+ //#region src/RootRouter.ts
19
+ var RootRouter = class {
20
+ root = (0, __alepha_react.$page)({
21
+ path: "/",
22
+ lazy: () => Promise.resolve().then(() => require("./AlephaMantineProvider-DCF5kidi.cjs"))
23
+ });
24
+ };
25
+
26
+ //#endregion
27
+ //#region src/components/data/JsonViewer.tsx
28
+ const getSizeConfig = (size = "sm") => {
29
+ const configs = {
30
+ xs: {
31
+ text: "xs",
32
+ icon: 12,
33
+ indent: 16,
34
+ gap: 2
35
+ },
36
+ sm: {
37
+ text: "sm",
38
+ icon: 14,
39
+ indent: 20,
40
+ gap: 4
41
+ },
42
+ md: {
43
+ text: "md",
44
+ icon: 16,
45
+ indent: 24,
46
+ gap: 6
47
+ },
48
+ lg: {
49
+ text: "lg",
50
+ icon: 18,
51
+ indent: 28,
52
+ gap: 8
53
+ },
54
+ xl: {
55
+ text: "xl",
56
+ icon: 20,
57
+ indent: 32,
58
+ gap: 10
59
+ }
60
+ };
61
+ return configs[size] || configs.sm;
62
+ };
63
+ const JsonNode = ({ name, value, depth, maxDepth, isLast = false, isArrayItem = false, size = "sm" }) => {
64
+ const [expanded, setExpanded] = (0, react.useState)(depth < 2);
65
+ const sizeConfig = getSizeConfig(size);
66
+ const getValueType = (val) => {
67
+ if (val === null) return "null";
68
+ if (val === void 0) return "undefined";
69
+ if (Array.isArray(val)) return "array";
70
+ return typeof val;
71
+ };
72
+ const valueType = getValueType(value);
73
+ const renderPrimitive = (val) => {
74
+ switch (getValueType(val)) {
75
+ case "string": return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Text, {
76
+ component: "span",
77
+ c: "teal",
78
+ ff: "monospace",
79
+ size: sizeConfig.text,
80
+ style: { whiteSpace: "nowrap" },
81
+ children: [
82
+ "\"",
83
+ val,
84
+ "\""
85
+ ]
86
+ });
87
+ case "number": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Text, {
88
+ component: "span",
89
+ c: "blue",
90
+ ff: "monospace",
91
+ size: sizeConfig.text,
92
+ style: { whiteSpace: "nowrap" },
93
+ children: val
94
+ });
95
+ case "boolean": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Text, {
96
+ component: "span",
97
+ c: "violet",
98
+ ff: "monospace",
99
+ size: sizeConfig.text,
100
+ style: { whiteSpace: "nowrap" },
101
+ children: String(val)
102
+ });
103
+ case "null": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Text, {
104
+ component: "span",
105
+ c: "dimmed",
106
+ ff: "monospace",
107
+ size: sizeConfig.text,
108
+ style: { whiteSpace: "nowrap" },
109
+ children: "null"
110
+ });
111
+ case "undefined": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Text, {
112
+ component: "span",
113
+ c: "dimmed",
114
+ ff: "monospace",
115
+ size: sizeConfig.text,
116
+ style: { whiteSpace: "nowrap" },
117
+ children: "undefined"
118
+ });
119
+ default: return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Text, {
120
+ component: "span",
121
+ ff: "monospace",
122
+ size: sizeConfig.text,
123
+ style: { whiteSpace: "nowrap" },
124
+ children: String(val)
125
+ });
126
+ }
127
+ };
128
+ const renderKey = () => {
129
+ if (!name) return null;
130
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Text, {
131
+ component: "span",
132
+ c: "cyan",
133
+ ff: "monospace",
134
+ fw: 500,
135
+ size: sizeConfig.text,
136
+ children: [isArrayItem ? `[${name}]` : `"${name}"`, ":"]
137
+ });
138
+ };
139
+ if (valueType === "object" || valueType === "array") {
140
+ const isObject = valueType === "object";
141
+ const entries = isObject ? Object.entries(value) : value.map((v, i) => [i, v]);
142
+ const isEmpty = entries.length === 0;
143
+ const canExpand = depth < maxDepth && !isEmpty;
144
+ const preview = isObject ? "{...}" : "[...]";
145
+ const brackets = isObject ? ["{", "}"] : ["[", "]"];
146
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Box, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Box, {
147
+ style: {
148
+ display: "flex",
149
+ alignItems: "center",
150
+ gap: sizeConfig.gap,
151
+ minWidth: "max-content"
152
+ },
153
+ children: [
154
+ canExpand && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.ActionIcon, {
155
+ size: "xs",
156
+ variant: "transparent",
157
+ c: "dimmed",
158
+ onClick: () => setExpanded(!expanded),
159
+ style: {
160
+ cursor: "pointer",
161
+ flexShrink: 0
162
+ },
163
+ children: expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconChevronDown, { size: sizeConfig.icon }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconChevronRight, { size: sizeConfig.icon })
164
+ }),
165
+ !canExpand && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Box, {
166
+ w: sizeConfig.icon + 6,
167
+ style: { flexShrink: 0 }
168
+ }),
169
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Box, {
170
+ style: { flexShrink: 0 },
171
+ children: renderKey()
172
+ }),
173
+ " ",
174
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Text, {
175
+ component: "span",
176
+ c: "dimmed",
177
+ ff: "monospace",
178
+ size: sizeConfig.text,
179
+ style: { flexShrink: 0 },
180
+ children: brackets[0]
181
+ }),
182
+ !expanded && !isEmpty && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Text, {
183
+ component: "span",
184
+ c: "dimmed",
185
+ ff: "monospace",
186
+ fs: "italic",
187
+ size: sizeConfig.text,
188
+ style: { flexShrink: 0 },
189
+ children: preview
190
+ }),
191
+ (isEmpty || !expanded) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Text, {
192
+ component: "span",
193
+ c: "dimmed",
194
+ ff: "monospace",
195
+ size: sizeConfig.text,
196
+ style: { flexShrink: 0 },
197
+ children: brackets[1]
198
+ }),
199
+ !isEmpty && !expanded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Text, {
200
+ component: "span",
201
+ c: "dimmed",
202
+ size: sizeConfig.text,
203
+ style: { flexShrink: 0 },
204
+ children: [
205
+ entries.length,
206
+ " ",
207
+ entries.length === 1 ? "item" : "items"
208
+ ]
209
+ })
210
+ ]
211
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Collapse, {
212
+ in: expanded && canExpand,
213
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Box, {
214
+ pl: sizeConfig.indent,
215
+ style: {
216
+ borderLeft: "1px solid var(--mantine-color-default-border)",
217
+ marginLeft: Math.floor((sizeConfig.icon + 6) / 2)
218
+ },
219
+ children: entries.map(([key, val], index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(JsonNode, {
220
+ name: String(key),
221
+ value: val,
222
+ depth: depth + 1,
223
+ maxDepth,
224
+ isLast: index === entries.length - 1,
225
+ isArrayItem: !isObject,
226
+ size
227
+ }, String(key)))
228
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Box, {
229
+ style: {
230
+ display: "flex",
231
+ minWidth: "max-content"
232
+ },
233
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Box, {
234
+ w: sizeConfig.icon + 6,
235
+ style: { flexShrink: 0 }
236
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Text, {
237
+ c: "dimmed",
238
+ ff: "monospace",
239
+ size: sizeConfig.text,
240
+ style: { flexShrink: 0 },
241
+ children: brackets[1]
242
+ })]
243
+ })]
244
+ })] });
245
+ }
246
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Box, {
247
+ style: {
248
+ display: "flex",
249
+ alignItems: "center",
250
+ gap: sizeConfig.gap,
251
+ minWidth: "max-content"
252
+ },
253
+ children: [
254
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Box, {
255
+ w: sizeConfig.icon + 6,
256
+ style: { flexShrink: 0 }
257
+ }),
258
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Box, {
259
+ style: { flexShrink: 0 },
260
+ children: renderKey()
261
+ }),
262
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Box, {
263
+ style: { flexShrink: 0 },
264
+ children: renderPrimitive(value)
265
+ }),
266
+ !isLast && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Text, {
267
+ component: "span",
268
+ c: "dimmed",
269
+ ff: "monospace",
270
+ size: sizeConfig.text,
271
+ style: { flexShrink: 0 },
272
+ children: ","
273
+ })
274
+ ]
275
+ });
276
+ };
277
+ const JsonViewer = ({ data, defaultExpanded = true, maxDepth = 10, copyable = true, size = "sm" }) => {
278
+ const copyIconSize = getSizeConfig(size).icon + 2;
279
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Box, {
280
+ pos: "relative",
281
+ w: "100%",
282
+ children: [copyable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Box, {
283
+ pos: "absolute",
284
+ top: 0,
285
+ right: 0,
286
+ style: { zIndex: 1 },
287
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.CopyButton, {
288
+ value: JSON.stringify(data, null, 2),
289
+ children: ({ copied, copy }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Tooltip, {
290
+ label: copied ? "Copied" : "Copy JSON",
291
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.ActionIcon, {
292
+ color: copied ? "teal" : "gray",
293
+ variant: "subtle",
294
+ onClick: copy,
295
+ size,
296
+ children: copied ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconCheck, { size: copyIconSize }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconCopy, { size: copyIconSize })
297
+ })
298
+ })
299
+ })
300
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Box, {
301
+ pt: copyable ? 30 : 0,
302
+ style: { overflowX: "auto" },
303
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(JsonNode, {
304
+ value: data,
305
+ depth: 0,
306
+ maxDepth,
307
+ size
308
+ })
309
+ })]
310
+ });
311
+ };
312
+ var JsonViewer_default = JsonViewer;
313
+
314
+ //#endregion
315
+ //#region src/components/dialogs/AlertDialog.tsx
316
+ const AlertDialog = ({ options, onClose }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [options?.message && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Text, {
317
+ mb: "md",
318
+ children: options.message
319
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Group, {
320
+ justify: "flex-end",
321
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Button, {
322
+ onClick: onClose,
323
+ children: options?.okLabel || "OK"
324
+ })
325
+ })] });
326
+ var AlertDialog_default = AlertDialog;
327
+
328
+ //#endregion
329
+ //#region src/components/dialogs/ConfirmDialog.tsx
330
+ const ConfirmDialog = ({ options, onConfirm }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [options?.message && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Text, {
331
+ mb: "md",
332
+ children: options.message
333
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Group, {
334
+ justify: "flex-end",
335
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Button, {
336
+ variant: "subtle",
337
+ onClick: () => onConfirm(false),
338
+ children: options?.cancelLabel || "Cancel"
339
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Button, {
340
+ color: options?.confirmColor || "blue",
341
+ onClick: () => onConfirm(true),
342
+ children: options?.confirmLabel || "Confirm"
343
+ })]
344
+ })] });
345
+ var ConfirmDialog_default = ConfirmDialog;
346
+
347
+ //#endregion
348
+ //#region src/components/dialogs/PromptDialog.tsx
349
+ const PromptDialog = ({ options, onSubmit }) => {
350
+ const [value, setValue] = (0, react.useState)(options?.defaultValue || "");
351
+ const inputRef = (0, react.useRef)(null);
352
+ (0, react.useEffect)(() => {
353
+ inputRef.current?.focus();
354
+ }, []);
355
+ const handleSubmit = () => {
356
+ if (!options?.required || value.trim()) onSubmit(value);
357
+ };
358
+ const handleKeyDown = (event) => {
359
+ if (event.key === "Enter") handleSubmit();
360
+ };
361
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
362
+ options?.message && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Text, {
363
+ mb: "md",
364
+ children: options.message
365
+ }),
366
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.TextInput, {
367
+ ref: inputRef,
368
+ label: options?.label,
369
+ placeholder: options?.placeholder,
370
+ value,
371
+ onChange: (event) => setValue(event.currentTarget.value),
372
+ onKeyDown: handleKeyDown,
373
+ required: options?.required,
374
+ mb: "md"
375
+ }),
376
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Group, {
377
+ justify: "flex-end",
378
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Button, {
379
+ variant: "subtle",
380
+ onClick: () => onSubmit(null),
381
+ children: options?.cancelLabel || "Cancel"
382
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Button, {
383
+ onClick: handleSubmit,
384
+ disabled: options?.required && !value.trim(),
385
+ children: options?.submitLabel || "OK"
386
+ })]
387
+ })
388
+ ] });
389
+ };
390
+ var PromptDialog_default = PromptDialog;
391
+
392
+ //#endregion
393
+ //#region src/constants/ui.ts
394
+ const ui = { colors: {
395
+ transparent: "transparent",
396
+ background: "var(--alepha-background)",
397
+ surface: "var(--alepha-surface)",
398
+ elevated: "var(--alepha-elevated)",
399
+ border: "var(--alepha-border)"
400
+ } };
401
+
402
+ //#endregion
403
+ //#region src/services/DialogService.tsx
404
+ var DialogService = class {
405
+ options = { default: {
406
+ centered: true,
407
+ withCloseButton: true,
408
+ size: "md",
409
+ overlayProps: {
410
+ backgroundOpacity: .55,
411
+ blur: 3
412
+ },
413
+ transitionProps: {
414
+ transition: "pop",
415
+ duration: 200
416
+ }
417
+ } };
418
+ /**
419
+ * Show an alert dialog with a message
420
+ */
421
+ alert(options) {
422
+ return new Promise((resolve) => {
423
+ const modalId = this.open({
424
+ ...options,
425
+ title: options?.title || "Alert",
426
+ content: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AlertDialog_default, {
427
+ options,
428
+ onClose: () => {
429
+ this.close(modalId);
430
+ resolve();
431
+ }
432
+ })
433
+ });
434
+ });
435
+ }
436
+ /**
437
+ * Show a confirmation dialog that returns a promise
438
+ */
439
+ confirm(options) {
440
+ return new Promise((resolve) => {
441
+ const modalId = this.open({
442
+ ...options,
443
+ title: options?.title || "Confirm",
444
+ closeOnClickOutside: false,
445
+ closeOnEscape: false,
446
+ content: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfirmDialog_default, {
447
+ options,
448
+ onConfirm: (confirmed) => {
449
+ this.close(modalId);
450
+ resolve(confirmed);
451
+ }
452
+ })
453
+ });
454
+ });
455
+ }
456
+ /**
457
+ * Show a prompt dialog to get user input
458
+ */
459
+ prompt(options) {
460
+ return new Promise((resolve) => {
461
+ const modalId = this.open({
462
+ ...options,
463
+ title: options?.title || "Input",
464
+ closeOnClickOutside: false,
465
+ closeOnEscape: false,
466
+ content: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PromptDialog_default, {
467
+ options,
468
+ onSubmit: (value) => {
469
+ this.close(modalId);
470
+ resolve(value);
471
+ }
472
+ })
473
+ });
474
+ });
475
+ }
476
+ /**
477
+ * Open a custom dialog with provided content
478
+ */
479
+ open(options) {
480
+ return __mantine_modals.modals.open({
481
+ ...this.options.default,
482
+ ...options,
483
+ children: options?.content || options?.message
484
+ });
485
+ }
486
+ /**
487
+ * Close the currently open dialog or a specific dialog by ID
488
+ */
489
+ close(modalId) {
490
+ if (modalId) __mantine_modals.modals.close(modalId);
491
+ else __mantine_modals.modals.closeAll();
492
+ }
493
+ /**
494
+ * Show a JSON editor/viewer dialog
495
+ */
496
+ json(data, options) {
497
+ this.open({
498
+ size: "lg",
499
+ title: options?.title || "Json Viewer",
500
+ ...options,
501
+ content: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Flex, {
502
+ bdrs: "md",
503
+ w: "100%",
504
+ flex: 1,
505
+ p: "sm",
506
+ bg: ui.colors.surface,
507
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(JsonViewer_default, {
508
+ size: "xs",
509
+ data
510
+ })
511
+ })
512
+ });
513
+ }
514
+ /**
515
+ * Show a form dialog for structured input
516
+ */
517
+ form(options) {
518
+ return Promise.resolve(null);
519
+ }
520
+ /**
521
+ * Show a loading/progress dialog with optional progress percentage
522
+ */
523
+ loading(options) {}
524
+ /**
525
+ * Show an image viewer/gallery dialog
526
+ */
527
+ image(src, options) {}
528
+ };
529
+
530
+ //#endregion
531
+ //#region src/components/buttons/ActionButton.tsx
532
+ const ActionMenuItem = (props) => {
533
+ const { item, index } = props;
534
+ const router = (0, __alepha_react.useRouter)();
535
+ const action = (0, __alepha_react.useAction)({ handler: async (e) => {
536
+ await item.onClick?.();
537
+ } }, [item.onClick]);
538
+ if (item.type === "divider") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Menu.Divider, {}, index);
539
+ if (item.type === "label") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Menu.Label, { children: item.label }, index);
540
+ if (item.children && item.children.length > 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Menu, {
541
+ trigger: "hover",
542
+ position: "right-start",
543
+ offset: 2,
544
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Menu.Target, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Menu.Item, {
545
+ leftSection: item.icon,
546
+ rightSection: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconChevronRight, { size: 14 }),
547
+ children: item.label
548
+ }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Menu.Dropdown, { children: item.children.map((child, childIndex) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionMenuItem, {
549
+ item: child,
550
+ index: childIndex
551
+ }, childIndex)) })]
552
+ }, index);
553
+ const menuItemProps = {};
554
+ if (props.item.onClick) menuItemProps.onClick = action.run;
555
+ else if (props.item.href) Object.assign(menuItemProps, router.anchor(props.item.href));
556
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Menu.Item, {
557
+ leftSection: item.icon,
558
+ onClick: item.onClick,
559
+ color: item.color,
560
+ rightSection: item.active ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.ThemeIcon, {
561
+ size: "xs",
562
+ variant: "transparent",
563
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconCheck, {})
564
+ }) : void 0,
565
+ ...menuItemProps,
566
+ children: item.label
567
+ }, index);
568
+ };
569
+ const ActionButton = (_props) => {
570
+ const props = {
571
+ variant: "subtle",
572
+ ..._props
573
+ };
574
+ const { tooltip, menu, icon, ...restProps } = props;
575
+ if (props.icon) {
576
+ const icon$1 = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.ThemeIcon, {
577
+ w: 24,
578
+ variant: "transparent",
579
+ size: "sm",
580
+ c: "var(--mantine-color-text)",
581
+ ...props.themeIconProps,
582
+ children: props.icon
583
+ });
584
+ if (!props.children) {
585
+ restProps.children = icon$1;
586
+ restProps.p ??= "xs";
587
+ } else restProps.leftSection = icon$1;
588
+ }
589
+ if (props.leftSection && !props.children) {
590
+ restProps.className ??= "mantine-Action-iconOnly";
591
+ restProps.p ??= "xs";
592
+ }
593
+ if (props.textVisibleFrom) {
594
+ const { children, textVisibleFrom, leftSection, ...rest } = restProps;
595
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Flex, {
596
+ w: "100%",
597
+ visibleFrom: textVisibleFrom,
598
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionButton, {
599
+ flex: 1,
600
+ ...rest,
601
+ leftSection,
602
+ tooltip,
603
+ menu,
604
+ children
605
+ })
606
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Flex, {
607
+ w: "100%",
608
+ hiddenFrom: textVisibleFrom,
609
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionButton, {
610
+ px: "xs",
611
+ ...rest,
612
+ tooltip,
613
+ menu,
614
+ children: leftSection
615
+ })
616
+ })] });
617
+ }
618
+ const renderAction = () => {
619
+ if ("href" in restProps && restProps.href) {
620
+ if (restProps.href.startsWith("http") || restProps.target) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionHrefButton, {
621
+ ...restProps,
622
+ href: restProps.href,
623
+ children: restProps.children
624
+ });
625
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionNavigationButton, {
626
+ ...restProps,
627
+ href: restProps.href,
628
+ children: restProps.children
629
+ });
630
+ }
631
+ delete restProps.classNameActive;
632
+ delete restProps.variantActive;
633
+ if ("action" in restProps && restProps.action) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionHookButton, {
634
+ ...restProps,
635
+ action: restProps.action,
636
+ children: restProps.children
637
+ });
638
+ if ("onClick" in restProps && restProps.onClick) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionClickButton, {
639
+ ...restProps,
640
+ onClick: restProps.onClick,
641
+ children: restProps.children
642
+ });
643
+ if ("form" in restProps && restProps.form) {
644
+ if (restProps.type === "reset") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionResetButton, {
645
+ ...restProps,
646
+ form: restProps.form,
647
+ children: restProps.children
648
+ });
649
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionSubmitButton, {
650
+ ...restProps,
651
+ form: restProps.form,
652
+ children: restProps.children
653
+ });
654
+ }
655
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Button, {
656
+ ...restProps,
657
+ children: restProps.children
658
+ });
659
+ };
660
+ let actionElement = renderAction();
661
+ if (menu) actionElement = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Menu, {
662
+ position: menu.position || "bottom-start",
663
+ width: menu.width || 200,
664
+ shadow: menu.shadow || "md",
665
+ trigger: menu.on === "hover" ? "hover" : "click",
666
+ ...menu.menuProps,
667
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Menu.Target, {
668
+ ...menu.targetProps,
669
+ children: actionElement
670
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Menu.Dropdown, { children: menu.items.map((item, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionMenuItem, {
671
+ item,
672
+ index
673
+ }, index)) })]
674
+ });
675
+ if (tooltip) {
676
+ const defaultTooltipProps = { openDelay: 1e3 };
677
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Tooltip, { ...typeof tooltip === "string" ? {
678
+ ...defaultTooltipProps,
679
+ label: tooltip,
680
+ children: actionElement
681
+ } : {
682
+ ...defaultTooltipProps,
683
+ ...tooltip,
684
+ children: actionElement
685
+ } });
686
+ }
687
+ return actionElement;
688
+ };
689
+ var ActionButton_default = ActionButton;
690
+ /**
691
+ * Action button that submits a form with loading and disabled state handling.
692
+ */
693
+ const ActionSubmitButton = (props) => {
694
+ const { form, ...buttonProps } = props;
695
+ const state = (0, __alepha_react_form.useFormState)(form);
696
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Button, {
697
+ ...buttonProps,
698
+ loading: state.loading,
699
+ disabled: state.loading,
700
+ type: "submit",
701
+ children: props.children
702
+ });
703
+ };
704
+ const ActionResetButton = (props) => {
705
+ const { form, ...buttonProps } = props;
706
+ const state = (0, __alepha_react_form.useFormState)(form);
707
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Button, {
708
+ ...buttonProps,
709
+ disabled: state.loading,
710
+ type: "reset",
711
+ children: props.children
712
+ });
713
+ };
714
+ /**
715
+ * Action button that integrates with useAction hook return value.
716
+ * Automatically handles loading state and executes the action on click.
717
+ *
718
+ * @example
719
+ * ```tsx
720
+ * const saveAction = useAction({
721
+ * handler: async (data) => {
722
+ * await api.save(data);
723
+ * }
724
+ * }, []);
725
+ *
726
+ * <ActionButton action={saveAction}>
727
+ * Save
728
+ * </ActionButton>
729
+ * ```
730
+ */
731
+ const ActionHookButton = (props) => {
732
+ const { action, ...buttonProps } = props;
733
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Button, {
734
+ ...buttonProps,
735
+ disabled: action.loading || props.disabled,
736
+ loading: action.loading,
737
+ onClick: () => action.run(),
738
+ children: props.children
739
+ });
740
+ };
741
+ /**
742
+ * Basic action button that handles click events with loading and error handling.
743
+ *
744
+ * @example
745
+ * ```tsx
746
+ * <ActionButton onClick={() => api.doSomething()}>
747
+ * Do Something
748
+ * </ActionButton>
749
+ * ```
750
+ */
751
+ const ActionClickButton = (props) => {
752
+ const action = (0, __alepha_react.useAction)({ handler: async (e) => {
753
+ await props.onClick(e);
754
+ } }, [props.onClick]);
755
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Button, {
756
+ ...props,
757
+ disabled: action.loading || props.disabled,
758
+ loading: action.loading,
759
+ onClick: action.run,
760
+ children: props.children
761
+ });
762
+ };
763
+ /**
764
+ * Action for navigation with active state support.
765
+ */
766
+ const ActionNavigationButton = (props) => {
767
+ const { active: options, classNameActive, variantActive, routerGoOptions, ...buttonProps } = props;
768
+ const router = (0, __alepha_react.useRouter)();
769
+ const { isPending, isActive } = (0, __alepha_react.useActive)(options ? {
770
+ href: props.href,
771
+ ...options
772
+ } : { href: props.href });
773
+ const anchorProps = router.anchor(props.href, routerGoOptions);
774
+ const className = buttonProps.className || "";
775
+ if (isActive && options !== false && classNameActive) buttonProps.className = `${className} ${classNameActive}`.trim();
776
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Button, {
777
+ component: "a",
778
+ loading: isPending,
779
+ ...buttonProps,
780
+ ...anchorProps,
781
+ variant: isActive && options !== false ? variantActive ?? "filled" : buttonProps.variant ?? "subtle",
782
+ children: props.children
783
+ });
784
+ };
785
+ const ActionHrefButton = (props) => {
786
+ const { active: options, classNameActive, variantActive, routerGoOptions, target, ...buttonProps } = props;
787
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Button, {
788
+ component: "a",
789
+ target,
790
+ ...buttonProps,
791
+ children: props.children
792
+ });
793
+ };
794
+
795
+ //#endregion
796
+ //#region src/components/buttons/DarkModeButton.tsx
797
+ const DarkModeButton = (props) => {
798
+ const { setColorScheme } = (0, __mantine_core.useMantineColorScheme)();
799
+ const computedColorScheme = (0, __mantine_core.useComputedColorScheme)("light");
800
+ const [colorScheme, setColorScheme2] = (0, react.useState)("default");
801
+ const mode = props.mode ?? "minimal";
802
+ (0, react.useEffect)(() => {
803
+ setColorScheme2(computedColorScheme);
804
+ }, [computedColorScheme]);
805
+ const toggleColorScheme = () => {
806
+ setColorScheme(computedColorScheme === "dark" ? "light" : "dark");
807
+ };
808
+ if (mode === "segmented") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.SegmentedControl, {
809
+ value: colorScheme,
810
+ onChange: (value) => setColorScheme(value),
811
+ data: [{
812
+ value: "light",
813
+ label: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Flex, {
814
+ h: 20,
815
+ align: "center",
816
+ justify: "center",
817
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconSun, { size: 16 })
818
+ })
819
+ }, {
820
+ value: "dark",
821
+ label: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Flex, {
822
+ h: 20,
823
+ align: "center",
824
+ justify: "center",
825
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconMoon, { size: 16 })
826
+ })
827
+ }],
828
+ w: props.fullWidth ? "100%" : void 0,
829
+ ...props.segmentedProps
830
+ });
831
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionButton_default, {
832
+ onClick: toggleColorScheme,
833
+ variant: props.variant ?? "outline",
834
+ size: props.size ?? "sm",
835
+ "aria-label": "Toggle color scheme",
836
+ px: "xs",
837
+ fullWidth: props.fullWidth ?? false,
838
+ icon: colorScheme === "dark" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconSun, { size: 20 }) : colorScheme === "light" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconMoon, { size: 20 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Flex, {
839
+ h: 20,
840
+ w: 20
841
+ }),
842
+ ...props.actionProps
843
+ });
844
+ };
845
+ var DarkModeButton_default = DarkModeButton;
846
+
847
+ //#endregion
848
+ //#region src/components/buttons/OmnibarButton.tsx
849
+ const OmnibarButton = (props) => {
850
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionButton_default, {
851
+ variant: "outline",
852
+ miw: 256,
853
+ onClick: __mantine_spotlight.spotlight.open,
854
+ justify: "space-between",
855
+ rightSection: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Kbd, {
856
+ size: "sm",
857
+ children: "⌘+K"
858
+ }),
859
+ radius: "md",
860
+ ...props.actionProps,
861
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Flex, {
862
+ align: "center",
863
+ gap: "xs",
864
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconSearch, {
865
+ size: 16,
866
+ color: "gray"
867
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Text, {
868
+ size: "xs",
869
+ c: "dimmed",
870
+ children: "Search..."
871
+ })]
872
+ })
873
+ });
874
+ };
875
+ var OmnibarButton_default = OmnibarButton;
876
+
877
+ //#endregion
878
+ //#region src/utils/icons.tsx
879
+ /**
880
+ * Icon size presets following Mantine's size conventions
881
+ */
882
+ const ICON_SIZES = {
883
+ xs: 12,
884
+ sm: 16,
885
+ md: 20,
886
+ lg: 24,
887
+ xl: 28
888
+ };
889
+ /**
890
+ * Get the default icon for an input based on its type, format, or name.
891
+ */
892
+ const getDefaultIcon = (params) => {
893
+ const { type, format, name, isEnum, isArray, size = "sm" } = params;
894
+ const iconSize = ICON_SIZES[size];
895
+ if (format) switch (format) {
896
+ case "email": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconMail, { size: iconSize });
897
+ case "url":
898
+ case "uri": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconLink, { size: iconSize });
899
+ case "tel":
900
+ case "phone": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconPhone, { size: iconSize });
901
+ case "date": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconCalendar, { size: iconSize });
902
+ case "date-time": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconCalendar, { size: iconSize });
903
+ case "time": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconClock, { size: iconSize });
904
+ case "color": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconColorPicker, { size: iconSize });
905
+ case "uuid": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconKey, { size: iconSize });
906
+ }
907
+ if (name) {
908
+ const nameLower = name.toLowerCase();
909
+ if (nameLower.includes("password") || nameLower.includes("secret")) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconKey, { size: iconSize });
910
+ if (nameLower.includes("email") || nameLower.includes("mail")) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconMail, { size: iconSize });
911
+ if (nameLower.includes("url") || nameLower.includes("link")) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconLink, { size: iconSize });
912
+ if (nameLower.includes("phone") || nameLower.includes("tel")) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconPhone, { size: iconSize });
913
+ if (nameLower.includes("color")) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconPalette, { size: iconSize });
914
+ if (nameLower.includes("file") || nameLower.includes("upload")) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconFile, { size: iconSize });
915
+ if (nameLower.includes("date")) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconCalendar, { size: iconSize });
916
+ if (nameLower.includes("time")) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconClock, { size: iconSize });
917
+ }
918
+ if (isEnum || isArray) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconSelector, { size: iconSize });
919
+ if (type) switch (type) {
920
+ case "boolean": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconToggleLeft, { size: iconSize });
921
+ case "number":
922
+ case "integer": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconHash, { size: iconSize });
923
+ case "array": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconList, { size: iconSize });
924
+ case "string": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconLetterCase, { size: iconSize });
925
+ }
926
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconAt, { size: iconSize });
927
+ };
928
+
929
+ //#endregion
930
+ //#region src/utils/string.ts
931
+ /**
932
+ * Capitalizes the first letter of a string.
933
+ *
934
+ * @example
935
+ * capitalize("hello") // "Hello"
936
+ */
937
+ const capitalize = (str) => {
938
+ return str.charAt(0).toUpperCase() + str.slice(1);
939
+ };
940
+ /**
941
+ * Converts a path or identifier string into a pretty display name.
942
+ * Removes slashes and capitalizes the first letter.
943
+ *
944
+ * @example
945
+ * prettyName("/userName") // "UserName"
946
+ * prettyName("email") // "Email"
947
+ */
948
+ const prettyName = (name) => {
949
+ return capitalize(name.replaceAll("/", ""));
950
+ };
951
+
952
+ //#endregion
953
+ //#region src/utils/parseInput.ts
954
+ const parseInput = (props, form) => {
955
+ const disabled = false;
956
+ const id = props.input.props.id;
957
+ const label = props.title ?? ("title" in props.input.schema && typeof props.input.schema.title === "string" ? props.input.schema.title : void 0) ?? prettyName(props.input.path);
958
+ const description = props.description ?? ("description" in props.input.schema && typeof props.input.schema.description === "string" ? props.input.schema.description : void 0);
959
+ const error = form.error && form.error instanceof __alepha_core.TypeBoxError ? form.error.value.message : void 0;
960
+ const icon = props.icon ?? getDefaultIcon({
961
+ type: props.input.schema && "type" in props.input.schema ? String(props.input.schema.type) : void 0,
962
+ format: props.input.schema && "format" in props.input.schema && typeof props.input.schema.format === "string" ? props.input.schema.format : void 0,
963
+ name: props.input.props.name,
964
+ isEnum: props.input.schema && "enum" in props.input.schema && Boolean(props.input.schema.enum),
965
+ isArray: props.input.schema && "type" in props.input.schema && props.input.schema.type === "array"
966
+ });
967
+ const format = props.input.schema && "format" in props.input.schema && typeof props.input.schema.format === "string" ? props.input.schema.format : void 0;
968
+ const required = props.input.required;
969
+ const schema = props.input.schema;
970
+ const inputProps = {
971
+ label,
972
+ description,
973
+ error,
974
+ required,
975
+ disabled
976
+ };
977
+ if ("minLength" in schema && typeof schema.minLength === "number") inputProps.minLength = schema.minLength;
978
+ if ("maxLength" in schema && typeof schema.maxLength === "number") inputProps.maxLength = schema.maxLength;
979
+ if ("minimum" in schema && typeof schema.minimum === "number") inputProps.minimum = schema.minimum;
980
+ if ("maximum" in schema && typeof schema.maximum === "number") inputProps.maximum = schema.maximum;
981
+ return {
982
+ id,
983
+ icon,
984
+ format,
985
+ schema: props.input.schema,
986
+ inputProps
987
+ };
988
+ };
989
+
990
+ //#endregion
991
+ //#region src/components/form/ControlDate.tsx
992
+ /**
993
+ * ControlDate component for handling date, datetime, and time inputs.
994
+ *
995
+ * Features:
996
+ * - DateInput for date format
997
+ * - DateTimePicker for date-time format
998
+ * - TimeInput for time format
999
+ *
1000
+ * Automatically detects date formats from schema and renders appropriate picker.
1001
+ */
1002
+ const ControlDate = (props) => {
1003
+ const { inputProps, id, icon, format } = parseInput(props, (0, __alepha_react_form.useFormState)(props.input));
1004
+ if (!props.input?.props) return null;
1005
+ if (props.datetime || format === "date-time") {
1006
+ const dateTimePickerProps = typeof props.datetime === "object" ? props.datetime : {};
1007
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_dates.DateTimePicker, {
1008
+ ...inputProps,
1009
+ id,
1010
+ leftSection: icon,
1011
+ defaultValue: props.input.props.defaultValue ? new Date(props.input.props.defaultValue) : void 0,
1012
+ onChange: (value) => {
1013
+ props.input.set(value ? new Date(value).toISOString() : void 0);
1014
+ },
1015
+ ...dateTimePickerProps
1016
+ });
1017
+ }
1018
+ if (props.date || format === "date") {
1019
+ const dateInputProps = typeof props.date === "object" ? props.date : {};
1020
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_dates.DateInput, {
1021
+ ...inputProps,
1022
+ id,
1023
+ leftSection: icon,
1024
+ defaultValue: props.input.props.defaultValue ? new Date(props.input.props.defaultValue) : void 0,
1025
+ onChange: (value) => {
1026
+ props.input.set(value ? new Date(value).toISOString().slice(0, 10) : void 0);
1027
+ },
1028
+ ...dateInputProps
1029
+ });
1030
+ }
1031
+ if (props.time || format === "time") {
1032
+ const timeInputProps = typeof props.time === "object" ? props.time : {};
1033
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_dates.TimeInput, {
1034
+ ...inputProps,
1035
+ id,
1036
+ leftSection: icon,
1037
+ defaultValue: props.input.props.defaultValue,
1038
+ onChange: (event) => {
1039
+ props.input.set(event.currentTarget.value);
1040
+ },
1041
+ ...timeInputProps
1042
+ });
1043
+ }
1044
+ return null;
1045
+ };
1046
+ var ControlDate_default = ControlDate;
1047
+
1048
+ //#endregion
1049
+ //#region src/components/form/ControlNumber.tsx
1050
+ /**
1051
+ *
1052
+ */
1053
+ const ControlNumber = (props) => {
1054
+ const { inputProps, id, icon } = parseInput(props, (0, __alepha_react_form.useFormState)(props.input));
1055
+ const ref = (0, react.useRef)(null);
1056
+ const [value, setValue] = (0, react.useState)(props.input.props.defaultValue);
1057
+ (0, __alepha_react.useEvents)({ "form:reset": (event) => {
1058
+ if (event.id === props.input?.form.id && ref.current) setValue(props.input.props.defaultValue);
1059
+ } }, [props.input]);
1060
+ if (!props.input?.props) return null;
1061
+ const { type, ...inputPropsWithoutType } = props.input.props;
1062
+ if (props.sliderProps) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Input.Wrapper, {
1063
+ ...inputProps,
1064
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1065
+ style: {
1066
+ height: 32,
1067
+ padding: 8
1068
+ },
1069
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Slider, {
1070
+ ...inputProps,
1071
+ ref,
1072
+ id,
1073
+ ...inputPropsWithoutType,
1074
+ ...props.sliderProps,
1075
+ value,
1076
+ onChange: (val) => {
1077
+ setValue(val);
1078
+ props.input.set(val);
1079
+ }
1080
+ })
1081
+ })
1082
+ });
1083
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.NumberInput, {
1084
+ ...inputProps,
1085
+ ref,
1086
+ id,
1087
+ leftSection: icon,
1088
+ ...inputPropsWithoutType,
1089
+ ...props.numberInputProps,
1090
+ value: value ?? "",
1091
+ onChange: (val) => {
1092
+ const newValue = val !== null ? Number(val) : void 0;
1093
+ setValue(newValue);
1094
+ props.input.set(newValue);
1095
+ }
1096
+ });
1097
+ };
1098
+ var ControlNumber_default = ControlNumber;
1099
+
1100
+ //#endregion
1101
+ //#region src/utils/extractSchemaFields.ts
1102
+ /**
1103
+ * Extract field information from a TypeBox schema for query building.
1104
+ * Supports nested objects and provides field metadata for autocomplete.
1105
+ */
1106
+ function extractSchemaFields(schema, prefix = "") {
1107
+ const fields = [];
1108
+ if (!schema || typeof schema !== "object") return fields;
1109
+ const properties = "properties" in schema ? schema.properties : schema;
1110
+ if (!properties || typeof properties !== "object") return fields;
1111
+ for (const [key, value] of Object.entries(properties)) {
1112
+ if (typeof value !== "object" || value === null) continue;
1113
+ const fieldSchema = value;
1114
+ const path = prefix ? `${prefix}.${key}` : key;
1115
+ const format = "format" in fieldSchema ? fieldSchema.format : void 0;
1116
+ let displayType = "type" in fieldSchema ? fieldSchema.type : "object";
1117
+ if (format === "date-time") displayType = "datetime";
1118
+ else if (format === "date") displayType = "date";
1119
+ else if (format === "time") displayType = "time";
1120
+ else if (format === "duration") displayType = "duration";
1121
+ const field = {
1122
+ name: key,
1123
+ path,
1124
+ type: displayType,
1125
+ format,
1126
+ description: "description" in fieldSchema ? fieldSchema.description : void 0
1127
+ };
1128
+ if ("enum" in fieldSchema && fieldSchema.enum) {
1129
+ field.enum = fieldSchema.enum;
1130
+ field.type = "enum";
1131
+ }
1132
+ if ("type" in fieldSchema && fieldSchema.type === "object" && "properties" in fieldSchema && typeof fieldSchema.properties === "object") field.nested = extractSchemaFields(fieldSchema.properties, path);
1133
+ fields.push(field);
1134
+ if (field.nested) fields.push(...field.nested);
1135
+ }
1136
+ return fields;
1137
+ }
1138
+ /**
1139
+ * Get suggested operators based on field type
1140
+ */
1141
+ function getOperatorsForField(field) {
1142
+ const allOperators = ["=", "!="];
1143
+ if (field.enum) return [...allOperators, "in"];
1144
+ switch (field.type) {
1145
+ case "string":
1146
+ case "text": return [
1147
+ ...allOperators,
1148
+ "~",
1149
+ "~*",
1150
+ "null"
1151
+ ];
1152
+ case "number":
1153
+ case "integer": return [
1154
+ ...allOperators,
1155
+ ">",
1156
+ ">=",
1157
+ "<",
1158
+ "<="
1159
+ ];
1160
+ case "boolean": return allOperators;
1161
+ case "datetime":
1162
+ case "date": return [
1163
+ ...allOperators,
1164
+ ">",
1165
+ ">=",
1166
+ "<",
1167
+ "<="
1168
+ ];
1169
+ default: return [...allOperators, "null"];
1170
+ }
1171
+ }
1172
+ /**
1173
+ * Get operator symbol and description
1174
+ */
1175
+ const OPERATOR_INFO = {
1176
+ eq: {
1177
+ symbol: "=",
1178
+ label: "equals",
1179
+ example: "name=John"
1180
+ },
1181
+ ne: {
1182
+ symbol: "!=",
1183
+ label: "not equals",
1184
+ example: "status!=archived"
1185
+ },
1186
+ gt: {
1187
+ symbol: ">",
1188
+ label: "greater than",
1189
+ example: "age>18"
1190
+ },
1191
+ gte: {
1192
+ symbol: ">=",
1193
+ label: "greater or equal",
1194
+ example: "age>=18"
1195
+ },
1196
+ lt: {
1197
+ symbol: "<",
1198
+ label: "less than",
1199
+ example: "age<65"
1200
+ },
1201
+ lte: {
1202
+ symbol: "<=",
1203
+ label: "less or equal",
1204
+ example: "age<=65"
1205
+ },
1206
+ like: {
1207
+ symbol: "~",
1208
+ label: "like (case-sensitive)",
1209
+ example: "name~John"
1210
+ },
1211
+ ilike: {
1212
+ symbol: "~*",
1213
+ label: "like (case-insensitive)",
1214
+ example: "name~*john"
1215
+ },
1216
+ null: {
1217
+ symbol: "=null",
1218
+ label: "is null",
1219
+ example: "deletedAt=null"
1220
+ },
1221
+ notNull: {
1222
+ symbol: "!=null",
1223
+ label: "is not null",
1224
+ example: "email!=null"
1225
+ },
1226
+ in: {
1227
+ symbol: "[...]",
1228
+ label: "in array",
1229
+ example: "status=[active,pending]"
1230
+ }
1231
+ };
1232
+
1233
+ //#endregion
1234
+ //#region src/components/form/ControlQueryBuilder.tsx
1235
+ /**
1236
+ * Query builder with text input and help popover.
1237
+ * Generates query strings for parseQueryString syntax.
1238
+ */
1239
+ const ControlQueryBuilder = ({ schema, value = "", onChange, placeholder = "Enter query or click for assistance...", ...textInputProps }) => {
1240
+ const [helpOpened, setHelpOpened] = (0, react.useState)(false);
1241
+ const [textValue, setTextValue] = (0, react.useState)(value);
1242
+ const inputRef = (0, react.useRef)(null);
1243
+ const fields = schema ? extractSchemaFields(schema) : [];
1244
+ const [error, setError] = (0, react.useState)(null);
1245
+ const isValid = (value$1) => {
1246
+ try {
1247
+ (0, __alepha_postgres.parseQueryString)(value$1.trim());
1248
+ } catch (e) {
1249
+ setError(e.message);
1250
+ return false;
1251
+ }
1252
+ setError(null);
1253
+ return true;
1254
+ };
1255
+ const handleTextChange = (newValue) => {
1256
+ setTextValue(newValue);
1257
+ if (isValid(newValue)) onChange?.(newValue);
1258
+ };
1259
+ const handleClear = () => {
1260
+ setTextValue("");
1261
+ onChange?.("");
1262
+ isValid("");
1263
+ };
1264
+ const handleInsert = (text) => {
1265
+ const newValue = textValue ? `${textValue}${text} ` : `${text} `;
1266
+ setTextValue(newValue);
1267
+ if (isValid(newValue)) onChange?.(newValue);
1268
+ setTimeout(() => {
1269
+ inputRef.current?.focus();
1270
+ const length = inputRef.current?.value.length || 0;
1271
+ inputRef.current?.setSelectionRange(length, length);
1272
+ }, 0);
1273
+ };
1274
+ (0, __alepha_react.useEvents)({ "form:change": (event) => {
1275
+ if (event.id === inputRef.current?.form?.id) {
1276
+ if (event.path === textInputProps["data-path"]) setTextValue(event.value ?? "");
1277
+ }
1278
+ } }, []);
1279
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Popover, {
1280
+ width: 800,
1281
+ position: "bottom-start",
1282
+ shadow: "md",
1283
+ opened: helpOpened,
1284
+ onChange: setHelpOpened,
1285
+ closeOnClickOutside: true,
1286
+ closeOnEscape: true,
1287
+ transitionProps: {
1288
+ transition: "fade-up",
1289
+ duration: 200,
1290
+ timingFunction: "ease"
1291
+ },
1292
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Popover.Target, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.TextInput, {
1293
+ ref: inputRef,
1294
+ placeholder,
1295
+ value: textValue,
1296
+ onChange: (e) => handleTextChange(e.currentTarget.value),
1297
+ onFocus: () => setHelpOpened(true),
1298
+ leftSection: error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconInfoTriangle, { size: 16 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconFilter, { size: 16 }),
1299
+ rightSection: textValue && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.ActionIcon, {
1300
+ size: "sm",
1301
+ variant: "subtle",
1302
+ color: "gray",
1303
+ onClick: handleClear,
1304
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconX, { size: 14 })
1305
+ }),
1306
+ ...textInputProps
1307
+ }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Popover.Dropdown, {
1308
+ bg: "transparent",
1309
+ p: "xs",
1310
+ bd: `1px solid ${ui.colors.border}`,
1311
+ style: { backdropFilter: "blur(20px)" },
1312
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QueryHelp, {
1313
+ fields,
1314
+ onInsert: handleInsert
1315
+ })
1316
+ })]
1317
+ });
1318
+ };
1319
+ function QueryHelp({ fields, onInsert }) {
1320
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Group, {
1321
+ gap: "md",
1322
+ align: "flex-start",
1323
+ wrap: "nowrap",
1324
+ bg: ui.colors.surface,
1325
+ p: "sm",
1326
+ bdrs: "sm",
1327
+ children: [
1328
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Stack, {
1329
+ gap: "md",
1330
+ style: { flex: 1 },
1331
+ children: [
1332
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Stack, {
1333
+ gap: "xs",
1334
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Text, {
1335
+ size: "sm",
1336
+ fw: 600,
1337
+ children: "Operators"
1338
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Stack, {
1339
+ gap: 4,
1340
+ children: Object.entries(OPERATOR_INFO).map(([key, info]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Group, {
1341
+ gap: "xs",
1342
+ wrap: "nowrap",
1343
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionButton_default, {
1344
+ px: "xs",
1345
+ size: "xs",
1346
+ h: 24,
1347
+ variant: "default",
1348
+ justify: "center",
1349
+ miw: 48,
1350
+ onClick: () => onInsert(info.symbol),
1351
+ children: info.symbol
1352
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Text, {
1353
+ size: "xs",
1354
+ c: "dimmed",
1355
+ style: { flex: 1 },
1356
+ children: info.label
1357
+ })]
1358
+ }, key))
1359
+ })]
1360
+ }),
1361
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Divider, {}),
1362
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Stack, {
1363
+ gap: "xs",
1364
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Text, {
1365
+ size: "sm",
1366
+ fw: 600,
1367
+ children: "Logic"
1368
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Stack, {
1369
+ gap: 4,
1370
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Group, {
1371
+ gap: "xs",
1372
+ wrap: "nowrap",
1373
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionButton_default, {
1374
+ px: "xs",
1375
+ size: "xs",
1376
+ h: 24,
1377
+ variant: "default",
1378
+ justify: "center",
1379
+ miw: 48,
1380
+ onClick: () => onInsert("&"),
1381
+ children: "&"
1382
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Text, {
1383
+ size: "xs",
1384
+ c: "dimmed",
1385
+ children: "AND"
1386
+ })]
1387
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Group, {
1388
+ gap: "xs",
1389
+ wrap: "nowrap",
1390
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionButton_default, {
1391
+ px: "xs",
1392
+ size: "xs",
1393
+ h: 24,
1394
+ variant: "default",
1395
+ justify: "center",
1396
+ miw: 48,
1397
+ onClick: () => onInsert("|"),
1398
+ children: "|"
1399
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Text, {
1400
+ size: "xs",
1401
+ c: "dimmed",
1402
+ children: "OR"
1403
+ })]
1404
+ })]
1405
+ })]
1406
+ })
1407
+ ]
1408
+ }),
1409
+ fields.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Divider, { orientation: "vertical" }),
1410
+ fields.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Flex, {
1411
+ direction: "column",
1412
+ gap: "xs",
1413
+ style: { flex: 2 },
1414
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Text, {
1415
+ size: "sm",
1416
+ fw: 600,
1417
+ children: "Fields"
1418
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Flex, {
1419
+ direction: "column",
1420
+ gap: 4,
1421
+ style: {
1422
+ maxHeight: 300,
1423
+ overflowY: "auto"
1424
+ },
1425
+ children: fields.map((field) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Flex, {
1426
+ gap: "xs",
1427
+ wrap: "nowrap",
1428
+ align: "flex-start",
1429
+ children: [
1430
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionButton_default, {
1431
+ px: "xs",
1432
+ size: "xs",
1433
+ h: 24,
1434
+ variant: "default",
1435
+ justify: "end",
1436
+ miw: 120,
1437
+ onClick: () => onInsert(field.path),
1438
+ children: field.path
1439
+ }),
1440
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Flex, {
1441
+ mt: 3,
1442
+ direction: "column",
1443
+ gap: 2,
1444
+ style: {
1445
+ flex: 1,
1446
+ minWidth: 0
1447
+ },
1448
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Text, {
1449
+ size: "xs",
1450
+ c: "dimmed",
1451
+ lineClamp: 1,
1452
+ children: field.description || field.type
1453
+ }), field.enum && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Group, {
1454
+ gap: 0,
1455
+ wrap: "wrap",
1456
+ children: field.enum.map((enumValue) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionButton_default, {
1457
+ px: "xs",
1458
+ size: "xs",
1459
+ h: 24,
1460
+ onClick: () => onInsert(enumValue),
1461
+ children: enumValue
1462
+ }, enumValue))
1463
+ })]
1464
+ }),
1465
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Badge, {
1466
+ size: "xs",
1467
+ variant: "light",
1468
+ style: { flexShrink: 0 },
1469
+ children: field.type
1470
+ })
1471
+ ]
1472
+ }, field.path))
1473
+ })]
1474
+ })
1475
+ ]
1476
+ });
1477
+ }
1478
+ var ControlQueryBuilder_default = ControlQueryBuilder;
1479
+
1480
+ //#endregion
1481
+ //#region src/components/form/ControlSelect.tsx
1482
+ /**
1483
+ * ControlSelect component for handling Select, MultiSelect, and TagsInput.
1484
+ *
1485
+ * Features:
1486
+ * - Basic Select with enum support
1487
+ * - MultiSelect for array of enums
1488
+ * - TagsInput for array of strings (no enum)
1489
+ * - Future: Lazy loading
1490
+ * - Future: Searchable/filterable options
1491
+ * - Future: Custom option rendering
1492
+ *
1493
+ * Automatically detects enum values and array types from schema.
1494
+ */
1495
+ const ControlSelect = (props) => {
1496
+ const { inputProps, id, icon } = parseInput(props, (0, __alepha_react_form.useFormState)(props.input));
1497
+ const isArray = props.input.schema && "type" in props.input.schema && props.input.schema.type === "array";
1498
+ let itemsEnum;
1499
+ if (isArray && "items" in props.input.schema && props.input.schema.items) {
1500
+ const items = props.input.schema.items;
1501
+ if ("enum" in items && Array.isArray(items.enum)) itemsEnum = items.enum;
1502
+ }
1503
+ const enumValues = props.input.schema && "enum" in props.input.schema && Array.isArray(props.input.schema.enum) ? props.input.schema.enum : [];
1504
+ const [data, setData] = (0, react.useState)([]);
1505
+ (0, react.useEffect)(() => {
1506
+ if (!props.input?.props) return;
1507
+ if (props.loader) props.loader().then(setData);
1508
+ else setData(enumValues);
1509
+ }, [props.input, props.loader]);
1510
+ if (!props.input?.props) return null;
1511
+ if (props.segmented) {
1512
+ const segmentedControlProps = typeof props.segmented === "object" ? props.segmented : {};
1513
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Input.Wrapper, {
1514
+ ...inputProps,
1515
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Flex, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.SegmentedControl, {
1516
+ disabled: inputProps.disabled,
1517
+ defaultValue: String(props.input.props.defaultValue),
1518
+ ...segmentedControlProps,
1519
+ onChange: (value) => {
1520
+ props.input.set(value);
1521
+ },
1522
+ data: data.slice(0, 10)
1523
+ }) })
1524
+ });
1525
+ }
1526
+ if (props.autocomplete) {
1527
+ const autocompleteProps = typeof props.autocomplete === "object" ? props.autocomplete : {};
1528
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Autocomplete, {
1529
+ ...inputProps,
1530
+ id,
1531
+ leftSection: icon,
1532
+ data,
1533
+ ...props.input.props,
1534
+ ...autocompleteProps
1535
+ });
1536
+ }
1537
+ if (isArray && !itemsEnum || props.tags) {
1538
+ const tagsInputProps = typeof props.tags === "object" ? props.tags : {};
1539
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.TagsInput, {
1540
+ ...inputProps,
1541
+ id,
1542
+ leftSection: icon,
1543
+ defaultValue: Array.isArray(props.input.props.defaultValue) ? props.input.props.defaultValue : [],
1544
+ onChange: (value) => {
1545
+ props.input.set(value);
1546
+ },
1547
+ ...tagsInputProps
1548
+ });
1549
+ }
1550
+ if (isArray && itemsEnum || props.multi) {
1551
+ const data$1 = itemsEnum?.map((value) => ({
1552
+ value,
1553
+ label: value
1554
+ })) || [];
1555
+ const multiSelectProps = typeof props.multi === "object" ? props.multi : {};
1556
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.MultiSelect, {
1557
+ ...inputProps,
1558
+ id,
1559
+ leftSection: icon,
1560
+ data: data$1,
1561
+ defaultValue: Array.isArray(props.input.props.defaultValue) ? props.input.props.defaultValue : [],
1562
+ onChange: (value) => {
1563
+ props.input.set(value);
1564
+ },
1565
+ ...multiSelectProps
1566
+ });
1567
+ }
1568
+ const selectProps = typeof props.select === "object" ? props.select : {};
1569
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Select, {
1570
+ ...inputProps,
1571
+ id,
1572
+ leftSection: icon,
1573
+ data,
1574
+ ...props.input.props,
1575
+ ...selectProps
1576
+ });
1577
+ };
1578
+ var ControlSelect_default = ControlSelect;
1579
+
1580
+ //#endregion
1581
+ //#region src/components/form/Control.tsx
1582
+ /**
1583
+ * Generic form control that renders the appropriate input based on the schema and props.
1584
+ *
1585
+ * Supports:
1586
+ * - TextInput (with format detection: email, url, tel)
1587
+ * - Textarea
1588
+ * - NumberInput (for number/integer types)
1589
+ * - FileInput
1590
+ * - ColorInput (for color format)
1591
+ * - Select (for enum types)
1592
+ * - Autocomplete
1593
+ * - PasswordInput
1594
+ * - Switch (for boolean types)
1595
+ * - SegmentedControl (for enum types)
1596
+ * - DateInput (for date format)
1597
+ * - DateTimePicker (for date-time format)
1598
+ * - TimeInput (for time format)
1599
+ * - QueryBuilder (for building type-safe queries with autocomplete)
1600
+ * - Custom component
1601
+ *
1602
+ * Automatically handles labels, descriptions, error messages, required state, and default icons.
1603
+ */
1604
+ const Control = (_props) => {
1605
+ const { inputProps, id, icon, format, schema } = parseInput(_props, (0, __alepha_react_form.useFormState)(_props.input, ["error"]));
1606
+ if (!_props.input?.props) return null;
1607
+ const props = {
1608
+ ..._props,
1609
+ ...schema.$control
1610
+ };
1611
+ if (props.query) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ControlQueryBuilder_default, {
1612
+ ...props.input.props,
1613
+ ...inputProps,
1614
+ schema: props.query,
1615
+ value: props.input.props.value,
1616
+ onChange: (value) => {
1617
+ props.input.set(value);
1618
+ }
1619
+ });
1620
+ if (props.custom) {
1621
+ const Custom = props.custom;
1622
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Input.Wrapper, {
1623
+ ...inputProps,
1624
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Flex, {
1625
+ flex: 1,
1626
+ mt: "calc(var(--mantine-spacing-xs) / 2)",
1627
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Custom, {
1628
+ defaultValue: props.input.props.defaultValue,
1629
+ onChange: (value) => {
1630
+ props.input.set(value);
1631
+ }
1632
+ })
1633
+ })
1634
+ });
1635
+ }
1636
+ if (props.number || props.input.schema && "type" in props.input.schema && (props.input.schema.type === "number" || props.input.schema.type === "integer")) {
1637
+ const controlNumberProps = typeof props.number === "object" ? props.number : {};
1638
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ControlNumber_default, {
1639
+ input: props.input,
1640
+ title: props.title,
1641
+ description: props.description,
1642
+ icon,
1643
+ ...controlNumberProps
1644
+ });
1645
+ }
1646
+ if (props.file) {
1647
+ const fileInputProps = typeof props.file === "object" ? props.file : {};
1648
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.FileInput, {
1649
+ ...inputProps,
1650
+ id,
1651
+ leftSection: icon,
1652
+ onChange: (file) => {
1653
+ props.input.set(file);
1654
+ },
1655
+ ...fileInputProps
1656
+ });
1657
+ }
1658
+ if (props.color || format === "color") {
1659
+ const colorInputProps = typeof props.color === "object" ? props.color : {};
1660
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.ColorInput, {
1661
+ ...inputProps,
1662
+ id,
1663
+ leftSection: icon,
1664
+ ...props.input.props,
1665
+ ...colorInputProps
1666
+ });
1667
+ }
1668
+ const isEnum = props.input.schema && "enum" in props.input.schema && props.input.schema.enum;
1669
+ const isArray = props.input.schema && "type" in props.input.schema && props.input.schema.type === "array";
1670
+ if (isEnum || isArray || props.select) {
1671
+ const opts = typeof props.select === "object" ? props.select : {};
1672
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ControlSelect_default, {
1673
+ input: props.input,
1674
+ title: props.title,
1675
+ description: props.description,
1676
+ icon,
1677
+ ...opts
1678
+ });
1679
+ }
1680
+ if (props.input.schema && "type" in props.input.schema && props.input.schema.type === "boolean" || props.switch) {
1681
+ const switchProps = typeof props.switch === "object" ? props.switch : {};
1682
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Switch, {
1683
+ ...inputProps,
1684
+ id,
1685
+ color: "blue",
1686
+ defaultChecked: props.input.props.defaultValue,
1687
+ ...props.input.props,
1688
+ ...switchProps
1689
+ });
1690
+ }
1691
+ if (props.password || props.input.props.name?.includes("password")) {
1692
+ const passwordInputProps = typeof props.password === "object" ? props.password : {};
1693
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.PasswordInput, {
1694
+ ...inputProps,
1695
+ id,
1696
+ leftSection: icon,
1697
+ ...props.input.props,
1698
+ ...passwordInputProps
1699
+ });
1700
+ }
1701
+ if (props.area) {
1702
+ const textAreaProps = typeof props.area === "object" ? props.area : {};
1703
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Textarea, {
1704
+ ...inputProps,
1705
+ id,
1706
+ leftSection: icon,
1707
+ ...props.input.props,
1708
+ ...textAreaProps
1709
+ });
1710
+ }
1711
+ if (props.date || props.datetime || props.time || format === "date" || format === "date-time" || format === "time") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ControlDate_default, {
1712
+ input: props.input,
1713
+ title: props.title,
1714
+ description: props.description,
1715
+ icon,
1716
+ date: props.date,
1717
+ datetime: props.datetime,
1718
+ time: props.time
1719
+ });
1720
+ const textInputProps = typeof props.text === "object" ? props.text : {};
1721
+ const getInputType = () => {
1722
+ switch (format) {
1723
+ case "email": return "email";
1724
+ case "url":
1725
+ case "uri": return "url";
1726
+ case "tel":
1727
+ case "phone": return "tel";
1728
+ default: return;
1729
+ }
1730
+ };
1731
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.TextInput, {
1732
+ ...inputProps,
1733
+ id,
1734
+ leftSection: icon,
1735
+ type: getInputType(),
1736
+ ...props.input.props,
1737
+ ...textInputProps
1738
+ });
1739
+ };
1740
+ var Control_default = Control;
1741
+
1742
+ //#endregion
1743
+ //#region src/components/form/TypeForm.tsx
1744
+ /**
1745
+ * TypeForm component that automatically renders all form inputs based on schema.
1746
+ * Uses the Control component to render individual fields and Mantine Grid for responsive layout.
1747
+ *
1748
+ * @example
1749
+ * ```tsx
1750
+ * import { t } from "alepha";
1751
+ * import { useForm } from "@alepha/react-form";
1752
+ * import { TypeForm } from "@alepha/ui";
1753
+ *
1754
+ * const form = useForm({
1755
+ * schema: t.object({
1756
+ * username: t.text(),
1757
+ * email: t.text(),
1758
+ * age: t.integer(),
1759
+ * subscribe: t.boolean(),
1760
+ * }),
1761
+ * handler: (values) => {
1762
+ * console.log(values);
1763
+ * },
1764
+ * });
1765
+ *
1766
+ * return <TypeForm form={form} columns={2} />;
1767
+ * ```
1768
+ */
1769
+ const TypeForm = (props) => {
1770
+ const { form, columns = 3, children, controlProps, skipFormElement = false, skipSubmitButton = false, submitButtonProps } = props;
1771
+ const schema = props.schema || form.options.schema;
1772
+ if (!schema?.properties) return null;
1773
+ const supportedFields = Object.keys(schema.properties).filter((fieldName) => {
1774
+ const field = form.input[fieldName];
1775
+ if (!field || typeof field !== "object" || !("schema" in field)) return false;
1776
+ const schema$1 = field.schema;
1777
+ if ("type" in schema$1) {
1778
+ if (schema$1.type === "object") return false;
1779
+ }
1780
+ if ("properties" in schema$1 && schema$1.properties) return false;
1781
+ return true;
1782
+ });
1783
+ const colSpan = typeof columns === "number" ? {
1784
+ xs: 12,
1785
+ sm: 6,
1786
+ lg: 12 / columns
1787
+ } : {
1788
+ base: columns.base ? 12 / columns.base : void 0,
1789
+ xs: columns.xs ? 12 / columns.xs : 12,
1790
+ sm: columns.sm ? 12 / columns.sm : 6,
1791
+ md: columns.md ? 12 / columns.md : void 0,
1792
+ lg: columns.lg ? 12 / columns.lg : 4,
1793
+ xl: columns.xl ? 12 / columns.xl : void 0
1794
+ };
1795
+ const renderFields = () => {
1796
+ if (children) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: children(form.input) });
1797
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Grid, { children: supportedFields.map((fieldName) => {
1798
+ const field = form.input[fieldName];
1799
+ if (!field || typeof field !== "object" || !("schema" in field)) return null;
1800
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Grid.Col, {
1801
+ span: colSpan,
1802
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Control_default, {
1803
+ input: field,
1804
+ ...controlProps
1805
+ })
1806
+ }, fieldName);
1807
+ }) });
1808
+ };
1809
+ const content = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Flex, {
1810
+ direction: "column",
1811
+ gap: "sm",
1812
+ children: [renderFields(), !skipSubmitButton && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Flex, {
1813
+ gap: "sm",
1814
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionButton_default, {
1815
+ form,
1816
+ ...submitButtonProps,
1817
+ children: submitButtonProps?.children ?? "Submit"
1818
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionButton_default, {
1819
+ type: "reset",
1820
+ children: "Reset"
1821
+ })]
1822
+ })]
1823
+ });
1824
+ if (skipFormElement) return content;
1825
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("form", {
1826
+ ...form.props,
1827
+ children: content
1828
+ });
1829
+ };
1830
+ var TypeForm_default = TypeForm;
1831
+
1832
+ //#endregion
1833
+ //#region src/components/buttons/BurgerButton.tsx
1834
+ const BurgerButton = (props) => {
1835
+ const [opened, setOpened] = (0, __alepha_react.useStore)("alepha.ui.sidebar.opened");
1836
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Burger, {
1837
+ opened,
1838
+ onClick: () => setOpened(!opened),
1839
+ hiddenFrom: "sm",
1840
+ size: "sm",
1841
+ ...props
1842
+ });
1843
+ };
1844
+ var BurgerButton_default = BurgerButton;
1845
+
1846
+ //#endregion
1847
+ //#region src/components/buttons/LanguageButton.tsx
1848
+ const LanguageButton = (props) => {
1849
+ const i18n = (0, __alepha_react_i18n.useI18n)();
1850
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionButton_default, {
1851
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconLanguage, {}),
1852
+ variant: "outline",
1853
+ menu: { items: i18n.languages.map((lang) => ({
1854
+ label: i18n.tr(lang),
1855
+ onClick: () => i18n.setLang(lang),
1856
+ active: i18n.lang === lang
1857
+ })) },
1858
+ ...props.actionProps
1859
+ });
1860
+ };
1861
+ var LanguageButton_default = LanguageButton;
1862
+
1863
+ //#endregion
1864
+ //#region src/components/layout/AppBar.tsx
1865
+ const AppBar = (props) => {
1866
+ const { items = [] } = props;
1867
+ const renderItem = (item, index) => {
1868
+ if ("type" in item) {
1869
+ if (item.type === "burger") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BurgerButton_default, {}, index);
1870
+ if (item.type === "dark") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DarkModeButton_default, { ...item.props }, index);
1871
+ if (item.type === "search") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OmnibarButton_default, { ...item.props }, index);
1872
+ if (item.type === "lang") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LanguageButton_default, { ...item.props }, index);
1873
+ if (item.type === "spacer") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Flex, { w: 16 }, index);
1874
+ if (item.type === "divider") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Divider, { orientation: "vertical" }, index);
1875
+ }
1876
+ if ("element" in item) return item.element;
1877
+ return null;
1878
+ };
1879
+ const leftItems = items.filter((item) => item.position === "left");
1880
+ const centerItems = items.filter((item) => item.position === "center");
1881
+ const rightItems = items.filter((item) => item.position === "right");
1882
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Flex, {
1883
+ h: "100%",
1884
+ align: "center",
1885
+ px: "md",
1886
+ justify: "space-between",
1887
+ ...props.flexProps,
1888
+ children: [
1889
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Flex, {
1890
+ flex: 1,
1891
+ children: leftItems.map((item, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Flex, {
1892
+ ml: index === 0 ? 0 : "md",
1893
+ align: "center",
1894
+ children: renderItem(item, index)
1895
+ }, index))
1896
+ }),
1897
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Flex, { children: centerItems.map((item, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Flex, {
1898
+ mx: "md",
1899
+ align: "center",
1900
+ children: renderItem(item, index)
1901
+ }, index)) }),
1902
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Flex, {
1903
+ flex: 1,
1904
+ gap: "md",
1905
+ align: "center",
1906
+ justify: "end",
1907
+ children: rightItems.map((item, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Flex, {
1908
+ ml: index === 0 ? 0 : "md",
1909
+ align: "center",
1910
+ children: renderItem(item, index)
1911
+ }, index))
1912
+ })
1913
+ ]
1914
+ });
1915
+ };
1916
+ var AppBar_default = AppBar;
1917
+
1918
+ //#endregion
1919
+ //#region src/components/layout/Sidebar.tsx
1920
+ const Sidebar = (props) => {
1921
+ const router = (0, __alepha_react.useRouter)();
1922
+ const { top = [], bottom = [], onItemClick } = props;
1923
+ const renderNode = (item, key) => {
1924
+ if ("type" in item) {
1925
+ if (item.type === "spacer") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Flex, { h: 16 }, key);
1926
+ if (item.type === "divider") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Flex, {
1927
+ h: 1,
1928
+ bg: "var(--alepha-border)",
1929
+ my: "md",
1930
+ mx: "sm"
1931
+ }, key);
1932
+ if (item.type === "search") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OmnibarButton_default, { collapsed: props.collapsed }, key);
1933
+ if (item.type === "section") {
1934
+ if (props.collapsed) return;
1935
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Flex, {
1936
+ mt: "md",
1937
+ mb: "xs",
1938
+ align: "center",
1939
+ gap: "xs",
1940
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.ThemeIcon, {
1941
+ c: "dimmed",
1942
+ size: "xs",
1943
+ variant: "transparent",
1944
+ children: item.icon
1945
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Text, {
1946
+ size: "xs",
1947
+ c: "dimmed",
1948
+ tt: "uppercase",
1949
+ fw: "bold",
1950
+ children: item.label
1951
+ }, key)]
1952
+ });
1953
+ }
1954
+ }
1955
+ if ("element" in item) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Flex, { children: item.element }, key);
1956
+ if (props.collapsed) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarCollapsedItem, {
1957
+ item,
1958
+ level: 0,
1959
+ onItemClick,
1960
+ theme: props.theme ?? {}
1961
+ }, key);
1962
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarItem, {
1963
+ item,
1964
+ level: 0,
1965
+ onItemClick,
1966
+ theme: props.theme ?? {}
1967
+ }, key);
1968
+ };
1969
+ const padding = "md";
1970
+ const gap = props.gap;
1971
+ const menu = props.menu ?? router.concretePages.map((page) => ({
1972
+ label: page.label ?? page.name,
1973
+ description: page.description,
1974
+ icon: page.icon,
1975
+ href: page.path
1976
+ }));
1977
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Flex, {
1978
+ flex: 1,
1979
+ py: padding,
1980
+ direction: "column",
1981
+ className: "overflow-auto",
1982
+ ...props.flexProps,
1983
+ children: [
1984
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Flex, {
1985
+ gap,
1986
+ px: padding,
1987
+ direction: "column",
1988
+ children: [top.map((item, index) => renderNode(item, index)), menu.filter((it) => it.position === "top").map((item, index) => renderNode(item, index + top.length))]
1989
+ }),
1990
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Flex, {
1991
+ gap,
1992
+ px: padding,
1993
+ direction: "column",
1994
+ flex: 1,
1995
+ className: "overflow-auto",
1996
+ children: menu.filter((it) => !it.position).map((item, index) => renderNode(item, index))
1997
+ }),
1998
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Flex, {
1999
+ gap,
2000
+ px: padding,
2001
+ direction: "column",
2002
+ children: [bottom.map((item, index) => renderNode(item, index)), menu.filter((it) => it.position === "bottom").map((item, index) => renderNode(item, index + bottom.length))]
2003
+ })
2004
+ ]
2005
+ });
2006
+ };
2007
+ const SidebarItem = (props) => {
2008
+ const { item, level } = props;
2009
+ const maxLevel = 2;
2010
+ const router = (0, __alepha_react.useRouter)();
2011
+ const isActive = (0, react.useCallback)((item$1) => {
2012
+ if (!item$1.children) return false;
2013
+ for (const child of item$1.children) {
2014
+ if (child.href) {
2015
+ if (router.isActive(child.href)) return true;
2016
+ }
2017
+ if (isActive(child)) return true;
2018
+ }
2019
+ return false;
2020
+ }, []);
2021
+ const [isOpen, setIsOpen] = (0, react.useState)(isActive(item));
2022
+ (0, __alepha_react.useEvents)({ "react:transition:end": () => {
2023
+ if (isActive(item)) setIsOpen(true);
2024
+ } }, []);
2025
+ if (level > maxLevel) return null;
2026
+ const handleItemClick = (e) => {
2027
+ if (!props.item.target) e.preventDefault();
2028
+ if (item.children && item.children.length > 0) setIsOpen(!isOpen);
2029
+ else {
2030
+ props.onItemClick?.(item);
2031
+ item.onClick?.();
2032
+ }
2033
+ };
2034
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Flex, {
2035
+ direction: "column",
2036
+ ps: level === 0 ? 0 : 32,
2037
+ pos: "relative",
2038
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionButton_default, {
2039
+ w: "100%",
2040
+ justify: "space-between",
2041
+ href: props.item.href,
2042
+ target: props.item.target,
2043
+ variant: "subtle",
2044
+ size: props.item.theme?.size ?? props.theme.button?.size ?? (level === 0 ? "sm" : "xs"),
2045
+ variantActive: "default",
2046
+ radius: props.item.theme?.radius ?? props.theme.button?.radius ?? "md",
2047
+ onClick: handleItemClick,
2048
+ leftSection: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Flex, {
2049
+ w: "100%",
2050
+ align: "center",
2051
+ gap: "sm",
2052
+ children: [item.icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.ThemeIcon, {
2053
+ size: level === 0 ? "sm" : "xs",
2054
+ variant: "transparent",
2055
+ children: item.icon
2056
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Flex, {
2057
+ direction: "column",
2058
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Flex, { children: item.label }), item.description && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Text, {
2059
+ size: "xs",
2060
+ c: "dimmed",
2061
+ children: item.description
2062
+ })]
2063
+ })]
2064
+ }),
2065
+ rightSection: item.children ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Flex, { children: isOpen ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconChevronDown, { size: 14 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconChevronRight, { size: 14 }) }) : props.item.rightSection,
2066
+ ...props.item.actionProps
2067
+ }), item.children && isOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Flex, {
2068
+ direction: "column",
2069
+ "data-parent-level": level,
2070
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Flex, { style: {
2071
+ position: "absolute",
2072
+ width: 1,
2073
+ background: "linear-gradient(to bottom, transparent, var(--alepha-border), transparent)",
2074
+ top: 48,
2075
+ left: 20 + 32 * level,
2076
+ bottom: 16
2077
+ } }), item.children.map((child, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarItem, {
2078
+ item: child,
2079
+ level: level + 1,
2080
+ onItemClick: props.onItemClick,
2081
+ theme: props.theme
2082
+ }, index))]
2083
+ })]
2084
+ });
2085
+ };
2086
+ const SidebarCollapsedItem = (props) => {
2087
+ const { item, level } = props;
2088
+ const router = (0, __alepha_react.useRouter)();
2089
+ const isActive = (0, react.useCallback)((item$1) => {
2090
+ if (!item$1.children) return false;
2091
+ for (const child of item$1.children) {
2092
+ if (child.href) {
2093
+ if (router.isActive(child.href)) return true;
2094
+ }
2095
+ if (isActive(child)) return true;
2096
+ }
2097
+ return false;
2098
+ }, []);
2099
+ const [isOpen, setIsOpen] = (0, react.useState)(isActive(item));
2100
+ const handleItemClick = (e) => {
2101
+ if (!props.item.target) e.preventDefault();
2102
+ if (item.children && item.children.length > 0) setIsOpen(!isOpen);
2103
+ else {
2104
+ props.onItemClick?.(item);
2105
+ item.onClick?.();
2106
+ }
2107
+ };
2108
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionButton_default, {
2109
+ variant: "subtle",
2110
+ size: props.item.theme?.size ?? props.theme.button?.size ?? (level === 0 ? "sm" : "xs"),
2111
+ variantActive: "default",
2112
+ radius: props.item.theme?.radius ?? props.theme.button?.radius ?? "md",
2113
+ onClick: handleItemClick,
2114
+ icon: item.icon ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tabler_icons_react.IconSquareRounded, {}),
2115
+ href: props.item.href,
2116
+ target: props.item.target,
2117
+ menu: item.children ? {
2118
+ position: "right",
2119
+ on: "hover",
2120
+ items: item.children.map((child) => ({
2121
+ label: child.label,
2122
+ href: child.href,
2123
+ icon: child.icon,
2124
+ children: child.children
2125
+ }))
2126
+ } : void 0,
2127
+ ...props.item.actionProps
2128
+ });
2129
+ };
2130
+
2131
+ //#endregion
2132
+ //#region src/components/layout/AdminShell.tsx
2133
+ const AdminShell = (props) => {
2134
+ const [opened, setOpened] = (0, __alepha_react.useStore)("alepha.ui.sidebar.opened");
2135
+ const [collapsed] = (0, __alepha_react.useStore)("alepha.ui.sidebar.collapsed", props.sidebarProps?.collapsed);
2136
+ (0, __alepha_react.useEvents)({ "react:transition:begin": () => {
2137
+ setOpened(false);
2138
+ } }, []);
2139
+ const defaultAppBarItems = [{
2140
+ position: "left",
2141
+ type: "burger"
2142
+ }];
2143
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.AppShell, {
2144
+ padding: "md",
2145
+ header: { height: 60 },
2146
+ navbar: props.sidebarProps !== void 0 ? {
2147
+ width: collapsed ? { base: 72 } : { base: 300 },
2148
+ breakpoint: "sm",
2149
+ collapsed: { mobile: !opened }
2150
+ } : void 0,
2151
+ footer: props.footer ? { height: 60 } : void 0,
2152
+ ...props.appShellProps,
2153
+ children: [
2154
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.AppShell.Header, {
2155
+ bg: ui.colors.surface,
2156
+ ...props.appShellHeaderProps,
2157
+ children: props.header ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AppBar_default, {
2158
+ items: defaultAppBarItems,
2159
+ ...props.appBarProps
2160
+ })
2161
+ }),
2162
+ props.sidebarProps !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.AppShell.Navbar, {
2163
+ bg: ui.colors.surface,
2164
+ ...props.appShellNavbarProps,
2165
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Sidebar, {
2166
+ collapsed,
2167
+ ...props.sidebarProps
2168
+ })
2169
+ }),
2170
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.AppShell.Main, {
2171
+ ...props.appShellMainProps,
2172
+ children: props.children ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__alepha_react.NestedView, {})
2173
+ }),
2174
+ props.footer && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.AppShell.Footer, {
2175
+ bg: ui.colors.surface,
2176
+ ...props.appShellFooterProps,
2177
+ children: props.footer
2178
+ })
2179
+ ]
2180
+ });
2181
+ };
2182
+ var AdminShell_default = AdminShell;
2183
+
2184
+ //#endregion
2185
+ //#region src/components/table/DataTable.tsx
2186
+ const DataTable = (props) => {
2187
+ const [items, setItems] = (0, react.useState)(typeof props.items === "function" ? { content: [] } : props.items);
2188
+ const defaultSize = props.infinityScroll ? 100 : props.defaultSize || 10;
2189
+ const [page, setPage] = (0, react.useState)(1);
2190
+ const [size, setSize] = (0, react.useState)(String(defaultSize));
2191
+ const [currentPage, setCurrentPage] = (0, react.useState)(0);
2192
+ const alepha = (0, __alepha_react.useInject)(__alepha_core.Alepha);
2193
+ const form = (0, __alepha_react_form.useForm)({
2194
+ schema: __alepha_core.t.object({
2195
+ ...props.filters ? props.filters.properties : {},
2196
+ page: __alepha_core.t.number({ default: 0 }),
2197
+ size: __alepha_core.t.number({ default: defaultSize }),
2198
+ sort: __alepha_core.t.optional(__alepha_core.t.string())
2199
+ }),
2200
+ handler: async (values, args) => {
2201
+ if (typeof props.items === "function") {
2202
+ const response = await props.items(values, { items: items.content });
2203
+ if (props.infinityScroll && values.page > 0) setItems((prev) => ({
2204
+ ...response,
2205
+ content: [...prev.content, ...response.content]
2206
+ }));
2207
+ else setItems(response);
2208
+ setCurrentPage(values.page);
2209
+ }
2210
+ },
2211
+ onReset: async () => {
2212
+ setPage(1);
2213
+ setSize("10");
2214
+ await form.submit();
2215
+ },
2216
+ onChange: async (key, value) => {
2217
+ if (key === "page") {
2218
+ setPage(value + 1);
2219
+ await form.submit();
2220
+ return;
2221
+ }
2222
+ if (key === "size") {
2223
+ setSize(String(value));
2224
+ form.input.page.set(0);
2225
+ return;
2226
+ }
2227
+ props.onFilterChange?.(key, value, form);
2228
+ }
2229
+ }, [items]);
2230
+ (0, __mantine_hooks.useDebouncedCallback)(() => form.submit(), { delay: 800 });
2231
+ const dt = (0, __alepha_react.useInject)(__alepha_datetime.DateTimeProvider);
2232
+ (0, react.useEffect)(() => {
2233
+ if (props.submitOnInit) form.submit();
2234
+ if (props.submitEvery) {
2235
+ const it = dt.createInterval(() => {
2236
+ form.submit();
2237
+ }, props.submitEvery);
2238
+ return () => dt.clearInterval(it);
2239
+ }
2240
+ }, []);
2241
+ (0, react.useEffect)(() => {
2242
+ if (typeof props.items !== "function") setItems(props.items);
2243
+ }, [props.items]);
2244
+ (0, react.useEffect)(() => {
2245
+ if (!props.infinityScroll || typeof props.items !== "function") return;
2246
+ const handleScroll = () => {
2247
+ if (form.submitting) return;
2248
+ const scrollTop = window.scrollY;
2249
+ const windowHeight = window.innerHeight;
2250
+ const docHeight = document.documentElement.scrollHeight;
2251
+ if (scrollTop + windowHeight >= docHeight - 300) {
2252
+ const totalPages = items.page?.totalPages ?? 1;
2253
+ if (currentPage + 1 < totalPages) form.input.page.set(currentPage + 1);
2254
+ }
2255
+ };
2256
+ window.addEventListener("scroll", handleScroll);
2257
+ return () => window.removeEventListener("scroll", handleScroll);
2258
+ }, [
2259
+ props.infinityScroll,
2260
+ form.submitting,
2261
+ items.page?.totalPages,
2262
+ currentPage,
2263
+ form
2264
+ ]);
2265
+ const head = Object.entries(props.columns).map(([key, col]) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Table.Th, {
2266
+ style: { ...col.fit ? {
2267
+ width: "1%",
2268
+ whiteSpace: "nowrap"
2269
+ } : {} },
2270
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionButton_default, {
2271
+ justify: "space-between",
2272
+ radius: 0,
2273
+ fullWidth: true,
2274
+ size: "xs",
2275
+ children: col.label
2276
+ })
2277
+ }, key));
2278
+ const rows = items.content.map((item, index) => {
2279
+ const trProps = props.tableTrProps ? props.tableTrProps(item) : {};
2280
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Table.Tr, {
2281
+ ...trProps,
2282
+ children: Object.entries(props.columns).map(([key, col]) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Table.Td, { children: col.value(item, {
2283
+ index,
2284
+ form,
2285
+ alepha
2286
+ }) }, key))
2287
+ }, JSON.stringify(item));
2288
+ });
2289
+ const schema = __alepha_core.t.omit(form.options.schema, [
2290
+ "page",
2291
+ "size",
2292
+ "sort"
2293
+ ]);
2294
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Flex, {
2295
+ direction: "column",
2296
+ gap: "sm",
2297
+ flex: 1,
2298
+ children: [
2299
+ props.filters ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TypeForm_default, {
2300
+ ...props.typeFormProps,
2301
+ form,
2302
+ schema
2303
+ }) : null,
2304
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Flex, {
2305
+ flex: 1,
2306
+ className: "overflow-auto",
2307
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Table, {
2308
+ striped: true,
2309
+ withRowBorders: true,
2310
+ withColumnBorders: true,
2311
+ withTableBorder: true,
2312
+ stripedColor: "",
2313
+ ...props.tableProps,
2314
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Table.Thead, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Table.Tr, { children: head }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Table.Tbody, { children: rows })]
2315
+ })
2316
+ }),
2317
+ !props.infinityScroll && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__mantine_core.Flex, {
2318
+ justify: "space-between",
2319
+ align: "center",
2320
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Pagination, {
2321
+ withEdges: true,
2322
+ total: items.page?.totalPages ?? 1,
2323
+ value: page,
2324
+ onChange: (value) => {
2325
+ form.input.page.set(value - 1);
2326
+ }
2327
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Flex, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__mantine_core.Select, {
2328
+ value: size,
2329
+ onChange: (value) => {
2330
+ form.input.size.set(Number(value));
2331
+ },
2332
+ data: [
2333
+ {
2334
+ value: "5",
2335
+ label: "5"
2336
+ },
2337
+ {
2338
+ value: "10",
2339
+ label: "10"
2340
+ },
2341
+ {
2342
+ value: "25",
2343
+ label: "25"
2344
+ },
2345
+ {
2346
+ value: "50",
2347
+ label: "50"
2348
+ },
2349
+ {
2350
+ value: "100",
2351
+ label: "100"
2352
+ }
2353
+ ]
2354
+ }) })]
2355
+ })
2356
+ ]
2357
+ });
2358
+ };
2359
+ var DataTable_default = DataTable;
2360
+
2361
+ //#endregion
2362
+ //#region src/hooks/useDialog.ts
2363
+ /**
2364
+ * Use this hook to access the Dialog Service for showing various dialog types.
2365
+ *
2366
+ * @example
2367
+ * const dialog = useDialog();
2368
+ * await dialog.alert({ title: "Alert", message: "This is an alert message" });
2369
+ * const confirmed = await dialog.confirm({ title: "Confirm", message: "Are you sure?" });
2370
+ * const input = await dialog.prompt({ title: "Input", message: "Enter your name:" });
2371
+ */
2372
+ const useDialog = () => {
2373
+ return (0, __alepha_react.useInject)(DialogService);
2374
+ };
2375
+
2376
+ //#endregion
2377
+ //#region src/index.ts
2378
+ /**
2379
+ * Mantine
2380
+ *
2381
+ * @module alepha.ui
2382
+ */
2383
+ const AlephaUI = (0, __alepha_core.$module)({
2384
+ name: "alepha.ui",
2385
+ services: [
2386
+ DialogService,
2387
+ require_AlephaMantineProvider.ToastService,
2388
+ RootRouter
2389
+ ],
2390
+ register: (alepha) => {
2391
+ alepha.with(__alepha_react_i18n.AlephaReactI18n);
2392
+ alepha.with(__alepha_react_head.AlephaReactHead);
2393
+ alepha.with(__alepha_react_form.AlephaReactForm);
2394
+ alepha.with(DialogService);
2395
+ alepha.with(require_AlephaMantineProvider.ToastService);
2396
+ }
2397
+ });
2398
+
2399
+ //#endregion
2400
+ exports.ActionButton = ActionButton_default;
2401
+ exports.AdminShell = AdminShell_default;
2402
+ exports.AlephaMantineProvider = require_AlephaMantineProvider.AlephaMantineProvider_default;
2403
+ exports.AlephaUI = AlephaUI;
2404
+ exports.AlertDialog = AlertDialog_default;
2405
+ exports.AppBar = AppBar_default;
2406
+ exports.ConfirmDialog = ConfirmDialog_default;
2407
+ exports.Control = Control_default;
2408
+ exports.ControlDate = ControlDate_default;
2409
+ exports.ControlQueryBuilder = ControlQueryBuilder_default;
2410
+ exports.ControlSelect = ControlSelect_default;
2411
+ exports.DarkModeButton = DarkModeButton_default;
2412
+ exports.DataTable = DataTable_default;
2413
+ exports.DialogService = DialogService;
2414
+ Object.defineProperty(exports, 'Flex', {
2415
+ enumerable: true,
2416
+ get: function () {
2417
+ return __mantine_core.Flex;
2418
+ }
2419
+ });
2420
+ exports.ICON_SIZES = ICON_SIZES;
2421
+ exports.JsonViewer = JsonViewer_default;
2422
+ exports.OPERATOR_INFO = OPERATOR_INFO;
2423
+ exports.Omnibar = require_AlephaMantineProvider.Omnibar_default;
2424
+ exports.OmnibarButton = OmnibarButton_default;
2425
+ exports.PromptDialog = PromptDialog_default;
2426
+ exports.RootRouter = RootRouter;
2427
+ exports.Sidebar = Sidebar;
2428
+ Object.defineProperty(exports, 'Text', {
2429
+ enumerable: true,
2430
+ get: function () {
2431
+ return __mantine_core.Text;
2432
+ }
2433
+ });
2434
+ exports.ToastService = require_AlephaMantineProvider.ToastService;
2435
+ exports.TypeForm = TypeForm_default;
2436
+ exports.capitalize = capitalize;
2437
+ exports.extractSchemaFields = extractSchemaFields;
2438
+ exports.getDefaultIcon = getDefaultIcon;
2439
+ exports.getOperatorsForField = getOperatorsForField;
2440
+ exports.prettyName = prettyName;
2441
+ exports.ui = ui;
2442
+ exports.useDialog = useDialog;
2443
+ exports.useToast = require_AlephaMantineProvider.useToast;
2444
+ //# sourceMappingURL=index.cjs.map