@alepha/ui 0.10.7 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,22 +1,224 @@
1
- import { n as Omnibar_default, t as AlephaMantineProvider_default } from "./AlephaMantineProvider-WfiC2EH6.js";
1
+ import { n as Omnibar_default, t as AlephaMantineProvider_default } from "./AlephaMantineProvider-DDbIijPF.js";
2
2
  import { $module, TypeBoxError } from "@alepha/core";
3
3
  import { $page, AlephaReact, useActive, useAlepha, useInject, useRouter } from "@alepha/react";
4
- import { notifications } from "@mantine/notifications";
5
- import { IconAlertTriangle, IconAt, IconCalendar, IconCheck, IconClock, IconColorPicker, IconFile, IconHash, IconInfoCircle, IconKey, IconLetterCase, IconLink, IconList, IconMail, IconMoon, IconPalette, IconPhone, IconSelector, IconSun, IconToggleLeft, IconX } from "@tabler/icons-react";
4
+ import { modals } from "@mantine/modals";
5
+ import { ActionIcon, Autocomplete, Badge, Box, Button, Center, Checkbox, ColorInput, FileInput, Flex, Flex as Flex$1, Grid, Group, Input, Loader, Menu, MultiSelect, NumberInput, Pagination, Paper, PasswordInput, ScrollArea, SegmentedControl, Select, Switch, Table, TagsInput, Text, TextInput, Textarea, Tooltip, UnstyledButton, useComputedColorScheme, useMantineColorScheme } from "@mantine/core";
6
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
- import { ActionIcon, Autocomplete, Button, ColorInput, FileInput, Flex, Flex as Flex$1, Grid, Input, MultiSelect, NumberInput, PasswordInput, SegmentedControl, Select, Stack, Switch, TagsInput, TextInput, Textarea, useComputedColorScheme, useMantineColorScheme } from "@mantine/core";
7
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
8
+ import { notifications } from "@mantine/notifications";
9
+ import { IconAlertTriangle, IconAt, IconCalendar, IconCheck, IconChevronDown, IconChevronRight, IconChevronUp, IconCircle, IconClock, IconColorPicker, IconColumns, IconDownload, IconFile, IconHash, IconInfoCircle, IconKey, IconLetterCase, IconLink, IconList, IconMail, IconMoon, IconPalette, IconPhone, IconRefresh, IconSearch, IconSelector, IconSun, IconToggleLeft, IconX } from "@tabler/icons-react";
8
10
  import { useFormState } from "@alepha/react-form";
9
- import { useEffect, useState } from "react";
10
11
  import { DateInput, DateTimePicker, TimeInput } from "@mantine/dates";
12
+ import { useActive as useActive$1 } from "@alepha/react/src/hooks/useActive";
11
13
 
12
14
  //#region src/RootRouter.ts
13
15
  var RootRouter = class {
14
16
  root = $page({
15
17
  path: "/",
16
- lazy: () => import("./AlephaMantineProvider-EemOtraW.js")
18
+ lazy: () => import("./AlephaMantineProvider-pOu8hOzK.js")
17
19
  });
18
20
  };
19
21
 
22
+ //#endregion
23
+ //#region src/components/dialogs/AlertDialog.tsx
24
+ function AlertDialog({ options, onClose }) {
25
+ return /* @__PURE__ */ jsxs(Fragment, { children: [options?.message && /* @__PURE__ */ jsx(Text, {
26
+ mb: "md",
27
+ children: options.message
28
+ }), /* @__PURE__ */ jsx(Group, {
29
+ justify: "flex-end",
30
+ children: /* @__PURE__ */ jsx(Button, {
31
+ onClick: onClose,
32
+ children: options?.okLabel || "OK"
33
+ })
34
+ })] });
35
+ }
36
+
37
+ //#endregion
38
+ //#region src/components/dialogs/ConfirmDialog.tsx
39
+ function ConfirmDialog({ options, onConfirm }) {
40
+ return /* @__PURE__ */ jsxs(Fragment, { children: [options?.message && /* @__PURE__ */ jsx(Text, {
41
+ mb: "md",
42
+ children: options.message
43
+ }), /* @__PURE__ */ jsxs(Group, {
44
+ justify: "flex-end",
45
+ children: [/* @__PURE__ */ jsx(Button, {
46
+ variant: "subtle",
47
+ onClick: () => onConfirm(false),
48
+ children: options?.cancelLabel || "Cancel"
49
+ }), /* @__PURE__ */ jsx(Button, {
50
+ color: options?.confirmColor || "blue",
51
+ onClick: () => onConfirm(true),
52
+ children: options?.confirmLabel || "Confirm"
53
+ })]
54
+ })] });
55
+ }
56
+
57
+ //#endregion
58
+ //#region src/components/dialogs/PromptDialog.tsx
59
+ function PromptDialog({ options, onSubmit }) {
60
+ const [value, setValue] = useState(options?.defaultValue || "");
61
+ const inputRef = useRef(null);
62
+ useEffect(() => {
63
+ inputRef.current?.focus();
64
+ }, []);
65
+ const handleSubmit = () => {
66
+ if (!options?.required || value.trim()) onSubmit(value);
67
+ };
68
+ const handleKeyDown = (event) => {
69
+ if (event.key === "Enter") handleSubmit();
70
+ };
71
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
72
+ options?.message && /* @__PURE__ */ jsx(Text, {
73
+ mb: "md",
74
+ children: options.message
75
+ }),
76
+ /* @__PURE__ */ jsx(TextInput, {
77
+ ref: inputRef,
78
+ label: options?.label,
79
+ placeholder: options?.placeholder,
80
+ value,
81
+ onChange: (event) => setValue(event.currentTarget.value),
82
+ onKeyDown: handleKeyDown,
83
+ required: options?.required,
84
+ mb: "md"
85
+ }),
86
+ /* @__PURE__ */ jsxs(Group, {
87
+ justify: "flex-end",
88
+ children: [/* @__PURE__ */ jsx(Button, {
89
+ variant: "subtle",
90
+ onClick: () => onSubmit(null),
91
+ children: options?.cancelLabel || "Cancel"
92
+ }), /* @__PURE__ */ jsx(Button, {
93
+ onClick: handleSubmit,
94
+ disabled: options?.required && !value.trim(),
95
+ children: options?.submitLabel || "OK"
96
+ })]
97
+ })
98
+ ] });
99
+ }
100
+
101
+ //#endregion
102
+ //#region src/services/DialogService.tsx
103
+ var DialogService = class {
104
+ options = { default: {
105
+ centered: true,
106
+ withCloseButton: true,
107
+ size: "md",
108
+ overlayProps: {
109
+ backgroundOpacity: .55,
110
+ blur: 3
111
+ },
112
+ transitionProps: {
113
+ transition: "pop",
114
+ duration: 200
115
+ }
116
+ } };
117
+ /**
118
+ * Show an alert dialog with a message
119
+ */
120
+ alert(options) {
121
+ return new Promise((resolve) => {
122
+ const modalId = this.open({
123
+ ...options,
124
+ title: options?.title || "Alert",
125
+ content: /* @__PURE__ */ jsx(AlertDialog, {
126
+ options,
127
+ onClose: () => {
128
+ this.close(modalId);
129
+ resolve();
130
+ }
131
+ })
132
+ });
133
+ });
134
+ }
135
+ /**
136
+ * Show a confirmation dialog that returns a promise
137
+ */
138
+ confirm(options) {
139
+ return new Promise((resolve) => {
140
+ const modalId = this.open({
141
+ ...options,
142
+ title: options?.title || "Confirm",
143
+ closeOnClickOutside: false,
144
+ closeOnEscape: false,
145
+ content: /* @__PURE__ */ jsx(ConfirmDialog, {
146
+ options,
147
+ onConfirm: (confirmed) => {
148
+ this.close(modalId);
149
+ resolve(confirmed);
150
+ }
151
+ })
152
+ });
153
+ });
154
+ }
155
+ /**
156
+ * Show a prompt dialog to get user input
157
+ */
158
+ prompt(options) {
159
+ return new Promise((resolve) => {
160
+ const modalId = this.open({
161
+ ...options,
162
+ title: options?.title || "Input",
163
+ closeOnClickOutside: false,
164
+ closeOnEscape: false,
165
+ content: /* @__PURE__ */ jsx(PromptDialog, {
166
+ options,
167
+ onSubmit: (value) => {
168
+ this.close(modalId);
169
+ resolve(value);
170
+ }
171
+ })
172
+ });
173
+ });
174
+ }
175
+ /**
176
+ * Open a custom dialog with provided content
177
+ */
178
+ open(options) {
179
+ return modals.open({
180
+ ...this.options.default,
181
+ ...options,
182
+ children: options?.content || options?.message
183
+ });
184
+ }
185
+ /**
186
+ * Show a JSON editor/viewer dialog
187
+ */
188
+ json(data, options) {}
189
+ /**
190
+ * Show a form dialog for structured input
191
+ */
192
+ form(options) {
193
+ return Promise.resolve(null);
194
+ }
195
+ /**
196
+ * Close the currently open dialog or a specific dialog by ID
197
+ */
198
+ close(modalId) {
199
+ if (modalId) modals.close(modalId);
200
+ else modals.closeAll();
201
+ }
202
+ /**
203
+ * Show a loading/progress dialog with optional progress percentage
204
+ */
205
+ loading(options) {}
206
+ /**
207
+ * Show an image viewer/gallery dialog
208
+ */
209
+ image(src, options) {}
210
+ /**
211
+ * Show a table/data grid dialog for displaying tabular data
212
+ */
213
+ table(data, options) {}
214
+ /**
215
+ * Show a multi-step wizard dialog
216
+ */
217
+ wizard(steps, options) {
218
+ return Promise.resolve(null);
219
+ }
220
+ };
221
+
20
222
  //#endregion
21
223
  //#region src/services/ToastService.tsx
22
224
  var ToastService = class {
@@ -72,17 +274,38 @@ var ToastService = class {
72
274
 
73
275
  //#endregion
74
276
  //#region src/components/Action.tsx
277
+ const renderMenuItem = (item, index) => {
278
+ if (item.type === "divider") return /* @__PURE__ */ jsx(Menu.Divider, {}, index);
279
+ if (item.type === "label") return /* @__PURE__ */ jsx(Menu.Label, { children: item.label }, index);
280
+ if (item.children && item.children.length > 0) return /* @__PURE__ */ jsxs(Menu, {
281
+ trigger: "hover",
282
+ position: "right-start",
283
+ offset: 2,
284
+ children: [/* @__PURE__ */ jsx(Menu.Target, { children: /* @__PURE__ */ jsx(Menu.Item, {
285
+ leftSection: item.icon,
286
+ rightSection: /* @__PURE__ */ jsx(IconChevronRight, { size: 14 }),
287
+ children: item.label
288
+ }) }), /* @__PURE__ */ jsx(Menu.Dropdown, { children: item.children.map((child, childIndex) => renderMenuItem(child, childIndex)) })]
289
+ }, index);
290
+ return /* @__PURE__ */ jsx(Menu.Item, {
291
+ leftSection: item.icon,
292
+ onClick: item.onClick,
293
+ color: item.color,
294
+ children: item.label
295
+ }, index);
296
+ };
75
297
  const Action = (_props) => {
76
298
  const props = {
77
299
  variant: "subtle",
78
300
  ..._props
79
301
  };
302
+ const { tooltip, menu,...restProps } = props;
80
303
  if (props.leftSection && !props.children) {
81
- props.className ??= "mantine-Action-iconOnly";
82
- props.p ??= "xs";
304
+ restProps.className ??= "mantine-Action-iconOnly";
305
+ restProps.p ??= "xs";
83
306
  }
84
307
  if (props.textVisibleFrom) {
85
- const { children, textVisibleFrom, leftSection,...rest } = props;
308
+ const { children, textVisibleFrom, leftSection,...rest } = restProps;
86
309
  return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Flex$1, {
87
310
  w: "100%",
88
311
  visibleFrom: textVisibleFrom,
@@ -90,6 +313,8 @@ const Action = (_props) => {
90
313
  flex: 1,
91
314
  ...rest,
92
315
  leftSection,
316
+ tooltip,
317
+ menu,
93
318
  children
94
319
  })
95
320
  }), /* @__PURE__ */ jsx(Flex$1, {
@@ -98,32 +323,48 @@ const Action = (_props) => {
98
323
  children: /* @__PURE__ */ jsx(Action, {
99
324
  px: "xs",
100
325
  ...rest,
326
+ tooltip,
327
+ menu,
101
328
  children: leftSection
102
329
  })
103
330
  })] });
104
331
  }
105
332
  const renderAction = () => {
106
- if ("href" in props && props.href) return /* @__PURE__ */ jsx(ActionHref, {
107
- ...props,
108
- href: props.href,
109
- children: props.children
333
+ if ("href" in restProps && restProps.href) return /* @__PURE__ */ jsx(ActionHref, {
334
+ ...restProps,
335
+ href: restProps.href,
336
+ children: restProps.children
110
337
  });
111
- if ("onClick" in props && props.onClick) return /* @__PURE__ */ jsx(ActionClick, {
112
- ...props,
113
- onClick: props.onClick,
114
- children: props.children
338
+ if ("onClick" in restProps && restProps.onClick) return /* @__PURE__ */ jsx(ActionClick, {
339
+ ...restProps,
340
+ onClick: restProps.onClick,
341
+ children: restProps.children
115
342
  });
116
- if ("form" in props && props.form) return /* @__PURE__ */ jsx(ActionSubmit, {
117
- ...props,
118
- form: props.form,
119
- children: props.children
343
+ if ("form" in restProps && restProps.form) return /* @__PURE__ */ jsx(ActionSubmit, {
344
+ ...restProps,
345
+ form: restProps.form,
346
+ children: restProps.children
120
347
  });
121
348
  return /* @__PURE__ */ jsx(Button, {
122
- ...props,
123
- children: props.children
349
+ ...restProps,
350
+ children: restProps.children
124
351
  });
125
352
  };
126
- return renderAction();
353
+ let actionElement = renderAction();
354
+ if (menu) actionElement = /* @__PURE__ */ jsxs(Menu, {
355
+ position: menu.position || "bottom-start",
356
+ width: menu.width || 200,
357
+ shadow: menu.shadow || "md",
358
+ children: [/* @__PURE__ */ jsx(Menu.Target, { children: actionElement }), /* @__PURE__ */ jsx(Menu.Dropdown, { children: menu.items.map((item, index) => renderMenuItem(item, index)) })]
359
+ });
360
+ if (tooltip) return /* @__PURE__ */ jsx(Tooltip, { ...typeof tooltip === "string" ? {
361
+ label: tooltip,
362
+ children: actionElement
363
+ } : {
364
+ ...tooltip,
365
+ children: actionElement
366
+ } });
367
+ return actionElement;
127
368
  };
128
369
  var Action_default = Action;
129
370
  /**
@@ -263,6 +504,44 @@ const prettyName = (name) => {
263
504
  return capitalize(name.replaceAll("/", ""));
264
505
  };
265
506
 
507
+ //#endregion
508
+ //#region src/utils/parseInput.ts
509
+ const parseInput = (props, form) => {
510
+ const disabled = false;
511
+ const id = props.input.props.id;
512
+ 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);
513
+ const description = props.description ?? ("description" in props.input.schema && typeof props.input.schema.description === "string" ? props.input.schema.description : void 0);
514
+ const error = form.error && form.error instanceof TypeBoxError ? form.error.value.message : void 0;
515
+ const icon = props.icon ?? getDefaultIcon({
516
+ type: props.input.schema && "type" in props.input.schema ? String(props.input.schema.type) : void 0,
517
+ format: props.input.schema && "format" in props.input.schema && typeof props.input.schema.format === "string" ? props.input.schema.format : void 0,
518
+ name: props.input.props.name,
519
+ isEnum: props.input.schema && "enum" in props.input.schema && Boolean(props.input.schema.enum),
520
+ isArray: props.input.schema && "type" in props.input.schema && props.input.schema.type === "array"
521
+ });
522
+ const format = props.input.schema && "format" in props.input.schema && typeof props.input.schema.format === "string" ? props.input.schema.format : void 0;
523
+ const required = props.input.required;
524
+ const schema = props.input.schema;
525
+ const inputProps = {
526
+ label,
527
+ description,
528
+ error,
529
+ required,
530
+ disabled
531
+ };
532
+ if ("minLength" in schema && typeof schema.minLength === "number") inputProps.minLength = schema.minLength;
533
+ if ("maxLength" in schema && typeof schema.maxLength === "number") inputProps.maxLength = schema.maxLength;
534
+ if ("minimum" in schema && typeof schema.minimum === "number") inputProps.minimum = schema.minimum;
535
+ if ("maximum" in schema && typeof schema.maximum === "number") inputProps.maximum = schema.maximum;
536
+ return {
537
+ id,
538
+ icon,
539
+ format,
540
+ schema: props.input.schema,
541
+ inputProps
542
+ };
543
+ };
544
+
266
545
  //#endregion
267
546
  //#region src/components/ControlDate.tsx
268
547
  /**
@@ -276,9 +555,8 @@ const prettyName = (name) => {
276
555
  * Automatically detects date formats from schema and renders appropriate picker.
277
556
  */
278
557
  const ControlDate = (props) => {
279
- const { inputProps, id, icon } = parseInput(props, useFormState(props.input));
558
+ const { inputProps, id, icon, format } = parseInput(props, useFormState(props.input));
280
559
  if (!props.input?.props) return null;
281
- const format = props.input.schema && "format" in props.input.schema && typeof props.input.schema.format === "string" ? props.input.schema.format : void 0;
282
560
  if (props.datetime || format === "date-time") {
283
561
  const dateTimePickerProps = typeof props.datetime === "object" ? props.datetime : {};
284
562
  return /* @__PURE__ */ jsx(DateTimePicker, {
@@ -339,7 +617,6 @@ var ControlDate_default = ControlDate;
339
617
  */
340
618
  const ControlSelect = (props) => {
341
619
  const { inputProps, id, icon } = parseInput(props, useFormState(props.input));
342
- if (!props.input?.props) return null;
343
620
  const isArray = props.input.schema && "type" in props.input.schema && props.input.schema.type === "array";
344
621
  let itemsEnum;
345
622
  if (isArray && "items" in props.input.schema && props.input.schema.items) {
@@ -347,6 +624,42 @@ const ControlSelect = (props) => {
347
624
  if ("enum" in items && Array.isArray(items.enum)) itemsEnum = items.enum;
348
625
  }
349
626
  const enumValues = props.input.schema && "enum" in props.input.schema && Array.isArray(props.input.schema.enum) ? props.input.schema.enum : [];
627
+ const [data, setData] = useState([]);
628
+ useEffect(() => {
629
+ if (!props.input?.props) return;
630
+ if (props.loader) props.loader().then(setData);
631
+ else setData(enumValues);
632
+ }, [props.input, props.loader]);
633
+ if (!props.input?.props) return null;
634
+ if (props.segmented) {
635
+ const segmentedControlProps = typeof props.segmented === "object" ? props.segmented : {};
636
+ return /* @__PURE__ */ jsx(Input.Wrapper, {
637
+ ...inputProps,
638
+ children: /* @__PURE__ */ jsx(Flex$1, {
639
+ mt: "calc(var(--mantine-spacing-xs) / 2)",
640
+ children: /* @__PURE__ */ jsx(SegmentedControl, {
641
+ disabled: inputProps.disabled,
642
+ defaultValue: String(props.input.props.defaultValue),
643
+ ...segmentedControlProps,
644
+ onChange: (value) => {
645
+ props.input.set(value);
646
+ },
647
+ data: data.slice(0, 10)
648
+ })
649
+ })
650
+ });
651
+ }
652
+ if (props.autocomplete) {
653
+ const autocompleteProps = typeof props.autocomplete === "object" ? props.autocomplete : {};
654
+ return /* @__PURE__ */ jsx(Autocomplete, {
655
+ ...inputProps,
656
+ id,
657
+ leftSection: icon,
658
+ data,
659
+ ...props.input.props,
660
+ ...autocompleteProps
661
+ });
662
+ }
350
663
  if (isArray && !itemsEnum || props.tags) {
351
664
  const tagsInputProps = typeof props.tags === "object" ? props.tags : {};
352
665
  return /* @__PURE__ */ jsx(TagsInput, {
@@ -378,10 +691,6 @@ const ControlSelect = (props) => {
378
691
  ...multiSelectProps
379
692
  });
380
693
  }
381
- const data = enumValues.map((value) => ({
382
- value,
383
- label: value
384
- }));
385
694
  const selectProps = typeof props.select === "object" ? props.select : {};
386
695
  return /* @__PURE__ */ jsx(Select, {
387
696
  ...inputProps,
@@ -417,10 +726,13 @@ var ControlSelect_default = ControlSelect;
417
726
  *
418
727
  * Automatically handles labels, descriptions, error messages, required state, and default icons.
419
728
  */
420
- const Control = (props) => {
421
- const { inputProps, id, icon } = parseInput(props, useFormState(props.input));
422
- if (!props.input?.props) return null;
423
- const format = props.input.schema && "format" in props.input.schema && typeof props.input.schema.format === "string" ? props.input.schema.format : void 0;
729
+ const Control = (_props) => {
730
+ const { inputProps, id, icon, format, schema } = parseInput(_props, useFormState(_props.input, ["error"]));
731
+ if (!_props.input?.props) return null;
732
+ const props = {
733
+ ..._props,
734
+ ...schema.$control
735
+ };
424
736
  if (props.custom) {
425
737
  const Custom = props.custom;
426
738
  return /* @__PURE__ */ jsx(Input.Wrapper, {
@@ -470,47 +782,18 @@ const Control = (props) => {
470
782
  ...colorInputProps
471
783
  });
472
784
  }
473
- if (props.segmented) {
474
- const segmentedControlProps = typeof props.segmented === "object" ? props.segmented : {};
475
- const data = segmentedControlProps.data ?? (props.input.schema && "enum" in props.input.schema && Array.isArray(props.input.schema.enum) ? props.input.schema.enum?.map((value) => ({
476
- value,
477
- label: value
478
- })) : []);
479
- return /* @__PURE__ */ jsx(Input.Wrapper, {
480
- ...inputProps,
481
- children: /* @__PURE__ */ jsx(Flex$1, {
482
- mt: "calc(var(--mantine-spacing-xs) / 2)",
483
- children: /* @__PURE__ */ jsx(SegmentedControl, {
484
- disabled: inputProps.disabled,
485
- defaultValue: String(props.input.props.defaultValue),
486
- ...segmentedControlProps,
487
- onChange: (value) => {
488
- props.input.set(value);
489
- },
490
- data
491
- })
492
- })
493
- });
494
- }
495
- if (props.autocomplete) {
496
- const autocompleteProps = typeof props.autocomplete === "object" ? props.autocomplete : {};
497
- return /* @__PURE__ */ jsx(Autocomplete, {
498
- ...inputProps,
499
- id,
500
- leftSection: icon,
501
- ...props.input.props,
502
- ...autocompleteProps
503
- });
504
- }
505
785
  const isEnum = props.input.schema && "enum" in props.input.schema && props.input.schema.enum;
506
786
  const isArray = props.input.schema && "type" in props.input.schema && props.input.schema.type === "array";
507
- if (isEnum || isArray || props.select) return /* @__PURE__ */ jsx(ControlSelect_default, {
508
- input: props.input,
509
- title: props.title,
510
- description: props.description,
511
- icon,
512
- select: props.select
513
- });
787
+ if (isEnum || isArray || props.select) {
788
+ const opts = typeof props.select === "object" ? props.select : {};
789
+ return /* @__PURE__ */ jsx(ControlSelect_default, {
790
+ input: props.input,
791
+ title: props.title,
792
+ description: props.description,
793
+ icon,
794
+ ...opts
795
+ });
796
+ }
514
797
  if (props.input.schema && "type" in props.input.schema && props.input.schema.type === "boolean" || props.switch) {
515
798
  const switchProps = typeof props.switch === "object" ? props.switch : {};
516
799
  return /* @__PURE__ */ jsx(Switch, {
@@ -572,30 +855,6 @@ const Control = (props) => {
572
855
  });
573
856
  };
574
857
  var Control_default = Control;
575
- const parseInput = (props, form) => {
576
- const disabled = false;
577
- const id = props.input.props.id;
578
- 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);
579
- const description = props.description ?? ("description" in props.input.schema && typeof props.input.schema.description === "string" ? props.input.schema.description : void 0);
580
- const error = form.error && form.error instanceof TypeBoxError ? form.error.value.message : void 0;
581
- return {
582
- id,
583
- icon: props.icon ?? getDefaultIcon({
584
- type: props.input.schema && "type" in props.input.schema ? String(props.input.schema.type) : void 0,
585
- format: props.input.schema && "format" in props.input.schema && typeof props.input.schema.format === "string" ? props.input.schema.format : void 0,
586
- name: props.input.props.name,
587
- isEnum: props.input.schema && "enum" in props.input.schema && Boolean(props.input.schema.enum),
588
- isArray: props.input.schema && "type" in props.input.schema && props.input.schema.type === "array"
589
- }),
590
- inputProps: {
591
- label,
592
- description,
593
- error,
594
- required: props.input.required,
595
- disabled
596
- }
597
- };
598
- };
599
858
 
600
859
  //#endregion
601
860
  //#region src/components/DarkModeButton.tsx
@@ -603,10 +862,10 @@ const DarkModeButton = (props) => {
603
862
  const { setColorScheme } = useMantineColorScheme();
604
863
  const computedColorScheme = useComputedColorScheme("light");
605
864
  const [colorScheme, setColorScheme2] = useState("default");
865
+ const mode = props.mode ?? "minimal";
606
866
  useEffect(() => {
607
867
  setColorScheme2(computedColorScheme);
608
868
  }, [computedColorScheme]);
609
- const mode = props.mode ?? "minimal";
610
869
  const toggleColorScheme = () => {
611
870
  setColorScheme(computedColorScheme === "dark" ? "light" : "dark");
612
871
  };
@@ -636,11 +895,523 @@ const DarkModeButton = (props) => {
636
895
  variant: props.variant ?? "default",
637
896
  size: props.size ?? "lg",
638
897
  "aria-label": "Toggle color scheme",
639
- children: colorScheme === "dark" ? /* @__PURE__ */ jsx(IconSun, { size: 20 }) : /* @__PURE__ */ jsx(IconMoon, { size: 20 })
898
+ children: colorScheme === "dark" ? /* @__PURE__ */ jsx(IconSun, { size: 20 }) : colorScheme === "light" ? /* @__PURE__ */ jsx(IconMoon, { size: 20 }) : /* @__PURE__ */ jsx(Flex$1, { h: 20 })
640
899
  });
641
900
  };
642
901
  var DarkModeButton_default = DarkModeButton;
643
902
 
903
+ //#endregion
904
+ //#region src/components/DataTable.tsx
905
+ function getNestedValue(obj, path) {
906
+ return path.split(".").reduce((acc, part) => acc?.[part], obj);
907
+ }
908
+ function DataTable({ data = [], columns: initialColumns = [], loading = false, emptyMessage = "No data available", selectable = false, selectedRows = [], onRowSelect, sortable = false, sort, onSortChange, filterable = false, filters = [], onFiltersChange, filterPlaceholder = "Search...", paginate = false, page = 1, pageSize = 10, totalRecords, pageSizeOptions = [
909
+ 10,
910
+ 25,
911
+ 50,
912
+ 100
913
+ ], onPageChange, onPageSizeChange, rowActions, onRowClick, rowClassName, showHeader = true, showFooter = true, stickyHeader = false, striped = false, highlightOnHover = true, showToolbar = true, title, actions, showColumnToggle = true, showRefresh = false, onRefresh, showExport = false, onExport, height, minHeight, maxHeight,...tableProps }) {
914
+ const [hiddenColumns, setHiddenColumns] = useState(/* @__PURE__ */ new Set());
915
+ const [globalFilter, setGlobalFilter] = useState("");
916
+ const [internalPage, setInternalPage] = useState(page);
917
+ const [internalPageSize, setInternalPageSize] = useState(pageSize);
918
+ const [internalSort, setInternalSort] = useState(sort);
919
+ const [internalFilters, setInternalFilters] = useState(filters);
920
+ const [internalSelectedRows, setInternalSelectedRows] = useState(selectedRows);
921
+ const currentPage = onPageChange ? page : internalPage;
922
+ const currentPageSize = onPageSizeChange ? pageSize : internalPageSize;
923
+ const currentSort = onSortChange ? sort : internalSort;
924
+ const currentFilters = onFiltersChange ? filters : internalFilters;
925
+ const currentSelectedRows = onRowSelect ? selectedRows : internalSelectedRows;
926
+ const visibleColumns = useMemo(() => initialColumns.filter((col) => !col.hidden && !hiddenColumns.has(String(col.accessor))), [initialColumns, hiddenColumns]);
927
+ const processedData = useMemo(() => {
928
+ let result = [...data];
929
+ if (filterable && globalFilter) result = result.filter((row) => {
930
+ return visibleColumns.some((col) => {
931
+ const value = getNestedValue(row, String(col.accessor));
932
+ return String(value).toLowerCase().includes(globalFilter.toLowerCase());
933
+ });
934
+ });
935
+ if (filterable && currentFilters.length > 0) result = result.filter((row) => {
936
+ return currentFilters.every((filter) => {
937
+ const value = String(getNestedValue(row, filter.column)).toLowerCase();
938
+ const filterValue = filter.value.toLowerCase();
939
+ switch (filter.operator) {
940
+ case "equals": return value === filterValue;
941
+ case "startsWith": return value.startsWith(filterValue);
942
+ case "endsWith": return value.endsWith(filterValue);
943
+ default: return value.includes(filterValue);
944
+ }
945
+ });
946
+ });
947
+ if (sortable && currentSort?.direction) result.sort((a, b) => {
948
+ const aVal = getNestedValue(a, currentSort.column);
949
+ const bVal = getNestedValue(b, currentSort.column);
950
+ if (aVal === bVal) return 0;
951
+ if (aVal === null || aVal === void 0) return 1;
952
+ if (bVal === null || bVal === void 0) return -1;
953
+ const comparison = aVal < bVal ? -1 : 1;
954
+ return currentSort.direction === "asc" ? comparison : -comparison;
955
+ });
956
+ return result;
957
+ }, [
958
+ data,
959
+ visibleColumns,
960
+ filterable,
961
+ globalFilter,
962
+ currentFilters,
963
+ sortable,
964
+ currentSort
965
+ ]);
966
+ const paginatedData = useMemo(() => {
967
+ if (!paginate) return processedData;
968
+ const startIndex = (currentPage - 1) * currentPageSize;
969
+ return processedData.slice(startIndex, startIndex + currentPageSize);
970
+ }, [
971
+ processedData,
972
+ paginate,
973
+ currentPage,
974
+ currentPageSize
975
+ ]);
976
+ const totalPages = useMemo(() => {
977
+ const total = totalRecords ?? processedData.length;
978
+ return Math.ceil(total / currentPageSize);
979
+ }, [
980
+ totalRecords,
981
+ processedData.length,
982
+ currentPageSize
983
+ ]);
984
+ const handleSort = useCallback((column) => {
985
+ if (!sortable) return;
986
+ const newSort = {
987
+ column,
988
+ direction: currentSort?.column === column ? currentSort.direction === "asc" ? "desc" : currentSort.direction === "desc" ? null : "asc" : "asc"
989
+ };
990
+ if (onSortChange) onSortChange(newSort);
991
+ else setInternalSort(newSort);
992
+ }, [
993
+ sortable,
994
+ currentSort,
995
+ onSortChange
996
+ ]);
997
+ const handlePageChange = useCallback((newPage) => {
998
+ if (onPageChange) onPageChange(newPage);
999
+ else setInternalPage(newPage);
1000
+ }, [onPageChange]);
1001
+ const handlePageSizeChange = useCallback((size) => {
1002
+ const newSize = Number(size) || currentPageSize;
1003
+ if (onPageSizeChange) {
1004
+ onPageSizeChange(newSize);
1005
+ onPageChange?.(1);
1006
+ } else {
1007
+ setInternalPageSize(newSize);
1008
+ setInternalPage(1);
1009
+ }
1010
+ }, [
1011
+ currentPageSize,
1012
+ onPageSizeChange,
1013
+ onPageChange
1014
+ ]);
1015
+ const handleSelectAll = useCallback((checked) => {
1016
+ const newSelection = checked ? paginatedData : [];
1017
+ if (onRowSelect) onRowSelect(newSelection);
1018
+ else setInternalSelectedRows(newSelection);
1019
+ }, [paginatedData, onRowSelect]);
1020
+ const handleSelectRow = useCallback((row, checked) => {
1021
+ const newSelection = checked ? [...currentSelectedRows, row] : currentSelectedRows.filter((r) => r !== row);
1022
+ if (onRowSelect) onRowSelect(newSelection);
1023
+ else setInternalSelectedRows(newSelection);
1024
+ }, [currentSelectedRows, onRowSelect]);
1025
+ const handleColumnToggle = useCallback((column) => {
1026
+ const newHidden = new Set(hiddenColumns);
1027
+ if (newHidden.has(column)) newHidden.delete(column);
1028
+ else newHidden.add(column);
1029
+ setHiddenColumns(newHidden);
1030
+ }, [hiddenColumns]);
1031
+ const isAllSelected = currentSelectedRows.length > 0 && currentSelectedRows.length === paginatedData.length;
1032
+ const isIndeterminate = currentSelectedRows.length > 0 && currentSelectedRows.length < paginatedData.length;
1033
+ const renderSortIcon = (column) => {
1034
+ if (!sortable) return null;
1035
+ if (currentSort?.column === column) {
1036
+ if (currentSort.direction === "asc") return /* @__PURE__ */ jsx(IconChevronUp, {
1037
+ className: "alepha-datatable-sort-icon",
1038
+ size: 16
1039
+ });
1040
+ if (currentSort.direction === "desc") return /* @__PURE__ */ jsx(IconChevronDown, {
1041
+ className: "alepha-datatable-sort-icon",
1042
+ size: 16
1043
+ });
1044
+ }
1045
+ return /* @__PURE__ */ jsx(IconChevronUp, {
1046
+ className: "alepha-datatable-sort-icon-inactive",
1047
+ size: 16
1048
+ });
1049
+ };
1050
+ const toolbar = showToolbar && (title || actions || filterable || showColumnToggle || showRefresh || showExport) && /* @__PURE__ */ jsx(Paper, {
1051
+ className: "alepha-datatable-toolbar",
1052
+ p: "md",
1053
+ mb: "sm",
1054
+ children: /* @__PURE__ */ jsxs(Flex$1, {
1055
+ justify: "space-between",
1056
+ align: "center",
1057
+ gap: "md",
1058
+ children: [/* @__PURE__ */ jsxs(Group, { children: [title && /* @__PURE__ */ jsx(Text, {
1059
+ size: "lg",
1060
+ fw: 600,
1061
+ children: title
1062
+ }), currentSelectedRows.length > 0 && /* @__PURE__ */ jsxs(Badge, {
1063
+ color: "blue",
1064
+ variant: "light",
1065
+ children: [currentSelectedRows.length, " selected"]
1066
+ })] }), /* @__PURE__ */ jsxs(Group, { children: [
1067
+ filterable && /* @__PURE__ */ jsx(TextInput, {
1068
+ placeholder: filterPlaceholder,
1069
+ value: globalFilter,
1070
+ onChange: (e) => setGlobalFilter(e.target.value),
1071
+ leftSection: /* @__PURE__ */ jsx(IconSearch, { size: 16 }),
1072
+ rightSection: globalFilter && /* @__PURE__ */ jsx(ActionIcon, {
1073
+ size: "xs",
1074
+ variant: "subtle",
1075
+ onClick: () => setGlobalFilter(""),
1076
+ children: /* @__PURE__ */ jsx(IconX, { size: 14 })
1077
+ }),
1078
+ className: "alepha-datatable-search-input"
1079
+ }),
1080
+ showColumnToggle && /* @__PURE__ */ jsxs(Menu, {
1081
+ position: "bottom-end",
1082
+ children: [/* @__PURE__ */ jsx(Menu.Target, { children: /* @__PURE__ */ jsx(Tooltip, {
1083
+ label: "Toggle columns",
1084
+ children: /* @__PURE__ */ jsx(ActionIcon, {
1085
+ variant: "subtle",
1086
+ children: /* @__PURE__ */ jsx(IconColumns, { size: 20 })
1087
+ })
1088
+ }) }), /* @__PURE__ */ jsxs(Menu.Dropdown, { children: [/* @__PURE__ */ jsx(Menu.Label, { children: "Visible columns" }), initialColumns.map((col) => /* @__PURE__ */ jsx(Menu.Item, {
1089
+ onClick: () => handleColumnToggle(String(col.accessor)),
1090
+ leftSection: /* @__PURE__ */ jsx(Checkbox, {
1091
+ checked: !hiddenColumns.has(String(col.accessor)) && !col.hidden,
1092
+ readOnly: true,
1093
+ size: "xs"
1094
+ }),
1095
+ children: col.title || String(col.accessor)
1096
+ }, String(col.accessor)))] })]
1097
+ }),
1098
+ showRefresh && /* @__PURE__ */ jsx(Tooltip, {
1099
+ label: "Refresh",
1100
+ children: /* @__PURE__ */ jsx(ActionIcon, {
1101
+ variant: "subtle",
1102
+ onClick: onRefresh,
1103
+ loading,
1104
+ children: /* @__PURE__ */ jsx(IconRefresh, { size: 20 })
1105
+ })
1106
+ }),
1107
+ showExport && /* @__PURE__ */ jsx(Tooltip, {
1108
+ label: "Export",
1109
+ children: /* @__PURE__ */ jsx(ActionIcon, {
1110
+ variant: "subtle",
1111
+ onClick: onExport,
1112
+ children: /* @__PURE__ */ jsx(IconDownload, { size: 20 })
1113
+ })
1114
+ }),
1115
+ actions
1116
+ ] })]
1117
+ })
1118
+ });
1119
+ const tableContent = /* @__PURE__ */ jsxs(Table, {
1120
+ striped,
1121
+ highlightOnHover,
1122
+ stickyHeader,
1123
+ className: "alepha-datatable-table",
1124
+ ...tableProps,
1125
+ children: [
1126
+ showHeader && /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
1127
+ selectable && /* @__PURE__ */ jsx(Table.Th, {
1128
+ className: "alepha-datatable-checkbox-column",
1129
+ children: /* @__PURE__ */ jsx(Checkbox, {
1130
+ checked: isAllSelected,
1131
+ indeterminate: isIndeterminate,
1132
+ onChange: (e) => handleSelectAll(e.currentTarget.checked)
1133
+ })
1134
+ }),
1135
+ visibleColumns.map((column) => /* @__PURE__ */ jsx(Table.Th, {
1136
+ className: `alepha-datatable-th ${column.headerClassName || ""}`,
1137
+ style: {
1138
+ width: column.width,
1139
+ textAlign: column.align,
1140
+ cursor: column.sortable && sortable ? "pointer" : "default"
1141
+ },
1142
+ onClick: () => column.sortable && handleSort(String(column.accessor)),
1143
+ children: /* @__PURE__ */ jsxs(Group, {
1144
+ gap: "xs",
1145
+ justify: column.align === "center" ? "center" : column.align === "right" ? "flex-end" : "flex-start",
1146
+ children: [column.renderHeader ? column.renderHeader() : column.title || String(column.accessor), column.sortable && renderSortIcon(String(column.accessor))]
1147
+ })
1148
+ }, String(column.accessor))),
1149
+ rowActions && /* @__PURE__ */ jsx(Table.Th, {
1150
+ className: "alepha-datatable-actions-column",
1151
+ children: "Actions"
1152
+ })
1153
+ ] }) }),
1154
+ /* @__PURE__ */ jsx(Table.Tbody, { children: loading ? /* @__PURE__ */ jsx(Table.Tr, { children: /* @__PURE__ */ jsx(Table.Td, {
1155
+ colSpan: visibleColumns.length + (selectable ? 1 : 0) + (rowActions ? 1 : 0),
1156
+ children: /* @__PURE__ */ jsx(Center, {
1157
+ py: "xl",
1158
+ children: /* @__PURE__ */ jsx(Loader, { size: "sm" })
1159
+ })
1160
+ }) }) : paginatedData.length === 0 ? /* @__PURE__ */ jsx(Table.Tr, { children: /* @__PURE__ */ jsx(Table.Td, {
1161
+ colSpan: visibleColumns.length + (selectable ? 1 : 0) + (rowActions ? 1 : 0),
1162
+ children: /* @__PURE__ */ jsx(Center, {
1163
+ py: "xl",
1164
+ children: /* @__PURE__ */ jsx(Text, {
1165
+ c: "dimmed",
1166
+ children: emptyMessage
1167
+ })
1168
+ })
1169
+ }) }) : paginatedData.map((row, index) => {
1170
+ const isSelected = currentSelectedRows.includes(row);
1171
+ return /* @__PURE__ */ jsxs(Table.Tr, {
1172
+ className: `alepha-datatable-tr ${isSelected ? "alepha-datatable-selected" : ""} ${rowClassName?.(row, index) || ""}`,
1173
+ onClick: () => onRowClick?.(row, index),
1174
+ style: { cursor: onRowClick ? "pointer" : "default" },
1175
+ children: [
1176
+ selectable && /* @__PURE__ */ jsx(Table.Td, {
1177
+ className: "alepha-datatable-checkbox-column",
1178
+ children: /* @__PURE__ */ jsx(Checkbox, {
1179
+ checked: isSelected,
1180
+ onChange: (e) => handleSelectRow(row, e.currentTarget.checked),
1181
+ onClick: (e) => e.stopPropagation()
1182
+ })
1183
+ }),
1184
+ visibleColumns.map((column) => {
1185
+ const value = getNestedValue(row, String(column.accessor));
1186
+ return /* @__PURE__ */ jsx(Table.Td, {
1187
+ className: column.className,
1188
+ style: {
1189
+ textAlign: column.align,
1190
+ ...column.ellipsis && {
1191
+ maxWidth: column.width,
1192
+ overflow: "hidden",
1193
+ textOverflow: "ellipsis",
1194
+ whiteSpace: "nowrap"
1195
+ }
1196
+ },
1197
+ children: column.render ? column.render(value, row, index) : value
1198
+ }, String(column.accessor));
1199
+ }),
1200
+ rowActions && /* @__PURE__ */ jsx(Table.Td, {
1201
+ className: "alepha-datatable-actions-column",
1202
+ children: rowActions(row, index)
1203
+ })
1204
+ ]
1205
+ }, index);
1206
+ }) }),
1207
+ showFooter && paginate && /* @__PURE__ */ jsx(Table.Tfoot, { children: /* @__PURE__ */ jsx(Table.Tr, { children: /* @__PURE__ */ jsx(Table.Td, {
1208
+ colSpan: visibleColumns.length + (selectable ? 1 : 0) + (rowActions ? 1 : 0),
1209
+ children: /* @__PURE__ */ jsxs(Flex$1, {
1210
+ justify: "space-between",
1211
+ align: "center",
1212
+ py: "xs",
1213
+ children: [/* @__PURE__ */ jsxs(Group, {
1214
+ gap: "xs",
1215
+ children: [/* @__PURE__ */ jsxs(Text, {
1216
+ size: "sm",
1217
+ c: "dimmed",
1218
+ children: [
1219
+ "Showing ",
1220
+ (currentPage - 1) * currentPageSize + 1,
1221
+ " to",
1222
+ " ",
1223
+ Math.min(currentPage * currentPageSize, totalRecords ?? processedData.length),
1224
+ " ",
1225
+ "of ",
1226
+ totalRecords ?? processedData.length,
1227
+ " records"
1228
+ ]
1229
+ }), /* @__PURE__ */ jsx(Select, {
1230
+ size: "xs",
1231
+ value: String(currentPageSize),
1232
+ onChange: handlePageSizeChange,
1233
+ data: pageSizeOptions.map((size) => ({
1234
+ value: String(size),
1235
+ label: `${size} / page`
1236
+ })),
1237
+ className: "alepha-datatable-page-size-select"
1238
+ })]
1239
+ }), /* @__PURE__ */ jsx(Pagination, {
1240
+ size: "sm",
1241
+ value: currentPage,
1242
+ onChange: handlePageChange,
1243
+ total: totalPages,
1244
+ siblings: 1,
1245
+ boundaries: 1
1246
+ })]
1247
+ })
1248
+ }) }) })
1249
+ ]
1250
+ });
1251
+ return /* @__PURE__ */ jsxs(Box, {
1252
+ className: "alepha-datatable-container",
1253
+ children: [toolbar, height || maxHeight ? /* @__PURE__ */ jsx(ScrollArea.Autosize, {
1254
+ mah: maxHeight,
1255
+ h: height,
1256
+ mih: minHeight,
1257
+ children: tableContent
1258
+ }) : tableContent]
1259
+ });
1260
+ }
1261
+
1262
+ //#endregion
1263
+ //#region src/components/Sidebar.tsx
1264
+ const Sidebar = ({ menu, defaultOpenIds = [], onItemClick, showSearchButton = false, onSearchClick }) => {
1265
+ const [openIds, setOpenIds] = useState(new Set(defaultOpenIds));
1266
+ const toggleOpen = (id) => {
1267
+ setOpenIds((prev) => {
1268
+ const newSet = new Set(prev);
1269
+ if (newSet.has(id)) newSet.delete(id);
1270
+ else newSet.add(id);
1271
+ return newSet;
1272
+ });
1273
+ };
1274
+ return /* @__PURE__ */ jsxs(Box, {
1275
+ component: "nav",
1276
+ className: "alepha-sidebar",
1277
+ children: [showSearchButton && /* @__PURE__ */ jsx(UnstyledButton, {
1278
+ className: "alepha-sidebar-search-button",
1279
+ onClick: onSearchClick,
1280
+ children: /* @__PURE__ */ jsxs(Box, {
1281
+ className: "alepha-sidebar-search-button-content",
1282
+ children: [/* @__PURE__ */ jsxs(Box, {
1283
+ className: "alepha-sidebar-search-button-left",
1284
+ children: [/* @__PURE__ */ jsx(IconSearch, { size: 16 }), /* @__PURE__ */ jsx("span", { children: "Search..." })]
1285
+ }), /* @__PURE__ */ jsx(Box, {
1286
+ className: "alepha-sidebar-search-button-shortcut",
1287
+ children: /* @__PURE__ */ jsx("kbd", { children: "⌘+K" })
1288
+ })]
1289
+ })
1290
+ }), menu.map((item) => /* @__PURE__ */ jsx(SidebarItem, {
1291
+ item,
1292
+ level: 0,
1293
+ openIds,
1294
+ onToggle: toggleOpen,
1295
+ onItemClick
1296
+ }, item.id))]
1297
+ });
1298
+ };
1299
+ const SidebarItem = (props) => {
1300
+ const { item, level } = props;
1301
+ if (level > 2) return null;
1302
+ if (item.href) return /* @__PURE__ */ jsx(SidebarItemHref, { ...props });
1303
+ return /* @__PURE__ */ jsx(SidebarItemButton, { ...props });
1304
+ };
1305
+ const SidebarItemHref = ({ item, level, openIds, onToggle, onItemClick }) => {
1306
+ const hasChildren = item.children && item.children.length > 0;
1307
+ const isOpen = openIds.has(item.id);
1308
+ const { isActive, anchorProps } = useActive$1({
1309
+ href: item.href,
1310
+ startWith: item.activeStartsWith
1311
+ });
1312
+ const handleItemClick = (e) => {
1313
+ if (hasChildren) {
1314
+ e.preventDefault();
1315
+ onToggle(item.id);
1316
+ }
1317
+ };
1318
+ return /* @__PURE__ */ jsxs(Box, {
1319
+ className: "alepha-sidebar-item-wrapper",
1320
+ children: [/* @__PURE__ */ jsx(UnstyledButton, {
1321
+ component: "a",
1322
+ ...anchorProps,
1323
+ className: `alepha-sidebar-item alepha-sidebar-level-${level} ${isActive ? "alepha-sidebar-item-active" : ""}`,
1324
+ onClick: hasChildren ? handleItemClick : anchorProps.onClick,
1325
+ children: /* @__PURE__ */ jsxs(Flex$1, {
1326
+ justify: "space-between",
1327
+ align: "center",
1328
+ w: "100%",
1329
+ children: [/* @__PURE__ */ jsxs(Flex$1, {
1330
+ className: "alepha-sidebar-item-content",
1331
+ align: "center",
1332
+ gap: 10,
1333
+ children: [/* @__PURE__ */ jsx(Box, {
1334
+ className: "alepha-sidebar-item-icon",
1335
+ children: item.icon || /* @__PURE__ */ jsx(IconCircle, { size: 16 })
1336
+ }), /* @__PURE__ */ jsx(Box, {
1337
+ className: "alepha-sidebar-item-label",
1338
+ children: item.label
1339
+ })]
1340
+ }), hasChildren && /* @__PURE__ */ jsx(Box, {
1341
+ className: "alepha-sidebar-item-caret",
1342
+ children: isOpen ? /* @__PURE__ */ jsx(IconChevronDown, { size: 14 }) : /* @__PURE__ */ jsx(IconChevronRight, { size: 14 })
1343
+ })]
1344
+ })
1345
+ }), hasChildren && isOpen && /* @__PURE__ */ jsxs(Box, {
1346
+ className: "alepha-sidebar-children",
1347
+ "data-parent-level": level,
1348
+ children: [(level === 0 || level === 1) && /* @__PURE__ */ jsx(Box, { className: "alepha-sidebar-vertical-bar" }), /* @__PURE__ */ jsx(Box, {
1349
+ className: "alepha-sidebar-children-items",
1350
+ children: item.children.map((child) => /* @__PURE__ */ jsx(SidebarItem, {
1351
+ item: child,
1352
+ level: level + 1,
1353
+ openIds,
1354
+ onToggle,
1355
+ onItemClick
1356
+ }, child.id))
1357
+ })]
1358
+ })]
1359
+ });
1360
+ };
1361
+ const SidebarItemButton = ({ item, level, openIds, onToggle, onItemClick }) => {
1362
+ const hasChildren = item.children && item.children.length > 0;
1363
+ const isOpen = openIds.has(item.id);
1364
+ const handleItemClick = (e) => {
1365
+ e.preventDefault();
1366
+ if (hasChildren) onToggle(item.id);
1367
+ else {
1368
+ onItemClick?.(item);
1369
+ item.onClick?.();
1370
+ }
1371
+ };
1372
+ return /* @__PURE__ */ jsxs(Box, {
1373
+ className: "alepha-sidebar-item-wrapper",
1374
+ children: [/* @__PURE__ */ jsx(UnstyledButton, {
1375
+ component: "button",
1376
+ className: `alepha-sidebar-item alepha-sidebar-level-${level}`,
1377
+ onClick: handleItemClick,
1378
+ children: /* @__PURE__ */ jsxs(Flex$1, {
1379
+ justify: "space-between",
1380
+ align: "center",
1381
+ w: "100%",
1382
+ children: [/* @__PURE__ */ jsxs(Flex$1, {
1383
+ className: "alepha-sidebar-item-content",
1384
+ align: "center",
1385
+ gap: 10,
1386
+ children: [/* @__PURE__ */ jsx(Box, {
1387
+ className: "alepha-sidebar-item-icon",
1388
+ children: item.icon || /* @__PURE__ */ jsx(IconCircle, { size: 16 })
1389
+ }), /* @__PURE__ */ jsx(Box, {
1390
+ className: "alepha-sidebar-item-label",
1391
+ children: item.label
1392
+ })]
1393
+ }), hasChildren && /* @__PURE__ */ jsx(Box, {
1394
+ className: "alepha-sidebar-item-caret",
1395
+ children: isOpen ? /* @__PURE__ */ jsx(IconChevronDown, { size: 14 }) : /* @__PURE__ */ jsx(IconChevronRight, { size: 14 })
1396
+ })]
1397
+ })
1398
+ }), hasChildren && isOpen && /* @__PURE__ */ jsxs(Box, {
1399
+ className: "alepha-sidebar-children",
1400
+ "data-parent-level": level,
1401
+ children: [(level === 0 || level === 1) && /* @__PURE__ */ jsx(Box, { className: "alepha-sidebar-vertical-bar" }), /* @__PURE__ */ jsx(Box, {
1402
+ className: "alepha-sidebar-children-items",
1403
+ children: item.children.map((child) => /* @__PURE__ */ jsx(SidebarItem, {
1404
+ item: child,
1405
+ level: level + 1,
1406
+ openIds,
1407
+ onToggle,
1408
+ onItemClick
1409
+ }, child.id))
1410
+ })]
1411
+ })]
1412
+ });
1413
+ };
1414
+
644
1415
  //#endregion
645
1416
  //#region src/components/TypeForm.tsx
646
1417
  /**
@@ -669,7 +1440,7 @@ var DarkModeButton_default = DarkModeButton;
669
1440
  * ```
670
1441
  */
671
1442
  const TypeForm = (props) => {
672
- const { form, columns = 1, children, controlProps, skipFormElement = false, skipSubmitButton = false, submitButtonProps } = props;
1443
+ const { form, columns = 3, children, controlProps, skipFormElement = false, skipSubmitButton = false, submitButtonProps } = props;
673
1444
  if (!form.options?.schema?.properties) return null;
674
1445
  const supportedFields = Object.keys(form.options.schema.properties).filter((fieldName) => {
675
1446
  const field = form.input[fieldName];
@@ -684,7 +1455,7 @@ const TypeForm = (props) => {
684
1455
  const colSpan = typeof columns === "number" ? {
685
1456
  xs: 12,
686
1457
  sm: 6,
687
- lg: 12 / 3
1458
+ lg: 12 / columns
688
1459
  } : {
689
1460
  base: columns.base ? 12 / columns.base : void 0,
690
1461
  xs: columns.xs ? 12 / columns.xs : 12,
@@ -707,20 +1478,41 @@ const TypeForm = (props) => {
707
1478
  }, fieldName);
708
1479
  }) });
709
1480
  };
710
- const content = /* @__PURE__ */ jsxs(Stack, { children: [renderFields(), !skipSubmitButton && /* @__PURE__ */ jsx(Action_default, {
711
- form,
712
- ...submitButtonProps,
713
- children: submitButtonProps?.children ?? "Submit"
714
- })] });
1481
+ const content = /* @__PURE__ */ jsxs(Flex$1, {
1482
+ direction: "column",
1483
+ gap: "sm",
1484
+ children: [renderFields(), !skipSubmitButton && /* @__PURE__ */ jsxs(Flex$1, { children: [/* @__PURE__ */ jsx(Action_default, {
1485
+ form,
1486
+ ...submitButtonProps,
1487
+ children: submitButtonProps?.children ?? "Submit"
1488
+ }), /* @__PURE__ */ jsx("button", {
1489
+ type: "reset",
1490
+ children: "Reset"
1491
+ })] })]
1492
+ });
715
1493
  if (skipFormElement) return content;
716
1494
  return /* @__PURE__ */ jsx("form", {
717
- onSubmit: form.onSubmit,
718
- noValidate: true,
1495
+ ...form.props,
719
1496
  children: content
720
1497
  });
721
1498
  };
722
1499
  var TypeForm_default = TypeForm;
723
1500
 
1501
+ //#endregion
1502
+ //#region src/hooks/useDialog.ts
1503
+ /**
1504
+ * Use this hook to access the Dialog Service for showing various dialog types.
1505
+ *
1506
+ * @example
1507
+ * const dialog = useDialog();
1508
+ * await dialog.alert({ title: "Alert", message: "This is an alert message" });
1509
+ * const confirmed = await dialog.confirm({ title: "Confirm", message: "Are you sure?" });
1510
+ * const input = await dialog.prompt({ title: "Input", message: "Enter your name:" });
1511
+ */
1512
+ const useDialog = () => {
1513
+ return useInject(DialogService);
1514
+ };
1515
+
724
1516
  //#endregion
725
1517
  //#region src/hooks/useToast.ts
726
1518
  /**
@@ -738,7 +1530,7 @@ const useToast = () => {
738
1530
  //#endregion
739
1531
  //#region src/index.ts
740
1532
  /**
741
- *
1533
+ * Mantine
742
1534
  *
743
1535
  * @module alepha.ui
744
1536
  */
@@ -746,11 +1538,12 @@ const AlephaUI = $module({
746
1538
  name: "alepha.ui",
747
1539
  services: [
748
1540
  AlephaReact,
1541
+ DialogService,
749
1542
  ToastService,
750
1543
  RootRouter
751
1544
  ]
752
1545
  });
753
1546
 
754
1547
  //#endregion
755
- export { Action_default as Action, AlephaMantineProvider_default as AlephaMantineProvider, AlephaUI, Control_default as Control, ControlDate_default as ControlDate, ControlSelect_default as ControlSelect, DarkModeButton_default as DarkModeButton, Flex, ICON_SIZES, Omnibar_default as Omnibar, RootRouter, ToastService, TypeForm_default as TypeForm, capitalize, getDefaultIcon, prettyName, useToast };
1548
+ export { Action_default as Action, AlephaMantineProvider_default as AlephaMantineProvider, AlephaUI, AlertDialog, ConfirmDialog, Control_default as Control, ControlDate_default as ControlDate, ControlSelect_default as ControlSelect, DarkModeButton_default as DarkModeButton, DataTable, DialogService, Flex, ICON_SIZES, Omnibar_default as Omnibar, PromptDialog, RootRouter, Sidebar, SidebarItem, ToastService, TypeForm_default as TypeForm, capitalize, getDefaultIcon, prettyName, useDialog, useToast };
756
1549
  //# sourceMappingURL=index.js.map