formstand 0.22.1 → 0.23.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.cjs CHANGED
@@ -210,11 +210,11 @@ var hasOwnSlot = (obj, path) => parsePath(path).reduce(
210
210
  },
211
211
  { ok: true, current: obj }
212
212
  ).ok;
213
- var writeIntoArray = (arr, index, rest, value) => {
213
+ var writeIntoArray = (arr, index, rest, value, kindAt, prefix) => {
214
214
  const length = Math.max(arr.length, index + 1);
215
215
  return Array.from(
216
216
  { length },
217
- (_, i) => i === index ? writeSegments(arr[i], rest, value) : arr[i]
217
+ (_, i) => i === index ? writeSegments(arr[i], rest, value, kindAt, [...prefix, index]) : arr[i]
218
218
  );
219
219
  };
220
220
  var refusesHugeIndex = (index) => {
@@ -226,7 +226,7 @@ var refusesHugeIndex = (index) => {
226
226
  }
227
227
  return false;
228
228
  };
229
- var writeSegments = (obj, segments, value) => {
229
+ var writeSegments = (obj, segments, value, kindAt, prefix) => {
230
230
  const [head, ...rest] = segments;
231
231
  if (head === void 0) return value;
232
232
  if (Array.isArray(obj)) {
@@ -237,7 +237,7 @@ var writeSegments = (obj, segments, value) => {
237
237
  return obj;
238
238
  }
239
239
  if (refusesHugeIndex(head)) return obj;
240
- return writeIntoArray(obj, head, rest, value);
240
+ return writeIntoArray(obj, head, rest, value, kindAt, prefix);
241
241
  }
242
242
  if (isPlainRecord(obj)) {
243
243
  if (!isPlainObject(obj)) {
@@ -246,16 +246,27 @@ var writeSegments = (obj, segments, value) => {
246
246
  );
247
247
  return obj;
248
248
  }
249
- const key = String(head);
250
- return { ...obj, [key]: writeSegments(obj[key], rest, value) };
249
+ const key2 = String(head);
250
+ return {
251
+ ...obj,
252
+ [key2]: writeSegments(obj[key2], rest, value, kindAt, [...prefix, key2])
253
+ };
251
254
  }
252
- if (typeof head === "number") {
255
+ const kind = kindAt?.(prefix.map(String).join(".")) ?? (typeof head === "number" ? "array" : "record");
256
+ if (kind === "array") {
257
+ if (typeof head !== "number") {
258
+ console.warn(
259
+ `[formstand] cannot write string key "${head}" into an array; value left unchanged.`
260
+ );
261
+ return obj;
262
+ }
253
263
  if (refusesHugeIndex(head)) return obj;
254
- return writeIntoArray([], head, rest, value);
264
+ return writeIntoArray([], head, rest, value, kindAt, prefix);
255
265
  }
256
- return { [head]: writeSegments(void 0, rest, value) };
266
+ const key = String(head);
267
+ return { [key]: writeSegments(void 0, rest, value, kindAt, [...prefix, key]) };
257
268
  };
258
- var setAtPath = (obj, path, value) => writeSegments(obj, parsePath(path), value);
269
+ var setAtPath = (obj, path, value, containerKindAt2) => writeSegments(obj, parsePath(path), value, containerKindAt2, []);
259
270
  var issuePathToFormPath = (path) => path.filter((segment) => typeof segment !== "symbol").join(".");
260
271
  var expandIssue = (issue, prefix) => {
261
272
  const at = [...prefix, ...issue.path];
@@ -356,6 +367,9 @@ var fieldSchemaForIntrospection = (schema, path) => {
356
367
  if (s instanceof zod.z.ZodTuple && typeof segment === "number") {
357
368
  return s.def.items[segment] ?? null;
358
369
  }
370
+ if (s instanceof zod.z.ZodRecord) {
371
+ return s.valueType;
372
+ }
359
373
  if (s instanceof zod.z.ZodDiscriminatedUnion || s instanceof zod.z.ZodUnion) {
360
374
  const options = s.def.options;
361
375
  return options.reduce(
@@ -370,6 +384,22 @@ var fieldSchemaForIntrospection = (schema, path) => {
370
384
  schema
371
385
  );
372
386
  };
387
+ var containerKindAt = (schema) => (prefix) => {
388
+ const sub = fieldSchemaForIntrospection(schema, prefix);
389
+ if (sub === null) return null;
390
+ const unwrapAll = (s) => {
391
+ const inner2 = unwrapForWalk(s);
392
+ return inner2 === null ? s : unwrapAll(inner2);
393
+ };
394
+ const inner = unwrapAll(sub);
395
+ if (inner instanceof zod.z.ZodArray || inner instanceof zod.z.ZodTuple) {
396
+ return "array";
397
+ }
398
+ if (inner instanceof zod.z.ZodObject || inner instanceof zod.z.ZodRecord) {
399
+ return "record";
400
+ }
401
+ return null;
402
+ };
373
403
  var fieldSchemaAtPath = (schema, path) => parsePath(path).reduce((current, segment) => {
374
404
  if (current === null || hasChecks(current)) return null;
375
405
  if (current instanceof zod.z.ZodObject && typeof segment === "string") {
@@ -473,6 +503,7 @@ var releaseChangedSlices = (server, prevValues, nextValues) => {
473
503
  };
474
504
  var createForm = (schema, options) => {
475
505
  const initialMode = options.mode ?? "onBlur";
506
+ const kindAt = containerKindAt(schema);
476
507
  const initialReValidateMode = options.reValidateMode ?? "onChange";
477
508
  const initial = {
478
509
  values: options.initialValues,
@@ -830,7 +861,7 @@ var createForm = (schema, options) => {
830
861
  }
831
862
  const arr = Array.isArray(current) ? current : [];
832
863
  const next = nextArray(arr);
833
- const written = setAtPath(store.getState().values, path, next);
864
+ const written = setAtPath(store.getState().values, path, next, kindAt);
834
865
  clearArrayOpsUnder(store, path, true);
835
866
  if (Array.isArray(current) && getAtPath(written, path) === next) {
836
867
  recordArrayOp(store, path, {
@@ -992,7 +1023,7 @@ var createForm = (schema, options) => {
992
1023
  clearArrayOpsUnder(store, path);
993
1024
  store.setState((state) => ({
994
1025
  ...state,
995
- values: setAtPath(state.values, path, value),
1026
+ values: setAtPath(state.values, path, value, kindAt),
996
1027
  ...errorChannels(state, {
997
1028
  serverErrors: releaseSpine(state.serverErrors, path)
998
1029
  })
@@ -1139,7 +1170,7 @@ var createForm = (schema, options) => {
1139
1170
  }
1140
1171
  store.setState((state) => ({
1141
1172
  ...state,
1142
- values: initialSlot.exists ? setAtPath(state.values, path, initialSlot.value) : state.values,
1173
+ values: initialSlot.exists ? setAtPath(state.values, path, initialSlot.value, kindAt) : state.values,
1143
1174
  // A runtime "" path scopes the whole form (the imperative surface is
1144
1175
  // string-typed even though FieldPath excludes ""). Server entries
1145
1176
  // follow the setValue release contract (releaseSpine).
@@ -2023,7 +2054,10 @@ function useFieldArray(form, pathArg) {
2023
2054
  items: Array.isArray(value) ? value : EMPTY_ITEMS,
2024
2055
  // Own-key read, like useField: an array named "constructor" must
2025
2056
  // not read Object as its error list.
2026
- error: entryAt(state.errors, path)
2057
+ error: entryAt(state.errors, path),
2058
+ touched: flagAt(state.touched, path),
2059
+ dirty: isFieldDirty(value, getAtPath(state.initialValues, path)),
2060
+ isValidating: flagAt(state.isValidating, path)
2027
2061
  };
2028
2062
  })
2029
2063
  );
@@ -2092,6 +2126,52 @@ function useFieldArray(form, pathArg) {
2092
2126
  [form, path]
2093
2127
  );
2094
2128
  const clearError = react.useCallback(() => form.clearErrors?.(path), [form, path]);
2129
+ const setTouched = react.useCallback(
2130
+ (touched) => form.setTouched?.(path, touched),
2131
+ [form, path]
2132
+ );
2133
+ const onBlur = react.useCallback(() => {
2134
+ form.setTouched?.(path, true);
2135
+ const state = form.store.getState();
2136
+ if (shouldValidateOn(
2137
+ "blur",
2138
+ state.mode,
2139
+ state.reValidateMode,
2140
+ state.submitCount > 0,
2141
+ true
2142
+ )) {
2143
+ form.validateField?.(path);
2144
+ }
2145
+ }, [form, path]);
2146
+ const validate = react.useCallback(
2147
+ () => form.validateField?.(path) ?? { kind: "valid" },
2148
+ [form, path]
2149
+ );
2150
+ const replace = react.useCallback(
2151
+ (next) => {
2152
+ form.setValue?.(path, next);
2153
+ revalidate();
2154
+ },
2155
+ [form, path, revalidate]
2156
+ );
2157
+ const update = react.useCallback(
2158
+ (index, item) => {
2159
+ form.setValue?.(`${path}.${index}`, item);
2160
+ revalidate();
2161
+ },
2162
+ [form, path, revalidate]
2163
+ );
2164
+ const prepend = react.useCallback(
2165
+ (item) => {
2166
+ form.arrayInsert(path, 0, item);
2167
+ revalidate();
2168
+ },
2169
+ [form, path, revalidate]
2170
+ );
2171
+ const clear = react.useCallback(() => {
2172
+ form.setValue?.(path, []);
2173
+ revalidate();
2174
+ }, [form, path, revalidate]);
2095
2175
  return react.useMemo(
2096
2176
  () => ({
2097
2177
  path,
@@ -2102,13 +2182,45 @@ function useFieldArray(form, pathArg) {
2102
2182
  firstError: error?.[0],
2103
2183
  setError,
2104
2184
  clearError,
2185
+ touched: slice.touched,
2186
+ dirty: slice.dirty,
2187
+ isValidating: slice.isValidating,
2188
+ setTouched,
2189
+ onBlur,
2190
+ validate,
2105
2191
  push,
2106
2192
  remove,
2107
2193
  insert,
2108
2194
  move,
2109
- swap
2195
+ swap,
2196
+ replace,
2197
+ update,
2198
+ prepend,
2199
+ clear
2110
2200
  }),
2111
- [path, fields, items, error, setError, clearError, push, remove, insert, move, swap]
2201
+ [
2202
+ path,
2203
+ fields,
2204
+ items,
2205
+ error,
2206
+ setError,
2207
+ clearError,
2208
+ slice.touched,
2209
+ slice.dirty,
2210
+ slice.isValidating,
2211
+ setTouched,
2212
+ onBlur,
2213
+ validate,
2214
+ push,
2215
+ remove,
2216
+ insert,
2217
+ move,
2218
+ swap,
2219
+ replace,
2220
+ update,
2221
+ prepend,
2222
+ clear
2223
+ ]
2112
2224
  );
2113
2225
  }
2114
2226
  var useFormError = (form) => react$1.useStore(