svelora 3.0.0 → 3.0.2
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/Accordion/Accordion.svelte +66 -97
- package/dist/Alert/Alert.svelte +39 -64
- package/dist/Alert/Alert.svelte.d.ts +1 -1
- package/dist/Avatar/Avatar.svelte +35 -75
- package/dist/AvatarGroup/AvatarGroup.svelte +38 -55
- package/dist/Badge/Badge.svelte +28 -50
- package/dist/Banner/Banner.svelte +46 -41
- package/dist/Banner/Banner.svelte.d.ts +1 -1
- package/dist/Breadcrumb/Breadcrumb.svelte +32 -26
- package/dist/Button/Button.svelte +70 -138
- package/dist/Calendar/Calendar.svelte +94 -157
- package/dist/Calendar/Calendar.svelte.d.ts +1 -1
- package/dist/Card/Card.svelte +18 -31
- package/dist/Carousel/Carousel.svelte +118 -173
- package/dist/Checkbox/Checkbox.svelte +52 -97
- package/dist/CheckboxGroup/CheckboxGroup.svelte +62 -107
- package/dist/CheckboxGroup/CheckboxGroup.svelte.d.ts +1 -1
- package/dist/Chip/Chip.svelte +22 -34
- package/dist/CodeBlock/CodeBlock.svelte +42 -59
- package/dist/Collapsible/Collapsible.svelte +22 -38
- package/dist/Collapsible/Collapsible.svelte.d.ts +1 -1
- package/dist/Collapsible/CollapsibleTestWrapper.svelte +2 -5
- package/dist/Collapsible/CollapsibleTestWrapper.svelte.d.ts +1 -1
- package/dist/Command/Command.svelte +40 -77
- package/dist/Command/Command.svelte.d.ts +1 -1
- package/dist/Command/CommandTestWrapper.svelte +2 -10
- package/dist/Command/CommandTestWrapper.svelte.d.ts +1 -1
- package/dist/Container/Container.svelte +11 -14
- package/dist/ContextMenu/ContextMenu.svelte +51 -114
- package/dist/ContextMenu/ContextMenu.svelte.d.ts +1 -1
- package/dist/Drawer/Drawer.svelte +72 -110
- package/dist/Drawer/DrawerTriggerTestWrapper.svelte +1 -2
- package/dist/DropdownMenu/DropdownMenu.svelte +63 -124
- package/dist/DropdownMenu/DropdownMenu.svelte.d.ts +1 -1
- package/dist/DropdownMenu/DropdownMenuTriggerTestWrapper.svelte +2 -5
- package/dist/Editor/Editor.svelte +441 -576
- package/dist/Editor/Editor.svelte.d.ts +1 -1
- package/dist/Editor/EditorUrlPrompt.svelte +40 -53
- package/dist/Editor/SlashPopup.svelte +12 -24
- package/dist/Empty/Empty.svelte +32 -63
- package/dist/FieldGroup/FieldGroup.svelte +23 -38
- package/dist/FileUpload/FileUpload.svelte +242 -320
- package/dist/FileUpload/FileUpload.svelte.d.ts +1 -1
- package/dist/Fonts/Fonts.svelte +15 -37
- package/dist/Form/Form.svelte +112 -170
- package/dist/FormField/FormField.svelte +102 -135
- package/dist/Icon/Icon.svelte +7 -32
- package/dist/Input/Input.svelte +71 -141
- package/dist/Input/Input.svelte.d.ts +2 -2
- package/dist/Kbd/Kbd.svelte +18 -34
- package/dist/Link/Link.svelte +129 -196
- package/dist/LocaleButton/LocaleButton.svelte +165 -0
- package/dist/LocaleButton/LocaleButton.svelte.d.ts +5 -0
- package/dist/LocaleButton/index.d.ts +2 -0
- package/dist/LocaleButton/index.js +1 -0
- package/dist/LocaleButton/locale-button.types.d.ts +182 -0
- package/dist/LocaleButton/locale-button.types.js +1 -0
- package/dist/LocaleButton/locale-button.variants.d.ts +61 -0
- package/dist/LocaleButton/locale-button.variants.js +34 -0
- package/dist/Modal/Modal.svelte +52 -106
- package/dist/Modal/ModalTriggerTestWrapper.svelte +1 -2
- package/dist/Pagination/Pagination.svelte +48 -92
- package/dist/Pagination/pagination.variants.d.ts +1 -1
- package/dist/PinInput/PinInput.svelte +57 -111
- package/dist/PinInput/PinInput.svelte.d.ts +1 -1
- package/dist/Popover/Popover.svelte +28 -61
- package/dist/Popover/Popover.svelte.d.ts +1 -1
- package/dist/Progress/Progress.svelte +75 -94
- package/dist/RadioGroup/RadioGroup.svelte +54 -99
- package/dist/RadioGroup/RadioGroup.svelte.d.ts +1 -1
- package/dist/Select/Select.svelte +112 -269
- package/dist/Select/Select.svelte.d.ts +1 -1
- package/dist/SelectMenu/SelectMenu.svelte +211 -409
- package/dist/SelectMenu/SelectMenu.svelte.d.ts +1 -1
- package/dist/SelectMenu/SelectMenuFormFieldTestWrapper.svelte +3 -6
- package/dist/Separator/Separator.svelte +29 -44
- package/dist/Skeleton/Skeleton.svelte +11 -23
- package/dist/Slideover/Slideover.svelte +52 -106
- package/dist/Slideover/SlideoverTriggerTestWrapper.svelte +1 -2
- package/dist/Slider/Slider.svelte +48 -84
- package/dist/Slider/Slider.svelte.d.ts +1 -1
- package/dist/Stepper/Stepper.svelte +139 -132
- package/dist/Stepper/Stepper.svelte.d.ts +1 -1
- package/dist/Switch/Switch.svelte +62 -98
- package/dist/Table/Table.svelte +232 -283
- package/dist/Table/table.variants.d.ts +1 -1
- package/dist/Tabs/Tabs.svelte +96 -129
- package/dist/Tabs/Tabs.svelte.d.ts +1 -1
- package/dist/Textarea/Textarea.svelte +90 -173
- package/dist/Textarea/Textarea.svelte.d.ts +1 -1
- package/dist/ThemeModeButton/ThemeModeButton.svelte +16 -38
- package/dist/Timeline/Timeline.svelte +75 -54
- package/dist/Toast/Toaster.svelte +8 -25
- package/dist/Tooltip/Tooltip.svelte +34 -66
- package/dist/Tooltip/Tooltip.svelte.d.ts +1 -1
- package/dist/Tooltip/TooltipTestWrapper.svelte +2 -5
- package/dist/User/User.svelte +33 -49
- package/dist/docs/navigation.d.ts +1 -1
- package/dist/docs/navigation.js +8 -1
- package/dist/hooks/HookContextProbe.svelte +2 -4
- package/dist/hooks/HookContextProvider.svelte +8 -6
- package/dist/hooks/HookEmitProbe.svelte +8 -11
- package/dist/i18n.d.ts +2 -0
- package/dist/i18n.js +19 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/mcp/svelora-docs.data.json +4 -2
- package/dist/theme.css +1 -1
- package/package.json +16 -8
package/dist/Form/Form.svelte
CHANGED
|
@@ -1,178 +1,120 @@
|
|
|
1
|
-
<script lang="ts" module>
|
|
2
|
-
import type { FormProps, FormSchema } from './form.types.js'
|
|
3
|
-
|
|
4
|
-
export type Props<
|
|
5
|
-
S extends FormSchema | undefined = FormSchema | undefined,
|
|
6
|
-
T extends boolean = true,
|
|
7
|
-
N extends boolean = false
|
|
8
|
-
> = FormProps<S, T, N>
|
|
1
|
+
<script lang="ts" module>export {};
|
|
9
2
|
</script>
|
|
10
3
|
|
|
11
4
|
<script
|
|
12
5
|
lang="ts"
|
|
13
6
|
generics="S extends FormSchema | undefined = FormSchema | undefined, T extends boolean = true, N extends boolean = false"
|
|
14
|
-
>
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
get submitCount() {
|
|
126
|
-
return ctx.submitCount
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Sync the api object to the bindable prop once at setup time. `apiObject`
|
|
131
|
-
// is a stable reference built from getters, so there's nothing reactive to
|
|
132
|
-
// track — direct assignment is sufficient.
|
|
133
|
-
api = apiObject as typeof api
|
|
134
|
-
|
|
135
|
-
// Nested form attach/detach lifecycle.
|
|
136
|
-
onMount(() => {
|
|
137
|
-
if (parentCtx && nested) {
|
|
138
|
-
parentCtx.attachChild(formId, {
|
|
139
|
-
formId,
|
|
140
|
-
name: name as string | undefined,
|
|
141
|
-
validate: (opts) => ctx.validate(opts) as Promise<unknown | false>,
|
|
142
|
-
clear: (n) => ctx.clear(n),
|
|
143
|
-
reset: () => ctx.reset(),
|
|
144
|
-
setErrors: (errs, n) => ctx.setErrors(errs, n)
|
|
145
|
-
})
|
|
146
|
-
}
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
onDestroy(() => {
|
|
150
|
-
if (parentCtx && nested) parentCtx.detachChild(formId)
|
|
151
|
-
ctx.dispose()
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
async function handleSubmit(event: SubmitEvent) {
|
|
155
|
-
event.preventDefault()
|
|
156
|
-
// Forward the real SubmitEvent so user handlers can read `submitter`,
|
|
157
|
-
// `target`, etc. When `api.submit()` is called programmatically, ctx.submit
|
|
158
|
-
// synthesizes a fresh event instead.
|
|
159
|
-
await ctx.submit(event)
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// `formVariants` has no variants (only a single `root` slot), so its
|
|
163
|
-
// output is stable — compute once at setup rather than via $derived.
|
|
164
|
-
const variantSlots = formVariants()
|
|
165
|
-
const classes = $derived({
|
|
166
|
-
root: variantSlots.root({
|
|
167
|
-
class: [config.slots.root, className, ui?.root]
|
|
168
|
-
})
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
// Slot props for children
|
|
172
|
-
const slotProps = $derived({
|
|
173
|
-
errors: ctx.errors as FormErrorWithId[],
|
|
174
|
-
loading: ctx.loading
|
|
175
|
-
})
|
|
7
|
+
>import { onDestroy, onMount, setContext, untrack } from "svelte";
|
|
8
|
+
import { getComponentConfig } from "../config.js";
|
|
9
|
+
import { FORM_CONTEXT_KEY, FormContext, getFormContext } from "./form.context.svelte.js";
|
|
10
|
+
import { formDefaults, formVariants } from "./form.variants.js";
|
|
11
|
+
const config = getComponentConfig("form", formDefaults);
|
|
12
|
+
let { ref = $bindable(null), api = $bindable(), id, schema, state = $bindable({}), validate: customValidate, validateOn = [
|
|
13
|
+
"input",
|
|
14
|
+
"blur",
|
|
15
|
+
"change"
|
|
16
|
+
], validateOnInputDelay = 300, disabled = false, loadingAuto = true, transform = true, nested = false, name, onsubmit, onerror, class: className, ui, children, ...restProps } = $props();
|
|
17
|
+
// Generate a stable form id. Both `id` and `nested` are effectively init-only
|
|
18
|
+
// (they should not change over a form instance's lifetime), so `untrack`
|
|
19
|
+
// silences Svelte's state_referenced_locally warning.
|
|
20
|
+
const formId = untrack(() => id ?? (typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `svelora-form-${Math.random().toString(36).slice(2)}`));
|
|
21
|
+
// Parent context captured once at init — `nested` is effectively immutable
|
|
22
|
+
// over a form instance's lifetime. We warn (dev-only) if it's mutated later.
|
|
23
|
+
const parentCtx = untrack(() => nested ? getFormContext() : undefined);
|
|
24
|
+
const initialNested = untrack(() => nested);
|
|
25
|
+
$effect(() => {
|
|
26
|
+
if (nested !== initialNested) {}
|
|
27
|
+
});
|
|
28
|
+
// Create the reactive form context. All config is passed via getter closures
|
|
29
|
+
// so that reactive prop changes are visible inside the class.
|
|
30
|
+
const ctx = new FormContext({
|
|
31
|
+
getState: () => state,
|
|
32
|
+
getSchema: () => schema,
|
|
33
|
+
getCustomValidate: () => customValidate,
|
|
34
|
+
getValidateOn: () => validateOn,
|
|
35
|
+
getValidateOnInputDelay: () => validateOnInputDelay,
|
|
36
|
+
getDisabled: () => disabled,
|
|
37
|
+
getLoadingAuto: () => loadingAuto,
|
|
38
|
+
getTransform: () => transform,
|
|
39
|
+
getName: () => name,
|
|
40
|
+
getOnSubmit: () => onsubmit,
|
|
41
|
+
getOnError: () => onerror
|
|
42
|
+
}, formId, parentCtx);
|
|
43
|
+
setContext(FORM_CONTEXT_KEY, ctx);
|
|
44
|
+
// Build the bindable API object. Getters ensure reactive state reads stay live.
|
|
45
|
+
const apiObject = {
|
|
46
|
+
validate: (opts) => ctx.validate(opts),
|
|
47
|
+
submit: () => ctx.submit(),
|
|
48
|
+
clear: (name) => ctx.clear(name),
|
|
49
|
+
getErrors: (name) => ctx.getErrors(name),
|
|
50
|
+
setErrors: (errs, name) => ctx.setErrors(errs, name),
|
|
51
|
+
reset: () => ctx.reset(),
|
|
52
|
+
get errors() {
|
|
53
|
+
return ctx.errors;
|
|
54
|
+
},
|
|
55
|
+
get loading() {
|
|
56
|
+
return ctx.loading;
|
|
57
|
+
},
|
|
58
|
+
get disabled() {
|
|
59
|
+
return ctx.disabled;
|
|
60
|
+
},
|
|
61
|
+
get dirty() {
|
|
62
|
+
return ctx.dirty;
|
|
63
|
+
},
|
|
64
|
+
get dirtyFields() {
|
|
65
|
+
return ctx.dirtyFields;
|
|
66
|
+
},
|
|
67
|
+
get touchedFields() {
|
|
68
|
+
return ctx.touchedFields;
|
|
69
|
+
},
|
|
70
|
+
get blurredFields() {
|
|
71
|
+
return ctx.blurredFields;
|
|
72
|
+
},
|
|
73
|
+
get submitCount() {
|
|
74
|
+
return ctx.submitCount;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
// Sync the api object to the bindable prop once at setup time. `apiObject`
|
|
78
|
+
// is a stable reference built from getters, so there's nothing reactive to
|
|
79
|
+
// track — direct assignment is sufficient.
|
|
80
|
+
api = apiObject;
|
|
81
|
+
// Nested form attach/detach lifecycle.
|
|
82
|
+
onMount(() => {
|
|
83
|
+
if (parentCtx && nested) {
|
|
84
|
+
parentCtx.attachChild(formId, {
|
|
85
|
+
formId,
|
|
86
|
+
name,
|
|
87
|
+
validate: (opts) => ctx.validate(opts),
|
|
88
|
+
clear: (n) => ctx.clear(n),
|
|
89
|
+
reset: () => ctx.reset(),
|
|
90
|
+
setErrors: (errs, n) => ctx.setErrors(errs, n)
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
onDestroy(() => {
|
|
95
|
+
if (parentCtx && nested) parentCtx.detachChild(formId);
|
|
96
|
+
ctx.dispose();
|
|
97
|
+
});
|
|
98
|
+
async function handleSubmit(event) {
|
|
99
|
+
event.preventDefault();
|
|
100
|
+
// Forward the real SubmitEvent so user handlers can read `submitter`,
|
|
101
|
+
// `target`, etc. When `api.submit()` is called programmatically, ctx.submit
|
|
102
|
+
// synthesizes a fresh event instead.
|
|
103
|
+
await ctx.submit(event);
|
|
104
|
+
}
|
|
105
|
+
// `formVariants` has no variants (only a single `root` slot), so its
|
|
106
|
+
// output is stable — compute once at setup rather than via $derived.
|
|
107
|
+
const variantSlots = formVariants();
|
|
108
|
+
const classes = $derived({ root: variantSlots.root({ class: [
|
|
109
|
+
config.slots.root,
|
|
110
|
+
className,
|
|
111
|
+
ui?.root
|
|
112
|
+
] }) });
|
|
113
|
+
// Slot props for children
|
|
114
|
+
const slotProps = $derived({
|
|
115
|
+
errors: ctx.errors,
|
|
116
|
+
loading: ctx.loading
|
|
117
|
+
});
|
|
176
118
|
</script>
|
|
177
119
|
|
|
178
120
|
{#if nested}
|
|
@@ -1,140 +1,107 @@
|
|
|
1
|
-
<script lang="ts" module>
|
|
2
|
-
import type { FormFieldProps } from './form-field.types.js'
|
|
3
|
-
|
|
4
|
-
export type Props = FormFieldProps
|
|
1
|
+
<script lang="ts" module>export {};
|
|
5
2
|
</script>
|
|
6
3
|
|
|
7
|
-
<script lang="ts">
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
hint: variantSlots.hint({ class: [config.slots.hint, ui?.hint] }),
|
|
109
|
-
help: variantSlots.help({ class: [config.slots.help, ui?.help] })
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
const hasError = $derived(resolvedError !== undefined && resolvedError !== false)
|
|
113
|
-
const errorMessage = $derived(typeof resolvedError === 'string' ? resolvedError : undefined)
|
|
114
|
-
|
|
115
|
-
setContext<FormFieldContext>(FORM_FIELD_CONTEXT_KEY, {
|
|
116
|
-
get name() {
|
|
117
|
-
return name
|
|
118
|
-
},
|
|
119
|
-
get size() {
|
|
120
|
-
return size
|
|
121
|
-
},
|
|
122
|
-
get error() {
|
|
123
|
-
return resolvedError
|
|
124
|
-
},
|
|
125
|
-
get ariaId() {
|
|
126
|
-
return ariaId
|
|
127
|
-
},
|
|
128
|
-
get eagerValidation() {
|
|
129
|
-
return eagerValidation
|
|
130
|
-
},
|
|
131
|
-
get validateOnInputDelay() {
|
|
132
|
-
return validateOnInputDelay
|
|
133
|
-
},
|
|
134
|
-
get errorPattern() {
|
|
135
|
-
return errorPattern
|
|
136
|
-
}
|
|
137
|
-
})
|
|
4
|
+
<script lang="ts">import { Label, useId } from "bits-ui";
|
|
5
|
+
import { setContext, untrack } from "svelte";
|
|
6
|
+
import { getComponentConfig } from "../config.js";
|
|
7
|
+
import { getFormContext } from "../Form/form.context.svelte.js";
|
|
8
|
+
import { FORM_FIELD_CONTEXT_KEY } from "../hooks/useFormField.svelte.js";
|
|
9
|
+
import { formFieldDefaults, formFieldVariants } from "./form-field.variants.js";
|
|
10
|
+
const config = getComponentConfig("formField", formFieldDefaults);
|
|
11
|
+
let { ref = $bindable(null), ui, name, label, description, hint, help, error, size = config.defaultVariants.size ?? "md", required = false, orientation = config.defaultVariants.orientation ?? "vertical", eagerValidation, validateOnInputDelay, errorPattern, class: className, children, labelSlot, hintSlot, descriptionSlot, helpSlot, errorSlot, ...restProps } = $props();
|
|
12
|
+
const id = useId();
|
|
13
|
+
const ariaId = $derived(name ? `form-field-${name}` : id);
|
|
14
|
+
// Form-level context (undefined when FormField is used standalone).
|
|
15
|
+
const formCtx = getFormContext();
|
|
16
|
+
// Resolve error: explicit `error` prop always wins; otherwise ask the Form for
|
|
17
|
+
// errors matching this field's name (or errorPattern regex, if provided).
|
|
18
|
+
const resolvedError = $derived.by(() => {
|
|
19
|
+
if (error !== undefined) return error;
|
|
20
|
+
if (!formCtx || !name) return undefined;
|
|
21
|
+
const errs = errorPattern ? formCtx.getErrors(errorPattern) : formCtx.getErrors(name);
|
|
22
|
+
return errs[0]?.message;
|
|
23
|
+
});
|
|
24
|
+
// Register this field with the form. Split into two effects:
|
|
25
|
+
//
|
|
26
|
+
// 1. Lifecycle effect — tracks only `name`. Handles the initial registration
|
|
27
|
+
// and cleanup on unmount / rename. Reads other entry fields via `untrack`
|
|
28
|
+
// so they don't retrigger the lifecycle.
|
|
29
|
+
// 2. Update effect — tracks ariaId / errorPattern / eagerValidation /
|
|
30
|
+
// validateOnInputDelay and rewrites the entry via `.set()`. No cleanup —
|
|
31
|
+
// a single atomic overwrite, so no window where the registry is empty.
|
|
32
|
+
//
|
|
33
|
+
// Without this split, changing an unrelated prop (e.g. errorPattern) would
|
|
34
|
+
// cause cleanup→re-register, briefly leaving `#fieldRegistry.get(name)`
|
|
35
|
+
// undefined for any concurrent `#resolveErrorIds` call.
|
|
36
|
+
$effect(() => {
|
|
37
|
+
if (!formCtx || !name) return;
|
|
38
|
+
const registeredName = name;
|
|
39
|
+
untrack(() => {
|
|
40
|
+
formCtx.registerField(registeredName, {
|
|
41
|
+
id: ariaId,
|
|
42
|
+
pattern: errorPattern,
|
|
43
|
+
eagerValidation,
|
|
44
|
+
validateOnInputDelay
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
return () => {
|
|
48
|
+
formCtx.unregisterField(registeredName);
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
$effect(() => {
|
|
52
|
+
if (!formCtx || !name) return;
|
|
53
|
+
formCtx.registerField(name, {
|
|
54
|
+
id: ariaId,
|
|
55
|
+
pattern: errorPattern,
|
|
56
|
+
eagerValidation,
|
|
57
|
+
validateOnInputDelay
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
const variantSlots = $derived(formFieldVariants({
|
|
61
|
+
size,
|
|
62
|
+
required,
|
|
63
|
+
orientation
|
|
64
|
+
}));
|
|
65
|
+
const classes = $derived({
|
|
66
|
+
root: variantSlots.root({ class: [
|
|
67
|
+
config.slots.root,
|
|
68
|
+
className,
|
|
69
|
+
ui?.root
|
|
70
|
+
] }),
|
|
71
|
+
wrapper: variantSlots.wrapper({ class: [config.slots.wrapper, ui?.wrapper] }),
|
|
72
|
+
labelWrapper: variantSlots.labelWrapper({ class: [config.slots.labelWrapper, ui?.labelWrapper] }),
|
|
73
|
+
label: variantSlots.label({ class: [config.slots.label, ui?.label] }),
|
|
74
|
+
container: variantSlots.container({ class: [config.slots.container, ui?.container] }),
|
|
75
|
+
description: variantSlots.description({ class: [config.slots.description, ui?.description] }),
|
|
76
|
+
error: variantSlots.error({ class: [config.slots.error, ui?.error] }),
|
|
77
|
+
hint: variantSlots.hint({ class: [config.slots.hint, ui?.hint] }),
|
|
78
|
+
help: variantSlots.help({ class: [config.slots.help, ui?.help] })
|
|
79
|
+
});
|
|
80
|
+
const hasError = $derived(resolvedError !== undefined && resolvedError !== false);
|
|
81
|
+
const errorMessage = $derived(typeof resolvedError === "string" ? resolvedError : undefined);
|
|
82
|
+
setContext(FORM_FIELD_CONTEXT_KEY, {
|
|
83
|
+
get name() {
|
|
84
|
+
return name;
|
|
85
|
+
},
|
|
86
|
+
get size() {
|
|
87
|
+
return size;
|
|
88
|
+
},
|
|
89
|
+
get error() {
|
|
90
|
+
return resolvedError;
|
|
91
|
+
},
|
|
92
|
+
get ariaId() {
|
|
93
|
+
return ariaId;
|
|
94
|
+
},
|
|
95
|
+
get eagerValidation() {
|
|
96
|
+
return eagerValidation;
|
|
97
|
+
},
|
|
98
|
+
get validateOnInputDelay() {
|
|
99
|
+
return validateOnInputDelay;
|
|
100
|
+
},
|
|
101
|
+
get errorPattern() {
|
|
102
|
+
return errorPattern;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
138
105
|
</script>
|
|
139
106
|
|
|
140
107
|
<div bind:this={ref} class={classes.root} {...restProps}>
|
package/dist/Icon/Icon.svelte
CHANGED
|
@@ -1,37 +1,12 @@
|
|
|
1
|
-
<script lang="ts" module>
|
|
2
|
-
import type { IconProps } from './icon.types.js'
|
|
3
|
-
|
|
4
|
-
export type Props = IconProps
|
|
1
|
+
<script lang="ts" module>export {};
|
|
5
2
|
</script>
|
|
6
3
|
|
|
7
|
-
<script lang="ts">
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
size = 24,
|
|
14
|
-
color,
|
|
15
|
-
flipH = false,
|
|
16
|
-
flipV = false,
|
|
17
|
-
rotate = 0,
|
|
18
|
-
class: className,
|
|
19
|
-
...restProps
|
|
20
|
-
}: Props = $props()
|
|
21
|
-
|
|
22
|
-
const flip = $derived(
|
|
23
|
-
flipH && flipV
|
|
24
|
-
? 'horizontal,vertical'
|
|
25
|
-
: flipH
|
|
26
|
-
? 'horizontal'
|
|
27
|
-
: flipV
|
|
28
|
-
? 'vertical'
|
|
29
|
-
: undefined
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
const rotateValue = $derived(rotate ? rotate / 90 : undefined)
|
|
33
|
-
|
|
34
|
-
const iconClass = $derived(twMerge('shrink-0', className))
|
|
4
|
+
<script lang="ts">import Icon from "@iconify/svelte";
|
|
5
|
+
import { twMerge } from "tailwind-merge";
|
|
6
|
+
let { name, size = 24, color, flipH = false, flipV = false, rotate = 0, class: className, ...restProps } = $props();
|
|
7
|
+
const flip = $derived(flipH && flipV ? "horizontal,vertical" : flipH ? "horizontal" : flipV ? "vertical" : undefined);
|
|
8
|
+
const rotateValue = $derived(rotate ? rotate / 90 : undefined);
|
|
9
|
+
const iconClass = $derived(twMerge("shrink-0", className));
|
|
35
10
|
</script>
|
|
36
11
|
|
|
37
12
|
<Icon
|