mobx-formly 0.0.1 → 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/package.json +13 -14
- package/dist/create-form.d.ts +0 -11
- package/dist/create-form.js +0 -7
- package/dist/form.d.ts +0 -193
- package/dist/form.js +0 -569
- package/dist/index.d.ts +0 -4
- package/dist/index.js +0 -2
- package/dist/src/form.d.ts +0 -48
- package/dist/src/form.js +0 -261
- package/dist/src/index.d.ts +0 -2
- package/dist/src/index.js +0 -1
- package/dist/src/types.d.ts +0 -88
- package/dist/src/types.js +0 -1
- package/dist/src/utils.d.ts +0 -6
- package/dist/src/utils.js +0 -59
- package/dist/types.d.ts +0 -132
- package/dist/types.js +0 -1
- package/dist/utils.d.ts +0 -6
- package/dist/utils.js +0 -60
package/dist/src/form.js
DELETED
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
import { makeAutoObservable, runInAction } from 'mobx';
|
|
2
|
-
import { createRef } from 'yummies/mobx';
|
|
3
|
-
import { clone, deleteAtPath, extractValue, getAtPath, isEqual, setAtPath } from './utils.js';
|
|
4
|
-
export class Form {
|
|
5
|
-
values;
|
|
6
|
-
errors = {};
|
|
7
|
-
dirtyFields = {};
|
|
8
|
-
touchedFields = {};
|
|
9
|
-
validatingFields = {};
|
|
10
|
-
isSubmitting = false;
|
|
11
|
-
isSubmitted = false;
|
|
12
|
-
isSubmitSuccessful = false;
|
|
13
|
-
submitCount = 0;
|
|
14
|
-
refs = new Map();
|
|
15
|
-
defaultValues;
|
|
16
|
-
options;
|
|
17
|
-
fieldOptions = new Map();
|
|
18
|
-
listeners = new Set();
|
|
19
|
-
constructor(options = {}) {
|
|
20
|
-
this.options = {
|
|
21
|
-
...options,
|
|
22
|
-
mode: options.mode ?? 'onSubmit',
|
|
23
|
-
reValidateMode: options.reValidateMode ?? 'onChange',
|
|
24
|
-
disabled: options.disabled ?? false,
|
|
25
|
-
};
|
|
26
|
-
this.defaultValues = clone((options.defaultValues ?? {}));
|
|
27
|
-
this.values = clone((options.values ?? options.defaultValues ?? {}));
|
|
28
|
-
makeAutoObservable(this, { defaultValues: false, options: false, fieldOptions: false, listeners: false, refs: false }, { autoBind: true });
|
|
29
|
-
}
|
|
30
|
-
get formState() {
|
|
31
|
-
return {
|
|
32
|
-
errors: this.errors,
|
|
33
|
-
dirtyFields: this.dirtyFields,
|
|
34
|
-
touchedFields: this.touchedFields,
|
|
35
|
-
validatingFields: this.validatingFields,
|
|
36
|
-
isDirty: Object.keys(this.dirtyFields).length > 0,
|
|
37
|
-
isValid: Object.keys(this.errors).length === 0,
|
|
38
|
-
isValidating: Object.keys(this.validatingFields).length > 0,
|
|
39
|
-
isSubmitting: this.isSubmitting,
|
|
40
|
-
isSubmitted: this.isSubmitted,
|
|
41
|
-
isSubmitSuccessful: this.isSubmitSuccessful,
|
|
42
|
-
submitCount: this.submitCount,
|
|
43
|
-
disabled: this.options.disabled,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
get isDirty() { return this.formState.isDirty; }
|
|
47
|
-
get isValid() { return this.formState.isValid; }
|
|
48
|
-
register(name, options = {}) {
|
|
49
|
-
const path = name;
|
|
50
|
-
this.fieldOptions.set(path, options);
|
|
51
|
-
let ref = this.refs.get(path);
|
|
52
|
-
if (!ref) {
|
|
53
|
-
ref = createRef();
|
|
54
|
-
this.refs.set(path, ref);
|
|
55
|
-
}
|
|
56
|
-
return {
|
|
57
|
-
name: path,
|
|
58
|
-
ref,
|
|
59
|
-
onChange: async (eventOrValue) => {
|
|
60
|
-
const value = this.transformValue(extractValue(eventOrValue), options);
|
|
61
|
-
const shouldValidate = this.shouldValidateOnChange(path);
|
|
62
|
-
this.setValue(path, value, { shouldDirty: true, shouldValidate: false });
|
|
63
|
-
if (shouldValidate)
|
|
64
|
-
await this.trigger(path);
|
|
65
|
-
},
|
|
66
|
-
onBlur: async () => {
|
|
67
|
-
this.markTouched(path);
|
|
68
|
-
if (this.options.mode === 'onBlur' || this.options.mode === 'all' || (path in this.errors && this.options.reValidateMode === 'onBlur'))
|
|
69
|
-
await this.trigger(path);
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
unregister(name) {
|
|
74
|
-
const path = name;
|
|
75
|
-
deleteAtPath(this.values, path);
|
|
76
|
-
delete this.errors[path];
|
|
77
|
-
delete this.dirtyFields[path];
|
|
78
|
-
delete this.touchedFields[path];
|
|
79
|
-
delete this.validatingFields[path];
|
|
80
|
-
this.fieldOptions.delete(path);
|
|
81
|
-
this.refs.delete(path);
|
|
82
|
-
this.notify(path);
|
|
83
|
-
}
|
|
84
|
-
getValues(name) {
|
|
85
|
-
return name === undefined ? clone(this.values) : getAtPath(this.values, name);
|
|
86
|
-
}
|
|
87
|
-
setValue(name, value, config = {}) {
|
|
88
|
-
const path = name;
|
|
89
|
-
setAtPath(this.values, path, value);
|
|
90
|
-
if (config.shouldDirty ?? true)
|
|
91
|
-
this.updateDirty(path);
|
|
92
|
-
if (config.shouldTouch)
|
|
93
|
-
this.markTouched(path);
|
|
94
|
-
this.notify(path);
|
|
95
|
-
if (config.shouldValidate)
|
|
96
|
-
void this.trigger(path);
|
|
97
|
-
}
|
|
98
|
-
setError(name, error) {
|
|
99
|
-
this.errors[name] = error;
|
|
100
|
-
}
|
|
101
|
-
clearErrors(name) {
|
|
102
|
-
if (!name) {
|
|
103
|
-
this.errors = {};
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
for (const path of Array.isArray(name) ? name : [name])
|
|
107
|
-
delete this.errors[path];
|
|
108
|
-
}
|
|
109
|
-
getFieldState(name) {
|
|
110
|
-
const path = name;
|
|
111
|
-
return { invalid: !!this.errors[path], isDirty: !!this.dirtyFields[path], isTouched: !!this.touchedFields[path], error: this.errors[path] };
|
|
112
|
-
}
|
|
113
|
-
async trigger(name) {
|
|
114
|
-
const paths = name ? (Array.isArray(name) ? name : [name]).map(String) : undefined;
|
|
115
|
-
for (const path of paths ?? this.fieldOptions.keys())
|
|
116
|
-
this.validatingFields[path] = true;
|
|
117
|
-
try {
|
|
118
|
-
const schemaErrors = await this.validateSchema();
|
|
119
|
-
const validationPaths = paths ?? [...new Set([...this.fieldOptions.keys(), ...Object.keys(schemaErrors)])];
|
|
120
|
-
runInAction(() => {
|
|
121
|
-
for (const path of validationPaths) {
|
|
122
|
-
const error = schemaErrors[path] ?? this.validateRules(path);
|
|
123
|
-
if (error instanceof Promise)
|
|
124
|
-
continue;
|
|
125
|
-
if (error)
|
|
126
|
-
this.errors[path] = error;
|
|
127
|
-
else
|
|
128
|
-
delete this.errors[path];
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
for (const path of validationPaths) {
|
|
132
|
-
const ruleError = await this.validateRules(path);
|
|
133
|
-
runInAction(() => {
|
|
134
|
-
if (ruleError)
|
|
135
|
-
this.errors[path] = ruleError;
|
|
136
|
-
else if (!schemaErrors[path])
|
|
137
|
-
delete this.errors[path];
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
return (paths ?? Object.keys(this.errors)).every((path) => !this.errors[path]);
|
|
141
|
-
}
|
|
142
|
-
finally {
|
|
143
|
-
runInAction(() => {
|
|
144
|
-
for (const path of paths ?? this.fieldOptions.keys())
|
|
145
|
-
delete this.validatingFields[path];
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
handleSubmit({ onValid, onInvalid }) {
|
|
150
|
-
return async () => {
|
|
151
|
-
this.isSubmitting = true;
|
|
152
|
-
this.isSubmitted = true;
|
|
153
|
-
this.submitCount += 1;
|
|
154
|
-
try {
|
|
155
|
-
const valid = await this.trigger();
|
|
156
|
-
if (valid) {
|
|
157
|
-
await onValid(this.getValues(), this);
|
|
158
|
-
runInAction(() => { this.isSubmitSuccessful = true; });
|
|
159
|
-
}
|
|
160
|
-
else {
|
|
161
|
-
await onInvalid?.(this.errors, this);
|
|
162
|
-
runInAction(() => { this.isSubmitSuccessful = false; });
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
finally {
|
|
166
|
-
runInAction(() => { this.isSubmitting = false; });
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
reset(values, options = {}) {
|
|
171
|
-
const next = clone((values ?? this.defaultValues));
|
|
172
|
-
this.values = next;
|
|
173
|
-
if (!options.keepDefaultValues && values)
|
|
174
|
-
Object.assign(this.defaultValues, clone(values));
|
|
175
|
-
if (!options.keepDirty)
|
|
176
|
-
this.dirtyFields = {};
|
|
177
|
-
if (!options.keepTouched)
|
|
178
|
-
this.touchedFields = {};
|
|
179
|
-
if (!options.keepErrors)
|
|
180
|
-
this.errors = {};
|
|
181
|
-
if (!options.keepIsSubmitted) {
|
|
182
|
-
this.isSubmitted = false;
|
|
183
|
-
this.isSubmitSuccessful = false;
|
|
184
|
-
}
|
|
185
|
-
if (!options.keepSubmitCount)
|
|
186
|
-
this.submitCount = 0;
|
|
187
|
-
this.notify();
|
|
188
|
-
}
|
|
189
|
-
resetField(name) {
|
|
190
|
-
const path = name;
|
|
191
|
-
setAtPath(this.values, path, clone(getAtPath(this.defaultValues, path)));
|
|
192
|
-
delete this.errors[path];
|
|
193
|
-
delete this.dirtyFields[path];
|
|
194
|
-
delete this.touchedFields[path];
|
|
195
|
-
this.notify(path);
|
|
196
|
-
}
|
|
197
|
-
setFocus(name) { this.refs.get(name)?.current?.focus(); }
|
|
198
|
-
watch(name) { return name === undefined ? this.values : getAtPath(this.values, name); }
|
|
199
|
-
subscribe(listener) {
|
|
200
|
-
this.listeners.add(listener);
|
|
201
|
-
return () => this.listeners.delete(listener);
|
|
202
|
-
}
|
|
203
|
-
notify(name) { for (const listener of this.listeners)
|
|
204
|
-
listener(this.getValues(), { name }); }
|
|
205
|
-
markTouched(path) { this.touchedFields[path] = true; }
|
|
206
|
-
updateDirty(path) { if (isEqual(getAtPath(this.values, path), getAtPath(this.defaultValues, path)))
|
|
207
|
-
delete this.dirtyFields[path];
|
|
208
|
-
else
|
|
209
|
-
this.dirtyFields[path] = true; }
|
|
210
|
-
shouldValidateOnChange(path) { return this.options.mode === 'onChange' || this.options.mode === 'all' || (path in this.errors && this.options.reValidateMode === 'onChange'); }
|
|
211
|
-
transformValue(value, options) {
|
|
212
|
-
if (options.setValueAs)
|
|
213
|
-
return options.setValueAs(value);
|
|
214
|
-
if (options.valueAsNumber)
|
|
215
|
-
return value === '' ? Number.NaN : Number(value);
|
|
216
|
-
if (options.valueAsDate)
|
|
217
|
-
return new Date(String(value));
|
|
218
|
-
return value;
|
|
219
|
-
}
|
|
220
|
-
async validateSchema() {
|
|
221
|
-
if (!this.options.schema)
|
|
222
|
-
return {};
|
|
223
|
-
const result = await this.options.schema.safeParseAsync(this.getValues());
|
|
224
|
-
if (result.success)
|
|
225
|
-
return {};
|
|
226
|
-
return this.zodErrors(result.error);
|
|
227
|
-
}
|
|
228
|
-
zodErrors(error) {
|
|
229
|
-
return error.issues.reduce((errors, issue) => {
|
|
230
|
-
const path = issue.path.map(String).join('.') || 'root';
|
|
231
|
-
if (!errors[path])
|
|
232
|
-
errors[path] = { type: issue.code, message: issue.message };
|
|
233
|
-
return errors;
|
|
234
|
-
}, {});
|
|
235
|
-
}
|
|
236
|
-
async validateRules(path) {
|
|
237
|
-
const rules = this.fieldOptions.get(path);
|
|
238
|
-
if (!rules)
|
|
239
|
-
return undefined;
|
|
240
|
-
const value = getAtPath(this.values, path);
|
|
241
|
-
const fail = (type, rule) => typeof rule === 'string' ? { type, message: rule } : { type };
|
|
242
|
-
if (rules.required && (value === undefined || value === null || value === ''))
|
|
243
|
-
return fail('required', rules.required);
|
|
244
|
-
if (rules.minLength && String(value ?? '').length < rules.minLength.value)
|
|
245
|
-
return { type: 'minLength', message: rules.minLength.message };
|
|
246
|
-
if (rules.maxLength && String(value ?? '').length > rules.maxLength.value)
|
|
247
|
-
return { type: 'maxLength', message: rules.maxLength.message };
|
|
248
|
-
if (rules.min && Number(value) < rules.min.value)
|
|
249
|
-
return { type: 'min', message: rules.min.message };
|
|
250
|
-
if (rules.max && Number(value) > rules.max.value)
|
|
251
|
-
return { type: 'max', message: rules.max.message };
|
|
252
|
-
if (rules.pattern && !rules.pattern.value.test(String(value ?? '')))
|
|
253
|
-
return { type: 'pattern', message: rules.pattern.message };
|
|
254
|
-
if (rules.validate) {
|
|
255
|
-
const result = await rules.validate(value, this.getValues());
|
|
256
|
-
if (result !== true)
|
|
257
|
-
return { type: 'validate', message: typeof result === 'string' ? result : undefined };
|
|
258
|
-
}
|
|
259
|
-
return undefined;
|
|
260
|
-
}
|
|
261
|
-
}
|
package/dist/src/index.d.ts
DELETED
package/dist/src/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { Form } from './form.js';
|
package/dist/src/types.d.ts
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import type { Ref } from 'yummies/mobx';
|
|
2
|
-
import type { ZodType } from 'zod';
|
|
3
|
-
export type FieldValues = Record<string, unknown>;
|
|
4
|
-
/** Dot-separated path into the form value tree, for example `user.email`. */
|
|
5
|
-
export type FieldPath<T extends FieldValues = FieldValues> = string;
|
|
6
|
-
export interface FieldError {
|
|
7
|
-
type: string;
|
|
8
|
-
message?: string;
|
|
9
|
-
}
|
|
10
|
-
export type FieldErrors<T extends FieldValues = FieldValues> = Partial<Record<FieldPath<T>, FieldError>>;
|
|
11
|
-
export interface FieldState {
|
|
12
|
-
invalid: boolean;
|
|
13
|
-
isDirty: boolean;
|
|
14
|
-
isTouched: boolean;
|
|
15
|
-
error?: FieldError;
|
|
16
|
-
}
|
|
17
|
-
export interface FormState<T extends FieldValues = FieldValues> {
|
|
18
|
-
errors: FieldErrors<T>;
|
|
19
|
-
dirtyFields: Partial<Record<FieldPath<T>, true>>;
|
|
20
|
-
touchedFields: Partial<Record<FieldPath<T>, true>>;
|
|
21
|
-
validatingFields: Partial<Record<FieldPath<T>, true>>;
|
|
22
|
-
isDirty: boolean;
|
|
23
|
-
isValid: boolean;
|
|
24
|
-
isValidating: boolean;
|
|
25
|
-
isSubmitting: boolean;
|
|
26
|
-
isSubmitted: boolean;
|
|
27
|
-
isSubmitSuccessful: boolean;
|
|
28
|
-
submitCount: number;
|
|
29
|
-
disabled: boolean;
|
|
30
|
-
}
|
|
31
|
-
export interface RegisterOptions<T extends FieldValues = FieldValues> {
|
|
32
|
-
required?: string | boolean;
|
|
33
|
-
minLength?: {
|
|
34
|
-
value: number;
|
|
35
|
-
message?: string;
|
|
36
|
-
};
|
|
37
|
-
maxLength?: {
|
|
38
|
-
value: number;
|
|
39
|
-
message?: string;
|
|
40
|
-
};
|
|
41
|
-
min?: {
|
|
42
|
-
value: number;
|
|
43
|
-
message?: string;
|
|
44
|
-
};
|
|
45
|
-
max?: {
|
|
46
|
-
value: number;
|
|
47
|
-
message?: string;
|
|
48
|
-
};
|
|
49
|
-
pattern?: {
|
|
50
|
-
value: RegExp;
|
|
51
|
-
message?: string;
|
|
52
|
-
};
|
|
53
|
-
validate?: (value: unknown, values: T) => boolean | string | Promise<boolean | string>;
|
|
54
|
-
valueAsNumber?: boolean;
|
|
55
|
-
valueAsDate?: boolean;
|
|
56
|
-
setValueAs?: (value: unknown) => unknown;
|
|
57
|
-
}
|
|
58
|
-
export interface RegisterReturn {
|
|
59
|
-
name: string;
|
|
60
|
-
ref: Ref<HTMLElement | null>;
|
|
61
|
-
onChange: (eventOrValue: unknown) => Promise<void>;
|
|
62
|
-
onBlur: () => Promise<void>;
|
|
63
|
-
}
|
|
64
|
-
export interface SetValueConfig {
|
|
65
|
-
shouldValidate?: boolean;
|
|
66
|
-
shouldDirty?: boolean;
|
|
67
|
-
shouldTouch?: boolean;
|
|
68
|
-
}
|
|
69
|
-
export interface ResetOptions {
|
|
70
|
-
keepDefaultValues?: boolean;
|
|
71
|
-
keepDirty?: boolean;
|
|
72
|
-
keepTouched?: boolean;
|
|
73
|
-
keepErrors?: boolean;
|
|
74
|
-
keepIsSubmitted?: boolean;
|
|
75
|
-
keepSubmitCount?: boolean;
|
|
76
|
-
}
|
|
77
|
-
export interface FormOptions<T extends FieldValues> {
|
|
78
|
-
defaultValues?: Partial<T>;
|
|
79
|
-
values?: Partial<T>;
|
|
80
|
-
schema?: ZodType<T>;
|
|
81
|
-
mode?: 'onSubmit' | 'onChange' | 'onBlur' | 'all';
|
|
82
|
-
reValidateMode?: 'onChange' | 'onBlur';
|
|
83
|
-
disabled?: boolean;
|
|
84
|
-
}
|
|
85
|
-
export interface SubmitHandlers<T extends FieldValues> {
|
|
86
|
-
onValid: (values: T, form: unknown) => void | Promise<void>;
|
|
87
|
-
onInvalid?: (errors: FieldErrors<T>, form: unknown) => void | Promise<void>;
|
|
88
|
-
}
|
package/dist/src/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/src/utils.d.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
export declare const clone: <T>(value: T) => T;
|
|
2
|
-
export declare const getAtPath: (source: unknown, path: string) => unknown;
|
|
3
|
-
export declare const setAtPath: (target: Record<string, unknown>, path: string, value: unknown) => void;
|
|
4
|
-
export declare const deleteAtPath: (target: Record<string, unknown>, path: string) => void;
|
|
5
|
-
export declare const isEqual: (a: unknown, b: unknown) => boolean;
|
|
6
|
-
export declare const extractValue: (eventOrValue: unknown) => unknown;
|
package/dist/src/utils.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { isUnsafeProperty } from 'yummies/data';
|
|
2
|
-
export const clone = (value) => {
|
|
3
|
-
if (typeof structuredClone === 'function') {
|
|
4
|
-
try {
|
|
5
|
-
return structuredClone(value);
|
|
6
|
-
}
|
|
7
|
-
catch {
|
|
8
|
-
// MobX observable proxies cannot be structured-cloned directly.
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
return JSON.parse(JSON.stringify(value));
|
|
12
|
-
};
|
|
13
|
-
export const getAtPath = (source, path) => path.split('.').reduce((value, key) => {
|
|
14
|
-
if (isUnsafeProperty(key) || value === null || value === undefined)
|
|
15
|
-
return undefined;
|
|
16
|
-
return value[key];
|
|
17
|
-
}, source);
|
|
18
|
-
export const setAtPath = (target, path, value) => {
|
|
19
|
-
const keys = path.split('.');
|
|
20
|
-
let current = target;
|
|
21
|
-
for (const key of keys.slice(0, -1)) {
|
|
22
|
-
if (isUnsafeProperty(key))
|
|
23
|
-
return;
|
|
24
|
-
const next = current[key];
|
|
25
|
-
if (!next || typeof next !== 'object' || Array.isArray(next))
|
|
26
|
-
current[key] = {};
|
|
27
|
-
current = current[key];
|
|
28
|
-
}
|
|
29
|
-
const lastKey = keys.at(-1);
|
|
30
|
-
if (lastKey && !isUnsafeProperty(lastKey))
|
|
31
|
-
current[lastKey] = value;
|
|
32
|
-
};
|
|
33
|
-
export const deleteAtPath = (target, path) => {
|
|
34
|
-
const keys = path.split('.');
|
|
35
|
-
const lastKey = keys.pop();
|
|
36
|
-
if (!lastKey || isUnsafeProperty(lastKey))
|
|
37
|
-
return;
|
|
38
|
-
const parent = keys.reduce((value, key) => {
|
|
39
|
-
if (isUnsafeProperty(key) || !value || typeof value !== 'object')
|
|
40
|
-
return undefined;
|
|
41
|
-
return value[key];
|
|
42
|
-
}, target);
|
|
43
|
-
if (parent && typeof parent === 'object')
|
|
44
|
-
delete parent[lastKey];
|
|
45
|
-
};
|
|
46
|
-
export const isEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
47
|
-
export const extractValue = (eventOrValue) => {
|
|
48
|
-
if (!eventOrValue || typeof eventOrValue !== 'object' || !('target' in eventOrValue))
|
|
49
|
-
return eventOrValue;
|
|
50
|
-
const target = eventOrValue.target;
|
|
51
|
-
if (target.type === 'checkbox')
|
|
52
|
-
return target.checked;
|
|
53
|
-
if (target.type === 'radio')
|
|
54
|
-
return target.checked ? target.value : undefined;
|
|
55
|
-
if (target.multiple && target instanceof HTMLSelectElement) {
|
|
56
|
-
return Array.from(target.selectedOptions, (option) => option.value);
|
|
57
|
-
}
|
|
58
|
-
return target.value;
|
|
59
|
-
};
|
package/dist/types.d.ts
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import type { Ref } from 'yummies/mobx';
|
|
2
|
-
export type FieldValues = Record<string, unknown>;
|
|
3
|
-
export interface SchemaIssue {
|
|
4
|
-
code?: string;
|
|
5
|
-
type?: string;
|
|
6
|
-
path?: Array<PropertyKey | {
|
|
7
|
-
key: PropertyKey;
|
|
8
|
-
}>;
|
|
9
|
-
message: string;
|
|
10
|
-
}
|
|
11
|
-
export type SchemaResult<T> = {
|
|
12
|
-
success: true;
|
|
13
|
-
data: T;
|
|
14
|
-
} | {
|
|
15
|
-
success: false;
|
|
16
|
-
error: {
|
|
17
|
-
issues: SchemaIssue[];
|
|
18
|
-
};
|
|
19
|
-
};
|
|
20
|
-
/** Safe-parse-compatible schema contract. */
|
|
21
|
-
export interface SafeParseFormSchema<T> {
|
|
22
|
-
safeParseAsync(value: unknown): Promise<SchemaResult<T>>;
|
|
23
|
-
}
|
|
24
|
-
/** Valibot-compatible schema contract. */
|
|
25
|
-
export interface ValibotFormSchema<T> {
|
|
26
|
-
readonly '~types'?: {
|
|
27
|
-
readonly output: T;
|
|
28
|
-
};
|
|
29
|
-
'~run': (...args: any[]) => any;
|
|
30
|
-
}
|
|
31
|
-
export interface ValibotRunResult<T> {
|
|
32
|
-
success?: boolean;
|
|
33
|
-
value?: T;
|
|
34
|
-
issues?: SchemaIssue[];
|
|
35
|
-
}
|
|
36
|
-
/** Schema contract accepted by Form. Both Zod and Valibot schemas fit this interface. */
|
|
37
|
-
export type FormSchema<T> = SafeParseFormSchema<T> | ValibotFormSchema<T>;
|
|
38
|
-
export type SchemaOutput<S> = S extends SafeParseFormSchema<infer T> ? T : S extends {
|
|
39
|
-
readonly '~types'?: {
|
|
40
|
-
readonly output: infer T;
|
|
41
|
-
};
|
|
42
|
-
} ? T : never;
|
|
43
|
-
/** Dot-separated path into the form value tree, for example `user.email`. */
|
|
44
|
-
export type FieldPath<T = FieldValues> = 'root' | {
|
|
45
|
-
[K in Extract<keyof T, string>]: T[K] extends readonly unknown[] ? T[K] extends readonly (infer I)[] ? I extends object ? K | `${K}.${number}` | `${K}.${number}.${FieldPath<I>}` : K | `${K}.${number}` : K : T[K] extends object ? K | `${K}.${FieldPath<T[K]>}` : K;
|
|
46
|
-
}[Extract<keyof T, string>];
|
|
47
|
-
export type FieldPathValue<T, P extends string> = P extends `${infer K}.${infer Rest}` ? K extends keyof T ? FieldPathValue<T[K], Rest> : T extends readonly (infer I)[] ? FieldPathValue<I, Rest> : never : P extends keyof T ? T[P] : T extends readonly (infer I)[] ? P extends `${number}` ? I : never : never;
|
|
48
|
-
export interface FieldError {
|
|
49
|
-
type: string;
|
|
50
|
-
message?: string;
|
|
51
|
-
}
|
|
52
|
-
export type FieldErrors<T extends FieldValues = FieldValues> = Record<string, FieldError | undefined>;
|
|
53
|
-
export interface FieldState {
|
|
54
|
-
invalid: boolean;
|
|
55
|
-
isDirty: boolean;
|
|
56
|
-
isTouched: boolean;
|
|
57
|
-
isValidating: boolean;
|
|
58
|
-
error?: FieldError;
|
|
59
|
-
}
|
|
60
|
-
export type FieldStateTree<T extends FieldValues = FieldValues> = Record<string, FieldState | undefined>;
|
|
61
|
-
export interface FormState<T extends FieldValues = FieldValues> {
|
|
62
|
-
errors: FieldErrors<T>;
|
|
63
|
-
dirtyFields: Partial<Record<FieldPath<T>, true>>;
|
|
64
|
-
touchedFields: Partial<Record<FieldPath<T>, true>>;
|
|
65
|
-
validatingFields: Partial<Record<FieldPath<T>, true>>;
|
|
66
|
-
isDirty: boolean;
|
|
67
|
-
isValid: boolean;
|
|
68
|
-
isValidating: boolean;
|
|
69
|
-
isSubmitting: boolean;
|
|
70
|
-
isSubmitted: boolean;
|
|
71
|
-
isSubmitSuccessful: boolean;
|
|
72
|
-
submitCount: number;
|
|
73
|
-
disabled: boolean;
|
|
74
|
-
}
|
|
75
|
-
export interface RegisterOptions<T extends FieldValues = FieldValues> {
|
|
76
|
-
required?: string | boolean;
|
|
77
|
-
minLength?: {
|
|
78
|
-
value: number;
|
|
79
|
-
message?: string;
|
|
80
|
-
};
|
|
81
|
-
maxLength?: {
|
|
82
|
-
value: number;
|
|
83
|
-
message?: string;
|
|
84
|
-
};
|
|
85
|
-
min?: {
|
|
86
|
-
value: number;
|
|
87
|
-
message?: string;
|
|
88
|
-
};
|
|
89
|
-
max?: {
|
|
90
|
-
value: number;
|
|
91
|
-
message?: string;
|
|
92
|
-
};
|
|
93
|
-
pattern?: {
|
|
94
|
-
value: RegExp;
|
|
95
|
-
message?: string;
|
|
96
|
-
};
|
|
97
|
-
validate?: (value: unknown, values: T) => boolean | string | Promise<boolean | string>;
|
|
98
|
-
valueAsNumber?: boolean;
|
|
99
|
-
valueAsDate?: boolean;
|
|
100
|
-
setValueAs?: (value: unknown) => unknown;
|
|
101
|
-
}
|
|
102
|
-
export interface RegisterReturn {
|
|
103
|
-
name: string;
|
|
104
|
-
ref: Ref<HTMLElement | null>;
|
|
105
|
-
onChange: (eventOrValue: unknown) => Promise<void>;
|
|
106
|
-
onBlur: () => Promise<void>;
|
|
107
|
-
}
|
|
108
|
-
export interface SetValueConfig {
|
|
109
|
-
shouldValidate?: boolean;
|
|
110
|
-
shouldDirty?: boolean;
|
|
111
|
-
shouldTouch?: boolean;
|
|
112
|
-
}
|
|
113
|
-
export interface ResetOptions {
|
|
114
|
-
keepDefaultValues?: boolean;
|
|
115
|
-
keepDirty?: boolean;
|
|
116
|
-
keepTouched?: boolean;
|
|
117
|
-
keepErrors?: boolean;
|
|
118
|
-
keepIsSubmitted?: boolean;
|
|
119
|
-
keepSubmitCount?: boolean;
|
|
120
|
-
}
|
|
121
|
-
export interface FormOptions<T extends FieldValues> {
|
|
122
|
-
defaultValues?: Partial<T>;
|
|
123
|
-
values?: Partial<T>;
|
|
124
|
-
schema?: FormSchema<T>;
|
|
125
|
-
mode?: 'onSubmit' | 'onChange' | 'onBlur' | 'all';
|
|
126
|
-
reValidateMode?: 'onChange' | 'onBlur';
|
|
127
|
-
disabled?: boolean;
|
|
128
|
-
}
|
|
129
|
-
export interface SubmitHandlers<T extends FieldValues> {
|
|
130
|
-
onValid: (values: T, form: unknown) => void | Promise<void>;
|
|
131
|
-
onInvalid?: (errors: FieldErrors<T>, form: unknown) => void | Promise<void>;
|
|
132
|
-
}
|
package/dist/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/utils.d.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
export declare const clone: <T>(value: T) => T;
|
|
2
|
-
export declare const getAtPath: (source: unknown, path: string) => unknown;
|
|
3
|
-
export declare const setAtPath: (target: Record<string, unknown>, path: string, value: unknown) => void;
|
|
4
|
-
export declare const deleteAtPath: (target: Record<string, unknown>, path: string) => void;
|
|
5
|
-
export declare const isEqual: (a: unknown, b: unknown) => boolean;
|
|
6
|
-
export declare const extractValue: (eventOrValue: unknown) => unknown;
|
package/dist/utils.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { isUnsafeProperty } from 'yummies/data';
|
|
2
|
-
export const clone = (value) => {
|
|
3
|
-
if (typeof structuredClone === 'function') {
|
|
4
|
-
try {
|
|
5
|
-
return structuredClone(value);
|
|
6
|
-
}
|
|
7
|
-
catch {
|
|
8
|
-
// MobX observable proxies cannot be structured-cloned directly.
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
return JSON.parse(JSON.stringify(value));
|
|
12
|
-
};
|
|
13
|
-
export const getAtPath = (source, path) => path.split('.').reduce((value, key) => {
|
|
14
|
-
if (isUnsafeProperty(key) || value === null || value === undefined)
|
|
15
|
-
return undefined;
|
|
16
|
-
return value[key];
|
|
17
|
-
}, source);
|
|
18
|
-
export const setAtPath = (target, path, value) => {
|
|
19
|
-
const keys = path.split('.');
|
|
20
|
-
let current = target;
|
|
21
|
-
for (const [index, key] of keys.slice(0, -1).entries()) {
|
|
22
|
-
if (isUnsafeProperty(key))
|
|
23
|
-
return;
|
|
24
|
-
const next = current[key];
|
|
25
|
-
const nextKey = keys[index + 1];
|
|
26
|
-
if (!next || typeof next !== 'object')
|
|
27
|
-
current[key] = /^\d+$/.test(nextKey) ? [] : {};
|
|
28
|
-
current = current[key];
|
|
29
|
-
}
|
|
30
|
-
const lastKey = keys.at(-1);
|
|
31
|
-
if (lastKey && !isUnsafeProperty(lastKey))
|
|
32
|
-
current[lastKey] = value;
|
|
33
|
-
};
|
|
34
|
-
export const deleteAtPath = (target, path) => {
|
|
35
|
-
const keys = path.split('.');
|
|
36
|
-
const lastKey = keys.pop();
|
|
37
|
-
if (!lastKey || isUnsafeProperty(lastKey))
|
|
38
|
-
return;
|
|
39
|
-
const parent = keys.reduce((value, key) => {
|
|
40
|
-
if (isUnsafeProperty(key) || !value || typeof value !== 'object')
|
|
41
|
-
return undefined;
|
|
42
|
-
return value[key];
|
|
43
|
-
}, target);
|
|
44
|
-
if (parent && typeof parent === 'object')
|
|
45
|
-
delete parent[lastKey];
|
|
46
|
-
};
|
|
47
|
-
export const isEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
48
|
-
export const extractValue = (eventOrValue) => {
|
|
49
|
-
if (!eventOrValue || typeof eventOrValue !== 'object' || !('target' in eventOrValue))
|
|
50
|
-
return eventOrValue;
|
|
51
|
-
const target = eventOrValue.target;
|
|
52
|
-
if (target.type === 'checkbox')
|
|
53
|
-
return target.checked;
|
|
54
|
-
if (target.type === 'radio')
|
|
55
|
-
return target.checked ? target.value : undefined;
|
|
56
|
-
if (target.multiple && typeof HTMLSelectElement !== 'undefined' && target instanceof HTMLSelectElement) {
|
|
57
|
-
return Array.from(target.selectedOptions, (option) => option.value);
|
|
58
|
-
}
|
|
59
|
-
return target.value;
|
|
60
|
-
};
|