@alepha/ui 0.10.5 → 0.10.7
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/AlephaMantineProvider-EemOtraW.js +3 -0
- package/dist/AlephaMantineProvider-WfiC2EH6.js +95 -0
- package/dist/AlephaMantineProvider-WfiC2EH6.js.map +1 -0
- package/dist/index.d.ts +236 -36
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +520 -81
- package/dist/index.js.map +1 -1
- package/package.json +17 -12
- package/src/RootRouter.ts +8 -0
- package/src/components/Action.tsx +134 -134
- package/src/components/AlephaMantineProvider.tsx +34 -31
- package/src/components/Control.tsx +383 -229
- package/src/components/ControlDate.tsx +112 -0
- package/src/components/ControlSelect.tsx +134 -0
- package/src/components/DarkModeButton.tsx +75 -0
- package/src/components/Omnibar.tsx +76 -0
- package/src/components/TypeForm.tsx +158 -0
- package/src/hooks/useToast.ts +14 -0
- package/src/index.ts +25 -9
- package/src/services/ToastService.tsx +71 -0
- package/src/utils/icons.tsx +121 -0
- package/src/utils/string.ts +21 -0
package/dist/index.js
CHANGED
|
@@ -1,17 +1,76 @@
|
|
|
1
|
+
import { n as Omnibar_default, t as AlephaMantineProvider_default } from "./AlephaMantineProvider-WfiC2EH6.js";
|
|
1
2
|
import { $module, TypeBoxError } from "@alepha/core";
|
|
2
|
-
import {
|
|
3
|
-
import "@mantine/
|
|
4
|
-
import "@
|
|
5
|
-
import "@mantine/spotlight/styles.css";
|
|
6
|
-
import "@mantine/notifications/styles.css";
|
|
7
|
-
import { useFormState } from "@alepha/react-form";
|
|
8
|
-
import { Autocomplete, Button, ColorSchemeScript, Flex, Input, MantineProvider, PasswordInput, SegmentedControl, Select, Switch, TextInput, Textarea } from "@mantine/core";
|
|
9
|
-
import { useState } from "react";
|
|
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";
|
|
10
6
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
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";
|
|
8
|
+
import { useFormState } from "@alepha/react-form";
|
|
9
|
+
import { useEffect, useState } from "react";
|
|
10
|
+
import { DateInput, DateTimePicker, TimeInput } from "@mantine/dates";
|
|
11
|
+
|
|
12
|
+
//#region src/RootRouter.ts
|
|
13
|
+
var RootRouter = class {
|
|
14
|
+
root = $page({
|
|
15
|
+
path: "/",
|
|
16
|
+
lazy: () => import("./AlephaMantineProvider-EemOtraW.js")
|
|
17
|
+
});
|
|
18
|
+
};
|
|
14
19
|
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/services/ToastService.tsx
|
|
22
|
+
var ToastService = class {
|
|
23
|
+
raw = notifications;
|
|
24
|
+
options = { default: {
|
|
25
|
+
autoClose: 5e3,
|
|
26
|
+
withCloseButton: true,
|
|
27
|
+
position: "top-center"
|
|
28
|
+
} };
|
|
29
|
+
show(options) {
|
|
30
|
+
notifications.show({
|
|
31
|
+
...this.options.default,
|
|
32
|
+
...options
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
info(options) {
|
|
36
|
+
this.show({
|
|
37
|
+
color: "blue",
|
|
38
|
+
icon: /* @__PURE__ */ jsx(IconInfoCircle, { size: 20 }),
|
|
39
|
+
title: "Info",
|
|
40
|
+
message: "Information notification",
|
|
41
|
+
...options
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
success(options) {
|
|
45
|
+
this.show({
|
|
46
|
+
color: "green",
|
|
47
|
+
icon: /* @__PURE__ */ jsx(IconCheck, { size: 16 }),
|
|
48
|
+
title: "Success",
|
|
49
|
+
message: "Operation completed successfully",
|
|
50
|
+
...options
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
warning(options) {
|
|
54
|
+
this.show({
|
|
55
|
+
color: "yellow",
|
|
56
|
+
icon: /* @__PURE__ */ jsx(IconAlertTriangle, { size: 20 }),
|
|
57
|
+
title: "Warning",
|
|
58
|
+
message: "Please review this warning",
|
|
59
|
+
...options
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
danger(options) {
|
|
63
|
+
this.show({
|
|
64
|
+
color: "red",
|
|
65
|
+
icon: /* @__PURE__ */ jsx(IconX, { size: 20 }),
|
|
66
|
+
title: "Error",
|
|
67
|
+
message: "An error occurred",
|
|
68
|
+
...options
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
//#endregion
|
|
15
74
|
//#region src/components/Action.tsx
|
|
16
75
|
const Action = (_props) => {
|
|
17
76
|
const props = {
|
|
@@ -24,7 +83,7 @@ const Action = (_props) => {
|
|
|
24
83
|
}
|
|
25
84
|
if (props.textVisibleFrom) {
|
|
26
85
|
const { children, textVisibleFrom, leftSection,...rest } = props;
|
|
27
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Flex, {
|
|
86
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Flex$1, {
|
|
28
87
|
w: "100%",
|
|
29
88
|
visibleFrom: textVisibleFrom,
|
|
30
89
|
children: /* @__PURE__ */ jsx(Action, {
|
|
@@ -33,7 +92,7 @@ const Action = (_props) => {
|
|
|
33
92
|
leftSection,
|
|
34
93
|
children
|
|
35
94
|
})
|
|
36
|
-
}), /* @__PURE__ */ jsx(Flex, {
|
|
95
|
+
}), /* @__PURE__ */ jsx(Flex$1, {
|
|
37
96
|
w: "100%",
|
|
38
97
|
hiddenFrom: textVisibleFrom,
|
|
39
98
|
children: /* @__PURE__ */ jsx(Action, {
|
|
@@ -66,6 +125,7 @@ const Action = (_props) => {
|
|
|
66
125
|
};
|
|
67
126
|
return renderAction();
|
|
68
127
|
};
|
|
128
|
+
var Action_default = Action;
|
|
69
129
|
/**
|
|
70
130
|
* Action button that submits a form with loading and disabled state handling.
|
|
71
131
|
*/
|
|
@@ -75,7 +135,7 @@ const ActionSubmit = (props) => {
|
|
|
75
135
|
return /* @__PURE__ */ jsx(Button, {
|
|
76
136
|
...buttonProps,
|
|
77
137
|
loading: state.loading,
|
|
78
|
-
disabled: state.loading
|
|
138
|
+
disabled: state.loading,
|
|
79
139
|
type: "submit",
|
|
80
140
|
children: props.children
|
|
81
141
|
});
|
|
@@ -118,11 +178,10 @@ const ActionHref = (props) => {
|
|
|
118
178
|
href: props.href,
|
|
119
179
|
...options
|
|
120
180
|
} : { href: props.href });
|
|
121
|
-
const anchorProps = router.anchor(props.href, routerGoOptions);
|
|
122
181
|
return /* @__PURE__ */ jsx(Button, {
|
|
123
182
|
component: "a",
|
|
124
183
|
loading: isPending,
|
|
125
|
-
...
|
|
184
|
+
...router.anchor(props.href, routerGoOptions),
|
|
126
185
|
...buttonProps,
|
|
127
186
|
variant: isActive && options !== false ? "filled" : "subtle",
|
|
128
187
|
children: props.children
|
|
@@ -130,31 +189,210 @@ const ActionHref = (props) => {
|
|
|
130
189
|
};
|
|
131
190
|
|
|
132
191
|
//#endregion
|
|
133
|
-
//#region src/
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
192
|
+
//#region src/utils/icons.tsx
|
|
193
|
+
/**
|
|
194
|
+
* Icon size presets following Mantine's size conventions
|
|
195
|
+
*/
|
|
196
|
+
const ICON_SIZES = {
|
|
197
|
+
xs: 12,
|
|
198
|
+
sm: 16,
|
|
199
|
+
md: 20,
|
|
200
|
+
lg: 24,
|
|
201
|
+
xl: 28
|
|
202
|
+
};
|
|
203
|
+
/**
|
|
204
|
+
* Get the default icon for an input based on its type, format, or name.
|
|
205
|
+
*/
|
|
206
|
+
const getDefaultIcon = (params) => {
|
|
207
|
+
const { type, format, name, isEnum, isArray, size = "sm" } = params;
|
|
208
|
+
const iconSize = ICON_SIZES[size];
|
|
209
|
+
if (format) switch (format) {
|
|
210
|
+
case "email": return /* @__PURE__ */ jsx(IconMail, { size: iconSize });
|
|
211
|
+
case "url":
|
|
212
|
+
case "uri": return /* @__PURE__ */ jsx(IconLink, { size: iconSize });
|
|
213
|
+
case "tel":
|
|
214
|
+
case "phone": return /* @__PURE__ */ jsx(IconPhone, { size: iconSize });
|
|
215
|
+
case "date": return /* @__PURE__ */ jsx(IconCalendar, { size: iconSize });
|
|
216
|
+
case "date-time": return /* @__PURE__ */ jsx(IconCalendar, { size: iconSize });
|
|
217
|
+
case "time": return /* @__PURE__ */ jsx(IconClock, { size: iconSize });
|
|
218
|
+
case "color": return /* @__PURE__ */ jsx(IconColorPicker, { size: iconSize });
|
|
219
|
+
case "uuid": return /* @__PURE__ */ jsx(IconKey, { size: iconSize });
|
|
220
|
+
}
|
|
221
|
+
if (name) {
|
|
222
|
+
const nameLower = name.toLowerCase();
|
|
223
|
+
if (nameLower.includes("password") || nameLower.includes("secret")) return /* @__PURE__ */ jsx(IconKey, { size: iconSize });
|
|
224
|
+
if (nameLower.includes("email") || nameLower.includes("mail")) return /* @__PURE__ */ jsx(IconMail, { size: iconSize });
|
|
225
|
+
if (nameLower.includes("url") || nameLower.includes("link")) return /* @__PURE__ */ jsx(IconLink, { size: iconSize });
|
|
226
|
+
if (nameLower.includes("phone") || nameLower.includes("tel")) return /* @__PURE__ */ jsx(IconPhone, { size: iconSize });
|
|
227
|
+
if (nameLower.includes("color")) return /* @__PURE__ */ jsx(IconPalette, { size: iconSize });
|
|
228
|
+
if (nameLower.includes("file") || nameLower.includes("upload")) return /* @__PURE__ */ jsx(IconFile, { size: iconSize });
|
|
229
|
+
if (nameLower.includes("date")) return /* @__PURE__ */ jsx(IconCalendar, { size: iconSize });
|
|
230
|
+
if (nameLower.includes("time")) return /* @__PURE__ */ jsx(IconClock, { size: iconSize });
|
|
231
|
+
}
|
|
232
|
+
if (isEnum || isArray) return /* @__PURE__ */ jsx(IconSelector, { size: iconSize });
|
|
233
|
+
if (type) switch (type) {
|
|
234
|
+
case "boolean": return /* @__PURE__ */ jsx(IconToggleLeft, { size: iconSize });
|
|
235
|
+
case "number":
|
|
236
|
+
case "integer": return /* @__PURE__ */ jsx(IconHash, { size: iconSize });
|
|
237
|
+
case "array": return /* @__PURE__ */ jsx(IconList, { size: iconSize });
|
|
238
|
+
case "string": return /* @__PURE__ */ jsx(IconLetterCase, { size: iconSize });
|
|
239
|
+
}
|
|
240
|
+
return /* @__PURE__ */ jsx(IconAt, { size: iconSize });
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
//#endregion
|
|
244
|
+
//#region src/utils/string.ts
|
|
245
|
+
/**
|
|
246
|
+
* Capitalizes the first letter of a string.
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* capitalize("hello") // "Hello"
|
|
250
|
+
*/
|
|
251
|
+
const capitalize = (str) => {
|
|
252
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
253
|
+
};
|
|
254
|
+
/**
|
|
255
|
+
* Converts a path or identifier string into a pretty display name.
|
|
256
|
+
* Removes slashes and capitalizes the first letter.
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* prettyName("/userName") // "UserName"
|
|
260
|
+
* prettyName("email") // "Email"
|
|
261
|
+
*/
|
|
262
|
+
const prettyName = (name) => {
|
|
263
|
+
return capitalize(name.replaceAll("/", ""));
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
//#endregion
|
|
267
|
+
//#region src/components/ControlDate.tsx
|
|
268
|
+
/**
|
|
269
|
+
* ControlDate component for handling date, datetime, and time inputs.
|
|
270
|
+
*
|
|
271
|
+
* Features:
|
|
272
|
+
* - DateInput for date format
|
|
273
|
+
* - DateTimePicker for date-time format
|
|
274
|
+
* - TimeInput for time format
|
|
275
|
+
*
|
|
276
|
+
* Automatically detects date formats from schema and renders appropriate picker.
|
|
277
|
+
*/
|
|
278
|
+
const ControlDate = (props) => {
|
|
279
|
+
const { inputProps, id, icon } = parseInput(props, useFormState(props.input));
|
|
280
|
+
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
|
+
if (props.datetime || format === "date-time") {
|
|
283
|
+
const dateTimePickerProps = typeof props.datetime === "object" ? props.datetime : {};
|
|
284
|
+
return /* @__PURE__ */ jsx(DateTimePicker, {
|
|
285
|
+
...inputProps,
|
|
286
|
+
id,
|
|
287
|
+
leftSection: icon,
|
|
288
|
+
defaultValue: props.input.props.defaultValue ? new Date(props.input.props.defaultValue) : void 0,
|
|
289
|
+
onChange: (value) => {
|
|
290
|
+
props.input.set(value ? new Date(value).toISOString() : void 0);
|
|
291
|
+
},
|
|
292
|
+
...dateTimePickerProps
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
if (props.date || format === "date") {
|
|
296
|
+
const dateInputProps = typeof props.date === "object" ? props.date : {};
|
|
297
|
+
return /* @__PURE__ */ jsx(DateInput, {
|
|
298
|
+
...inputProps,
|
|
299
|
+
id,
|
|
300
|
+
leftSection: icon,
|
|
301
|
+
defaultValue: props.input.props.defaultValue ? new Date(props.input.props.defaultValue) : void 0,
|
|
302
|
+
onChange: (value) => {
|
|
303
|
+
props.input.set(value ? new Date(value).toISOString().slice(0, 10) : void 0);
|
|
304
|
+
},
|
|
305
|
+
...dateInputProps
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
if (props.time || format === "time") {
|
|
309
|
+
const timeInputProps = typeof props.time === "object" ? props.time : {};
|
|
310
|
+
return /* @__PURE__ */ jsx(TimeInput, {
|
|
311
|
+
...inputProps,
|
|
312
|
+
id,
|
|
313
|
+
leftSection: icon,
|
|
314
|
+
defaultValue: props.input.props.defaultValue,
|
|
315
|
+
onChange: (event) => {
|
|
316
|
+
props.input.set(event.currentTarget.value);
|
|
317
|
+
},
|
|
318
|
+
...timeInputProps
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
return null;
|
|
322
|
+
};
|
|
323
|
+
var ControlDate_default = ControlDate;
|
|
324
|
+
|
|
325
|
+
//#endregion
|
|
326
|
+
//#region src/components/ControlSelect.tsx
|
|
327
|
+
/**
|
|
328
|
+
* ControlSelect component for handling Select, MultiSelect, and TagsInput.
|
|
329
|
+
*
|
|
330
|
+
* Features:
|
|
331
|
+
* - Basic Select with enum support
|
|
332
|
+
* - MultiSelect for array of enums
|
|
333
|
+
* - TagsInput for array of strings (no enum)
|
|
334
|
+
* - Future: Lazy loading
|
|
335
|
+
* - Future: Searchable/filterable options
|
|
336
|
+
* - Future: Custom option rendering
|
|
337
|
+
*
|
|
338
|
+
* Automatically detects enum values and array types from schema.
|
|
339
|
+
*/
|
|
340
|
+
const ControlSelect = (props) => {
|
|
341
|
+
const { inputProps, id, icon } = parseInput(props, useFormState(props.input));
|
|
342
|
+
if (!props.input?.props) return null;
|
|
343
|
+
const isArray = props.input.schema && "type" in props.input.schema && props.input.schema.type === "array";
|
|
344
|
+
let itemsEnum;
|
|
345
|
+
if (isArray && "items" in props.input.schema && props.input.schema.items) {
|
|
346
|
+
const items = props.input.schema.items;
|
|
347
|
+
if ("enum" in items && Array.isArray(items.enum)) itemsEnum = items.enum;
|
|
348
|
+
}
|
|
349
|
+
const enumValues = props.input.schema && "enum" in props.input.schema && Array.isArray(props.input.schema.enum) ? props.input.schema.enum : [];
|
|
350
|
+
if (isArray && !itemsEnum || props.tags) {
|
|
351
|
+
const tagsInputProps = typeof props.tags === "object" ? props.tags : {};
|
|
352
|
+
return /* @__PURE__ */ jsx(TagsInput, {
|
|
353
|
+
...inputProps,
|
|
354
|
+
id,
|
|
355
|
+
leftSection: icon,
|
|
356
|
+
defaultValue: Array.isArray(props.input.props.defaultValue) ? props.input.props.defaultValue : [],
|
|
357
|
+
onChange: (value) => {
|
|
358
|
+
props.input.set(value);
|
|
359
|
+
},
|
|
360
|
+
...tagsInputProps
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
if (isArray && itemsEnum || props.multi) {
|
|
364
|
+
const data$1 = itemsEnum?.map((value) => ({
|
|
365
|
+
value,
|
|
366
|
+
label: value
|
|
367
|
+
})) || [];
|
|
368
|
+
const multiSelectProps = typeof props.multi === "object" ? props.multi : {};
|
|
369
|
+
return /* @__PURE__ */ jsx(MultiSelect, {
|
|
370
|
+
...inputProps,
|
|
371
|
+
id,
|
|
372
|
+
leftSection: icon,
|
|
373
|
+
data: data$1,
|
|
374
|
+
defaultValue: Array.isArray(props.input.props.defaultValue) ? props.input.props.defaultValue : [],
|
|
375
|
+
onChange: (value) => {
|
|
376
|
+
props.input.set(value);
|
|
377
|
+
},
|
|
378
|
+
...multiSelectProps
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
const data = enumValues.map((value) => ({
|
|
382
|
+
value,
|
|
383
|
+
label: value
|
|
384
|
+
}));
|
|
385
|
+
const selectProps = typeof props.select === "object" ? props.select : {};
|
|
386
|
+
return /* @__PURE__ */ jsx(Select, {
|
|
387
|
+
...inputProps,
|
|
388
|
+
id,
|
|
389
|
+
leftSection: icon,
|
|
390
|
+
data,
|
|
391
|
+
...props.input.props,
|
|
392
|
+
...selectProps
|
|
142
393
|
});
|
|
143
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(ColorSchemeScript, {
|
|
144
|
-
defaultColorScheme: props.mantine?.defaultColorScheme,
|
|
145
|
-
...props.colorSchemeScript
|
|
146
|
-
}), /* @__PURE__ */ jsxs(MantineProvider, {
|
|
147
|
-
...props.mantine,
|
|
148
|
-
children: [
|
|
149
|
-
/* @__PURE__ */ jsx(Notifications, { ...props.notifications }),
|
|
150
|
-
/* @__PURE__ */ jsx(NavigationProgress, { ...props.navigationProgress }),
|
|
151
|
-
/* @__PURE__ */ jsx(ModalsProvider, {
|
|
152
|
-
...props.modals,
|
|
153
|
-
children: props.children ?? /* @__PURE__ */ jsx(NestedView, {})
|
|
154
|
-
})
|
|
155
|
-
]
|
|
156
|
-
})] });
|
|
157
394
|
};
|
|
395
|
+
var ControlSelect_default = ControlSelect;
|
|
158
396
|
|
|
159
397
|
//#endregion
|
|
160
398
|
//#region src/components/Control.tsx
|
|
@@ -162,39 +400,32 @@ const AlephaMantineProvider = (props) => {
|
|
|
162
400
|
* Generic form control that renders the appropriate input based on the schema and props.
|
|
163
401
|
*
|
|
164
402
|
* Supports:
|
|
165
|
-
* - TextInput
|
|
403
|
+
* - TextInput (with format detection: email, url, tel)
|
|
166
404
|
* - Textarea
|
|
405
|
+
* - NumberInput (for number/integer types)
|
|
406
|
+
* - FileInput
|
|
407
|
+
* - ColorInput (for color format)
|
|
167
408
|
* - Select (for enum types)
|
|
168
409
|
* - Autocomplete
|
|
169
410
|
* - PasswordInput
|
|
170
411
|
* - Switch (for boolean types)
|
|
171
412
|
* - SegmentedControl (for enum types)
|
|
413
|
+
* - DateInput (for date format)
|
|
414
|
+
* - DateTimePicker (for date-time format)
|
|
415
|
+
* - TimeInput (for time format)
|
|
172
416
|
* - Custom component
|
|
173
417
|
*
|
|
174
|
-
* Automatically handles labels, descriptions, error messages, and
|
|
418
|
+
* Automatically handles labels, descriptions, error messages, required state, and default icons.
|
|
175
419
|
*/
|
|
176
420
|
const Control = (props) => {
|
|
177
|
-
const
|
|
421
|
+
const { inputProps, id, icon } = parseInput(props, useFormState(props.input));
|
|
178
422
|
if (!props.input?.props) return null;
|
|
179
|
-
const
|
|
180
|
-
const id = props.input.props.id;
|
|
181
|
-
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);
|
|
182
|
-
const description = props.description ?? ("description" in props.input.schema && typeof props.input.schema.description === "string" ? props.input.schema.description : void 0);
|
|
183
|
-
const error = form.error && form.error instanceof TypeBoxError ? form.error.value.message : void 0;
|
|
184
|
-
const icon = props.icon;
|
|
185
|
-
const required = props.input.required;
|
|
186
|
-
const inputProps = {
|
|
187
|
-
label,
|
|
188
|
-
description,
|
|
189
|
-
error,
|
|
190
|
-
required,
|
|
191
|
-
disabled
|
|
192
|
-
};
|
|
423
|
+
const format = props.input.schema && "format" in props.input.schema && typeof props.input.schema.format === "string" ? props.input.schema.format : void 0;
|
|
193
424
|
if (props.custom) {
|
|
194
425
|
const Custom = props.custom;
|
|
195
426
|
return /* @__PURE__ */ jsx(Input.Wrapper, {
|
|
196
427
|
...inputProps,
|
|
197
|
-
children: /* @__PURE__ */ jsx(Flex, {
|
|
428
|
+
children: /* @__PURE__ */ jsx(Flex$1, {
|
|
198
429
|
flex: 1,
|
|
199
430
|
mt: "calc(var(--mantine-spacing-xs) / 2)",
|
|
200
431
|
children: /* @__PURE__ */ jsx(Custom, {
|
|
@@ -206,6 +437,39 @@ const Control = (props) => {
|
|
|
206
437
|
})
|
|
207
438
|
});
|
|
208
439
|
}
|
|
440
|
+
if (props.number || props.input.schema && "type" in props.input.schema && (props.input.schema.type === "number" || props.input.schema.type === "integer")) {
|
|
441
|
+
const numberInputProps = typeof props.number === "object" ? props.number : {};
|
|
442
|
+
const { type,...inputPropsWithoutType } = props.input.props;
|
|
443
|
+
return /* @__PURE__ */ jsx(NumberInput, {
|
|
444
|
+
...inputProps,
|
|
445
|
+
id,
|
|
446
|
+
leftSection: icon,
|
|
447
|
+
...inputPropsWithoutType,
|
|
448
|
+
...numberInputProps
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
if (props.file) {
|
|
452
|
+
const fileInputProps = typeof props.file === "object" ? props.file : {};
|
|
453
|
+
return /* @__PURE__ */ jsx(FileInput, {
|
|
454
|
+
...inputProps,
|
|
455
|
+
id,
|
|
456
|
+
leftSection: icon,
|
|
457
|
+
onChange: (file) => {
|
|
458
|
+
props.input.set(file);
|
|
459
|
+
},
|
|
460
|
+
...fileInputProps
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
if (props.color || format === "color") {
|
|
464
|
+
const colorInputProps = typeof props.color === "object" ? props.color : {};
|
|
465
|
+
return /* @__PURE__ */ jsx(ColorInput, {
|
|
466
|
+
...inputProps,
|
|
467
|
+
id,
|
|
468
|
+
leftSection: icon,
|
|
469
|
+
...props.input.props,
|
|
470
|
+
...colorInputProps
|
|
471
|
+
});
|
|
472
|
+
}
|
|
209
473
|
if (props.segmented) {
|
|
210
474
|
const segmentedControlProps = typeof props.segmented === "object" ? props.segmented : {};
|
|
211
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) => ({
|
|
@@ -214,10 +478,10 @@ const Control = (props) => {
|
|
|
214
478
|
})) : []);
|
|
215
479
|
return /* @__PURE__ */ jsx(Input.Wrapper, {
|
|
216
480
|
...inputProps,
|
|
217
|
-
children: /* @__PURE__ */ jsx(Flex, {
|
|
481
|
+
children: /* @__PURE__ */ jsx(Flex$1, {
|
|
218
482
|
mt: "calc(var(--mantine-spacing-xs) / 2)",
|
|
219
483
|
children: /* @__PURE__ */ jsx(SegmentedControl, {
|
|
220
|
-
disabled,
|
|
484
|
+
disabled: inputProps.disabled,
|
|
221
485
|
defaultValue: String(props.input.props.defaultValue),
|
|
222
486
|
...segmentedControlProps,
|
|
223
487
|
onChange: (value) => {
|
|
@@ -238,21 +502,15 @@ const Control = (props) => {
|
|
|
238
502
|
...autocompleteProps
|
|
239
503
|
});
|
|
240
504
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
leftSection: icon,
|
|
251
|
-
data,
|
|
252
|
-
...props.input.props,
|
|
253
|
-
...selectProps
|
|
254
|
-
});
|
|
255
|
-
}
|
|
505
|
+
const isEnum = props.input.schema && "enum" in props.input.schema && props.input.schema.enum;
|
|
506
|
+
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
|
+
});
|
|
256
514
|
if (props.input.schema && "type" in props.input.schema && props.input.schema.type === "boolean" || props.switch) {
|
|
257
515
|
const switchProps = typeof props.switch === "object" ? props.switch : {};
|
|
258
516
|
return /* @__PURE__ */ jsx(Switch, {
|
|
@@ -264,7 +522,7 @@ const Control = (props) => {
|
|
|
264
522
|
...switchProps
|
|
265
523
|
});
|
|
266
524
|
}
|
|
267
|
-
if (props.password) {
|
|
525
|
+
if (props.password || props.input.props.name?.includes("password")) {
|
|
268
526
|
const passwordInputProps = typeof props.password === "object" ? props.password : {};
|
|
269
527
|
return /* @__PURE__ */ jsx(PasswordInput, {
|
|
270
528
|
...inputProps,
|
|
@@ -284,20 +542,197 @@ const Control = (props) => {
|
|
|
284
542
|
...textAreaProps
|
|
285
543
|
});
|
|
286
544
|
}
|
|
545
|
+
if (props.date || props.datetime || props.time || format === "date" || format === "date-time" || format === "time") return /* @__PURE__ */ jsx(ControlDate_default, {
|
|
546
|
+
input: props.input,
|
|
547
|
+
title: props.title,
|
|
548
|
+
description: props.description,
|
|
549
|
+
icon,
|
|
550
|
+
date: props.date,
|
|
551
|
+
datetime: props.datetime,
|
|
552
|
+
time: props.time
|
|
553
|
+
});
|
|
287
554
|
const textInputProps = typeof props.text === "object" ? props.text : {};
|
|
555
|
+
const getInputType = () => {
|
|
556
|
+
switch (format) {
|
|
557
|
+
case "email": return "email";
|
|
558
|
+
case "url":
|
|
559
|
+
case "uri": return "url";
|
|
560
|
+
case "tel":
|
|
561
|
+
case "phone": return "tel";
|
|
562
|
+
default: return;
|
|
563
|
+
}
|
|
564
|
+
};
|
|
288
565
|
return /* @__PURE__ */ jsx(TextInput, {
|
|
289
566
|
...inputProps,
|
|
290
567
|
id,
|
|
291
568
|
leftSection: icon,
|
|
569
|
+
type: getInputType(),
|
|
292
570
|
...props.input.props,
|
|
293
571
|
...textInputProps
|
|
294
572
|
});
|
|
295
573
|
};
|
|
296
|
-
|
|
297
|
-
|
|
574
|
+
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
|
+
};
|
|
298
598
|
};
|
|
299
|
-
|
|
300
|
-
|
|
599
|
+
|
|
600
|
+
//#endregion
|
|
601
|
+
//#region src/components/DarkModeButton.tsx
|
|
602
|
+
const DarkModeButton = (props) => {
|
|
603
|
+
const { setColorScheme } = useMantineColorScheme();
|
|
604
|
+
const computedColorScheme = useComputedColorScheme("light");
|
|
605
|
+
const [colorScheme, setColorScheme2] = useState("default");
|
|
606
|
+
useEffect(() => {
|
|
607
|
+
setColorScheme2(computedColorScheme);
|
|
608
|
+
}, [computedColorScheme]);
|
|
609
|
+
const mode = props.mode ?? "minimal";
|
|
610
|
+
const toggleColorScheme = () => {
|
|
611
|
+
setColorScheme(computedColorScheme === "dark" ? "light" : "dark");
|
|
612
|
+
};
|
|
613
|
+
if (mode === "segmented") return /* @__PURE__ */ jsx(SegmentedControl, {
|
|
614
|
+
value: colorScheme,
|
|
615
|
+
onChange: (value) => setColorScheme(value),
|
|
616
|
+
data: [{
|
|
617
|
+
value: "light",
|
|
618
|
+
label: /* @__PURE__ */ jsx(Flex$1, {
|
|
619
|
+
h: 20,
|
|
620
|
+
align: "center",
|
|
621
|
+
justify: "center",
|
|
622
|
+
children: /* @__PURE__ */ jsx(IconSun, { size: 16 })
|
|
623
|
+
})
|
|
624
|
+
}, {
|
|
625
|
+
value: "dark",
|
|
626
|
+
label: /* @__PURE__ */ jsx(Flex$1, {
|
|
627
|
+
h: 20,
|
|
628
|
+
align: "center",
|
|
629
|
+
justify: "center",
|
|
630
|
+
children: /* @__PURE__ */ jsx(IconMoon, { size: 16 })
|
|
631
|
+
})
|
|
632
|
+
}]
|
|
633
|
+
});
|
|
634
|
+
return /* @__PURE__ */ jsx(ActionIcon, {
|
|
635
|
+
onClick: toggleColorScheme,
|
|
636
|
+
variant: props.variant ?? "default",
|
|
637
|
+
size: props.size ?? "lg",
|
|
638
|
+
"aria-label": "Toggle color scheme",
|
|
639
|
+
children: colorScheme === "dark" ? /* @__PURE__ */ jsx(IconSun, { size: 20 }) : /* @__PURE__ */ jsx(IconMoon, { size: 20 })
|
|
640
|
+
});
|
|
641
|
+
};
|
|
642
|
+
var DarkModeButton_default = DarkModeButton;
|
|
643
|
+
|
|
644
|
+
//#endregion
|
|
645
|
+
//#region src/components/TypeForm.tsx
|
|
646
|
+
/**
|
|
647
|
+
* TypeForm component that automatically renders all form inputs based on schema.
|
|
648
|
+
* Uses the Control component to render individual fields and Mantine Grid for responsive layout.
|
|
649
|
+
*
|
|
650
|
+
* @example
|
|
651
|
+
* ```tsx
|
|
652
|
+
* import { t } from "alepha";
|
|
653
|
+
* import { useForm } from "@alepha/react-form";
|
|
654
|
+
* import { TypeForm } from "@alepha/ui";
|
|
655
|
+
*
|
|
656
|
+
* const form = useForm({
|
|
657
|
+
* schema: t.object({
|
|
658
|
+
* username: t.text(),
|
|
659
|
+
* email: t.text(),
|
|
660
|
+
* age: t.integer(),
|
|
661
|
+
* subscribe: t.boolean(),
|
|
662
|
+
* }),
|
|
663
|
+
* handler: (values) => {
|
|
664
|
+
* console.log(values);
|
|
665
|
+
* },
|
|
666
|
+
* });
|
|
667
|
+
*
|
|
668
|
+
* return <TypeForm form={form} columns={2} />;
|
|
669
|
+
* ```
|
|
670
|
+
*/
|
|
671
|
+
const TypeForm = (props) => {
|
|
672
|
+
const { form, columns = 1, children, controlProps, skipFormElement = false, skipSubmitButton = false, submitButtonProps } = props;
|
|
673
|
+
if (!form.options?.schema?.properties) return null;
|
|
674
|
+
const supportedFields = Object.keys(form.options.schema.properties).filter((fieldName) => {
|
|
675
|
+
const field = form.input[fieldName];
|
|
676
|
+
if (!field || typeof field !== "object" || !("schema" in field)) return false;
|
|
677
|
+
const schema = field.schema;
|
|
678
|
+
if ("type" in schema) {
|
|
679
|
+
if (schema.type === "object") return false;
|
|
680
|
+
}
|
|
681
|
+
if ("properties" in schema && schema.properties) return false;
|
|
682
|
+
return true;
|
|
683
|
+
});
|
|
684
|
+
const colSpan = typeof columns === "number" ? {
|
|
685
|
+
xs: 12,
|
|
686
|
+
sm: 6,
|
|
687
|
+
lg: 12 / 3
|
|
688
|
+
} : {
|
|
689
|
+
base: columns.base ? 12 / columns.base : void 0,
|
|
690
|
+
xs: columns.xs ? 12 / columns.xs : 12,
|
|
691
|
+
sm: columns.sm ? 12 / columns.sm : 6,
|
|
692
|
+
md: columns.md ? 12 / columns.md : void 0,
|
|
693
|
+
lg: columns.lg ? 12 / columns.lg : 4,
|
|
694
|
+
xl: columns.xl ? 12 / columns.xl : void 0
|
|
695
|
+
};
|
|
696
|
+
const renderFields = () => {
|
|
697
|
+
if (children) return /* @__PURE__ */ jsx(Fragment, { children: children(form.input) });
|
|
698
|
+
return /* @__PURE__ */ jsx(Grid, { children: supportedFields.map((fieldName) => {
|
|
699
|
+
const field = form.input[fieldName];
|
|
700
|
+
if (!field || typeof field !== "object" || !("schema" in field)) return null;
|
|
701
|
+
return /* @__PURE__ */ jsx(Grid.Col, {
|
|
702
|
+
span: colSpan,
|
|
703
|
+
children: /* @__PURE__ */ jsx(Control_default, {
|
|
704
|
+
input: field,
|
|
705
|
+
...controlProps
|
|
706
|
+
})
|
|
707
|
+
}, fieldName);
|
|
708
|
+
}) });
|
|
709
|
+
};
|
|
710
|
+
const content = /* @__PURE__ */ jsxs(Stack, { children: [renderFields(), !skipSubmitButton && /* @__PURE__ */ jsx(Action_default, {
|
|
711
|
+
form,
|
|
712
|
+
...submitButtonProps,
|
|
713
|
+
children: submitButtonProps?.children ?? "Submit"
|
|
714
|
+
})] });
|
|
715
|
+
if (skipFormElement) return content;
|
|
716
|
+
return /* @__PURE__ */ jsx("form", {
|
|
717
|
+
onSubmit: form.onSubmit,
|
|
718
|
+
noValidate: true,
|
|
719
|
+
children: content
|
|
720
|
+
});
|
|
721
|
+
};
|
|
722
|
+
var TypeForm_default = TypeForm;
|
|
723
|
+
|
|
724
|
+
//#endregion
|
|
725
|
+
//#region src/hooks/useToast.ts
|
|
726
|
+
/**
|
|
727
|
+
* Use this hook to access the Toast Service for showing notifications.
|
|
728
|
+
*
|
|
729
|
+
* @example
|
|
730
|
+
* const toast = useToast();
|
|
731
|
+
* toast.success({ message: "Operation completed successfully!" });
|
|
732
|
+
* toast.error({ title: "Error", message: "Something went wrong" });
|
|
733
|
+
*/
|
|
734
|
+
const useToast = () => {
|
|
735
|
+
return useInject(ToastService);
|
|
301
736
|
};
|
|
302
737
|
|
|
303
738
|
//#endregion
|
|
@@ -309,9 +744,13 @@ const capitalize = (str) => {
|
|
|
309
744
|
*/
|
|
310
745
|
const AlephaUI = $module({
|
|
311
746
|
name: "alepha.ui",
|
|
312
|
-
services: [
|
|
747
|
+
services: [
|
|
748
|
+
AlephaReact,
|
|
749
|
+
ToastService,
|
|
750
|
+
RootRouter
|
|
751
|
+
]
|
|
313
752
|
});
|
|
314
753
|
|
|
315
754
|
//#endregion
|
|
316
|
-
export { Action, AlephaMantineProvider, AlephaUI, Control };
|
|
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 };
|
|
317
756
|
//# sourceMappingURL=index.js.map
|