formstand 0.13.0 → 0.15.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/CHANGELOG.md +66 -0
- package/README.md +88 -386
- package/dist/chunk-N7WPXYCU.js +144 -0
- package/dist/chunk-N7WPXYCU.js.map +1 -0
- package/dist/createForm-C9hZffCQ.d.cts +190 -0
- package/dist/createForm-C9hZffCQ.d.ts +190 -0
- package/dist/devtools/index.cjs +313 -0
- package/dist/devtools/index.cjs.map +1 -0
- package/dist/devtools/index.d.cts +27 -0
- package/dist/devtools/index.d.ts +27 -0
- package/dist/devtools/index.js +276 -0
- package/dist/devtools/index.js.map +1 -0
- package/dist/index.cjs +44 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -188
- package/dist/index.d.ts +5 -188
- package/dist/index.js +47 -143
- package/dist/index.js.map +1 -1
- package/package.json +12 -2
package/dist/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { getAtPath, slotAtPath, setAtPath, isPlainObject, hasOwnSlot, valuesEqual, isFieldDirty, useFormSelector, useFormSelectorShallow, parsePath } from './chunk-N7WPXYCU.js';
|
|
2
|
+
export { getAtPath, parsePath, setAtPath, useFormSelector, useFormSelectorShallow } from './chunk-N7WPXYCU.js';
|
|
1
3
|
import { createStore } from 'zustand/vanilla';
|
|
2
4
|
import { devtools } from 'zustand/middleware';
|
|
3
5
|
import { z } from 'zod';
|
|
@@ -6,8 +8,6 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
|
6
8
|
import { useStore } from 'zustand/react';
|
|
7
9
|
import { useShallow } from 'zustand/react/shallow';
|
|
8
10
|
|
|
9
|
-
// src/core/createForm.ts
|
|
10
|
-
|
|
11
11
|
// src/core/array.ts
|
|
12
12
|
var removeAt = (i) => (n) => n === i ? null : n > i ? n - 1 : n;
|
|
13
13
|
var insertAt = (i) => (n) => n >= i ? n + 1 : n;
|
|
@@ -101,138 +101,6 @@ var clearArrayOpsUnder = (store, path, keepSelf = false) => {
|
|
|
101
101
|
);
|
|
102
102
|
related.forEach((p) => byPath.delete(p));
|
|
103
103
|
};
|
|
104
|
-
|
|
105
|
-
// src/core/equality.ts
|
|
106
|
-
var isPlainObject = (v) => {
|
|
107
|
-
if (typeof v !== "object" || v === null) return false;
|
|
108
|
-
const proto = Object.getPrototypeOf(v);
|
|
109
|
-
return proto === Object.prototype || proto === null;
|
|
110
|
-
};
|
|
111
|
-
var valuesEqual = (a, b) => {
|
|
112
|
-
if (Object.is(a, b)) return true;
|
|
113
|
-
if (a instanceof Date && b instanceof Date) {
|
|
114
|
-
return a.getTime() === b.getTime();
|
|
115
|
-
}
|
|
116
|
-
if (Array.isArray(a) && Array.isArray(b)) {
|
|
117
|
-
return a.length === b.length && a.every((v, i) => valuesEqual(v, b[i]));
|
|
118
|
-
}
|
|
119
|
-
if (isPlainObject(a) && isPlainObject(b)) {
|
|
120
|
-
const aKeys = Object.keys(a);
|
|
121
|
-
const bKeys = Object.keys(b);
|
|
122
|
-
return aKeys.length === bKeys.length && aKeys.every((k) => Object.hasOwn(b, k) && valuesEqual(a[k], b[k]));
|
|
123
|
-
}
|
|
124
|
-
return false;
|
|
125
|
-
};
|
|
126
|
-
var dirtyCache = /* @__PURE__ */ new WeakMap();
|
|
127
|
-
var MAX_VERDICTS = 4;
|
|
128
|
-
var isFieldDirty = (value, initialValue) => {
|
|
129
|
-
if (Object.is(value, initialValue)) return false;
|
|
130
|
-
if (typeof value !== "object" || value === null) {
|
|
131
|
-
return !valuesEqual(value, initialValue);
|
|
132
|
-
}
|
|
133
|
-
const cached = dirtyCache.get(value) ?? [];
|
|
134
|
-
const hit = cached.find((v) => Object.is(v.initial, initialValue));
|
|
135
|
-
if (hit !== void 0) return hit.dirty;
|
|
136
|
-
const dirty = !valuesEqual(value, initialValue);
|
|
137
|
-
dirtyCache.set(
|
|
138
|
-
value,
|
|
139
|
-
[{ initial: initialValue, dirty }, ...cached].slice(0, MAX_VERDICTS)
|
|
140
|
-
);
|
|
141
|
-
return dirty;
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
// src/core/path.ts
|
|
145
|
-
var MAX_ARRAY_INDEX = 1e5;
|
|
146
|
-
var isIndexSegment = (s) => {
|
|
147
|
-
const n = Number(s);
|
|
148
|
-
return Number.isInteger(n) && n >= 0 && String(n) === s;
|
|
149
|
-
};
|
|
150
|
-
var PARSE_CACHE_MAX = 4096;
|
|
151
|
-
var parseCache = /* @__PURE__ */ new Map();
|
|
152
|
-
var parsePath = (path) => {
|
|
153
|
-
const cached = parseCache.get(path);
|
|
154
|
-
if (cached !== void 0) return cached;
|
|
155
|
-
const segments = Object.freeze(
|
|
156
|
-
path === "" ? [] : path.split(".").map((s) => isIndexSegment(s) ? Number(s) : s)
|
|
157
|
-
);
|
|
158
|
-
if (parseCache.size >= PARSE_CACHE_MAX) parseCache.clear();
|
|
159
|
-
parseCache.set(path, segments);
|
|
160
|
-
return segments;
|
|
161
|
-
};
|
|
162
|
-
var isPlainRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
163
|
-
var readSegment = (obj, segment) => {
|
|
164
|
-
if (obj == null) return void 0;
|
|
165
|
-
if (Array.isArray(obj)) {
|
|
166
|
-
return typeof segment === "number" ? obj[segment] : void 0;
|
|
167
|
-
}
|
|
168
|
-
return isPlainRecord(obj) && Object.hasOwn(obj, String(segment)) ? obj[String(segment)] : void 0;
|
|
169
|
-
};
|
|
170
|
-
var getAtPath = (obj, path) => parsePath(path).reduce(readSegment, obj);
|
|
171
|
-
var slotAtPath = (obj, path) => {
|
|
172
|
-
const walked = parsePath(path).reduce(
|
|
173
|
-
(acc, segment) => {
|
|
174
|
-
const container = acc.current;
|
|
175
|
-
const addressable = typeof segment === "number" ? Array.isArray(container) ? segment < container.length : isPlainRecord(container) : isPlainRecord(container);
|
|
176
|
-
return !acc.ok || !addressable ? { ok: false, current: void 0 } : { ok: true, current: readSegment(container, segment) };
|
|
177
|
-
},
|
|
178
|
-
{ ok: true, current: obj }
|
|
179
|
-
);
|
|
180
|
-
return walked.ok ? { exists: true, value: walked.current } : { exists: false };
|
|
181
|
-
};
|
|
182
|
-
var hasOwnSlot = (obj, path) => parsePath(path).reduce(
|
|
183
|
-
(acc, segment) => {
|
|
184
|
-
const container = acc.current;
|
|
185
|
-
const owns = Array.isArray(container) ? typeof segment === "number" && segment < container.length : isPlainRecord(container) && Object.hasOwn(container, String(segment));
|
|
186
|
-
return !acc.ok || !owns ? { ok: false, current: void 0 } : { ok: true, current: readSegment(container, segment) };
|
|
187
|
-
},
|
|
188
|
-
{ ok: true, current: obj }
|
|
189
|
-
).ok;
|
|
190
|
-
var writeIntoArray = (arr, index, rest, value) => {
|
|
191
|
-
const length = Math.max(arr.length, index + 1);
|
|
192
|
-
return Array.from(
|
|
193
|
-
{ length },
|
|
194
|
-
(_, i) => i === index ? writeSegments(arr[i], rest, value) : arr[i]
|
|
195
|
-
);
|
|
196
|
-
};
|
|
197
|
-
var refusesHugeIndex = (index) => {
|
|
198
|
-
if (index > MAX_ARRAY_INDEX) {
|
|
199
|
-
console.warn(
|
|
200
|
-
`[formstand] refusing to write array index ${index} (max ${MAX_ARRAY_INDEX}); value left unchanged.`
|
|
201
|
-
);
|
|
202
|
-
return true;
|
|
203
|
-
}
|
|
204
|
-
return false;
|
|
205
|
-
};
|
|
206
|
-
var writeSegments = (obj, segments, value) => {
|
|
207
|
-
const [head, ...rest] = segments;
|
|
208
|
-
if (head === void 0) return value;
|
|
209
|
-
if (Array.isArray(obj)) {
|
|
210
|
-
if (typeof head !== "number") {
|
|
211
|
-
console.warn(
|
|
212
|
-
`[formstand] cannot write string key "${head}" into an array; value left unchanged.`
|
|
213
|
-
);
|
|
214
|
-
return obj;
|
|
215
|
-
}
|
|
216
|
-
if (refusesHugeIndex(head)) return obj;
|
|
217
|
-
return writeIntoArray(obj, head, rest, value);
|
|
218
|
-
}
|
|
219
|
-
if (isPlainRecord(obj)) {
|
|
220
|
-
if (!isPlainObject(obj)) {
|
|
221
|
-
console.warn(
|
|
222
|
-
`[formstand] cannot write key "${String(head)}" through a non-plain object (Date/Map/Set/class instance); value left unchanged.`
|
|
223
|
-
);
|
|
224
|
-
return obj;
|
|
225
|
-
}
|
|
226
|
-
const key = String(head);
|
|
227
|
-
return { ...obj, [key]: writeSegments(obj[key], rest, value) };
|
|
228
|
-
}
|
|
229
|
-
if (typeof head === "number") {
|
|
230
|
-
if (refusesHugeIndex(head)) return obj;
|
|
231
|
-
return writeIntoArray([], head, rest, value);
|
|
232
|
-
}
|
|
233
|
-
return { [head]: writeSegments(void 0, rest, value) };
|
|
234
|
-
};
|
|
235
|
-
var setAtPath = (obj, path, value) => writeSegments(obj, parsePath(path), value);
|
|
236
104
|
var issuePathToFormPath = (path) => path.filter((segment) => typeof segment !== "symbol").join(".");
|
|
237
105
|
var expandIssue = (issue, prefix) => {
|
|
238
106
|
const at = [...prefix, ...issue.path];
|
|
@@ -1138,6 +1006,25 @@ var shouldValidateOn = (trigger, mode, reValidateMode, submitAttempted, touched
|
|
|
1138
1006
|
};
|
|
1139
1007
|
|
|
1140
1008
|
// src/core/persist.ts
|
|
1009
|
+
var kindOf = (value) => value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
|
|
1010
|
+
var conflictsWith = (draft, reference) => {
|
|
1011
|
+
if (draft === void 0 || draft === null) return false;
|
|
1012
|
+
if (reference === void 0 || reference === null) return false;
|
|
1013
|
+
const draftKind = kindOf(draft);
|
|
1014
|
+
const referenceKind = kindOf(reference);
|
|
1015
|
+
if (draftKind !== referenceKind) return true;
|
|
1016
|
+
if (draftKind === "array") {
|
|
1017
|
+
const [d] = draft;
|
|
1018
|
+
const [r] = reference;
|
|
1019
|
+
return conflictsWith(d, r);
|
|
1020
|
+
}
|
|
1021
|
+
if (draftKind === "object") {
|
|
1022
|
+
const d = draft;
|
|
1023
|
+
const r = reference;
|
|
1024
|
+
return Object.keys(d).some((key) => conflictsWith(d[key], r[key]));
|
|
1025
|
+
}
|
|
1026
|
+
return false;
|
|
1027
|
+
};
|
|
1141
1028
|
var defaultStorage = () => {
|
|
1142
1029
|
try {
|
|
1143
1030
|
return typeof globalThis.localStorage === "undefined" ? null : globalThis.localStorage;
|
|
@@ -1150,11 +1037,29 @@ var persistForm = (form, options) => {
|
|
|
1150
1037
|
const debounceMs = options.debounceMs ?? 300;
|
|
1151
1038
|
const apply = options.apply ?? "adopt";
|
|
1152
1039
|
const applyDraft = (values) => apply === "restore" ? form.setValues(values) : form.adoptValues(values);
|
|
1040
|
+
const referenceShape = () => {
|
|
1041
|
+
try {
|
|
1042
|
+
return JSON.parse(JSON.stringify(form.getState().initialValues));
|
|
1043
|
+
} catch {
|
|
1044
|
+
return void 0;
|
|
1045
|
+
}
|
|
1046
|
+
};
|
|
1153
1047
|
const restore = () => {
|
|
1154
1048
|
try {
|
|
1155
1049
|
const raw = storage === null ? null : storage.getItem(options.key);
|
|
1156
1050
|
if (raw === null) return false;
|
|
1157
|
-
|
|
1051
|
+
const parsed = JSON.parse(raw);
|
|
1052
|
+
const versioned = options.version !== void 0 && typeof parsed === "object" && parsed !== null && "__v" in parsed;
|
|
1053
|
+
if (options.version !== void 0 && !versioned) return false;
|
|
1054
|
+
const wrapper = parsed;
|
|
1055
|
+
if (versioned && wrapper.__v !== options.version) return false;
|
|
1056
|
+
if (versioned && wrapper.values === void 0) return false;
|
|
1057
|
+
const values = versioned ? wrapper.values : parsed;
|
|
1058
|
+
const reference = referenceShape();
|
|
1059
|
+
if (reference !== void 0 && conflictsWith(values, reference)) {
|
|
1060
|
+
return false;
|
|
1061
|
+
}
|
|
1062
|
+
applyDraft(values);
|
|
1158
1063
|
return true;
|
|
1159
1064
|
} catch {
|
|
1160
1065
|
return false;
|
|
@@ -1162,7 +1067,12 @@ var persistForm = (form, options) => {
|
|
|
1162
1067
|
};
|
|
1163
1068
|
const writeDraft = (values) => {
|
|
1164
1069
|
try {
|
|
1165
|
-
storage?.setItem(
|
|
1070
|
+
storage?.setItem(
|
|
1071
|
+
options.key,
|
|
1072
|
+
JSON.stringify(
|
|
1073
|
+
options.version === void 0 ? values : { __v: options.version, values }
|
|
1074
|
+
)
|
|
1075
|
+
);
|
|
1166
1076
|
} catch {
|
|
1167
1077
|
}
|
|
1168
1078
|
};
|
|
@@ -1582,12 +1492,6 @@ function useIsValid(form, path) {
|
|
|
1582
1492
|
}
|
|
1583
1493
|
var useIsSubmitting = (form) => useStore(form.store, (state) => state.isSubmitting);
|
|
1584
1494
|
var useSubmitCount = (form) => useStore(form.store, (state) => state.submitCount);
|
|
1585
|
-
function useFormSelector(form, selector) {
|
|
1586
|
-
return useStore(form.store, selector);
|
|
1587
|
-
}
|
|
1588
|
-
function useFormSelectorShallow(form, selector) {
|
|
1589
|
-
return useStore(form.store, useShallow(selector));
|
|
1590
|
-
}
|
|
1591
1495
|
|
|
1592
1496
|
// src/react/createFormHooks.ts
|
|
1593
1497
|
var capitalize = (name) => name.length === 0 ? name : name.charAt(0).toUpperCase() + name.slice(1);
|
|
@@ -2013,6 +1917,6 @@ var focusField = (path, root) => {
|
|
|
2013
1917
|
return path === "" ? (root !== void 0 || document.querySelectorAll("form").length <= 1) && focusFirstOf(namedControls(scope).filter(isFocusCandidate)) : focusFirstOf(pathCandidates(scope, [path]).filter(isFocusCandidate));
|
|
2014
1918
|
};
|
|
2015
1919
|
|
|
2016
|
-
export { CheckboxField, DateField, NumberField, SelectField, TextField, checkboxProps, createForm, createFormContext, createFormHooks, dateInputProps, dateToInputText, emptyValueForSchema, flattenIssues, focusField, focusFirstError,
|
|
1920
|
+
export { CheckboxField, DateField, NumberField, SelectField, TextField, checkboxProps, createForm, createFormContext, createFormHooks, dateInputProps, dateToInputText, emptyValueForSchema, flattenIssues, focusField, focusFirstError, hasFieldError, isAsyncRequiredError, numberInputProps, numberToInputText, parseDateText, parseNumberText, persistForm, selectProps, shouldValidateOn, textInputProps, useField, useFieldArray, useForm, useFormError, useFormValues, useIsDirty, useIsSubmitting, useIsValid, useNumberInput, useSubmitCount, useVariantField, validateAsync, validateSync };
|
|
2017
1921
|
//# sourceMappingURL=index.js.map
|
|
2018
1922
|
//# sourceMappingURL=index.js.map
|