sv5ui 1.5.1 → 1.6.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/Checkbox/Checkbox.svelte +8 -2
- package/dist/CheckboxGroup/CheckboxGroup.svelte +15 -2
- package/dist/Form/Form.svelte +203 -0
- package/dist/Form/Form.svelte.d.ts +26 -0
- package/dist/Form/form.context.svelte.d.ts +64 -0
- package/dist/Form/form.context.svelte.js +478 -0
- package/dist/Form/form.types.d.ts +164 -0
- package/dist/Form/form.types.js +12 -0
- package/dist/Form/form.variants.d.ts +39 -0
- package/dist/Form/form.variants.js +17 -0
- package/dist/Form/index.d.ts +4 -0
- package/dist/Form/index.js +6 -0
- package/dist/Form/validate-schema.d.ts +13 -0
- package/dist/Form/validate-schema.js +113 -0
- package/dist/FormField/FormField.svelte +71 -8
- package/dist/FormField/form-field.types.d.ts +15 -0
- package/dist/Input/Input.svelte +31 -5
- package/dist/Input/Input.svelte.d.ts +25 -4
- package/dist/Input/input.types.d.ts +24 -3
- package/dist/PinInput/PinInput.svelte +9 -2
- package/dist/RadioGroup/RadioGroup.svelte +17 -3
- package/dist/Select/Select.svelte +14 -3
- package/dist/SelectMenu/SelectMenu.svelte +9 -2
- package/dist/Slider/Slider.svelte +4 -1
- package/dist/Switch/Switch.svelte +8 -2
- package/dist/Textarea/Textarea.svelte +27 -1
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/index.js +1 -1
- package/dist/hooks/useFormField.svelte.d.ts +64 -0
- package/dist/hooks/useFormField.svelte.js +70 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +26 -3
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
type FieldGroupVariantProps
|
|
14
14
|
} from '../FieldGroup/field-group.variants.js'
|
|
15
15
|
import Icon from '../Icon/Icon.svelte'
|
|
16
|
-
import { useFormField } from '../hooks/useFormField.svelte.js'
|
|
16
|
+
import { useFormField, useFormFieldEmit } from '../hooks/useFormField.svelte.js'
|
|
17
17
|
|
|
18
18
|
const config = getComponentConfig('textarea', textareaDefaults)
|
|
19
19
|
const icons = getComponentConfig('icons', iconsDefaults)
|
|
@@ -41,10 +41,32 @@
|
|
|
41
41
|
rows = 3,
|
|
42
42
|
maxrows = 0,
|
|
43
43
|
class: className,
|
|
44
|
+
onblur,
|
|
45
|
+
oninput,
|
|
46
|
+
onchange,
|
|
47
|
+
onfocus,
|
|
44
48
|
...restProps
|
|
45
49
|
}: Props = $props()
|
|
46
50
|
|
|
47
51
|
const formFieldContext = useFormField()
|
|
52
|
+
const emit = useFormFieldEmit()
|
|
53
|
+
|
|
54
|
+
function handleBlur(event: FocusEvent & { currentTarget: HTMLTextAreaElement }) {
|
|
55
|
+
emit.onBlur()
|
|
56
|
+
onblur?.(event)
|
|
57
|
+
}
|
|
58
|
+
function handleInput(event: Event & { currentTarget: HTMLTextAreaElement }) {
|
|
59
|
+
emit.onInput()
|
|
60
|
+
oninput?.(event)
|
|
61
|
+
}
|
|
62
|
+
function handleChange(event: Event & { currentTarget: HTMLTextAreaElement }) {
|
|
63
|
+
emit.onChange()
|
|
64
|
+
onchange?.(event)
|
|
65
|
+
}
|
|
66
|
+
function handleFocus(event: FocusEvent & { currentTarget: HTMLTextAreaElement }) {
|
|
67
|
+
emit.onFocus()
|
|
68
|
+
onfocus?.(event)
|
|
69
|
+
}
|
|
48
70
|
|
|
49
71
|
const fieldGroupContext = getContext<
|
|
50
72
|
| {
|
|
@@ -175,6 +197,10 @@
|
|
|
175
197
|
aria-describedby={ariaDescribedBy}
|
|
176
198
|
aria-invalid={resolvedHighlight ? true : undefined}
|
|
177
199
|
class={classes.base}
|
|
200
|
+
onblur={handleBlur}
|
|
201
|
+
oninput={handleInput}
|
|
202
|
+
onchange={handleChange}
|
|
203
|
+
onfocus={handleFocus}
|
|
178
204
|
></textarea>
|
|
179
205
|
|
|
180
206
|
{#if trailingSlot}
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export { useMediaQuery } from './useMediaQuery.svelte.js';
|
|
|
2
2
|
export type { UseMediaQueryOptions } from './useMediaQuery.svelte.js';
|
|
3
3
|
export { useClipboard } from './useClipboard.svelte.js';
|
|
4
4
|
export type { UseClipboardOptions } from './useClipboard.svelte.js';
|
|
5
|
-
export { useFormField } from './useFormField.svelte.js';
|
|
5
|
+
export { useFormField, useFormFieldEmit, wireFormEvents, FORM_FIELD_CONTEXT_KEY } from './useFormField.svelte.js';
|
|
6
6
|
export type { FormFieldContext } from './useFormField.svelte.js';
|
|
7
7
|
export { useClickOutside } from './useClickOutside.svelte.js';
|
|
8
8
|
export type { UseClickOutsideOptions } from './useClickOutside.svelte.js';
|
package/dist/hooks/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { useMediaQuery } from './useMediaQuery.svelte.js';
|
|
2
2
|
export { useClipboard } from './useClipboard.svelte.js';
|
|
3
|
-
export { useFormField } from './useFormField.svelte.js';
|
|
3
|
+
export { useFormField, useFormFieldEmit, wireFormEvents, FORM_FIELD_CONTEXT_KEY } from './useFormField.svelte.js';
|
|
4
4
|
export { useClickOutside } from './useClickOutside.svelte.js';
|
|
5
5
|
export { useInfiniteScroll } from './useInfiniteScroll.svelte.js';
|
|
6
6
|
export { useEscapeKeydown } from './useEscapeKeydown.svelte.js';
|
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
import type { FormFieldProps } from '../FormField/form-field.types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Symbol key for the FormField context. Using a Symbol instead of a string
|
|
4
|
+
* prevents collisions with unrelated `getContext('formField')` calls from
|
|
5
|
+
* user code or other libraries.
|
|
6
|
+
*/
|
|
7
|
+
export declare const FORM_FIELD_CONTEXT_KEY: unique symbol;
|
|
2
8
|
export interface FormFieldContext {
|
|
3
9
|
name?: string;
|
|
4
10
|
size: NonNullable<FormFieldProps['size']>;
|
|
5
11
|
error?: string | boolean;
|
|
6
12
|
ariaId: string;
|
|
13
|
+
/** Whether input events should validate this field before first blur. */
|
|
14
|
+
eagerValidation?: boolean;
|
|
15
|
+
/** Per-field override for the input debounce delay. */
|
|
16
|
+
validateOnInputDelay?: number;
|
|
17
|
+
/** Regex pattern used to match form errors for this field (in addition to exact name). */
|
|
18
|
+
errorPattern?: RegExp;
|
|
7
19
|
}
|
|
8
20
|
/**
|
|
9
21
|
* Access the nearest FormField context. Returns `undefined` when used outside a FormField.
|
|
@@ -19,3 +31,55 @@ export interface FormFieldContext {
|
|
|
19
31
|
* ```
|
|
20
32
|
*/
|
|
21
33
|
export declare function useFormField(): FormFieldContext | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Event emitter helpers for inputs nested inside a `<FormField>` within a `<Form>`.
|
|
36
|
+
* Each returned function is a safe no-op when used outside a Form, so inputs can
|
|
37
|
+
* unconditionally wire them to their native events.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```svelte
|
|
41
|
+
* <script>
|
|
42
|
+
* import { useFormFieldEmit } from 'sv5ui'
|
|
43
|
+
* const emit = useFormFieldEmit()
|
|
44
|
+
* </script>
|
|
45
|
+
*
|
|
46
|
+
* <input onblur={emit.onBlur} oninput={emit.onInput} onchange={emit.onChange} onfocus={emit.onFocus} />
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export declare function useFormFieldEmit(): {
|
|
50
|
+
onBlur(): void;
|
|
51
|
+
onFocus(): void;
|
|
52
|
+
onChange(): void;
|
|
53
|
+
onInput(): void;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Wires native DOM input events to the parent Form's event emitters while
|
|
57
|
+
* preserving any user-supplied handlers. Reduces boilerplate in wrapper
|
|
58
|
+
* components (Input, Textarea, etc.) from ~20 lines of handler definitions
|
|
59
|
+
* to 4 lines:
|
|
60
|
+
*
|
|
61
|
+
* ```svelte
|
|
62
|
+
* <script>
|
|
63
|
+
* const events = wireFormEvents({ onblur, oninput, onchange, onfocus })
|
|
64
|
+
* </script>
|
|
65
|
+
*
|
|
66
|
+
* <input {...events} />
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* Each handler fires the Form emitter first, then calls the user handler
|
|
70
|
+
* (if any) with the original event.
|
|
71
|
+
*/
|
|
72
|
+
type InputEventHandler<E extends Event = Event> = (event: E) => void;
|
|
73
|
+
type FocusEventHandler = InputEventHandler<FocusEvent>;
|
|
74
|
+
export declare function wireFormEvents(userHandlers: {
|
|
75
|
+
onblur?: FocusEventHandler | null;
|
|
76
|
+
oninput?: InputEventHandler | null;
|
|
77
|
+
onchange?: InputEventHandler | null;
|
|
78
|
+
onfocus?: FocusEventHandler | null;
|
|
79
|
+
}): {
|
|
80
|
+
onblur(event: FocusEvent): void;
|
|
81
|
+
oninput(event: Event): void;
|
|
82
|
+
onchange(event: Event): void;
|
|
83
|
+
onfocus(event: FocusEvent): void;
|
|
84
|
+
};
|
|
85
|
+
export {};
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { getContext } from 'svelte';
|
|
2
|
+
import { getFormContext } from '../Form/form.context.svelte.js';
|
|
3
|
+
/**
|
|
4
|
+
* Symbol key for the FormField context. Using a Symbol instead of a string
|
|
5
|
+
* prevents collisions with unrelated `getContext('formField')` calls from
|
|
6
|
+
* user code or other libraries.
|
|
7
|
+
*/
|
|
8
|
+
export const FORM_FIELD_CONTEXT_KEY = Symbol('sv5ui:form-field');
|
|
2
9
|
/**
|
|
3
10
|
* Access the nearest FormField context. Returns `undefined` when used outside a FormField.
|
|
4
11
|
*
|
|
@@ -13,5 +20,67 @@ import { getContext } from 'svelte';
|
|
|
13
20
|
* ```
|
|
14
21
|
*/
|
|
15
22
|
export function useFormField() {
|
|
16
|
-
return getContext(
|
|
23
|
+
return getContext(FORM_FIELD_CONTEXT_KEY);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Event emitter helpers for inputs nested inside a `<FormField>` within a `<Form>`.
|
|
27
|
+
* Each returned function is a safe no-op when used outside a Form, so inputs can
|
|
28
|
+
* unconditionally wire them to their native events.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```svelte
|
|
32
|
+
* <script>
|
|
33
|
+
* import { useFormFieldEmit } from 'sv5ui'
|
|
34
|
+
* const emit = useFormFieldEmit()
|
|
35
|
+
* </script>
|
|
36
|
+
*
|
|
37
|
+
* <input onblur={emit.onBlur} oninput={emit.onInput} onchange={emit.onChange} onfocus={emit.onFocus} />
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export function useFormFieldEmit() {
|
|
41
|
+
const fieldCtx = getContext(FORM_FIELD_CONTEXT_KEY);
|
|
42
|
+
const formCtx = getFormContext();
|
|
43
|
+
return {
|
|
44
|
+
onBlur() {
|
|
45
|
+
const n = fieldCtx?.name;
|
|
46
|
+
if (n)
|
|
47
|
+
formCtx?.onBlur(n);
|
|
48
|
+
},
|
|
49
|
+
onFocus() {
|
|
50
|
+
const n = fieldCtx?.name;
|
|
51
|
+
if (n)
|
|
52
|
+
formCtx?.onFocus(n);
|
|
53
|
+
},
|
|
54
|
+
onChange() {
|
|
55
|
+
const n = fieldCtx?.name;
|
|
56
|
+
if (n)
|
|
57
|
+
formCtx?.onChange(n);
|
|
58
|
+
},
|
|
59
|
+
onInput() {
|
|
60
|
+
const n = fieldCtx?.name;
|
|
61
|
+
if (n)
|
|
62
|
+
formCtx?.onInput(n, fieldCtx?.eagerValidation);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export function wireFormEvents(userHandlers) {
|
|
67
|
+
const emit = useFormFieldEmit();
|
|
68
|
+
return {
|
|
69
|
+
onblur(event) {
|
|
70
|
+
emit.onBlur();
|
|
71
|
+
userHandlers.onblur?.(event);
|
|
72
|
+
},
|
|
73
|
+
oninput(event) {
|
|
74
|
+
emit.onInput();
|
|
75
|
+
userHandlers.oninput?.(event);
|
|
76
|
+
},
|
|
77
|
+
onchange(event) {
|
|
78
|
+
emit.onChange();
|
|
79
|
+
userHandlers.onchange?.(event);
|
|
80
|
+
},
|
|
81
|
+
onfocus(event) {
|
|
82
|
+
emit.onFocus();
|
|
83
|
+
userHandlers.onfocus?.(event);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
17
86
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -31,6 +31,7 @@ export * from './Tabs/index.js';
|
|
|
31
31
|
export * from './Pagination/index.js';
|
|
32
32
|
export * from './FieldGroup/index.js';
|
|
33
33
|
export * from './FormField/index.js';
|
|
34
|
+
export * from './Form/index.js';
|
|
34
35
|
export * from './Input/index.js';
|
|
35
36
|
export * from './Textarea/index.js';
|
|
36
37
|
export * from './Select/index.js';
|
package/dist/index.js
CHANGED
|
@@ -32,6 +32,7 @@ export * from './Tabs/index.js';
|
|
|
32
32
|
export * from './Pagination/index.js';
|
|
33
33
|
export * from './FieldGroup/index.js';
|
|
34
34
|
export * from './FormField/index.js';
|
|
35
|
+
export * from './Form/index.js';
|
|
35
36
|
export * from './Input/index.js';
|
|
36
37
|
export * from './Textarea/index.js';
|
|
37
38
|
export * from './Select/index.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sv5ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "A modern Svelte 5 UI component library with Tailwind CSS",
|
|
5
5
|
"author": "ndlabdev",
|
|
6
6
|
"license": "MIT",
|
|
@@ -48,9 +48,27 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
+
"joi": "^17.0.0 || ^18.0.0",
|
|
51
52
|
"mode-watcher": "^1.0.0",
|
|
52
53
|
"svelte": "^5.0.0",
|
|
53
|
-
"tailwindcss": "^4.0.0"
|
|
54
|
+
"tailwindcss": "^4.0.0",
|
|
55
|
+
"valibot": "^1.0.0",
|
|
56
|
+
"yup": "^1.7.0",
|
|
57
|
+
"zod": "^3.24.0 || ^4.0.0"
|
|
58
|
+
},
|
|
59
|
+
"peerDependenciesMeta": {
|
|
60
|
+
"joi": {
|
|
61
|
+
"optional": true
|
|
62
|
+
},
|
|
63
|
+
"valibot": {
|
|
64
|
+
"optional": true
|
|
65
|
+
},
|
|
66
|
+
"yup": {
|
|
67
|
+
"optional": true
|
|
68
|
+
},
|
|
69
|
+
"zod": {
|
|
70
|
+
"optional": true
|
|
71
|
+
}
|
|
54
72
|
},
|
|
55
73
|
"devDependencies": {
|
|
56
74
|
"@eslint/compat": "^1.4.0",
|
|
@@ -66,6 +84,7 @@
|
|
|
66
84
|
"eslint-config-prettier": "^10.1.8",
|
|
67
85
|
"eslint-plugin-svelte": "^3.13.1",
|
|
68
86
|
"globals": "^16.5.0",
|
|
87
|
+
"joi": "^18.1.2",
|
|
69
88
|
"playwright": "^1.57.0",
|
|
70
89
|
"prettier": "^3.7.4",
|
|
71
90
|
"prettier-plugin-svelte": "^3.4.0",
|
|
@@ -76,9 +95,12 @@
|
|
|
76
95
|
"tailwindcss": "^4.1.17",
|
|
77
96
|
"typescript": "^5.9.3",
|
|
78
97
|
"typescript-eslint": "^8.48.1",
|
|
98
|
+
"valibot": "^1.3.1",
|
|
79
99
|
"vite": "^7.2.6",
|
|
80
100
|
"vitest": "^4.0.15",
|
|
81
|
-
"vitest-browser-svelte": "^2.0.1"
|
|
101
|
+
"vitest-browser-svelte": "^2.0.1",
|
|
102
|
+
"yup": "^1.7.1",
|
|
103
|
+
"zod": "^4.3.6"
|
|
82
104
|
},
|
|
83
105
|
"keywords": [
|
|
84
106
|
"svelte",
|
|
@@ -98,6 +120,7 @@
|
|
|
98
120
|
"dependencies": {
|
|
99
121
|
"@iconify/svelte": "^5.2.1",
|
|
100
122
|
"@internationalized/date": "^3.11.0",
|
|
123
|
+
"@standard-schema/spec": "^1.1.0",
|
|
101
124
|
"bits-ui": "^2.15.4",
|
|
102
125
|
"svelte-sonner": "^1.1.0",
|
|
103
126
|
"tailwind-merge": "^3.4.0",
|