@flightdev/forms 0.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/LICENSE +21 -0
- package/README.md +210 -0
- package/dist/adapters/valibot.d.ts +38 -0
- package/dist/adapters/valibot.js +66 -0
- package/dist/adapters/valibot.js.map +1 -0
- package/dist/adapters/yup.d.ts +44 -0
- package/dist/adapters/yup.js +56 -0
- package/dist/adapters/yup.js.map +1 -0
- package/dist/adapters/zod.d.ts +83 -0
- package/dist/adapters/zod.js +47 -0
- package/dist/adapters/zod.js.map +1 -0
- package/dist/chunk-5LLYBEXV.js +110 -0
- package/dist/chunk-5LLYBEXV.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +10 -0
- package/dist/chunk-DGUM43GV.js.map +1 -0
- package/dist/frameworks/react.d.ts +85 -0
- package/dist/frameworks/react.js +195 -0
- package/dist/frameworks/react.js.map +1 -0
- package/dist/frameworks/solid.d.ts +63 -0
- package/dist/frameworks/solid.js +138 -0
- package/dist/frameworks/solid.js.map +1 -0
- package/dist/frameworks/svelte.d.ts +61 -0
- package/dist/frameworks/svelte.js +143 -0
- package/dist/frameworks/svelte.js.map +1 -0
- package/dist/frameworks/vue.d.ts +60 -0
- package/dist/frameworks/vue.js +127 -0
- package/dist/frameworks/vue.js.map +1 -0
- package/dist/index.d.ts +181 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/package.json +102 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { errorsToObject } from '../chunk-5LLYBEXV.js';
|
|
2
|
+
import '../chunk-DGUM43GV.js';
|
|
3
|
+
import { createSignal } from 'solid-js';
|
|
4
|
+
|
|
5
|
+
function useForm(form, options = {}) {
|
|
6
|
+
const {
|
|
7
|
+
onSuccess,
|
|
8
|
+
onError,
|
|
9
|
+
resetOnSuccess = false,
|
|
10
|
+
validateOnBlur = true,
|
|
11
|
+
validateOnChange = false
|
|
12
|
+
} = options;
|
|
13
|
+
const [values, setValues] = createSignal({});
|
|
14
|
+
const [errors, setErrors] = createSignal({});
|
|
15
|
+
const [touched, setTouched] = createSignal(/* @__PURE__ */ new Set());
|
|
16
|
+
const [pending, setPending] = createSignal(false);
|
|
17
|
+
const [dirty, setDirty] = createSignal(false);
|
|
18
|
+
const [result, setResult] = createSignal();
|
|
19
|
+
function setValue(name, value) {
|
|
20
|
+
setValues((prev) => ({ ...prev, [name]: value }));
|
|
21
|
+
setDirty(true);
|
|
22
|
+
if (validateOnChange) {
|
|
23
|
+
const validation = form.validate({ ...values(), [name]: value });
|
|
24
|
+
if (!validation.success && validation.errors) {
|
|
25
|
+
const fieldError = validation.errors.find((e) => e.path === name);
|
|
26
|
+
if (fieldError) {
|
|
27
|
+
setErrors((prev) => ({ ...prev, [name]: fieldError.message }));
|
|
28
|
+
} else {
|
|
29
|
+
setErrors((prev) => {
|
|
30
|
+
const next = { ...prev };
|
|
31
|
+
delete next[name];
|
|
32
|
+
return next;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
} else {
|
|
36
|
+
setErrors((prev) => {
|
|
37
|
+
const next = { ...prev };
|
|
38
|
+
delete next[name];
|
|
39
|
+
return next;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function reset() {
|
|
45
|
+
setValues({});
|
|
46
|
+
setErrors({});
|
|
47
|
+
setTouched(/* @__PURE__ */ new Set());
|
|
48
|
+
setDirty(false);
|
|
49
|
+
setResult(void 0);
|
|
50
|
+
}
|
|
51
|
+
async function validate() {
|
|
52
|
+
const validation = await form.validateAsync(values());
|
|
53
|
+
if (!validation.success && validation.errors) {
|
|
54
|
+
setErrors(errorsToObject(validation.errors));
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
setErrors({});
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
function register(name) {
|
|
61
|
+
return {
|
|
62
|
+
name,
|
|
63
|
+
get value() {
|
|
64
|
+
return values()[name] ?? "";
|
|
65
|
+
},
|
|
66
|
+
onInput: (e) => {
|
|
67
|
+
const target = e.currentTarget;
|
|
68
|
+
const value = target.type === "checkbox" ? target.checked : target.value;
|
|
69
|
+
setValue(name, value);
|
|
70
|
+
},
|
|
71
|
+
onBlur: (e) => {
|
|
72
|
+
setTouched((prev) => new Set(prev).add(name));
|
|
73
|
+
if (validateOnBlur) {
|
|
74
|
+
const validation = form.validate(values());
|
|
75
|
+
if (!validation.success && validation.errors) {
|
|
76
|
+
const fieldError = validation.errors.find((e2) => e2.path === name);
|
|
77
|
+
if (fieldError) {
|
|
78
|
+
setErrors((prev) => ({ ...prev, [name]: fieldError.message }));
|
|
79
|
+
} else {
|
|
80
|
+
setErrors((prev) => {
|
|
81
|
+
const next = { ...prev };
|
|
82
|
+
delete next[name];
|
|
83
|
+
return next;
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
async function handleSubmit(e) {
|
|
92
|
+
e?.preventDefault();
|
|
93
|
+
setPending(true);
|
|
94
|
+
try {
|
|
95
|
+
const formResult = await form.execute(values());
|
|
96
|
+
setResult(formResult);
|
|
97
|
+
if (formResult.success) {
|
|
98
|
+
onSuccess?.(formResult);
|
|
99
|
+
if (resetOnSuccess) {
|
|
100
|
+
reset();
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
103
|
+
if (formResult.fieldErrors) {
|
|
104
|
+
setErrors(errorsToObject(formResult.fieldErrors));
|
|
105
|
+
}
|
|
106
|
+
onError?.(formResult);
|
|
107
|
+
}
|
|
108
|
+
} catch (error) {
|
|
109
|
+
const errorResult = {
|
|
110
|
+
success: false,
|
|
111
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
112
|
+
};
|
|
113
|
+
setResult(errorResult);
|
|
114
|
+
onError?.(errorResult);
|
|
115
|
+
} finally {
|
|
116
|
+
setPending(false);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
register,
|
|
121
|
+
handleSubmit,
|
|
122
|
+
values,
|
|
123
|
+
setValues,
|
|
124
|
+
setValue,
|
|
125
|
+
reset,
|
|
126
|
+
errors,
|
|
127
|
+
touched,
|
|
128
|
+
pending,
|
|
129
|
+
dirty,
|
|
130
|
+
result,
|
|
131
|
+
validate
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
var solid_default = { useForm };
|
|
135
|
+
|
|
136
|
+
export { solid_default as default, useForm };
|
|
137
|
+
//# sourceMappingURL=solid.js.map
|
|
138
|
+
//# sourceMappingURL=solid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/frameworks/solid.ts"],"names":["e"],"mappings":"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","file":"solid.js","sourcesContent":["/**\r\n * Solid.js Primitives for @flightdev/forms\r\n * \r\n * @example\r\n * ```tsx\r\n * import { createForm } from '@flightdev/forms';\r\n * import { useForm } from '@flightdev/forms/solid';\r\n * \r\n * function LoginPage() {\r\n * const { register, handleSubmit, errors, pending } = useForm(loginForm);\r\n * \r\n * return (\r\n * <form onSubmit={handleSubmit}>\r\n * <input {...register('email')} />\r\n * <Show when={errors().email}>\r\n * <span>{errors().email}</span>\r\n * </Show>\r\n * <button disabled={pending()}>Submit</button>\r\n * </form>\r\n * );\r\n * }\r\n * ```\r\n */\r\n\r\nimport { createSignal, type Accessor, type Setter } from 'solid-js';\r\nimport type { FormDefinition, FormResult } from '../index.js';\r\nimport { errorsToObject } from '../index.js';\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\nexport interface UseFormOptions<TResult = unknown> {\r\n onSuccess?: (result: FormResult<TResult>) => void;\r\n onError?: (result: FormResult<TResult>) => void;\r\n resetOnSuccess?: boolean;\r\n validateOnBlur?: boolean;\r\n validateOnChange?: boolean;\r\n}\r\n\r\nexport interface FieldBinding {\r\n name: string;\r\n value: unknown;\r\n onInput: (e: InputEvent) => void;\r\n onBlur: (e: FocusEvent) => void;\r\n}\r\n\r\nexport interface UseFormReturn<TInput, TResult = unknown> {\r\n register: (name: keyof TInput & string) => FieldBinding;\r\n handleSubmit: (e?: Event) => Promise<void>;\r\n values: Accessor<Partial<TInput>>;\r\n setValues: Setter<Partial<TInput>>;\r\n setValue: (name: keyof TInput & string, value: unknown) => void;\r\n reset: () => void;\r\n errors: Accessor<Record<string, string>>;\r\n touched: Accessor<Set<string>>;\r\n pending: Accessor<boolean>;\r\n dirty: Accessor<boolean>;\r\n result: Accessor<FormResult<TResult> | undefined>;\r\n validate: () => Promise<boolean>;\r\n}\r\n\r\n// ============================================================================\r\n// useForm\r\n// ============================================================================\r\n\r\n/**\r\n * Solid.js primitive for form handling\r\n */\r\nexport function useForm<TInput, TResult = unknown>(\r\n form: FormDefinition<TInput, TResult>,\r\n options: UseFormOptions<TResult> = {}\r\n): UseFormReturn<TInput, TResult> {\r\n const {\r\n onSuccess,\r\n onError,\r\n resetOnSuccess = false,\r\n validateOnBlur = true,\r\n validateOnChange = false,\r\n } = options;\r\n\r\n // Signals\r\n const [values, setValues] = createSignal<Partial<TInput>>({});\r\n const [errors, setErrors] = createSignal<Record<string, string>>({});\r\n const [touched, setTouched] = createSignal<Set<string>>(new Set());\r\n const [pending, setPending] = createSignal(false);\r\n const [dirty, setDirty] = createSignal(false);\r\n const [result, setResult] = createSignal<FormResult<TResult> | undefined>();\r\n\r\n // Set a single value\r\n function setValue(name: keyof TInput & string, value: unknown): void {\r\n setValues(prev => ({ ...prev, [name]: value }));\r\n setDirty(true);\r\n\r\n if (validateOnChange) {\r\n const validation = form.validate({ ...values(), [name]: value });\r\n if (!validation.success && validation.errors) {\r\n const fieldError = validation.errors.find(e => e.path === name);\r\n if (fieldError) {\r\n setErrors(prev => ({ ...prev, [name]: fieldError.message }));\r\n } else {\r\n setErrors(prev => {\r\n const next = { ...prev };\r\n delete next[name];\r\n return next;\r\n });\r\n }\r\n } else {\r\n setErrors(prev => {\r\n const next = { ...prev };\r\n delete next[name];\r\n return next;\r\n });\r\n }\r\n }\r\n }\r\n\r\n // Reset form\r\n function reset(): void {\r\n setValues({});\r\n setErrors({});\r\n setTouched(new Set<string>());\r\n setDirty(false);\r\n setResult(undefined);\r\n }\r\n\r\n // Validate entire form\r\n async function validate(): Promise<boolean> {\r\n const validation = await form.validateAsync(values());\r\n if (!validation.success && validation.errors) {\r\n setErrors(errorsToObject(validation.errors));\r\n return false;\r\n }\r\n setErrors({});\r\n return true;\r\n }\r\n\r\n // Register a field\r\n function register(name: keyof TInput & string): FieldBinding {\r\n return {\r\n name,\r\n get value() {\r\n return (values() as Record<string, unknown>)[name] ?? '';\r\n },\r\n onInput: (e: InputEvent) => {\r\n const target = e.currentTarget as HTMLInputElement;\r\n const value = target.type === 'checkbox' ? target.checked : target.value;\r\n setValue(name, value);\r\n },\r\n onBlur: (e: FocusEvent) => {\r\n setTouched(prev => new Set<string>(prev).add(name));\r\n if (validateOnBlur) {\r\n const validation = form.validate(values());\r\n if (!validation.success && validation.errors) {\r\n const fieldError = validation.errors.find(e => e.path === name);\r\n if (fieldError) {\r\n setErrors(prev => ({ ...prev, [name]: fieldError.message }));\r\n } else {\r\n setErrors(prev => {\r\n const next = { ...prev };\r\n delete next[name];\r\n return next;\r\n });\r\n }\r\n }\r\n }\r\n },\r\n };\r\n }\r\n\r\n // Handle form submission\r\n async function handleSubmit(e?: Event): Promise<void> {\r\n e?.preventDefault();\r\n setPending(true);\r\n\r\n try {\r\n const formResult = await form.execute(values());\r\n setResult(formResult);\r\n\r\n if (formResult.success) {\r\n onSuccess?.(formResult);\r\n if (resetOnSuccess) {\r\n reset();\r\n }\r\n } else {\r\n if (formResult.fieldErrors) {\r\n setErrors(errorsToObject(formResult.fieldErrors));\r\n }\r\n onError?.(formResult);\r\n }\r\n } catch (error) {\r\n const errorResult: FormResult<TResult> = {\r\n success: false,\r\n error: error instanceof Error ? error.message : 'Unknown error',\r\n };\r\n setResult(errorResult);\r\n onError?.(errorResult);\r\n } finally {\r\n setPending(false);\r\n }\r\n }\r\n\r\n return {\r\n register,\r\n handleSubmit,\r\n values,\r\n setValues,\r\n setValue,\r\n reset,\r\n errors,\r\n touched,\r\n pending,\r\n dirty,\r\n result,\r\n validate,\r\n };\r\n}\r\n\r\nexport default { useForm };\r\n"]}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { FormDefinition, FormResult } from '../index.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Svelte Utilities for @flightdev/forms
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```svelte
|
|
8
|
+
* <script>
|
|
9
|
+
* import { createFormStore } from '@flightdev/forms/svelte';
|
|
10
|
+
*
|
|
11
|
+
* const form = createFormStore(loginForm);
|
|
12
|
+
* const { values, errors, pending, register, submit } = form;
|
|
13
|
+
* </script>
|
|
14
|
+
*
|
|
15
|
+
* <form on:submit|preventDefault={submit}>
|
|
16
|
+
* <input use:register={'email'} bind:value={$values.email} />
|
|
17
|
+
* {#if $errors.email}<span class="error">{$errors.email}</span>{/if}
|
|
18
|
+
* <button disabled={$pending}>Submit</button>
|
|
19
|
+
* </form>
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
interface FormStoreOptions<TResult = unknown> {
|
|
24
|
+
onSuccess?: (result: FormResult<TResult>) => void;
|
|
25
|
+
onError?: (result: FormResult<TResult>) => void;
|
|
26
|
+
resetOnSuccess?: boolean;
|
|
27
|
+
validateOnBlur?: boolean;
|
|
28
|
+
}
|
|
29
|
+
interface Subscriber<T> {
|
|
30
|
+
(value: T): void;
|
|
31
|
+
}
|
|
32
|
+
interface Writable<T> {
|
|
33
|
+
subscribe: (subscriber: Subscriber<T>) => () => void;
|
|
34
|
+
set: (value: T) => void;
|
|
35
|
+
update: (updater: (value: T) => T) => void;
|
|
36
|
+
}
|
|
37
|
+
interface FormStore<TInput, TResult = unknown> {
|
|
38
|
+
values: Writable<Partial<TInput>>;
|
|
39
|
+
errors: Writable<Record<string, string>>;
|
|
40
|
+
touched: Writable<Set<string>>;
|
|
41
|
+
pending: Writable<boolean>;
|
|
42
|
+
dirty: Writable<boolean>;
|
|
43
|
+
result: Writable<FormResult<TResult> | undefined>;
|
|
44
|
+
setValue: (name: keyof TInput & string, value: unknown) => void;
|
|
45
|
+
setValues: (values: Partial<TInput>) => void;
|
|
46
|
+
reset: () => void;
|
|
47
|
+
validate: () => Promise<boolean>;
|
|
48
|
+
submit: () => Promise<void>;
|
|
49
|
+
register: (node: HTMLInputElement, name: keyof TInput & string) => {
|
|
50
|
+
destroy: () => void;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Create a Svelte form store
|
|
55
|
+
*/
|
|
56
|
+
declare function createFormStore<TInput, TResult = unknown>(form: FormDefinition<TInput, TResult>, options?: FormStoreOptions<TResult>): FormStore<TInput, TResult>;
|
|
57
|
+
declare const _default: {
|
|
58
|
+
createFormStore: typeof createFormStore;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export { type FormStore, type FormStoreOptions, type Subscriber, type Writable, createFormStore, _default as default };
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { errorsToObject } from '../chunk-5LLYBEXV.js';
|
|
2
|
+
import '../chunk-DGUM43GV.js';
|
|
3
|
+
|
|
4
|
+
// src/frameworks/svelte.ts
|
|
5
|
+
function writable(initial) {
|
|
6
|
+
let value = initial;
|
|
7
|
+
const subscribers = /* @__PURE__ */ new Set();
|
|
8
|
+
return {
|
|
9
|
+
subscribe(subscriber) {
|
|
10
|
+
subscribers.add(subscriber);
|
|
11
|
+
subscriber(value);
|
|
12
|
+
return () => subscribers.delete(subscriber);
|
|
13
|
+
},
|
|
14
|
+
set(newValue) {
|
|
15
|
+
value = newValue;
|
|
16
|
+
subscribers.forEach((s) => s(value));
|
|
17
|
+
},
|
|
18
|
+
update(updater) {
|
|
19
|
+
value = updater(value);
|
|
20
|
+
subscribers.forEach((s) => s(value));
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function createFormStore(form, options = {}) {
|
|
25
|
+
const {
|
|
26
|
+
onSuccess,
|
|
27
|
+
onError,
|
|
28
|
+
resetOnSuccess = false,
|
|
29
|
+
validateOnBlur = true
|
|
30
|
+
} = options;
|
|
31
|
+
const values = writable({});
|
|
32
|
+
const errors = writable({});
|
|
33
|
+
const touched = writable(/* @__PURE__ */ new Set());
|
|
34
|
+
const pending = writable(false);
|
|
35
|
+
const dirty = writable(false);
|
|
36
|
+
const result = writable(void 0);
|
|
37
|
+
let currentValues = {};
|
|
38
|
+
values.subscribe((v) => currentValues = v);
|
|
39
|
+
function setValue(name, value) {
|
|
40
|
+
values.update((v) => ({ ...v, [name]: value }));
|
|
41
|
+
dirty.set(true);
|
|
42
|
+
}
|
|
43
|
+
function setValues(newValues) {
|
|
44
|
+
values.update((v) => ({ ...v, ...newValues }));
|
|
45
|
+
dirty.set(true);
|
|
46
|
+
}
|
|
47
|
+
function reset() {
|
|
48
|
+
values.set({});
|
|
49
|
+
errors.set({});
|
|
50
|
+
touched.set(/* @__PURE__ */ new Set());
|
|
51
|
+
dirty.set(false);
|
|
52
|
+
result.set(void 0);
|
|
53
|
+
}
|
|
54
|
+
async function validate() {
|
|
55
|
+
const validation = await form.validateAsync(currentValues);
|
|
56
|
+
if (!validation.success && validation.errors) {
|
|
57
|
+
errors.set(errorsToObject(validation.errors));
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
errors.set({});
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
async function submit() {
|
|
64
|
+
pending.set(true);
|
|
65
|
+
try {
|
|
66
|
+
const formResult = await form.execute(currentValues);
|
|
67
|
+
result.set(formResult);
|
|
68
|
+
if (formResult.success) {
|
|
69
|
+
onSuccess?.(formResult);
|
|
70
|
+
if (resetOnSuccess) {
|
|
71
|
+
reset();
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
if (formResult.fieldErrors) {
|
|
75
|
+
errors.set(errorsToObject(formResult.fieldErrors));
|
|
76
|
+
}
|
|
77
|
+
onError?.(formResult);
|
|
78
|
+
}
|
|
79
|
+
} catch (error) {
|
|
80
|
+
const errorResult = {
|
|
81
|
+
success: false,
|
|
82
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
83
|
+
};
|
|
84
|
+
result.set(errorResult);
|
|
85
|
+
onError?.(errorResult);
|
|
86
|
+
} finally {
|
|
87
|
+
pending.set(false);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function register(node, name) {
|
|
91
|
+
function handleInput(e) {
|
|
92
|
+
const target = e.target;
|
|
93
|
+
const value = target.type === "checkbox" ? target.checked : target.value;
|
|
94
|
+
setValue(name, value);
|
|
95
|
+
}
|
|
96
|
+
function handleBlur() {
|
|
97
|
+
touched.update((t) => new Set(t).add(name));
|
|
98
|
+
if (validateOnBlur) {
|
|
99
|
+
const validation = form.validate(currentValues);
|
|
100
|
+
if (!validation.success && validation.errors) {
|
|
101
|
+
const fieldError = validation.errors.find((e) => e.path === name);
|
|
102
|
+
if (fieldError) {
|
|
103
|
+
errors.update((e) => ({ ...e, [name]: fieldError.message }));
|
|
104
|
+
} else {
|
|
105
|
+
errors.update((e) => {
|
|
106
|
+
const next = { ...e };
|
|
107
|
+
delete next[name];
|
|
108
|
+
return next;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
node.addEventListener("input", handleInput);
|
|
115
|
+
node.addEventListener("blur", handleBlur);
|
|
116
|
+
node.name = name;
|
|
117
|
+
return {
|
|
118
|
+
destroy() {
|
|
119
|
+
node.removeEventListener("input", handleInput);
|
|
120
|
+
node.removeEventListener("blur", handleBlur);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
values,
|
|
126
|
+
errors,
|
|
127
|
+
touched,
|
|
128
|
+
pending,
|
|
129
|
+
dirty,
|
|
130
|
+
result,
|
|
131
|
+
setValue,
|
|
132
|
+
setValues,
|
|
133
|
+
reset,
|
|
134
|
+
validate,
|
|
135
|
+
submit,
|
|
136
|
+
register
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
var svelte_default = { createFormStore };
|
|
140
|
+
|
|
141
|
+
export { createFormStore, svelte_default as default };
|
|
142
|
+
//# sourceMappingURL=svelte.js.map
|
|
143
|
+
//# sourceMappingURL=svelte.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/frameworks/svelte.ts"],"names":[],"mappings":"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","file":"svelte.js","sourcesContent":["/**\r\n * Svelte Utilities for @flightdev/forms\r\n * \r\n * @example\r\n * ```svelte\r\n * <script>\r\n * import { createFormStore } from '@flightdev/forms/svelte';\r\n * \r\n * const form = createFormStore(loginForm);\r\n * const { values, errors, pending, register, submit } = form;\r\n * </script>\r\n * \r\n * <form on:submit|preventDefault={submit}>\r\n * <input use:register={'email'} bind:value={$values.email} />\r\n * {#if $errors.email}<span class=\"error\">{$errors.email}</span>{/if}\r\n * <button disabled={$pending}>Submit</button>\r\n * </form>\r\n * ```\r\n */\r\n\r\nimport type { FormDefinition, FormResult } from '../index.js';\r\nimport { errorsToObject } from '../index.js';\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\nexport interface FormStoreOptions<TResult = unknown> {\r\n onSuccess?: (result: FormResult<TResult>) => void;\r\n onError?: (result: FormResult<TResult>) => void;\r\n resetOnSuccess?: boolean;\r\n validateOnBlur?: boolean;\r\n}\r\n\r\nexport interface Subscriber<T> {\r\n (value: T): void;\r\n}\r\n\r\nexport interface Writable<T> {\r\n subscribe: (subscriber: Subscriber<T>) => () => void;\r\n set: (value: T) => void;\r\n update: (updater: (value: T) => T) => void;\r\n}\r\n\r\n// Simple writable store implementation\r\nfunction writable<T>(initial: T): Writable<T> {\r\n let value = initial;\r\n const subscribers = new Set<Subscriber<T>>();\r\n\r\n return {\r\n subscribe(subscriber: Subscriber<T>) {\r\n subscribers.add(subscriber);\r\n subscriber(value);\r\n return () => subscribers.delete(subscriber);\r\n },\r\n set(newValue: T) {\r\n value = newValue;\r\n subscribers.forEach(s => s(value));\r\n },\r\n update(updater: (value: T) => T) {\r\n value = updater(value);\r\n subscribers.forEach(s => s(value));\r\n },\r\n };\r\n}\r\n\r\n// ============================================================================\r\n// Form Store\r\n// ============================================================================\r\n\r\nexport interface FormStore<TInput, TResult = unknown> {\r\n values: Writable<Partial<TInput>>;\r\n errors: Writable<Record<string, string>>;\r\n touched: Writable<Set<string>>;\r\n pending: Writable<boolean>;\r\n dirty: Writable<boolean>;\r\n result: Writable<FormResult<TResult> | undefined>;\r\n setValue: (name: keyof TInput & string, value: unknown) => void;\r\n setValues: (values: Partial<TInput>) => void;\r\n reset: () => void;\r\n validate: () => Promise<boolean>;\r\n submit: () => Promise<void>;\r\n register: (node: HTMLInputElement, name: keyof TInput & string) => { destroy: () => void };\r\n}\r\n\r\n/**\r\n * Create a Svelte form store\r\n */\r\nexport function createFormStore<TInput, TResult = unknown>(\r\n form: FormDefinition<TInput, TResult>,\r\n options: FormStoreOptions<TResult> = {}\r\n): FormStore<TInput, TResult> {\r\n const {\r\n onSuccess,\r\n onError,\r\n resetOnSuccess = false,\r\n validateOnBlur = true,\r\n } = options;\r\n\r\n // Stores\r\n const values = writable<Partial<TInput>>({});\r\n const errors = writable<Record<string, string>>({});\r\n const touched = writable<Set<string>>(new Set());\r\n const pending = writable(false);\r\n const dirty = writable(false);\r\n const result = writable<FormResult<TResult> | undefined>(undefined);\r\n\r\n // Current values (for reading in actions)\r\n let currentValues: Partial<TInput> = {};\r\n values.subscribe(v => currentValues = v);\r\n\r\n // Set a single value\r\n function setValue(name: keyof TInput & string, value: unknown): void {\r\n values.update(v => ({ ...v, [name]: value }));\r\n dirty.set(true);\r\n }\r\n\r\n // Set multiple values\r\n function setValues(newValues: Partial<TInput>): void {\r\n values.update(v => ({ ...v, ...newValues }));\r\n dirty.set(true);\r\n }\r\n\r\n // Reset form\r\n function reset(): void {\r\n values.set({});\r\n errors.set({});\r\n touched.set(new Set());\r\n dirty.set(false);\r\n result.set(undefined);\r\n }\r\n\r\n // Validate entire form\r\n async function validate(): Promise<boolean> {\r\n const validation = await form.validateAsync(currentValues);\r\n if (!validation.success && validation.errors) {\r\n errors.set(errorsToObject(validation.errors));\r\n return false;\r\n }\r\n errors.set({});\r\n return true;\r\n }\r\n\r\n // Submit form\r\n async function submit(): Promise<void> {\r\n pending.set(true);\r\n\r\n try {\r\n const formResult = await form.execute(currentValues);\r\n result.set(formResult);\r\n\r\n if (formResult.success) {\r\n onSuccess?.(formResult);\r\n if (resetOnSuccess) {\r\n reset();\r\n }\r\n } else {\r\n if (formResult.fieldErrors) {\r\n errors.set(errorsToObject(formResult.fieldErrors));\r\n }\r\n onError?.(formResult);\r\n }\r\n } catch (error) {\r\n const errorResult: FormResult<TResult> = {\r\n success: false,\r\n error: error instanceof Error ? error.message : 'Unknown error',\r\n };\r\n result.set(errorResult);\r\n onError?.(errorResult);\r\n } finally {\r\n pending.set(false);\r\n }\r\n }\r\n\r\n // Svelte action for registering inputs\r\n function register(node: HTMLInputElement, name: keyof TInput & string) {\r\n function handleInput(e: Event) {\r\n const target = e.target as HTMLInputElement;\r\n const value = target.type === 'checkbox' ? target.checked : target.value;\r\n setValue(name, value);\r\n }\r\n\r\n function handleBlur() {\r\n touched.update(t => new Set(t).add(name as string));\r\n if (validateOnBlur) {\r\n const validation = form.validate(currentValues);\r\n if (!validation.success && validation.errors) {\r\n const fieldError = validation.errors.find(e => e.path === name);\r\n if (fieldError) {\r\n errors.update(e => ({ ...e, [name]: fieldError.message }));\r\n } else {\r\n errors.update(e => {\r\n const next = { ...e };\r\n delete next[name as string];\r\n return next;\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n node.addEventListener('input', handleInput);\r\n node.addEventListener('blur', handleBlur);\r\n node.name = name as string;\r\n\r\n return {\r\n destroy() {\r\n node.removeEventListener('input', handleInput);\r\n node.removeEventListener('blur', handleBlur);\r\n },\r\n };\r\n }\r\n\r\n return {\r\n values,\r\n errors,\r\n touched,\r\n pending,\r\n dirty,\r\n result,\r\n setValue,\r\n setValues,\r\n reset,\r\n validate,\r\n submit,\r\n register,\r\n };\r\n}\r\n\r\nexport default { createFormStore };\r\n"]}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Ref } from 'vue';
|
|
2
|
+
import { FormDefinition, FormResult } from '../index.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Vue Composables for @flightdev/forms
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```vue
|
|
9
|
+
* <script setup>
|
|
10
|
+
* import { useForm } from '@flightdev/forms/vue';
|
|
11
|
+
*
|
|
12
|
+
* const { register, handleSubmit, errors, pending } = useForm(loginForm);
|
|
13
|
+
* </script>
|
|
14
|
+
*
|
|
15
|
+
* <template>
|
|
16
|
+
* <form @submit.prevent="handleSubmit">
|
|
17
|
+
* <input v-bind="register('email')" />
|
|
18
|
+
* <span v-if="errors.email">{{ errors.email }}</span>
|
|
19
|
+
* <button :disabled="pending">Submit</button>
|
|
20
|
+
* </form>
|
|
21
|
+
* </template>
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
interface UseFormOptions<TResult = unknown> {
|
|
26
|
+
onSuccess?: (result: FormResult<TResult>) => void;
|
|
27
|
+
onError?: (result: FormResult<TResult>) => void;
|
|
28
|
+
resetOnSuccess?: boolean;
|
|
29
|
+
validateOnBlur?: boolean;
|
|
30
|
+
validateOnChange?: boolean;
|
|
31
|
+
}
|
|
32
|
+
interface FieldBinding {
|
|
33
|
+
name: string;
|
|
34
|
+
value: unknown;
|
|
35
|
+
onInput: (e: Event) => void;
|
|
36
|
+
onBlur: (e: Event) => void;
|
|
37
|
+
}
|
|
38
|
+
interface UseFormReturn<TInput, TResult = unknown> {
|
|
39
|
+
register: (name: keyof TInput & string) => FieldBinding;
|
|
40
|
+
handleSubmit: () => Promise<void>;
|
|
41
|
+
values: Ref<Partial<TInput>>;
|
|
42
|
+
setValue: (name: keyof TInput & string, value: unknown) => void;
|
|
43
|
+
setValues: (values: Partial<TInput>) => void;
|
|
44
|
+
reset: () => void;
|
|
45
|
+
errors: Ref<Record<string, string>>;
|
|
46
|
+
touched: Ref<Set<string>>;
|
|
47
|
+
pending: Ref<boolean>;
|
|
48
|
+
dirty: Ref<boolean>;
|
|
49
|
+
result: Ref<FormResult<TResult> | undefined>;
|
|
50
|
+
validate: () => Promise<boolean>;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Vue composable for form handling
|
|
54
|
+
*/
|
|
55
|
+
declare function useForm<TInput, TResult = unknown>(form: FormDefinition<TInput, TResult>, options?: UseFormOptions<TResult>): UseFormReturn<TInput, TResult>;
|
|
56
|
+
declare const _default: {
|
|
57
|
+
useForm: typeof useForm;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export { type FieldBinding, type UseFormOptions, type UseFormReturn, _default as default, useForm };
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { errorsToObject } from '../chunk-5LLYBEXV.js';
|
|
2
|
+
import '../chunk-DGUM43GV.js';
|
|
3
|
+
import { ref } from 'vue';
|
|
4
|
+
|
|
5
|
+
function useForm(form, options = {}) {
|
|
6
|
+
const {
|
|
7
|
+
onSuccess,
|
|
8
|
+
onError,
|
|
9
|
+
resetOnSuccess = false,
|
|
10
|
+
validateOnBlur = true,
|
|
11
|
+
validateOnChange = false
|
|
12
|
+
} = options;
|
|
13
|
+
const values = ref({});
|
|
14
|
+
const errors = ref({});
|
|
15
|
+
const touched = ref(/* @__PURE__ */ new Set());
|
|
16
|
+
const pending = ref(false);
|
|
17
|
+
const dirty = ref(false);
|
|
18
|
+
const result = ref();
|
|
19
|
+
function setValue(name, value) {
|
|
20
|
+
values.value[name] = value;
|
|
21
|
+
dirty.value = true;
|
|
22
|
+
if (validateOnChange) {
|
|
23
|
+
const validation = form.validate(values.value);
|
|
24
|
+
if (!validation.success && validation.errors) {
|
|
25
|
+
const fieldError = validation.errors.find((e) => e.path === name);
|
|
26
|
+
if (fieldError) {
|
|
27
|
+
errors.value[name] = fieldError.message;
|
|
28
|
+
} else {
|
|
29
|
+
delete errors.value[name];
|
|
30
|
+
}
|
|
31
|
+
} else {
|
|
32
|
+
delete errors.value[name];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function setValues(newValues) {
|
|
37
|
+
Object.assign(values.value, newValues);
|
|
38
|
+
dirty.value = true;
|
|
39
|
+
}
|
|
40
|
+
function reset() {
|
|
41
|
+
values.value = {};
|
|
42
|
+
errors.value = {};
|
|
43
|
+
touched.value = /* @__PURE__ */ new Set();
|
|
44
|
+
dirty.value = false;
|
|
45
|
+
result.value = void 0;
|
|
46
|
+
}
|
|
47
|
+
async function validate() {
|
|
48
|
+
const validation = await form.validateAsync(values.value);
|
|
49
|
+
if (!validation.success && validation.errors) {
|
|
50
|
+
errors.value = errorsToObject(validation.errors);
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
errors.value = {};
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
function register(name) {
|
|
57
|
+
return {
|
|
58
|
+
name,
|
|
59
|
+
value: values.value[name] ?? "",
|
|
60
|
+
onInput: (e) => {
|
|
61
|
+
const target = e.target;
|
|
62
|
+
const value = target.type === "checkbox" ? target.checked : target.value;
|
|
63
|
+
setValue(name, value);
|
|
64
|
+
},
|
|
65
|
+
onBlur: (e) => {
|
|
66
|
+
touched.value.add(name);
|
|
67
|
+
if (validateOnBlur) {
|
|
68
|
+
const validation = form.validate(values.value);
|
|
69
|
+
if (!validation.success && validation.errors) {
|
|
70
|
+
const fieldError = validation.errors.find((e2) => e2.path === name);
|
|
71
|
+
if (fieldError) {
|
|
72
|
+
errors.value[name] = fieldError.message;
|
|
73
|
+
} else {
|
|
74
|
+
delete errors.value[name];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
async function handleSubmit() {
|
|
82
|
+
pending.value = true;
|
|
83
|
+
try {
|
|
84
|
+
const formResult = await form.execute(values.value);
|
|
85
|
+
result.value = formResult;
|
|
86
|
+
if (formResult.success) {
|
|
87
|
+
onSuccess?.(formResult);
|
|
88
|
+
if (resetOnSuccess) {
|
|
89
|
+
reset();
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
if (formResult.fieldErrors) {
|
|
93
|
+
errors.value = errorsToObject(formResult.fieldErrors);
|
|
94
|
+
}
|
|
95
|
+
onError?.(formResult);
|
|
96
|
+
}
|
|
97
|
+
} catch (error) {
|
|
98
|
+
const errorResult = {
|
|
99
|
+
success: false,
|
|
100
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
101
|
+
};
|
|
102
|
+
result.value = errorResult;
|
|
103
|
+
onError?.(errorResult);
|
|
104
|
+
} finally {
|
|
105
|
+
pending.value = false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
register,
|
|
110
|
+
handleSubmit,
|
|
111
|
+
values,
|
|
112
|
+
setValue,
|
|
113
|
+
setValues,
|
|
114
|
+
reset,
|
|
115
|
+
errors,
|
|
116
|
+
touched,
|
|
117
|
+
pending,
|
|
118
|
+
dirty,
|
|
119
|
+
result,
|
|
120
|
+
validate
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
var vue_default = { useForm };
|
|
124
|
+
|
|
125
|
+
export { vue_default as default, useForm };
|
|
126
|
+
//# sourceMappingURL=vue.js.map
|
|
127
|
+
//# sourceMappingURL=vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/frameworks/vue.ts"],"names":["e"],"mappings":"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","file":"vue.js","sourcesContent":["/**\r\n * Vue Composables for @flightdev/forms\r\n * \r\n * @example\r\n * ```vue\r\n * <script setup>\r\n * import { useForm } from '@flightdev/forms/vue';\r\n * \r\n * const { register, handleSubmit, errors, pending } = useForm(loginForm);\r\n * </script>\r\n * \r\n * <template>\r\n * <form @submit.prevent=\"handleSubmit\">\r\n * <input v-bind=\"register('email')\" />\r\n * <span v-if=\"errors.email\">{{ errors.email }}</span>\r\n * <button :disabled=\"pending\">Submit</button>\r\n * </form>\r\n * </template>\r\n * ```\r\n */\r\n\r\nimport { ref, reactive, computed, type Ref, type ComputedRef } from 'vue';\r\nimport type { FormDefinition, FormResult } from '../index.js';\r\nimport { errorsToObject } from '../index.js';\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\nexport interface UseFormOptions<TResult = unknown> {\r\n onSuccess?: (result: FormResult<TResult>) => void;\r\n onError?: (result: FormResult<TResult>) => void;\r\n resetOnSuccess?: boolean;\r\n validateOnBlur?: boolean;\r\n validateOnChange?: boolean;\r\n}\r\n\r\nexport interface FieldBinding {\r\n name: string;\r\n value: unknown;\r\n onInput: (e: Event) => void;\r\n onBlur: (e: Event) => void;\r\n}\r\n\r\nexport interface UseFormReturn<TInput, TResult = unknown> {\r\n register: (name: keyof TInput & string) => FieldBinding;\r\n handleSubmit: () => Promise<void>;\r\n values: Ref<Partial<TInput>>;\r\n setValue: (name: keyof TInput & string, value: unknown) => void;\r\n setValues: (values: Partial<TInput>) => void;\r\n reset: () => void;\r\n errors: Ref<Record<string, string>>;\r\n touched: Ref<Set<string>>;\r\n pending: Ref<boolean>;\r\n dirty: Ref<boolean>;\r\n result: Ref<FormResult<TResult> | undefined>;\r\n validate: () => Promise<boolean>;\r\n}\r\n\r\n// ============================================================================\r\n// useForm Composable\r\n// ============================================================================\r\n\r\n/**\r\n * Vue composable for form handling\r\n */\r\nexport function useForm<TInput, TResult = unknown>(\r\n form: FormDefinition<TInput, TResult>,\r\n options: UseFormOptions<TResult> = {}\r\n): UseFormReturn<TInput, TResult> {\r\n const {\r\n onSuccess,\r\n onError,\r\n resetOnSuccess = false,\r\n validateOnBlur = true,\r\n validateOnChange = false,\r\n } = options;\r\n\r\n // State\r\n const values = ref<Partial<TInput>>({}) as Ref<Partial<TInput>>;\r\n const errors = ref<Record<string, string>>({});\r\n const touched = ref<Set<string>>(new Set());\r\n const pending = ref(false);\r\n const dirty = ref(false);\r\n const result = ref<FormResult<TResult> | undefined>();\r\n\r\n // Set a single value\r\n function setValue(name: keyof TInput & string, value: unknown): void {\r\n (values.value as Record<string, unknown>)[name] = value;\r\n dirty.value = true;\r\n\r\n if (validateOnChange) {\r\n const validation = form.validate(values.value);\r\n if (!validation.success && validation.errors) {\r\n const fieldError = validation.errors.find(e => e.path === name);\r\n if (fieldError) {\r\n errors.value[name] = fieldError.message;\r\n } else {\r\n delete errors.value[name];\r\n }\r\n } else {\r\n delete errors.value[name];\r\n }\r\n }\r\n }\r\n\r\n // Set multiple values\r\n function setValues(newValues: Partial<TInput>): void {\r\n Object.assign(values.value, newValues);\r\n dirty.value = true;\r\n }\r\n\r\n // Reset form\r\n function reset(): void {\r\n values.value = {} as Partial<TInput>;\r\n errors.value = {};\r\n touched.value = new Set();\r\n dirty.value = false;\r\n result.value = undefined;\r\n }\r\n\r\n // Validate entire form\r\n async function validate(): Promise<boolean> {\r\n const validation = await form.validateAsync(values.value);\r\n if (!validation.success && validation.errors) {\r\n errors.value = errorsToObject(validation.errors);\r\n return false;\r\n }\r\n errors.value = {};\r\n return true;\r\n }\r\n\r\n // Register a field\r\n function register(name: keyof TInput & string): FieldBinding {\r\n return {\r\n name,\r\n value: (values.value as Record<string, unknown>)[name] ?? '',\r\n onInput: (e: Event) => {\r\n const target = e.target as HTMLInputElement;\r\n const value = target.type === 'checkbox' ? target.checked : target.value;\r\n setValue(name, value);\r\n },\r\n onBlur: (e: Event) => {\r\n touched.value.add(name);\r\n if (validateOnBlur) {\r\n const validation = form.validate(values.value);\r\n if (!validation.success && validation.errors) {\r\n const fieldError = validation.errors.find(e => e.path === name);\r\n if (fieldError) {\r\n errors.value[name] = fieldError.message;\r\n } else {\r\n delete errors.value[name];\r\n }\r\n }\r\n }\r\n },\r\n };\r\n }\r\n\r\n // Handle form submission\r\n async function handleSubmit(): Promise<void> {\r\n pending.value = true;\r\n\r\n try {\r\n const formResult = await form.execute(values.value);\r\n result.value = formResult;\r\n\r\n if (formResult.success) {\r\n onSuccess?.(formResult);\r\n if (resetOnSuccess) {\r\n reset();\r\n }\r\n } else {\r\n if (formResult.fieldErrors) {\r\n errors.value = errorsToObject(formResult.fieldErrors);\r\n }\r\n onError?.(formResult);\r\n }\r\n } catch (error) {\r\n const errorResult: FormResult<TResult> = {\r\n success: false,\r\n error: error instanceof Error ? error.message : 'Unknown error',\r\n };\r\n result.value = errorResult;\r\n onError?.(errorResult);\r\n } finally {\r\n pending.value = false;\r\n }\r\n }\r\n\r\n return {\r\n register,\r\n handleSubmit,\r\n values,\r\n setValue,\r\n setValues,\r\n reset,\r\n errors,\r\n touched,\r\n pending,\r\n dirty,\r\n result,\r\n validate,\r\n };\r\n}\r\n\r\nexport default { useForm };\r\n"]}
|