fluid-primitives 0.19.1 → 1.0.0-beta.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.
Files changed (67) hide show
  1. package/README.md +9 -0
  2. package/dist/Client-HrMIqv7d.js +1934 -0
  3. package/dist/accordion.d.ts +2 -2
  4. package/dist/accordion.js +25 -29
  5. package/dist/button.d.ts +52 -0
  6. package/dist/button.js +135 -0
  7. package/dist/checkbox-group.d.ts +6 -5
  8. package/dist/checkbox-group.js +36 -20
  9. package/dist/{checkbox-group.registry-CGwuF7SF.js → checkbox-group.registry-D8P8A4LY.js} +13 -8
  10. package/dist/checkbox.d.ts +18 -4
  11. package/dist/checkbox.js +30 -27
  12. package/dist/client.d.ts +2 -2
  13. package/dist/client.js +2 -2
  14. package/dist/clipboard.d.ts +12 -5
  15. package/dist/clipboard.js +14 -16
  16. package/dist/collapsible.d.ts +3 -3
  17. package/dist/collapsible.js +13 -17
  18. package/dist/combobox.d.ts +37 -0
  19. package/dist/combobox.js +151 -0
  20. package/dist/date-picker.d.ts +67 -0
  21. package/dist/date-picker.js +289 -0
  22. package/dist/dialog.d.ts +3 -3
  23. package/dist/dialog.js +11 -12
  24. package/dist/field-array.d.ts +97 -0
  25. package/dist/field-array.js +223 -0
  26. package/dist/field.d.ts +30 -54
  27. package/dist/field.js +589 -175
  28. package/dist/fieldset.d.ts +15 -0
  29. package/dist/fieldset.js +27 -0
  30. package/dist/file-upload.d.ts +85 -0
  31. package/dist/file-upload.js +255 -0
  32. package/dist/form.d.ts +2 -83
  33. package/dist/form.fields-B58TZukv.js +329 -0
  34. package/dist/form.js +263 -276
  35. package/dist/index-B06gIN_k.d.ts +1358 -0
  36. package/dist/input.d.ts +96 -0
  37. package/dist/input.js +233 -0
  38. package/dist/menu.d.ts +55 -0
  39. package/dist/menu.js +144 -0
  40. package/dist/navigation-menu.d.ts +3 -3
  41. package/dist/navigation-menu.js +29 -43
  42. package/dist/number-input.d.ts +13 -4
  43. package/dist/number-input.js +30 -31
  44. package/dist/popover.d.ts +11 -3
  45. package/dist/popover.js +17 -18
  46. package/dist/radio-group.d.ts +4 -4
  47. package/dist/radio-group.js +35 -42
  48. package/dist/scroll-area.d.ts +3 -3
  49. package/dist/scroll-area.js +10 -9
  50. package/dist/select.d.ts +13 -51
  51. package/dist/select.js +62 -55
  52. package/dist/slider.d.ts +27 -0
  53. package/dist/slider.js +71 -0
  54. package/dist/switch.d.ts +4 -4
  55. package/dist/switch.js +24 -33
  56. package/dist/tabs.d.ts +3 -3
  57. package/dist/tabs.js +12 -15
  58. package/dist/textarea.d.ts +101 -0
  59. package/dist/textarea.js +231 -0
  60. package/dist/tooltip.d.ts +11 -3
  61. package/dist/tooltip.js +11 -10
  62. package/package.json +79 -24
  63. package/dist/Client-C51NSHxg.js +0 -248
  64. package/dist/field.dom-CJQXpQbZ.js +0 -11
  65. package/dist/form.registry-Cb8Pvoe8.d.ts +0 -7
  66. package/dist/form.registry-CmpTny_s.js +0 -51
  67. package/dist/index-B8JCdyld.d.ts +0 -118
@@ -0,0 +1,329 @@
1
+ import { B as stringifyFieldPathAsBrackets, F as renameFieldMachineForForm, H as trimArraySuffix, L as appendFieldPathSegment, R as parseFieldPath, V as toRegisteredFieldName, _ as serializeFieldValue, h as getFieldElement, j as getFieldMachinesFor, k as isInsideField } from "./Client-HrMIqv7d.js";
2
+ import { createAnatomy } from "@zag-js/anatomy";
3
+ import { MachineStatus } from "@zag-js/core";
4
+
5
+ //#region Resources/Private/Primitives/Form/src/form.anatomy.ts
6
+ const anatomy = createAnatomy("form").parts("root", "content", "indicator", "errorText", "successText");
7
+ const parts = anatomy.build();
8
+
9
+ //#endregion
10
+ //#region Resources/Private/Primitives/Form/src/form.dom.ts
11
+ const getFormEl = (scope) => scope.query(scope.selector(parts.root));
12
+
13
+ //#endregion
14
+ //#region Resources/Private/Primitives/Form/src/form.types.ts
15
+ /**
16
+ * Error thrown by post() when server returns 422 validation errors.
17
+ * The machine catches this and transitions to 'invalid' state.
18
+ */
19
+ var ValidationError = class extends Error {
20
+ constructor(errors) {
21
+ super("Server validation failed");
22
+ this.errors = errors;
23
+ this.name = "ValidationError";
24
+ }
25
+ };
26
+ var FormError = class extends Error {
27
+ constructor(errors) {
28
+ super("Server form validation failed");
29
+ this.errors = errors;
30
+ this.name = "FormError";
31
+ }
32
+ };
33
+
34
+ //#endregion
35
+ //#region Resources/Private/Primitives/Form/src/form.values.ts
36
+ function createFormValues(formData) {
37
+ let cachedObject = null;
38
+ const getObject = () => {
39
+ if (cachedObject === null) cachedObject = formDataToObject(formData);
40
+ return cachedObject;
41
+ };
42
+ return {
43
+ get(path) {
44
+ return getFieldValues(formData, path)[0] ?? null;
45
+ },
46
+ getAll(path) {
47
+ return [...getFieldValues(formData, path)];
48
+ },
49
+ has(path) {
50
+ return getValueAtFieldPath(getObject(), path) !== null;
51
+ },
52
+ pick(path) {
53
+ return getValueAtFieldPath(getObject(), path);
54
+ },
55
+ toObject() {
56
+ return getObject();
57
+ }
58
+ };
59
+ }
60
+ function getFieldErrorValue(values, fieldName) {
61
+ return toFieldErrorValue(values.pick(fieldName));
62
+ }
63
+ function formDataToObject(formData) {
64
+ const fieldNames = new Set(Array.from(formData.keys(), trimArraySuffix));
65
+ const dataObject = {};
66
+ for (const fieldName of fieldNames) setValueAtFieldPath(dataObject, fieldName, getFieldFormDataValue(formData, fieldName) ?? "");
67
+ return dataObject;
68
+ }
69
+ function getFieldFormDataValue(formData, fieldName) {
70
+ const { entries, isArrayValue } = readFieldEntries(formData, fieldName);
71
+ return isArrayValue ? entries : entries[0] ?? null;
72
+ }
73
+ function getFieldValues(formData, fieldName) {
74
+ return readFieldEntries(formData, fieldName).entries;
75
+ }
76
+ /**
77
+ * Reads the raw FormData entries for a field name, checking both the direct
78
+ * and `[]`-suffixed key so callers don't each re-query FormData themselves.
79
+ */
80
+ function readFieldEntries(formData, fieldName) {
81
+ const normalizedFieldName = trimArraySuffix(fieldName);
82
+ const directEntries = formData.getAll(normalizedFieldName);
83
+ const bracketEntries = formData.getAll(`${normalizedFieldName}[]`);
84
+ const entries = directEntries.length > 0 ? directEntries : bracketEntries;
85
+ return {
86
+ entries,
87
+ isArrayValue: fieldName.endsWith("[]") || bracketEntries.length > 0 || entries.length > 1
88
+ };
89
+ }
90
+ function createContainerForFieldPathSegment(segment) {
91
+ return typeof segment === "number" || segment === appendFieldPathSegment ? [] : {};
92
+ }
93
+ function setValueAtFieldPath(target, fieldName, value) {
94
+ const fieldPath = parseFieldPath(fieldName);
95
+ if (fieldPath.length === 0) return;
96
+ let current = target;
97
+ for (let index = 0; index < fieldPath.length; index++) {
98
+ const segment = fieldPath[index];
99
+ const nextSegment = fieldPath[index + 1];
100
+ const isLeaf = index === fieldPath.length - 1;
101
+ if (segment === appendFieldPathSegment) {
102
+ if (!Array.isArray(current)) return;
103
+ if (isLeaf) {
104
+ current.push(value);
105
+ return;
106
+ }
107
+ const nextContainer = createContainerForFieldPathSegment(nextSegment);
108
+ current.push(nextContainer);
109
+ current = nextContainer;
110
+ continue;
111
+ }
112
+ if (typeof segment === "number") {
113
+ if (!Array.isArray(current)) return;
114
+ if (isLeaf) {
115
+ current[segment] = value;
116
+ return;
117
+ }
118
+ const existingValue = current[segment];
119
+ if (existingValue && typeof existingValue === "object") {
120
+ current = existingValue;
121
+ continue;
122
+ }
123
+ const nextContainer = createContainerForFieldPathSegment(nextSegment);
124
+ current[segment] = nextContainer;
125
+ current = nextContainer;
126
+ continue;
127
+ }
128
+ if (Array.isArray(current)) return;
129
+ if (isLeaf) {
130
+ current[segment] = value;
131
+ return;
132
+ }
133
+ const existingValue = current[segment];
134
+ if (existingValue && typeof existingValue === "object") {
135
+ current = existingValue;
136
+ continue;
137
+ }
138
+ const nextContainer = createContainerForFieldPathSegment(nextSegment);
139
+ current[segment] = nextContainer;
140
+ current = nextContainer;
141
+ }
142
+ }
143
+ function getValueAtFieldPath(values, fieldName) {
144
+ const fieldPath = parseFieldPath(trimArraySuffix(fieldName));
145
+ if (fieldPath.length === 0) return null;
146
+ let current = values;
147
+ for (const segment of fieldPath) {
148
+ if (segment === appendFieldPathSegment) return Array.isArray(current) ? current : null;
149
+ if (typeof current !== "object" || current === null) return null;
150
+ current = current[segment];
151
+ if (current === void 0) return null;
152
+ }
153
+ return current;
154
+ }
155
+ function toFieldErrorValue(value) {
156
+ if (value === null || typeof value === "object" && !Array.isArray(value) && !(value instanceof File)) return null;
157
+ if (typeof value === "string" || value instanceof File) return value;
158
+ if (Array.isArray(value) && value.every(isFormValueLeaf)) return value;
159
+ return null;
160
+ }
161
+ function isFormValueLeaf(value) {
162
+ return typeof value === "string" || value instanceof File;
163
+ }
164
+
165
+ //#endregion
166
+ //#region Resources/Private/Primitives/Form/src/form.validation.ts
167
+ function validateWithValidation(validation, values, fieldName) {
168
+ if (!validation) return {};
169
+ if (typeof validation === "function") return attachErrorValues(validation({
170
+ values,
171
+ fieldName,
172
+ validateWithStandardSchema: (schema) => validateWithStandardSchema(schema, values)
173
+ }) ?? {}, values);
174
+ return attachErrorValues(validateWithStandardSchema(validation, values), values);
175
+ }
176
+ function validateWithStandardSchema(schema, values) {
177
+ const validationResult = schema["~standard"].validate(values.toObject());
178
+ if (isPromiseLike(validationResult)) throw new TypeError("Form validation must be synchronous. Use onSubmit for async validation.");
179
+ if (!validationResult.issues || validationResult.issues.length === 0) return {};
180
+ return errorsFromIssues(validationResult.issues);
181
+ }
182
+ function attachErrorValues(errors, values) {
183
+ return Object.fromEntries(Object.entries(errors).map(([fieldName, error]) => [toRegisteredFieldName(fieldName), {
184
+ ...error,
185
+ value: error.value === void 0 ? getFieldErrorValue(values, fieldName) : error.value
186
+ }]));
187
+ }
188
+ function filterErrorsForCurrentValues(serverErrors, values) {
189
+ const errors = {};
190
+ for (const fieldName in serverErrors) {
191
+ const error = getCurrentErrorForField(serverErrors, fieldName, values);
192
+ if (error) errors[fieldName] = error;
193
+ }
194
+ return errors;
195
+ }
196
+ function getCurrentErrorForField(errors, fieldName, values) {
197
+ const error = errors[trimArraySuffix(fieldName)];
198
+ if (!error || error.value === void 0) return;
199
+ return serializeFieldValue(getFieldErrorValue(values, fieldName)) === serializeFieldValue(error.value) ? error : void 0;
200
+ }
201
+ function getFormErrorMessages(responseErrors, objectName) {
202
+ if (!objectName) return null;
203
+ return responseErrors[objectName] ?? null;
204
+ }
205
+ function mapServerErrors(responseErrors, objectName, values) {
206
+ const out = {};
207
+ for (const key in responseErrors) {
208
+ const canonicalFieldName = toRegisteredFieldName(key, objectName);
209
+ out[canonicalFieldName] = {
210
+ messages: responseErrors[key],
211
+ value: getFieldErrorValue(values, canonicalFieldName)
212
+ };
213
+ }
214
+ return out;
215
+ }
216
+ function errorsFromIssues(issues) {
217
+ const errors = {};
218
+ for (const issue of issues) {
219
+ const fieldName = getIssueFieldName(issue);
220
+ if (!fieldName) continue;
221
+ const existingError = errors[fieldName];
222
+ if (existingError) {
223
+ existingError.messages.push(issue.message);
224
+ continue;
225
+ }
226
+ errors[fieldName] = { messages: [issue.message] };
227
+ }
228
+ return errors;
229
+ }
230
+ function getIssueFieldName(issue) {
231
+ if (!issue.path || issue.path.length === 0) return;
232
+ const fieldPath = [];
233
+ for (const pathSegment of issue.path) {
234
+ const key = typeof pathSegment === "object" && pathSegment !== null && "key" in pathSegment ? pathSegment.key : pathSegment;
235
+ if (typeof key === "symbol") return;
236
+ if (typeof key === "number") {
237
+ fieldPath.push(key);
238
+ continue;
239
+ }
240
+ fieldPath.push(String(key));
241
+ }
242
+ if (fieldPath.length === 0) return;
243
+ return trimArraySuffix(stringifyFieldPathAsBrackets(fieldPath));
244
+ }
245
+ function isPromiseLike(result) {
246
+ return typeof result === "object" && result !== null && "then" in result;
247
+ }
248
+
249
+ //#endregion
250
+ //#region Resources/Private/Primitives/Form/src/form.fields.ts
251
+ function getFormData(scope) {
252
+ const form = getFormEl(scope);
253
+ return form ? new FormData(form) : new FormData();
254
+ }
255
+ /**
256
+ * The messages the form has for one field right now: a server error whose captured value still
257
+ * matches the field's current value, else what `validation` (schema or callback) reports for it.
258
+ */
259
+ function getFieldMessages(form, fieldName) {
260
+ const normalizedFieldName = trimArraySuffix(fieldName);
261
+ const values = createFormValues(getFormData(form.scope));
262
+ const serverError = getCurrentErrorForField(form.refs.get("serverErrors"), normalizedFieldName, values);
263
+ if (serverError) return serverError.messages;
264
+ const validation = form.prop("validation");
265
+ if (!validation) return [];
266
+ return validateWithValidation(validation, values, normalizedFieldName)[normalizedFieldName]?.messages ?? [];
267
+ }
268
+ function getRegisteredFieldMachines(scope) {
269
+ return getFieldMachinesFor(getFormEl(scope));
270
+ }
271
+ /** A field counts while its own root is in the DOM; it may have no named control (an empty combobox). */
272
+ function pruneStaleFieldMachines(scope) {
273
+ const machines = getRegisteredFieldMachines(scope);
274
+ for (const [name, fieldMachine] of machines) if (!fieldMachine.refs.get("rootEl")?.isConnected) machines.delete(name);
275
+ }
276
+ function sendToFieldMachines(scope, type) {
277
+ for (const fieldMachine of getRegisteredFieldMachines(scope).values()) fieldMachine.send({ type });
278
+ }
279
+ /**
280
+ * Resolves once the events just sent to the fields were handled (a machine handles `send` in a
281
+ * microtask) and no field is validating anymore, so an async validator can still block a submit.
282
+ */
283
+ async function settleFieldMachines(scope) {
284
+ await Promise.resolve();
285
+ const validating = () => Array.from(getRegisteredFieldMachines(scope).values()).filter((fieldMachine) => fieldMachine.service.getStatus() === MachineStatus.Started && fieldMachine.context.get("validating"));
286
+ for (let pending = validating(); pending.length > 0; pending = validating()) await new Promise((resolve) => {
287
+ const unsubscribe = pending.map((fieldMachine) => fieldMachine.subscribe(() => {
288
+ if (fieldMachine.context.get("validating")) return;
289
+ unsubscribe.forEach((stop) => stop());
290
+ resolve();
291
+ }));
292
+ });
293
+ }
294
+ function hasInvalidFieldMachines(scope) {
295
+ return Array.from(getRegisteredFieldMachines(scope).values()).some((fieldMachine) => fieldMachine.computed("invalid"));
296
+ }
297
+ function getFirstInvalidField(scope) {
298
+ return Array.from(getRegisteredFieldMachines(scope)).find(([, fieldMachine]) => fieldMachine.computed("invalid"));
299
+ }
300
+ const FOCUSABLE = [
301
+ "button:not([disabled])",
302
+ "input:not([disabled]):not([type=\"hidden\"]):not([aria-hidden=\"true\"])",
303
+ "select:not([disabled]):not([aria-hidden=\"true\"])",
304
+ "textarea:not([disabled])",
305
+ "[tabindex]:not([tabindex=\"-1\"])"
306
+ ].join(", ");
307
+ /**
308
+ * Moves the focus into an invalid field: to the control carrying its name (the hidden select of a
309
+ * Select and the hidden input of a Combobox hand it on to their visible control) and, when that
310
+ * cannot take focus (a Slider's input is `display: none`), to the first thing inside the field that can.
311
+ */
312
+ function focusField(form, name, fieldMachine) {
313
+ const rootEl = fieldMachine.refs.get("rootEl");
314
+ const target = getFieldElement(form, name);
315
+ target?.focus();
316
+ if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement) target.select();
317
+ if (rootEl && !isInsideField(rootEl, rootEl.ownerDocument.activeElement)) rootEl.querySelector(FOCUSABLE)?.focus();
318
+ }
319
+ /** Errors for names no registered field owns (a hidden input, say) still make the form invalid. */
320
+ function hasUnownedErrors(scope, errors) {
321
+ const fieldMachines = getRegisteredFieldMachines(scope);
322
+ return Object.keys(errors).some((name) => !fieldMachines.has(name));
323
+ }
324
+ function renameFieldMachine(scope, oldName, newName) {
325
+ renameFieldMachineForForm(getFormEl(scope), oldName, newName);
326
+ }
327
+
328
+ //#endregion
329
+ export { createFormValues as _, getRegisteredFieldMachines as a, getFormEl as b, pruneStaleFieldMachines as c, settleFieldMachines as d, attachErrorValues as f, validateWithValidation as g, mapServerErrors as h, getFormData as i, renameFieldMachine as l, getFormErrorMessages as m, getFieldMessages as n, hasInvalidFieldMachines as o, filterErrorsForCurrentValues as p, getFirstInvalidField as r, hasUnownedErrors as s, focusField as t, sendToFieldMachines as u, FormError as v, parts as x, ValidationError as y };