solid-hook-form 1.10.0 → 2.0.1

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/main.d.ts CHANGED
@@ -42,14 +42,13 @@ type Register<F extends FormValues> = (name: Path<F>, options?: Rules<F, Path<F>
42
42
 
43
43
  type Control<F extends FormValues> = {
44
44
  values: Accessor<F>;
45
- errors: Accessor<FieldErrors<F>>;
45
+ errors: FieldErrors<F>;
46
46
  register: Register<F>;
47
47
  };
48
48
  type UseControllerArg<F extends FormValues> = {
49
- control: Control<F>;
49
+ control?: Control<F>;
50
50
  name: Path<F>;
51
51
  rules?: Rules<F>;
52
- render(arg: UseControllerReturn<F>): JSXElement;
53
52
  };
54
53
  type UseControllerReturn<F extends FormValues> = {
55
54
  field: RegisterReturn<F> & {
@@ -60,7 +59,7 @@ type UseControllerReturn<F extends FormValues> = {
60
59
  };
61
60
  };
62
61
  type ControllerProps<F extends FormValues> = {
63
- control: Control<F>;
62
+ control?: Control<F>;
64
63
  name: Path<F>;
65
64
  rules?: Rules<F>;
66
65
  render(arg: UseControllerReturn<F>): JSXElement;
@@ -68,6 +67,8 @@ type ControllerProps<F extends FormValues> = {
68
67
 
69
68
  type TouchedFields<F extends FormValues = FormValues> = Partial<Record<Path<F>, boolean>>;
70
69
 
70
+ type DirtyFields<F extends FormValues = FormValues> = Partial<Record<Path<F>, boolean>>;
71
+
71
72
  type FormValues = Record<string, any>;
72
73
  type GetValues<F extends FormValues> = {
73
74
  (): F;
@@ -79,6 +80,7 @@ type SubmitErrorHandler<F extends FormValues> = (errors: FieldErrors<F>) => void
79
80
  type HandleSubmit<F extends FormValues> = (onSubmit: SubmitHandler<F>, onError?: SubmitErrorHandler<F>) => (event: SubmitEvent) => void;
80
81
  type ResetOptions = {
81
82
  keepTouched?: boolean;
83
+ keepDirty?: boolean;
82
84
  };
83
85
  type Reset<F extends FormValues> = (newDefaultValues?: Partial<F>, options?: ResetOptions) => void;
84
86
  type CreateFormArg<F extends FormValues> = {
@@ -89,21 +91,21 @@ type CreateFormArg<F extends FormValues> = {
89
91
  type CreateFormReturn<F extends FormValues = FormValues> = {
90
92
  control: Control<F>;
91
93
  formState: {
92
- errors: Accessor<FieldErrors<F>>;
94
+ errors: FieldErrors<F>;
93
95
  isValid: Accessor<boolean>;
96
+ isDirty: Accessor<boolean>;
94
97
  touchedFields: Accessor<TouchedFields<F>>;
98
+ dirtyFields: Accessor<DirtyFields<F>>;
95
99
  };
96
100
  values: Accessor<F>;
97
- errors: Accessor<FieldErrors<F>>;
98
- isValid: Accessor<boolean>;
101
+ errors: FieldErrors<F>;
99
102
  register: Register<F>;
100
103
  getValues: GetValues<F>;
101
104
  setValue: SetValue<F>;
102
- onSubmit: HandleSubmit<F>;
103
105
  handleSubmit: HandleSubmit<F>;
104
106
  reset: Reset<F>;
105
107
  };
106
- type CreateForm = <F extends FormValues>(arg?: CreateFormArg<F>) => CreateFormReturn<F>;
108
+ type CreateForm = <F extends FormValues>(arg: CreateFormArg<F>) => CreateFormReturn<F>;
107
109
 
108
110
  declare const createForm: CreateForm;
109
111
 
@@ -122,4 +124,4 @@ declare const get: (object: any, path: string, defaultValue?: unknown) => any;
122
124
 
123
125
  declare const set: (object: FormValues, path: string, value: unknown) => void;
124
126
 
125
- export { type Control, Controller, type CreateFormArg, type CreateFormReturn, type FieldError, type FieldErrors, FormProvider, type FormValues, type Rules, type SubmitErrorHandler, type SubmitHandler, type UseControllerArg, type UseControllerReturn, createForm, get, set, useController, createForm as useForm, useFormContext };
127
+ export { type Control, Controller, type CreateFormArg, type CreateFormReturn, type FieldError, type FieldErrors, FormProvider, type FormValues, type Rules, type SubmitErrorHandler, type SubmitHandler, type UseControllerArg, type UseControllerReturn, createForm, get, set, useController, useFormContext };
package/dist/main.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { createContext, createSignal, createMemo, useContext } from 'solid-js';
2
+ import { createStore, reconcile, produce } from 'solid-js/store';
2
3
  import { memo, createComponent } from 'solid-js/web';
3
4
 
4
5
  // src/create_form.ts
@@ -24,6 +25,12 @@ var getFieldValue = (event) => {
24
25
 
25
26
  // src/logic/set_value.ts
26
27
  var setFieldValue = (field, value) => {
28
+ if (!field) {
29
+ return;
30
+ }
31
+ if (value === void 0) {
32
+ return;
33
+ }
27
34
  if (field instanceof HTMLSelectElement) {
28
35
  field.value = value;
29
36
  return;
@@ -106,25 +113,26 @@ var validate = (values, name, rules = {}) => {
106
113
  }
107
114
  };
108
115
  var createErrors = () => {
109
- const [errors, setErrors] = createSignal({});
116
+ const [errors, setErrors] = createStore({});
110
117
  const getError = (name) => {
111
- return get(errors(), name);
118
+ return errors[name];
112
119
  };
113
120
  const appendError = (name, error) => {
114
- setErrors((prev) => {
115
- const newState = { ...prev, [name]: error };
116
- return newState;
117
- });
121
+ setErrors(
122
+ produce((prevState) => {
123
+ prevState[name] = error;
124
+ })
125
+ );
118
126
  };
119
127
  const removeError = (name) => {
120
- setErrors((prev) => {
121
- const newState = { ...prev };
122
- delete newState[name];
123
- return newState;
124
- });
128
+ setErrors(
129
+ produce((prevState) => {
130
+ delete prevState[name];
131
+ })
132
+ );
125
133
  };
126
134
  const resetErrors = () => {
127
- setErrors({});
135
+ setErrors(reconcile({}));
128
136
  };
129
137
  return {
130
138
  errors,
@@ -213,7 +221,7 @@ var getResolverFields = (fields) => {
213
221
  return acc;
214
222
  }, {});
215
223
  };
216
- var createTouched = () => {
224
+ var createTouchedFields = () => {
217
225
  const [touchedFields, setTouchedFields] = createSignal({});
218
226
  const addTouched = (name) => {
219
227
  setTouchedFields((prev) => {
@@ -230,17 +238,48 @@ var createTouched = () => {
230
238
  };
231
239
  return { touchedFields, addTouched, resetTouched };
232
240
  };
241
+ var isSomeFieldDirty = (value) => {
242
+ return Object.values(value).some((value2) => {
243
+ if (typeof value2 === "object") {
244
+ return isSomeFieldDirty(value2);
245
+ }
246
+ return value2;
247
+ });
248
+ };
249
+ var createDirtyFields = (defaultValues) => {
250
+ const [dirtyFields, setDirtyFields] = createSignal({});
251
+ const isDirty = createMemo(() => {
252
+ return isSomeFieldDirty(dirtyFields());
253
+ });
254
+ const checkDirty = (name, value) => {
255
+ const defaultValue = get(defaultValues, name);
256
+ const isDirty2 = value !== defaultValue;
257
+ setDirtyFields((prev) => {
258
+ const newState = { ...prev };
259
+ set(newState, name, isDirty2);
260
+ return newState;
261
+ });
262
+ };
263
+ const resetDirty = (keepDirty) => {
264
+ if (keepDirty) {
265
+ return;
266
+ }
267
+ setDirtyFields({});
268
+ };
269
+ return { dirtyFields, isDirty, checkDirty, resetDirty };
270
+ };
233
271
 
234
272
  // src/create_form.ts
235
- var createForm = (arg = { defaultValues: {} }) => {
273
+ var createForm = (arg) => {
236
274
  const { defaultValues, mode = "onChange", resolver } = arg;
237
275
  const { fields, getField, setField } = createFields();
238
276
  const { rules, addRule, getRule } = createRules();
239
- const [values, setValues] = createSignal(defaultValues);
277
+ const [values, setValues] = createSignal(structuredClone(defaultValues));
240
278
  const { errors, appendError, removeError, resetErrors, getError } = createErrors();
241
- const { touchedFields, addTouched, resetTouched } = createTouched();
279
+ const { touchedFields, addTouched, resetTouched } = createTouchedFields();
280
+ const { dirtyFields, isDirty, checkDirty, resetDirty } = createDirtyFields(defaultValues);
242
281
  const isValid = createMemo(() => {
243
- return !Object.keys(errors()).length;
282
+ return !Object.keys(errors).length;
244
283
  });
245
284
  const setFieldError = (name, error) => {
246
285
  const field = getField(name);
@@ -256,7 +295,7 @@ var createForm = (arg = { defaultValues: {} }) => {
256
295
  if (!resolver) {
257
296
  return;
258
297
  }
259
- const result = await resolver(values(), null, {
298
+ const result = await resolver(values() || {}, null, {
260
299
  fields: getResolverFields(fields),
261
300
  shouldUseNativeValidation: false
262
301
  });
@@ -302,13 +341,15 @@ var createForm = (arg = { defaultValues: {} }) => {
302
341
  }
303
342
  };
304
343
  const onFieldChange = (event, name) => {
305
- const value = formatValue(getFieldValue(event), rules[name]);
344
+ const fieldValue = getFieldValue(event);
345
+ const value = formatValue(fieldValue, rules[name]);
306
346
  setValues((prev) => {
307
347
  const newState = { ...prev };
308
348
  set(newState, name, value);
309
349
  return newState;
310
350
  });
311
351
  validateField(name);
352
+ checkDirty(name, value);
312
353
  };
313
354
  const register = (name, options = {}) => {
314
355
  addRule(name, options);
@@ -336,9 +377,7 @@ var createForm = (arg = { defaultValues: {} }) => {
336
377
  return;
337
378
  }
338
379
  setField(name, element);
339
- if (element) {
340
- setFieldValue(element, get(values(), name));
341
- }
380
+ setFieldValue(element, get(values(), name));
342
381
  }
343
382
  };
344
383
  };
@@ -360,9 +399,7 @@ var createForm = (arg = { defaultValues: {} }) => {
360
399
  return newValues;
361
400
  });
362
401
  const field = getField(name);
363
- if (field) {
364
- setFieldValue(field, value);
365
- }
402
+ setFieldValue(field, value);
366
403
  };
367
404
  const handleSubmit = (onSubmit, onError) => {
368
405
  return async (event) => {
@@ -372,22 +409,18 @@ var createForm = (arg = { defaultValues: {} }) => {
372
409
  onSubmit(getValues());
373
410
  return;
374
411
  }
375
- onError?.(errors());
412
+ onError?.(errors);
376
413
  focusFirstError();
377
414
  };
378
415
  };
379
- const reset = (newDefaultValues = {}, options = {}) => {
380
- const newValues = {
381
- ...structuredClone(defaultValues),
382
- ...newDefaultValues
383
- };
416
+ const reset = (values2, options = {}) => {
417
+ const newValues = values2 ? values2 : structuredClone(defaultValues);
384
418
  setValues(() => newValues);
385
419
  resetErrors();
386
420
  resetTouched(options.keepTouched);
421
+ resetDirty(options.keepDirty);
387
422
  Object.entries(fields).forEach(([name, field]) => {
388
- if (field) {
389
- setFieldValue(field, get(newValues, name));
390
- }
423
+ setFieldValue(field, get(newValues, name));
391
424
  });
392
425
  };
393
426
  return {
@@ -395,15 +428,15 @@ var createForm = (arg = { defaultValues: {} }) => {
395
428
  formState: {
396
429
  errors,
397
430
  isValid,
398
- touchedFields
431
+ isDirty,
432
+ touchedFields,
433
+ dirtyFields
399
434
  },
400
435
  values,
401
436
  errors,
402
- isValid,
403
437
  register,
404
438
  getValues,
405
439
  setValue,
406
- onSubmit: handleSubmit,
407
440
  handleSubmit,
408
441
  reset
409
442
  };
@@ -425,12 +458,12 @@ var useController = (arg) => {
425
458
  return get(control.values(), arg.name);
426
459
  });
427
460
  const error = createMemo(() => {
428
- return control.errors()[arg.name];
461
+ return control.errors[arg.name];
429
462
  });
430
463
  return {
431
464
  field: {
432
- value,
433
- ...fieldProps
465
+ ...fieldProps,
466
+ value
434
467
  },
435
468
  fieldState: {
436
469
  error
@@ -452,4 +485,4 @@ var FormProvider = (props) => {
452
485
  });
453
486
  };
454
487
 
455
- export { Controller, FormProvider, createForm, get, set, useController, createForm as useForm, useFormContext };
488
+ export { Controller, FormProvider, createForm, get, set, useController, useFormContext };
package/dist/main.jsx CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/create_form.ts
2
- import { createMemo, createSignal as createSignal3 } from "solid-js";
2
+ import { createMemo as createMemo2, createSignal as createSignal3 } from "solid-js";
3
3
 
4
4
  // src/logic/get_value.ts
5
5
  var getFieldValue = (event) => {
@@ -22,6 +22,12 @@ var getFieldValue = (event) => {
22
22
 
23
23
  // src/logic/set_value.ts
24
24
  var setFieldValue = (field, value) => {
25
+ if (!field) {
26
+ return;
27
+ }
28
+ if (value === void 0) {
29
+ return;
30
+ }
25
31
  if (field instanceof HTMLSelectElement) {
26
32
  field.value = value;
27
33
  return;
@@ -105,27 +111,28 @@ var validate = (values, name, rules = {}) => {
105
111
  };
106
112
 
107
113
  // src/logic/create_errors.ts
108
- import { createSignal } from "solid-js";
114
+ import { createStore, reconcile, produce } from "solid-js/store";
109
115
  var createErrors = () => {
110
- const [errors, setErrors] = createSignal({});
116
+ const [errors, setErrors] = createStore({});
111
117
  const getError = (name) => {
112
- return get(errors(), name);
118
+ return errors[name];
113
119
  };
114
120
  const appendError = (name, error) => {
115
- setErrors((prev) => {
116
- const newState = { ...prev, [name]: error };
117
- return newState;
118
- });
121
+ setErrors(
122
+ produce((prevState) => {
123
+ prevState[name] = error;
124
+ })
125
+ );
119
126
  };
120
127
  const removeError = (name) => {
121
- setErrors((prev) => {
122
- const newState = { ...prev };
123
- delete newState[name];
124
- return newState;
125
- });
128
+ setErrors(
129
+ produce((prevState) => {
130
+ delete prevState[name];
131
+ })
132
+ );
126
133
  };
127
134
  const resetErrors = () => {
128
- setErrors({});
135
+ setErrors(reconcile({}));
129
136
  };
130
137
  return {
131
138
  errors,
@@ -215,10 +222,10 @@ var getResolverFields = (fields) => {
215
222
  }, {});
216
223
  };
217
224
 
218
- // src/logic/create_touched.ts
219
- import { createSignal as createSignal2 } from "solid-js";
220
- var createTouched = () => {
221
- const [touchedFields, setTouchedFields] = createSignal2({});
225
+ // src/logic/create_touched_fields.ts
226
+ import { createSignal } from "solid-js";
227
+ var createTouchedFields = () => {
228
+ const [touchedFields, setTouchedFields] = createSignal({});
222
229
  const addTouched = (name) => {
223
230
  setTouchedFields((prev) => {
224
231
  const newState = { ...prev };
@@ -235,16 +242,50 @@ var createTouched = () => {
235
242
  return { touchedFields, addTouched, resetTouched };
236
243
  };
237
244
 
245
+ // src/logic/create_dirty_fields.ts
246
+ import { createMemo, createSignal as createSignal2 } from "solid-js";
247
+ var isSomeFieldDirty = (value) => {
248
+ return Object.values(value).some((value2) => {
249
+ if (typeof value2 === "object") {
250
+ return isSomeFieldDirty(value2);
251
+ }
252
+ return value2;
253
+ });
254
+ };
255
+ var createDirtyFields = (defaultValues) => {
256
+ const [dirtyFields, setDirtyFields] = createSignal2({});
257
+ const isDirty = createMemo(() => {
258
+ return isSomeFieldDirty(dirtyFields());
259
+ });
260
+ const checkDirty = (name, value) => {
261
+ const defaultValue = get(defaultValues, name);
262
+ const isDirty2 = value !== defaultValue;
263
+ setDirtyFields((prev) => {
264
+ const newState = { ...prev };
265
+ set(newState, name, isDirty2);
266
+ return newState;
267
+ });
268
+ };
269
+ const resetDirty = (keepDirty) => {
270
+ if (keepDirty) {
271
+ return;
272
+ }
273
+ setDirtyFields({});
274
+ };
275
+ return { dirtyFields, isDirty, checkDirty, resetDirty };
276
+ };
277
+
238
278
  // src/create_form.ts
239
- var createForm = (arg = { defaultValues: {} }) => {
279
+ var createForm = (arg) => {
240
280
  const { defaultValues, mode = "onChange", resolver } = arg;
241
281
  const { fields, getField, setField } = createFields();
242
282
  const { rules, addRule, getRule } = createRules();
243
- const [values, setValues] = createSignal3(defaultValues);
283
+ const [values, setValues] = createSignal3(structuredClone(defaultValues));
244
284
  const { errors, appendError, removeError, resetErrors, getError } = createErrors();
245
- const { touchedFields, addTouched, resetTouched } = createTouched();
246
- const isValid = createMemo(() => {
247
- return !Object.keys(errors()).length;
285
+ const { touchedFields, addTouched, resetTouched } = createTouchedFields();
286
+ const { dirtyFields, isDirty, checkDirty, resetDirty } = createDirtyFields(defaultValues);
287
+ const isValid = createMemo2(() => {
288
+ return !Object.keys(errors).length;
248
289
  });
249
290
  const setFieldError = (name, error) => {
250
291
  const field = getField(name);
@@ -260,7 +301,7 @@ var createForm = (arg = { defaultValues: {} }) => {
260
301
  if (!resolver) {
261
302
  return;
262
303
  }
263
- const result = await resolver(values(), null, {
304
+ const result = await resolver(values() || {}, null, {
264
305
  fields: getResolverFields(fields),
265
306
  shouldUseNativeValidation: false
266
307
  });
@@ -306,13 +347,15 @@ var createForm = (arg = { defaultValues: {} }) => {
306
347
  }
307
348
  };
308
349
  const onFieldChange = (event, name) => {
309
- const value = formatValue(getFieldValue(event), rules[name]);
350
+ const fieldValue = getFieldValue(event);
351
+ const value = formatValue(fieldValue, rules[name]);
310
352
  setValues((prev) => {
311
353
  const newState = { ...prev };
312
354
  set(newState, name, value);
313
355
  return newState;
314
356
  });
315
357
  validateField(name);
358
+ checkDirty(name, value);
316
359
  };
317
360
  const register = (name, options = {}) => {
318
361
  addRule(name, options);
@@ -340,9 +383,7 @@ var createForm = (arg = { defaultValues: {} }) => {
340
383
  return;
341
384
  }
342
385
  setField(name, element);
343
- if (element) {
344
- setFieldValue(element, get(values(), name));
345
- }
386
+ setFieldValue(element, get(values(), name));
346
387
  }
347
388
  };
348
389
  };
@@ -364,9 +405,7 @@ var createForm = (arg = { defaultValues: {} }) => {
364
405
  return newValues;
365
406
  });
366
407
  const field = getField(name);
367
- if (field) {
368
- setFieldValue(field, value);
369
- }
408
+ setFieldValue(field, value);
370
409
  };
371
410
  const handleSubmit = (onSubmit, onError) => {
372
411
  return async (event) => {
@@ -376,22 +415,18 @@ var createForm = (arg = { defaultValues: {} }) => {
376
415
  onSubmit(getValues());
377
416
  return;
378
417
  }
379
- onError?.(errors());
418
+ onError?.(errors);
380
419
  focusFirstError();
381
420
  };
382
421
  };
383
- const reset = (newDefaultValues = {}, options = {}) => {
384
- const newValues = {
385
- ...structuredClone(defaultValues),
386
- ...newDefaultValues
387
- };
422
+ const reset = (values2, options = {}) => {
423
+ const newValues = values2 ? values2 : structuredClone(defaultValues);
388
424
  setValues(() => newValues);
389
425
  resetErrors();
390
426
  resetTouched(options.keepTouched);
427
+ resetDirty(options.keepDirty);
391
428
  Object.entries(fields).forEach(([name, field]) => {
392
- if (field) {
393
- setFieldValue(field, get(newValues, name));
394
- }
429
+ setFieldValue(field, get(newValues, name));
395
430
  });
396
431
  };
397
432
  return {
@@ -399,22 +434,22 @@ var createForm = (arg = { defaultValues: {} }) => {
399
434
  formState: {
400
435
  errors,
401
436
  isValid,
402
- touchedFields
437
+ isDirty,
438
+ touchedFields,
439
+ dirtyFields
403
440
  },
404
441
  values,
405
442
  errors,
406
- isValid,
407
443
  register,
408
444
  getValues,
409
445
  setValue,
410
- onSubmit: handleSubmit,
411
446
  handleSubmit,
412
447
  reset
413
448
  };
414
449
  };
415
450
 
416
451
  // src/use_controller.ts
417
- import { createMemo as createMemo2 } from "solid-js";
452
+ import { createMemo as createMemo3 } from "solid-js";
418
453
 
419
454
  // src/use_form_context.ts
420
455
  import { useContext } from "solid-js";
@@ -434,16 +469,16 @@ var useController = (arg) => {
434
469
  const form = useFormContext();
435
470
  const control = form?.control || arg.control;
436
471
  const fieldProps = control.register(arg.name, arg.rules);
437
- const value = createMemo2(() => {
472
+ const value = createMemo3(() => {
438
473
  return get(control.values(), arg.name);
439
474
  });
440
- const error = createMemo2(() => {
441
- return control.errors()[arg.name];
475
+ const error = createMemo3(() => {
476
+ return control.errors[arg.name];
442
477
  });
443
478
  return {
444
479
  field: {
445
- value,
446
- ...fieldProps
480
+ ...fieldProps,
481
+ value
447
482
  },
448
483
  fieldState: {
449
484
  error
@@ -468,6 +503,5 @@ export {
468
503
  get,
469
504
  set,
470
505
  useController,
471
- createForm as useForm,
472
506
  useFormContext
473
507
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "solid-hook-form",
3
- "version": "1.10.0",
3
+ "version": "2.0.1",
4
4
  "type": "module",
5
5
  "main": "./dist/main.js",
6
6
  "module": "./dist/main.js",
@@ -26,10 +26,7 @@
26
26
  "typescript": "~5.7.2"
27
27
  },
28
28
  "scripts": {
29
- "preinstall": "cd ./tests && npm ci",
30
- "build": "tsc && tsup",
31
- "test": "cd ./tests && npm run test",
32
- "test:ui": "cd ./tests && npm run test:ui"
29
+ "build": "tsc && tsup"
33
30
  },
34
31
  "author": "thorn_pear",
35
32
  "license": "ISC",