@formisch/solid 0.9.6 → 0.11.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/index.jsx CHANGED
@@ -111,29 +111,40 @@ function copyItemState(fromInternalFieldStore, toInternalFieldStore) {
111
111
  });
112
112
  });
113
113
  }
114
- function resetItemState(internalFieldStore, initialInput) {
114
+ function resetItemState(internalFieldStore, input, keepStart = false) {
115
115
  batch(() => {
116
- internalFieldStore.elements = [];
116
+ const elements = [];
117
+ if (internalFieldStore.elements === internalFieldStore.initialElements) internalFieldStore.initialElements = elements;
118
+ internalFieldStore.elements = elements;
117
119
  internalFieldStore.errors.value = null;
118
120
  internalFieldStore.isTouched.value = false;
119
121
  internalFieldStore.isDirty.value = false;
120
122
  if (internalFieldStore.kind === "array" || internalFieldStore.kind === "object") {
121
- const objectInput = initialInput == null ? initialInput : true;
122
- internalFieldStore.startInput.value = objectInput;
123
+ const objectInput = input == null ? input : true;
124
+ if (!keepStart) internalFieldStore.startInput.value = objectInput;
123
125
  internalFieldStore.input.value = objectInput;
124
- if (internalFieldStore.kind === "array") if (initialInput) {
125
- const newItems = initialInput.map(createId);
126
- internalFieldStore.startItems.value = newItems;
126
+ if (internalFieldStore.kind === "array") if (input) {
127
+ const length = internalFieldStore.schema.type === "array" ? input.length : internalFieldStore.children.length;
128
+ const newItems = Array.from({ length }, createId);
129
+ if (!keepStart) internalFieldStore.startItems.value = newItems;
127
130
  internalFieldStore.items.value = newItems;
128
- for (let index = 0; index < initialInput.length; index++) if (internalFieldStore.children[index]) resetItemState(internalFieldStore.children[index], initialInput[index]);
131
+ let path;
132
+ for (let index = 0; index < length; index++) if (internalFieldStore.children[index]) resetItemState(internalFieldStore.children[index], input[index], keepStart);
133
+ else {
134
+ path ??= JSON.parse(internalFieldStore.name);
135
+ internalFieldStore.children[index] = {};
136
+ path.push(index);
137
+ initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, input[index], path);
138
+ path.pop();
139
+ }
129
140
  } else {
130
- internalFieldStore.startItems.value = [];
141
+ if (!keepStart) internalFieldStore.startItems.value = [];
131
142
  internalFieldStore.items.value = [];
132
143
  }
133
- else for (const key in internalFieldStore.children) resetItemState(internalFieldStore.children[key], initialInput?.[key]);
144
+ else for (const key in internalFieldStore.children) resetItemState(internalFieldStore.children[key], input?.[key], keepStart);
134
145
  } else {
135
- internalFieldStore.startInput.value = initialInput;
136
- internalFieldStore.input.value = initialInput;
146
+ if (!keepStart) internalFieldStore.startInput.value = input;
147
+ internalFieldStore.input.value = input;
137
148
  }
138
149
  });
139
150
  }
@@ -190,6 +201,50 @@ function swapItemState(firstInternalFieldStore, secondInternalFieldStore) {
190
201
  });
191
202
  });
192
203
  }
204
+ function focusFieldElement(internalFieldStore) {
205
+ for (const element of internalFieldStore.elements) {
206
+ element.focus();
207
+ if (element.getRootNode().activeElement === element) return true;
208
+ }
209
+ return false;
210
+ }
211
+ // @__NO_SIDE_EFFECTS__
212
+ function getFieldBool(internalFieldStore, type) {
213
+ if (internalFieldStore[type].value) return true;
214
+ if (internalFieldStore.kind === "array") {
215
+ for (let index = 0; index < internalFieldStore.items.value.length; index++) if (/* @__PURE__ */ getFieldBool(internalFieldStore.children[index], type)) return true;
216
+ return false;
217
+ }
218
+ if (internalFieldStore.kind == "object") {
219
+ for (const key in internalFieldStore.children) if (/* @__PURE__ */ getFieldBool(internalFieldStore.children[key], type)) return true;
220
+ return false;
221
+ }
222
+ return false;
223
+ }
224
+ // @__NO_SIDE_EFFECTS__
225
+ function getDirtyFieldInput(internalFieldStore, dirtyOnly = true) {
226
+ if (dirtyOnly && !/* @__PURE__ */ getFieldBool(internalFieldStore, "isDirty")) return;
227
+ if (internalFieldStore.kind === "array") {
228
+ if (internalFieldStore.input.value) {
229
+ const value = [];
230
+ for (let index = 0; index < internalFieldStore.items.value.length; index++) value[index] = /* @__PURE__ */ getDirtyFieldInput(internalFieldStore.children[index], false);
231
+ return value;
232
+ }
233
+ return internalFieldStore.input.value;
234
+ }
235
+ if (internalFieldStore.kind === "object") {
236
+ if (internalFieldStore.input.value) {
237
+ const value = {};
238
+ for (const key in internalFieldStore.children) {
239
+ const child = internalFieldStore.children[key];
240
+ if (!dirtyOnly || /* @__PURE__ */ getFieldBool(child, "isDirty")) value[key] = /* @__PURE__ */ getDirtyFieldInput(child, dirtyOnly);
241
+ }
242
+ return value;
243
+ }
244
+ return internalFieldStore.input.value;
245
+ }
246
+ return internalFieldStore.input.value;
247
+ }
193
248
  // @__NO_SIDE_EFFECTS__
194
249
  function getFieldInput(internalFieldStore) {
195
250
  if (internalFieldStore.kind === "array") {
@@ -229,19 +284,6 @@ function getElementInput(element, internalFieldStore) {
229
284
  return element.value;
230
285
  }
231
286
  // @__NO_SIDE_EFFECTS__
232
- function getFieldBool(internalFieldStore, type) {
233
- if (internalFieldStore[type].value) return true;
234
- if (internalFieldStore.kind === "array") {
235
- for (let index = 0; index < internalFieldStore.items.value.length; index++) if (/* @__PURE__ */ getFieldBool(internalFieldStore.children[index], type)) return true;
236
- return false;
237
- }
238
- if (internalFieldStore.kind == "object") {
239
- for (const key in internalFieldStore.children) if (/* @__PURE__ */ getFieldBool(internalFieldStore.children[key], type)) return true;
240
- return false;
241
- }
242
- return false;
243
- }
244
- // @__NO_SIDE_EFFECTS__
245
287
  function getFieldStore(internalFormStore, path) {
246
288
  let internalFieldStore = internalFormStore;
247
289
  for (const key of path) internalFieldStore = internalFieldStore.children[key];
@@ -249,11 +291,9 @@ function getFieldStore(internalFormStore, path) {
249
291
  }
250
292
  function setFieldBool(internalFieldStore, type, bool) {
251
293
  batch(() => {
252
- if (internalFieldStore.kind === "array") {
253
- internalFieldStore[type].value = bool;
254
- for (let index = 0; index < untrack(() => internalFieldStore.items.value).length; index++) setFieldBool(internalFieldStore.children[index], type, bool);
255
- } else if (internalFieldStore.kind == "object") for (const key in internalFieldStore.children) setFieldBool(internalFieldStore.children[key], type, bool);
256
- else internalFieldStore[type].value = bool;
294
+ internalFieldStore[type].value = bool;
295
+ if (internalFieldStore.kind === "array") for (let index = 0; index < untrack(() => internalFieldStore.items.value).length; index++) setFieldBool(internalFieldStore.children[index], type, bool);
296
+ else if (internalFieldStore.kind === "object") for (const key in internalFieldStore.children) setFieldBool(internalFieldStore.children[key], type, bool);
257
297
  });
258
298
  }
259
299
  function setNestedInput(internalFieldStore, input) {
@@ -261,20 +301,20 @@ function setNestedInput(internalFieldStore, input) {
261
301
  if (internalFieldStore.kind === "array") {
262
302
  const arrayInput = input ?? [];
263
303
  const items = internalFieldStore.items.value;
264
- if (arrayInput.length < items.length) internalFieldStore.items.value = items.slice(0, arrayInput.length);
265
- else if (arrayInput.length > items.length) {
266
- if (arrayInput.length > internalFieldStore.children.length) {
267
- const path = JSON.parse(internalFieldStore.name);
268
- for (let index = internalFieldStore.children.length; index < arrayInput.length; index++) {
269
- internalFieldStore.children[index] = {};
270
- path.push(index);
271
- initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, arrayInput[index], path);
272
- path.pop();
273
- }
304
+ const length = internalFieldStore.schema.type === "array" ? arrayInput.length : internalFieldStore.children.length;
305
+ if (length < items.length) internalFieldStore.items.value = items.slice(0, length);
306
+ else if (length > items.length) {
307
+ const path = JSON.parse(internalFieldStore.name);
308
+ for (let index = items.length; index < length; index++) if (internalFieldStore.children[index]) resetItemState(internalFieldStore.children[index], arrayInput[index], true);
309
+ else {
310
+ internalFieldStore.children[index] = {};
311
+ path.push(index);
312
+ initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, arrayInput[index], path);
313
+ path.pop();
274
314
  }
275
- internalFieldStore.items.value = [...items, ...arrayInput.slice(items.length).map(createId)];
315
+ internalFieldStore.items.value = [...items, ...Array.from({ length: length - items.length }, createId)];
276
316
  }
277
- for (let index = 0; index < arrayInput.length; index++) setNestedInput(internalFieldStore.children[index], arrayInput[index]);
317
+ for (let index = 0; index < length; index++) setNestedInput(internalFieldStore.children[index], arrayInput[index]);
278
318
  internalFieldStore.input.value = input == null ? input : true;
279
319
  internalFieldStore.isDirty.value = internalFieldStore.startInput.value !== internalFieldStore.input.value || internalFieldStore.startItems.value.length !== internalFieldStore.items.value.length;
280
320
  } else if (internalFieldStore.kind === "object") {
@@ -302,21 +342,22 @@ function setFieldInput(internalFormStore, path, input) {
302
342
  function setInitialFieldInput(internalFieldStore, initialInput) {
303
343
  batch(() => {
304
344
  if (internalFieldStore.kind === "array") {
305
- internalFieldStore.input.value = initialInput == null ? initialInput : true;
345
+ internalFieldStore.initialInput.value = initialInput == null ? initialInput : true;
306
346
  const initialArrayInput = initialInput ?? [];
307
- if (initialArrayInput.length > internalFieldStore.children.length) {
347
+ const length = internalFieldStore.schema.type === "array" ? initialArrayInput.length : internalFieldStore.children.length;
348
+ if (length > internalFieldStore.children.length) {
308
349
  const path = JSON.parse(internalFieldStore.name);
309
- for (let index = internalFieldStore.children.length; index < initialArrayInput.length; index++) {
350
+ for (let index = internalFieldStore.children.length; index < length; index++) {
310
351
  internalFieldStore.children[index] = {};
311
352
  path.push(index);
312
353
  initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, initialArrayInput[index], path);
313
354
  path.pop();
314
355
  }
315
356
  }
316
- internalFieldStore.initialItems.value = initialArrayInput.map(createId);
357
+ internalFieldStore.initialItems.value = Array.from({ length }, createId);
317
358
  for (let index = 0; index < internalFieldStore.children.length; index++) setInitialFieldInput(internalFieldStore.children[index], initialArrayInput[index]);
318
359
  } else if (internalFieldStore.kind === "object") {
319
- internalFieldStore.input.value = initialInput == null ? initialInput : true;
360
+ internalFieldStore.initialInput.value = initialInput == null ? initialInput : true;
320
361
  for (const key in internalFieldStore.children) setInitialFieldInput(internalFieldStore.children[key], initialInput?.[key]);
321
362
  } else internalFieldStore.initialInput.value = initialInput;
322
363
  });
@@ -341,44 +382,49 @@ function createFormStore(config, parse) {
341
382
  async function validateFormInput(internalFormStore, config) {
342
383
  internalFormStore.validators++;
343
384
  internalFormStore.isValidating.value = true;
344
- const result = await internalFormStore.parse(untrack(() => /* @__PURE__ */ getFieldInput(internalFormStore)));
345
- let rootErrors;
346
- let nestedErrors;
347
- if (result.issues) {
348
- nestedErrors = {};
349
- for (const issue of result.issues) if (issue.path) {
350
- const path = [];
351
- for (const pathItem of issue.path) {
352
- const key = pathItem.key;
353
- const keyType = typeof key;
354
- const itemType = pathItem.type;
355
- if (keyType !== "string" && keyType !== "number" || itemType === "map" || itemType === "set") break;
356
- path.push(key);
357
- }
358
- const name = JSON.stringify(path);
359
- const fieldErrors = nestedErrors[name];
360
- if (fieldErrors) fieldErrors.push(issue.message);
361
- else nestedErrors[name] = [issue.message];
362
- } else if (rootErrors) rootErrors.push(issue.message);
363
- else rootErrors = [issue.message];
364
- }
365
- let shouldFocus = config?.shouldFocus ?? false;
366
- batch(() => {
367
- walkFieldStore(internalFormStore, (internalFieldStore) => {
368
- if (internalFieldStore.name === "[]") internalFieldStore.errors.value = rootErrors ?? null;
369
- else {
370
- const fieldErrors = nestedErrors?.[internalFieldStore.name] ?? null;
371
- internalFieldStore.errors.value = fieldErrors;
372
- if (shouldFocus && fieldErrors) {
373
- internalFieldStore.elements[0]?.focus();
374
- shouldFocus = false;
385
+ try {
386
+ const result = await internalFormStore.parse(untrack(() => /* @__PURE__ */ getFieldInput(internalFormStore)));
387
+ let rootErrors;
388
+ let nestedErrors;
389
+ if (result.issues) {
390
+ nestedErrors = {};
391
+ for (const issue of result.issues) if (issue.path) {
392
+ const path = [];
393
+ for (const pathItem of issue.path) {
394
+ const key = pathItem.key;
395
+ const keyType = typeof key;
396
+ const itemType = pathItem.type;
397
+ if (keyType !== "string" && keyType !== "number" || itemType === "map" || itemType === "set") break;
398
+ path.push(key);
375
399
  }
376
- }
400
+ const name = JSON.stringify(path);
401
+ const fieldErrors = nestedErrors[name];
402
+ if (fieldErrors) fieldErrors.push(issue.message);
403
+ else nestedErrors[name] = [issue.message];
404
+ } else if (rootErrors) rootErrors.push(issue.message);
405
+ else rootErrors = [issue.message];
406
+ }
407
+ let shouldFocus = config?.shouldFocus ?? false;
408
+ batch(() => {
409
+ walkFieldStore(internalFormStore, (internalFieldStore) => {
410
+ if (internalFieldStore.name === "[]") internalFieldStore.errors.value = rootErrors ?? null;
411
+ else {
412
+ const fieldErrors = nestedErrors?.[internalFieldStore.name] ?? null;
413
+ internalFieldStore.errors.value = fieldErrors;
414
+ if (shouldFocus && fieldErrors && focusFieldElement(internalFieldStore)) shouldFocus = false;
415
+ }
416
+ });
417
+ internalFormStore.validators--;
418
+ internalFormStore.isValidating.value = internalFormStore.validators > 0;
377
419
  });
378
- internalFormStore.validators--;
379
- internalFormStore.isValidating.value = internalFormStore.validators > 0;
380
- });
381
- return result;
420
+ return result;
421
+ } catch (error) {
422
+ batch(() => {
423
+ internalFormStore.validators--;
424
+ internalFormStore.isValidating.value = internalFormStore.validators > 0;
425
+ });
426
+ throw error;
427
+ }
382
428
  }
383
429
  function validateIfRequired(internalFormStore, internalFieldStore, validationMode) {
384
430
  if (validationMode === (internalFormStore.validate === "initial" || (internalFormStore.validate === "submit" ? untrack(() => internalFormStore.isSubmitted.value) : untrack(() => /* @__PURE__ */ getFieldBool(internalFieldStore, "errors"))) ? internalFormStore.revalidate : internalFormStore.validate)) validateFormInput(internalFormStore);
@@ -387,7 +433,7 @@ var INTERNAL = "~internal";
387
433
 
388
434
  // ../../packages/methods/dist/index.solid.js
389
435
  function focus(form, config) {
390
- getFieldStore(form[INTERNAL], config.path).elements[0]?.focus();
436
+ focusFieldElement(getFieldStore(form[INTERNAL], config.path));
391
437
  }
392
438
  // @__NO_SIDE_EFFECTS__
393
439
  function getAllErrors(form) {
@@ -401,6 +447,17 @@ function getAllErrors(form) {
401
447
  return allErrors;
402
448
  }
403
449
  // @__NO_SIDE_EFFECTS__
450
+ function getDirtyInput(form, config) {
451
+ return getDirtyFieldInput(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL]);
452
+ }
453
+ // @__NO_SIDE_EFFECTS__
454
+ function getDirtyPaths(form, config) {
455
+ config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL];
456
+ const paths = [];
457
+ config?.path && [...config.path];
458
+ return paths;
459
+ }
460
+ // @__NO_SIDE_EFFECTS__
404
461
  function getErrors(form, config) {
405
462
  return (config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL]).errors.value;
406
463
  }
@@ -479,6 +536,24 @@ function move(form, config) {
479
536
  validateIfRequired(internalFormStore, internalArrayStore, "input");
480
537
  });
481
538
  }
539
+ // @__NO_SIDE_EFFECTS__
540
+ function pickDirty(form, config) {
541
+ if (!getFieldBool(form[INTERNAL], "isDirty")) return;
542
+ const result = /* @__PURE__ */ pickFieldValue(form[INTERNAL], config.from);
543
+ return Object.keys(result).length ? result : void 0;
544
+ }
545
+ // @__NO_SIDE_EFFECTS__
546
+ function pickFieldValue(internalFieldStore, value) {
547
+ if (internalFieldStore.kind === "object" && internalFieldStore.input.value && value && typeof value === "object" && !Array.isArray(value)) {
548
+ const result = {};
549
+ for (const key in internalFieldStore.children) {
550
+ const child = internalFieldStore.children[key];
551
+ if (getFieldBool(child, "isDirty") && key in value) result[key] = /* @__PURE__ */ pickFieldValue(child, value[key]);
552
+ }
553
+ return result;
554
+ }
555
+ return value;
556
+ }
482
557
  function remove(form, config) {
483
558
  const internalFormStore = form[INTERNAL];
484
559
  const internalArrayStore = getFieldStore(internalFormStore, config.path);
@@ -685,9 +760,13 @@ function useField(form, config) {
685
760
  const internalFieldStore = getInternalFieldStore();
686
761
  internalFieldStore.elements.push(element);
687
762
  onCleanup(() => {
688
- internalFieldStore.elements = internalFieldStore.elements.filter(
763
+ const elements = internalFieldStore.elements.filter(
689
764
  (el) => el !== element
690
765
  );
766
+ if (internalFieldStore.elements === internalFieldStore.initialElements) {
767
+ internalFieldStore.initialElements = elements;
768
+ }
769
+ internalFieldStore.elements = elements;
691
770
  });
692
771
  },
693
772
  onFocus() {
@@ -807,11 +886,14 @@ export {
807
886
  createForm,
808
887
  focus,
809
888
  getAllErrors,
889
+ getDirtyInput,
890
+ getDirtyPaths,
810
891
  getErrors,
811
892
  getInput,
812
893
  handleSubmit,
813
894
  insert,
814
895
  move,
896
+ pickDirty,
815
897
  remove,
816
898
  replace,
817
899
  reset,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@formisch/solid",
3
3
  "description": "The lightweight, schema-first, and fully type-safe form library for SolidJS",
4
- "version": "0.9.6",
4
+ "version": "0.11.0",
5
5
  "license": "MIT",
6
6
  "author": "Fabian Hiller",
7
7
  "homepage": "https://formisch.dev",
@@ -57,7 +57,8 @@
57
57
  "@formisch/methods": "workspace:*",
58
58
  "@solidjs/testing-library": "^0.8.10",
59
59
  "@testing-library/jest-dom": "^6.6.0",
60
- "@vitest/coverage-v8": "^3.2.4",
60
+ "@types/node": "24.0.13",
61
+ "@vitest/coverage-v8": "^4.1.7",
61
62
  "eslint": "^9.31.0",
62
63
  "eslint-plugin-solid": "^0.14.5",
63
64
  "jsdom": "^26.1.0",
@@ -68,14 +69,14 @@
68
69
  "tsup": "^8.5.0",
69
70
  "tsup-preset-solid": "^2.2.0",
70
71
  "typescript": "^5.8.3",
71
- "valibot": "^1.2.0",
72
+ "valibot": "^1.4.1",
72
73
  "vite-plugin-solid": "^2.11.6",
73
- "vitest": "3.2.4"
74
+ "vitest": "4.1.7"
74
75
  },
75
76
  "peerDependencies": {
76
77
  "solid-js": "^1.6.0",
77
78
  "typescript": ">=5",
78
- "valibot": "^1.0.0"
79
+ "valibot": "^1.4.1"
79
80
  },
80
81
  "peerDependenciesMeta": {
81
82
  "typescript": {