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.
- package/README.md +9 -0
- package/dist/Client-HrMIqv7d.js +1934 -0
- package/dist/accordion.d.ts +2 -2
- package/dist/accordion.js +25 -29
- package/dist/button.d.ts +52 -0
- package/dist/button.js +135 -0
- package/dist/checkbox-group.d.ts +6 -5
- package/dist/checkbox-group.js +36 -20
- package/dist/{checkbox-group.registry-CGwuF7SF.js → checkbox-group.registry-D8P8A4LY.js} +13 -8
- package/dist/checkbox.d.ts +18 -4
- package/dist/checkbox.js +30 -27
- package/dist/client.d.ts +2 -2
- package/dist/client.js +2 -2
- package/dist/clipboard.d.ts +12 -5
- package/dist/clipboard.js +14 -16
- package/dist/collapsible.d.ts +3 -3
- package/dist/collapsible.js +13 -17
- package/dist/combobox.d.ts +37 -0
- package/dist/combobox.js +151 -0
- package/dist/date-picker.d.ts +67 -0
- package/dist/date-picker.js +289 -0
- package/dist/dialog.d.ts +3 -3
- package/dist/dialog.js +11 -12
- package/dist/field-array.d.ts +97 -0
- package/dist/field-array.js +223 -0
- package/dist/field.d.ts +30 -54
- package/dist/field.js +589 -175
- package/dist/fieldset.d.ts +15 -0
- package/dist/fieldset.js +27 -0
- package/dist/file-upload.d.ts +85 -0
- package/dist/file-upload.js +255 -0
- package/dist/form.d.ts +2 -83
- package/dist/form.fields-B58TZukv.js +329 -0
- package/dist/form.js +263 -276
- package/dist/index-B06gIN_k.d.ts +1358 -0
- package/dist/input.d.ts +96 -0
- package/dist/input.js +233 -0
- package/dist/menu.d.ts +55 -0
- package/dist/menu.js +144 -0
- package/dist/navigation-menu.d.ts +3 -3
- package/dist/navigation-menu.js +29 -43
- package/dist/number-input.d.ts +13 -4
- package/dist/number-input.js +30 -31
- package/dist/popover.d.ts +11 -3
- package/dist/popover.js +17 -18
- package/dist/radio-group.d.ts +4 -4
- package/dist/radio-group.js +35 -42
- package/dist/scroll-area.d.ts +3 -3
- package/dist/scroll-area.js +10 -9
- package/dist/select.d.ts +13 -51
- package/dist/select.js +62 -55
- package/dist/slider.d.ts +27 -0
- package/dist/slider.js +71 -0
- package/dist/switch.d.ts +4 -4
- package/dist/switch.js +24 -33
- package/dist/tabs.d.ts +3 -3
- package/dist/tabs.js +12 -15
- package/dist/textarea.d.ts +101 -0
- package/dist/textarea.js +231 -0
- package/dist/tooltip.d.ts +11 -3
- package/dist/tooltip.js +11 -10
- package/package.json +79 -24
- package/dist/Client-C51NSHxg.js +0 -248
- package/dist/field.dom-CJQXpQbZ.js +0 -11
- package/dist/form.registry-Cb8Pvoe8.d.ts +0 -7
- package/dist/form.registry-CmpTny_s.js +0 -51
- package/dist/index-B8JCdyld.d.ts +0 -118
package/dist/field.js
CHANGED
|
@@ -1,211 +1,625 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { A as queryRootEl, C as toErrorArray, D as getVisibleErrorTextIds, E as getControlEl, H as trimArraySuffix, I as unregisterFieldMachineForForm, M as getFormMachineFor, N as registerFieldMachineForForm, O as hasHelperText, S as suppressValueMissing, T as FIELD_VALUE_CHANGE_EVENT, at as Machine$1, b as resolveValidation, c as registerFieldMachine, d as connectField, j as getFieldMachinesFor, k as isInsideField, l as unregisterFieldMachine, m as getCurrentFieldValue, p as getComparableFieldValue, rt as Component, u as FIELD_DEPENDENCY_CHANGE_EVENT, v as VALID_SNAPSHOT, w as withValueMissing, x as shouldCommit, y as getValiditySnapshot } from "./Client-HrMIqv7d.js";
|
|
2
|
+
import { _ as createFormValues, n as getFieldMessages } from "./form.fields-B58TZukv.js";
|
|
3
|
+
import { observeChildren, trackFormControl } from "@zag-js/dom-query";
|
|
4
|
+
import { createSplitProps, isEqual } from "@zag-js/utils";
|
|
5
|
+
import { setup } from "@zag-js/core";
|
|
6
|
+
import { createProps } from "@zag-js/types";
|
|
6
7
|
|
|
7
|
-
//#region Resources/Private/Primitives/Field/src/field.anatomy.ts
|
|
8
|
-
const anatomy = createAnatomy("field").parts("root", "label", "control", "error", "description");
|
|
9
|
-
const parts = anatomy.build();
|
|
10
|
-
|
|
11
|
-
//#endregion
|
|
12
|
-
//#region Resources/Private/Primitives/Field/src/field.connect.ts
|
|
13
|
-
function connect(service, normalize) {
|
|
14
|
-
const { scope, prop, context, computed } = service;
|
|
15
|
-
const invalid = context.get("invalid");
|
|
16
|
-
const disabled = context.get("disabled");
|
|
17
|
-
const required = context.get("required");
|
|
18
|
-
const readOnly = context.get("readOnly");
|
|
19
|
-
const errors = computed("errors");
|
|
20
|
-
return {
|
|
21
|
-
getFormMachine: () => context.get("formMachine"),
|
|
22
|
-
invalid,
|
|
23
|
-
disabled,
|
|
24
|
-
required,
|
|
25
|
-
readOnly,
|
|
26
|
-
errors,
|
|
27
|
-
name: prop("name"),
|
|
28
|
-
getErrorText() {
|
|
29
|
-
return errors.length > 0 ? errors.join(" ") : null;
|
|
30
|
-
},
|
|
31
|
-
getRootProps() {
|
|
32
|
-
return normalize.element({
|
|
33
|
-
...parts.root.attrs,
|
|
34
|
-
id: getRootId(scope),
|
|
35
|
-
"data-invalid": invalid ? "" : void 0,
|
|
36
|
-
"data-disabled": disabled ? "" : void 0,
|
|
37
|
-
"data-readonly": readOnly ? "" : void 0,
|
|
38
|
-
"data-required": required ? "" : void 0,
|
|
39
|
-
"data-name": prop("name")
|
|
40
|
-
});
|
|
41
|
-
},
|
|
42
|
-
getLabelProps() {
|
|
43
|
-
return normalize.label({
|
|
44
|
-
...parts.label.attrs,
|
|
45
|
-
id: getLabelId(scope),
|
|
46
|
-
htmlFor: getControlId(scope),
|
|
47
|
-
"data-invalid": invalid ? "" : void 0,
|
|
48
|
-
"data-disabled": disabled ? "" : void 0,
|
|
49
|
-
"data-required": required ? "" : void 0
|
|
50
|
-
});
|
|
51
|
-
},
|
|
52
|
-
getControlProps() {
|
|
53
|
-
return normalize.element({
|
|
54
|
-
...parts.control.attrs,
|
|
55
|
-
id: getControlId(scope),
|
|
56
|
-
name: prop("name"),
|
|
57
|
-
disabled: disabled || void 0,
|
|
58
|
-
readOnly: readOnly || void 0,
|
|
59
|
-
required: required || void 0,
|
|
60
|
-
"aria-invalid": invalid ? "true" : void 0,
|
|
61
|
-
"aria-describedby": context.get("describeIds") || void 0,
|
|
62
|
-
"aria-required": required ? "true" : void 0,
|
|
63
|
-
"data-invalid": invalid ? "" : void 0,
|
|
64
|
-
"data-disabled": disabled ? "" : void 0,
|
|
65
|
-
"data-readonly": readOnly ? "" : void 0
|
|
66
|
-
});
|
|
67
|
-
},
|
|
68
|
-
getErrorProps() {
|
|
69
|
-
return normalize.element({
|
|
70
|
-
...parts.error.attrs,
|
|
71
|
-
id: getErrorId(scope),
|
|
72
|
-
hidden: !invalid
|
|
73
|
-
});
|
|
74
|
-
},
|
|
75
|
-
getDescriptionProps() {
|
|
76
|
-
return normalize.element({
|
|
77
|
-
...parts.description.attrs,
|
|
78
|
-
id: getDescriptionId(scope)
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
//#endregion
|
|
85
8
|
//#region Resources/Private/Primitives/Field/src/field.machine.ts
|
|
86
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Derived from @zag-js/field of Zag.js (https://github.com/chakra-ui/zag, packages/machines/field,
|
|
11
|
+
* commit 0ce8e3de63b571d37e438bcf28e64759466e56e9), MIT License, Copyright (c) 2021 Chakra UI.
|
|
12
|
+
* Adapted for Fluid Primitives, the git history shows what changed from the original.
|
|
13
|
+
*/
|
|
14
|
+
const { createMachine: createMachine$1 } = setup();
|
|
15
|
+
const machine = createMachine$1({
|
|
16
|
+
props({ props }) {
|
|
17
|
+
return {
|
|
18
|
+
dir: "ltr",
|
|
19
|
+
disabled: false,
|
|
20
|
+
readOnly: false,
|
|
21
|
+
required: false,
|
|
22
|
+
validationMode: "onBlur",
|
|
23
|
+
...props
|
|
24
|
+
};
|
|
25
|
+
},
|
|
87
26
|
initialState() {
|
|
88
|
-
return "
|
|
27
|
+
return "idle";
|
|
89
28
|
},
|
|
90
|
-
context({
|
|
29
|
+
context({ prop, bindable }) {
|
|
91
30
|
return {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
defaultValue:
|
|
98
|
-
|
|
99
|
-
state: value?.state,
|
|
100
|
-
errors: value?.context.get("errors")
|
|
101
|
-
})
|
|
31
|
+
touched: bindable(() => ({
|
|
32
|
+
defaultValue: false,
|
|
33
|
+
value: prop("touched")
|
|
34
|
+
})),
|
|
35
|
+
dirty: bindable(() => ({
|
|
36
|
+
defaultValue: false,
|
|
37
|
+
value: prop("dirty")
|
|
102
38
|
})),
|
|
103
|
-
|
|
104
|
-
|
|
39
|
+
filled: bindable(() => ({ defaultValue: false })),
|
|
40
|
+
focused: bindable(() => ({ defaultValue: false })),
|
|
41
|
+
validating: bindable(() => ({ defaultValue: false })),
|
|
42
|
+
errors: bindable(() => ({ defaultValue: [] })),
|
|
43
|
+
validity: bindable(() => ({ defaultValue: null })),
|
|
44
|
+
errorTextIds: bindable(() => ({ defaultValue: [] })),
|
|
45
|
+
hasHelperText: bindable(() => ({ defaultValue: false })),
|
|
46
|
+
fieldsetDisabled: bindable(() => ({ defaultValue: false })),
|
|
47
|
+
submitAttempted: bindable(() => ({ defaultValue: false }))
|
|
105
48
|
};
|
|
106
49
|
},
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
50
|
+
refs() {
|
|
51
|
+
return {
|
|
52
|
+
markedDirty: false,
|
|
53
|
+
initialValue: null,
|
|
54
|
+
seq: 0,
|
|
55
|
+
rootEl: null
|
|
56
|
+
};
|
|
57
|
+
},
|
|
58
|
+
computed: {
|
|
59
|
+
disabled: ({ prop, context }) => !!prop("disabled") || context.get("fieldsetDisabled"),
|
|
60
|
+
valid: ({ prop, context }) => {
|
|
61
|
+
if (prop("invalid")) return false;
|
|
62
|
+
if (prop("disabled") || context.get("fieldsetDisabled")) return null;
|
|
63
|
+
return context.get("validity")?.valid ?? null;
|
|
64
|
+
},
|
|
65
|
+
invalid: ({ prop, context }) => {
|
|
66
|
+
if (prop("invalid")) return true;
|
|
67
|
+
if (prop("disabled") || context.get("fieldsetDisabled")) return false;
|
|
68
|
+
return context.get("validity")?.valid === false;
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
watch({ track, prop, context, refs }) {
|
|
72
|
+
track([() => prop("disabled")], () => {
|
|
73
|
+
if (prop("disabled") && context.get("focused")) context.set("focused", false);
|
|
122
74
|
});
|
|
123
|
-
track([() => context.get("
|
|
124
|
-
|
|
75
|
+
track([() => context.get("dirty")], () => {
|
|
76
|
+
if (context.get("dirty")) refs.set("markedDirty", true);
|
|
125
77
|
});
|
|
126
78
|
},
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
79
|
+
effects: [
|
|
80
|
+
"trackRoot",
|
|
81
|
+
"trackControlState",
|
|
82
|
+
"trackTextParts",
|
|
83
|
+
"trackFieldEvents"
|
|
84
|
+
],
|
|
85
|
+
on: {
|
|
86
|
+
"CONTROL.FOCUS": { actions: ["setFocused"] },
|
|
87
|
+
"CONTROL.BLUR": [{
|
|
88
|
+
guard: "shouldCommit",
|
|
89
|
+
actions: ["setBlurred", "commitValidation"]
|
|
90
|
+
}, { actions: ["setBlurred"] }],
|
|
91
|
+
"CONTROL.CHANGE": [{
|
|
92
|
+
guard: "shouldCommit",
|
|
93
|
+
actions: ["trackValueState", "commitValidation"]
|
|
94
|
+
}, { actions: [
|
|
95
|
+
"trackValueState",
|
|
96
|
+
"dropStaleValidation",
|
|
97
|
+
"recoverValueMissing"
|
|
98
|
+
] }],
|
|
99
|
+
"SUBMIT.INVALID": { actions: ["markSubmitAttempted", "commitValidation"] },
|
|
100
|
+
VALIDATE: { actions: ["forceDirty", "commitValidation"] },
|
|
101
|
+
"VALIDATE.RESOLVE": {
|
|
102
|
+
guard: "isCurrentValidation",
|
|
103
|
+
actions: ["applyAsyncValidation"]
|
|
144
104
|
},
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
105
|
+
"ERRORS.CLEAR": { actions: ["clearValidation"] },
|
|
106
|
+
RESET: { actions: ["resetField"] },
|
|
107
|
+
BASELINE: { actions: ["rebaseline"] }
|
|
108
|
+
},
|
|
109
|
+
states: { idle: {} },
|
|
110
|
+
implementations: {
|
|
111
|
+
guards: {
|
|
112
|
+
isCurrentValidation: ({ refs, event }) => event.seq === refs.get("seq"),
|
|
113
|
+
shouldCommit: ({ prop, context, refs, event }) => shouldCommit({
|
|
114
|
+
mode: prop("validationMode"),
|
|
115
|
+
submitAttempted: context.get("submitAttempted"),
|
|
116
|
+
eventType: event.type,
|
|
117
|
+
edited: refs.get("markedDirty") || context.get("dirty"),
|
|
118
|
+
showingErrors: context.get("errors").length > 0
|
|
119
|
+
})
|
|
148
120
|
},
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
121
|
+
effects: {
|
|
122
|
+
trackRoot({ refs, scope }) {
|
|
123
|
+
refs.set("rootEl", queryRootEl(scope));
|
|
124
|
+
},
|
|
125
|
+
trackControlState({ context, refs, send, prop }) {
|
|
126
|
+
const rootEl = refs.get("rootEl");
|
|
127
|
+
if (!rootEl) return;
|
|
128
|
+
if (refs.get("initialValue") == null) {
|
|
129
|
+
const value = readValue({
|
|
130
|
+
refs,
|
|
131
|
+
prop
|
|
132
|
+
});
|
|
133
|
+
refs.set("initialValue", value);
|
|
134
|
+
context.set("filled", value.length > 0);
|
|
135
|
+
}
|
|
136
|
+
return trackFormControl(rootEl, {
|
|
137
|
+
onFieldsetDisabledChange(disabled) {
|
|
138
|
+
context.set("fieldsetDisabled", disabled);
|
|
139
|
+
},
|
|
140
|
+
onFormReset() {
|
|
141
|
+
send({ type: "RESET" });
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
},
|
|
145
|
+
trackTextParts({ context, refs }) {
|
|
146
|
+
const rootEl = refs.get("rootEl");
|
|
147
|
+
if (!rootEl) return;
|
|
148
|
+
const sync = () => {
|
|
149
|
+
const errorTextIds = getVisibleErrorTextIds(rootEl);
|
|
150
|
+
if (!isEqual(context.get("errorTextIds"), errorTextIds)) context.set("errorTextIds", errorTextIds);
|
|
151
|
+
context.set("hasHelperText", hasHelperText(rootEl));
|
|
152
|
+
};
|
|
153
|
+
sync();
|
|
154
|
+
return observeChildren(rootEl, {
|
|
155
|
+
defer: true,
|
|
156
|
+
callback: sync,
|
|
157
|
+
attributes: true,
|
|
158
|
+
attributeFilter: ["hidden"]
|
|
159
|
+
});
|
|
160
|
+
},
|
|
161
|
+
trackFieldEvents({ refs, send, prop }) {
|
|
162
|
+
const rootEl = refs.get("rootEl");
|
|
163
|
+
if (!rootEl) return;
|
|
164
|
+
let timer;
|
|
165
|
+
const read = () => readValue({
|
|
166
|
+
refs,
|
|
167
|
+
prop
|
|
168
|
+
});
|
|
169
|
+
let announced = read();
|
|
170
|
+
const sendChange = () => {
|
|
171
|
+
timer = void 0;
|
|
172
|
+
const value = read();
|
|
173
|
+
send({
|
|
174
|
+
type: "CONTROL.CHANGE",
|
|
175
|
+
value
|
|
176
|
+
});
|
|
177
|
+
if (value === announced) return;
|
|
178
|
+
announced = value;
|
|
179
|
+
rootEl.dispatchEvent(new CustomEvent(FIELD_VALUE_CHANGE_EVENT, {
|
|
180
|
+
bubbles: true,
|
|
181
|
+
detail: { value }
|
|
182
|
+
}));
|
|
183
|
+
};
|
|
184
|
+
const schedule = () => {
|
|
185
|
+
clearTimeout(timer);
|
|
186
|
+
timer = setTimeout(sendChange, getSettleDelay(rootEl));
|
|
187
|
+
};
|
|
188
|
+
const sendNow = () => {
|
|
189
|
+
clearTimeout(timer);
|
|
190
|
+
sendChange();
|
|
191
|
+
};
|
|
192
|
+
const flush = () => {
|
|
193
|
+
if (timer !== void 0) sendNow();
|
|
194
|
+
};
|
|
195
|
+
const listeners = {
|
|
196
|
+
focusin: () => send({ type: "CONTROL.FOCUS" }),
|
|
197
|
+
focusout: (event) => {
|
|
198
|
+
if (isInsideField(rootEl, event.relatedTarget)) return;
|
|
199
|
+
flush();
|
|
200
|
+
send({
|
|
201
|
+
type: "CONTROL.BLUR",
|
|
202
|
+
value: read()
|
|
203
|
+
});
|
|
204
|
+
},
|
|
205
|
+
input: schedule,
|
|
206
|
+
change: (event) => event.isTrusted ? sendNow() : schedule(),
|
|
207
|
+
click: (event) => {
|
|
208
|
+
if (!event.isTrusted && isChoiceInput(event.target)) schedule();
|
|
209
|
+
},
|
|
210
|
+
invalid: (event) => {
|
|
211
|
+
event.preventDefault();
|
|
212
|
+
send({
|
|
213
|
+
type: "SUBMIT.INVALID",
|
|
214
|
+
value: read()
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
for (const [type, listener] of Object.entries(listeners)) rootEl.addEventListener(type, listener, true);
|
|
219
|
+
return () => {
|
|
220
|
+
clearTimeout(timer);
|
|
221
|
+
for (const [type, listener] of Object.entries(listeners)) rootEl.removeEventListener(type, listener, true);
|
|
222
|
+
};
|
|
223
|
+
}
|
|
155
224
|
},
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
225
|
+
actions: {
|
|
226
|
+
setFocused({ context }) {
|
|
227
|
+
context.set("focused", true);
|
|
228
|
+
},
|
|
229
|
+
setBlurred({ context }) {
|
|
230
|
+
context.set("focused", false);
|
|
231
|
+
context.set("touched", true);
|
|
232
|
+
},
|
|
233
|
+
trackValueState({ context, refs, event }) {
|
|
234
|
+
const value = event.value;
|
|
235
|
+
const dirty = value !== (refs.get("initialValue") ?? "");
|
|
236
|
+
context.set("dirty", dirty);
|
|
237
|
+
if (dirty) refs.set("markedDirty", true);
|
|
238
|
+
context.set("filled", value.length > 0);
|
|
239
|
+
},
|
|
240
|
+
dropStaleValidation({ context, refs }) {
|
|
241
|
+
if (!context.get("validating")) return;
|
|
242
|
+
refs.set("seq", refs.get("seq") + 1);
|
|
243
|
+
context.set("validating", false);
|
|
244
|
+
},
|
|
245
|
+
recoverValueMissing(params) {
|
|
246
|
+
const { context, event } = params;
|
|
247
|
+
const validity = context.get("validity");
|
|
248
|
+
if (!validity || validity.valid || !validity.valueMissing) return;
|
|
249
|
+
const nextValidity = readValidity(params, event.value);
|
|
250
|
+
if (!nextValidity.valid) return;
|
|
251
|
+
applyValidation(params, {
|
|
252
|
+
customErrors: [],
|
|
253
|
+
validity: nextValidity,
|
|
254
|
+
value: event.value,
|
|
255
|
+
nativeMessage: ""
|
|
256
|
+
});
|
|
257
|
+
},
|
|
258
|
+
markSubmitAttempted({ context, refs }) {
|
|
259
|
+
context.set("submitAttempted", true);
|
|
260
|
+
refs.set("markedDirty", true);
|
|
261
|
+
},
|
|
262
|
+
forceDirty({ refs }) {
|
|
263
|
+
refs.set("markedDirty", true);
|
|
264
|
+
},
|
|
265
|
+
commitValidation(params) {
|
|
266
|
+
commitValidation(params, { value: params.event.value });
|
|
267
|
+
},
|
|
268
|
+
applyAsyncValidation(params) {
|
|
269
|
+
const { event } = params;
|
|
270
|
+
applyValidation(params, {
|
|
271
|
+
customErrors: toErrorArray(event.result),
|
|
272
|
+
validity: event.validity,
|
|
273
|
+
value: event.value,
|
|
274
|
+
nativeMessage: event.nativeMessage
|
|
275
|
+
});
|
|
276
|
+
},
|
|
277
|
+
clearValidation(params) {
|
|
278
|
+
clearValidation(params);
|
|
279
|
+
},
|
|
280
|
+
resetField(params) {
|
|
281
|
+
const { context, refs } = params;
|
|
282
|
+
clearValidation(params);
|
|
283
|
+
context.set("touched", false);
|
|
284
|
+
context.set("dirty", false);
|
|
285
|
+
context.set("focused", false);
|
|
286
|
+
context.set("submitAttempted", false);
|
|
287
|
+
refs.set("markedDirty", false);
|
|
288
|
+
context.set("filled", (refs.get("initialValue") ?? "").length > 0);
|
|
289
|
+
},
|
|
290
|
+
rebaseline(params) {
|
|
291
|
+
rebaseline(params);
|
|
161
292
|
}
|
|
162
|
-
const errs = (formMachineCtx.get("errors")?.[prop("name")])?.messages ?? [];
|
|
163
|
-
context.set("invalid", errs.length > 0);
|
|
164
293
|
}
|
|
165
|
-
}
|
|
294
|
+
}
|
|
166
295
|
});
|
|
296
|
+
function commitValidation(params, options = {}) {
|
|
297
|
+
const { prop, context, refs, send } = params;
|
|
298
|
+
const hostEl = getHostEl(params);
|
|
299
|
+
hostEl?.setCustomValidity("");
|
|
300
|
+
const value = options.value ?? readValue(params);
|
|
301
|
+
let validity = readValidity(params, value);
|
|
302
|
+
if (!refs.get("markedDirty") && !context.get("dirty")) validity = suppressValueMissing(validity);
|
|
303
|
+
const nativeMessage = validity.valid ? "" : getNativeMessage(params, hostEl, validity);
|
|
304
|
+
const seq = refs.get("seq") + 1;
|
|
305
|
+
refs.set("seq", seq);
|
|
306
|
+
const result = prop("validate")?.({
|
|
307
|
+
value,
|
|
308
|
+
validity
|
|
309
|
+
});
|
|
310
|
+
if (isPromise$1(result)) {
|
|
311
|
+
context.set("validating", true);
|
|
312
|
+
result.then((resolved) => send({
|
|
313
|
+
type: "VALIDATE.RESOLVE",
|
|
314
|
+
seq,
|
|
315
|
+
result: resolved,
|
|
316
|
+
validity,
|
|
317
|
+
value,
|
|
318
|
+
nativeMessage
|
|
319
|
+
}), (error) => {
|
|
320
|
+
send({
|
|
321
|
+
type: "VALIDATE.RESOLVE",
|
|
322
|
+
seq,
|
|
323
|
+
result: null,
|
|
324
|
+
validity,
|
|
325
|
+
value,
|
|
326
|
+
nativeMessage
|
|
327
|
+
});
|
|
328
|
+
queueMicrotask(() => {
|
|
329
|
+
throw error;
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
applyValidation(params, {
|
|
335
|
+
customErrors: toErrorArray(result),
|
|
336
|
+
validity,
|
|
337
|
+
value,
|
|
338
|
+
nativeMessage
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
function applyValidation(params, options) {
|
|
342
|
+
const { context, prop } = params;
|
|
343
|
+
const { errors, validity } = resolveValidation(options);
|
|
344
|
+
if (options.customErrors.length > 0) getHostEl(params)?.setCustomValidity(options.customErrors.join(" "));
|
|
345
|
+
const changed = context.get("validity")?.valid !== validity.valid || !isEqual(context.get("errors"), errors);
|
|
346
|
+
context.set("validating", false);
|
|
347
|
+
context.set("validity", validity);
|
|
348
|
+
context.set("errors", errors);
|
|
349
|
+
if (changed) prop("onValidityChange")?.({
|
|
350
|
+
valid: validity.valid,
|
|
351
|
+
errors,
|
|
352
|
+
validity,
|
|
353
|
+
value: options.value
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
function clearValidation(params) {
|
|
357
|
+
const { context, refs } = params;
|
|
358
|
+
refs.set("seq", refs.get("seq") + 1);
|
|
359
|
+
context.set("validating", false);
|
|
360
|
+
context.set("errors", []);
|
|
361
|
+
context.set("validity", null);
|
|
362
|
+
getHostEl(params)?.setCustomValidity("");
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* The native control that hosts constraint validation (`setCustomValidity`, `validity`), when there
|
|
366
|
+
* is one: the element the control id points at, if it is the one carrying the field's name. Looked
|
|
367
|
+
* up at call time, and absent for groups, a Slider, or a Combobox's visible input (no name).
|
|
368
|
+
*/
|
|
369
|
+
function getHostEl({ scope, prop }) {
|
|
370
|
+
const controlEl = getControlEl(scope);
|
|
371
|
+
if (!controlEl || typeof controlEl.setCustomValidity !== "function") return null;
|
|
372
|
+
return trimArraySuffix(controlEl.name) === trimArraySuffix(prop("name")) ? controlEl : null;
|
|
373
|
+
}
|
|
374
|
+
/** Makes the current value the baseline for `dirty`, unless the user already changed it. */
|
|
375
|
+
function rebaseline(params) {
|
|
376
|
+
const { context, refs } = params;
|
|
377
|
+
if (context.get("dirty") || refs.get("markedDirty")) return;
|
|
378
|
+
const value = readValue(params);
|
|
379
|
+
refs.set("initialValue", value);
|
|
380
|
+
context.set("filled", value.length > 0);
|
|
381
|
+
}
|
|
382
|
+
function readValue({ refs, prop }) {
|
|
383
|
+
return getComparableFieldValue(refs.get("rootEl"), prop("name"), prop("defaultValue"));
|
|
384
|
+
}
|
|
385
|
+
/** Native flags of the host, with `valueMissing` decided by the field's `required` and its value. */
|
|
386
|
+
function readValidity(params, value) {
|
|
387
|
+
const { prop } = params;
|
|
388
|
+
const hostEl = getHostEl(params);
|
|
389
|
+
return withValueMissing(hostEl ? getValiditySnapshot(hostEl) : VALID_SNAPSHOT, !!prop("required") && !prop("readOnly") && value === "");
|
|
390
|
+
}
|
|
391
|
+
function getNativeMessage(params, hostEl, validity) {
|
|
392
|
+
if (hostEl?.validationMessage) return hostEl.validationMessage;
|
|
393
|
+
if (!validity.valueMissing) return "";
|
|
394
|
+
const probe = params.scope.getDoc().createElement("input");
|
|
395
|
+
probe.required = true;
|
|
396
|
+
return probe.validationMessage;
|
|
397
|
+
}
|
|
398
|
+
function isPromise$1(value) {
|
|
399
|
+
return typeof value === "object" && value !== null && "then" in value;
|
|
400
|
+
}
|
|
401
|
+
/** At least a short settle, whatever the form's `inputDebounceMs`: see `schedule` in `trackFieldEvents`. */
|
|
402
|
+
function getSettleDelay(rootEl) {
|
|
403
|
+
const debounceMs = getFormMachineFor(rootEl)?.prop("inputDebounceMs") ?? 100;
|
|
404
|
+
return Math.max(debounceMs, 50);
|
|
405
|
+
}
|
|
406
|
+
function isChoiceInput(target) {
|
|
407
|
+
return target instanceof HTMLInputElement && (target.type === "checkbox" || target.type === "radio");
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
//#endregion
|
|
411
|
+
//#region Resources/Private/Primitives/Field/src/field.pointer.ts
|
|
412
|
+
/**
|
|
413
|
+
* Whether a pointer button is held down, and a way to wait for its release. A field blurs on the
|
|
414
|
+
* mousedown that moves the focus away, and showing its error text then shifts the layout: by the
|
|
415
|
+
* mouseup the button the user pressed has moved, and the click is lost.
|
|
416
|
+
*/
|
|
417
|
+
const MAX_PRESS_MS = 1500;
|
|
418
|
+
const waiting = /* @__PURE__ */ new Set();
|
|
419
|
+
let pressed = false;
|
|
420
|
+
let installed = false;
|
|
421
|
+
let giveUp;
|
|
422
|
+
function release() {
|
|
423
|
+
pressed = false;
|
|
424
|
+
clearTimeout(giveUp);
|
|
425
|
+
const callbacks = Array.from(waiting);
|
|
426
|
+
waiting.clear();
|
|
427
|
+
callbacks.forEach((callback) => callback());
|
|
428
|
+
}
|
|
429
|
+
function install() {
|
|
430
|
+
if (installed) return;
|
|
431
|
+
installed = true;
|
|
432
|
+
document.addEventListener("pointerdown", () => {
|
|
433
|
+
pressed = true;
|
|
434
|
+
clearTimeout(giveUp);
|
|
435
|
+
giveUp = setTimeout(release, MAX_PRESS_MS);
|
|
436
|
+
}, true);
|
|
437
|
+
for (const type of ["pointerup", "pointercancel"]) document.addEventListener(type, () => setTimeout(release, 0), true);
|
|
438
|
+
}
|
|
439
|
+
function isPointerPressed() {
|
|
440
|
+
install();
|
|
441
|
+
return pressed;
|
|
442
|
+
}
|
|
443
|
+
/** Runs `callback` once no pointer button is held down, right away if none is. Returns a cancel function. */
|
|
444
|
+
function whenPointerReleased(callback) {
|
|
445
|
+
install();
|
|
446
|
+
if (!pressed) {
|
|
447
|
+
callback();
|
|
448
|
+
return () => {};
|
|
449
|
+
}
|
|
450
|
+
waiting.add(callback);
|
|
451
|
+
return () => waiting.delete(callback);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
//#endregion
|
|
455
|
+
//#region Resources/Private/Primitives/Field/src/field.props.ts
|
|
456
|
+
/**
|
|
457
|
+
* Derived from @zag-js/field of Zag.js (https://github.com/chakra-ui/zag, packages/machines/field,
|
|
458
|
+
* commit 0ce8e3de63b571d37e438bcf28e64759466e56e9), MIT License, Copyright (c) 2021 Chakra UI.
|
|
459
|
+
* Adapted for Fluid Primitives, the git history shows what changed from the original.
|
|
460
|
+
*/
|
|
461
|
+
const props = createProps()([
|
|
462
|
+
"defaultValue",
|
|
463
|
+
"dir",
|
|
464
|
+
"dirty",
|
|
465
|
+
"disabled",
|
|
466
|
+
"getRootNode",
|
|
467
|
+
"id",
|
|
468
|
+
"ids",
|
|
469
|
+
"invalid",
|
|
470
|
+
"listenTo",
|
|
471
|
+
"name",
|
|
472
|
+
"onValidityChange",
|
|
473
|
+
"readOnly",
|
|
474
|
+
"required",
|
|
475
|
+
"touched",
|
|
476
|
+
"validate",
|
|
477
|
+
"validationMode"
|
|
478
|
+
]);
|
|
479
|
+
const splitProps = createSplitProps(props);
|
|
167
480
|
|
|
168
481
|
//#endregion
|
|
169
482
|
//#region Resources/Private/Primitives/Field/Field.ts
|
|
170
483
|
var Field = class extends Component {
|
|
171
|
-
static
|
|
172
|
-
|
|
484
|
+
static componentName = "field";
|
|
485
|
+
/** The `validate` a consumer set; the machine itself runs {@link validate}, which adds the form's. */
|
|
486
|
+
ownValidate;
|
|
487
|
+
cleanups = [];
|
|
488
|
+
cancelDeferredRender;
|
|
489
|
+
/** The base class types its machine as `Machine<any>`. */
|
|
490
|
+
get fieldMachine() {
|
|
491
|
+
return this.machine;
|
|
492
|
+
}
|
|
173
493
|
initMachine(props) {
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
494
|
+
const [machineProps] = splitProps(props);
|
|
495
|
+
this.ownValidate = machineProps.validate;
|
|
496
|
+
const createdMachine = new Machine$1(machine, {
|
|
497
|
+
...machineProps,
|
|
498
|
+
validate: (details) => this.validate(details)
|
|
499
|
+
});
|
|
500
|
+
const rootEl = this.hydrator.query("root");
|
|
501
|
+
registerFieldMachine(rootEl, createdMachine);
|
|
502
|
+
registerFieldMachineForForm(rootEl, createdMachine);
|
|
177
503
|
return createdMachine;
|
|
178
504
|
}
|
|
179
505
|
initApi() {
|
|
180
|
-
return
|
|
506
|
+
return connectField(this.fieldMachine);
|
|
181
507
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
508
|
+
init() {
|
|
509
|
+
super.init();
|
|
510
|
+
this.listenToDependencies();
|
|
511
|
+
}
|
|
512
|
+
updateProps(newProps) {
|
|
513
|
+
const { validate, ...rest } = newProps;
|
|
514
|
+
if ("validate" in newProps) this.ownValidate = validate;
|
|
515
|
+
super.updateProps(rest);
|
|
190
516
|
}
|
|
191
517
|
render() {
|
|
192
|
-
this.
|
|
193
|
-
const rootEl = this.getElement("root");
|
|
518
|
+
const rootEl = this.hydrator.query("root");
|
|
194
519
|
if (rootEl) this.spreadProps(rootEl, this.api.getRootProps());
|
|
195
|
-
const labelEl = this.
|
|
520
|
+
const labelEl = this.hydrator.query("label");
|
|
196
521
|
if (labelEl) this.spreadProps(labelEl, this.api.getLabelProps());
|
|
197
|
-
const controlEl = this.
|
|
198
|
-
if (controlEl) this.spreadProps(controlEl, this.
|
|
199
|
-
const
|
|
200
|
-
if (
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
522
|
+
const controlEl = this.hydrator.query("control");
|
|
523
|
+
if (controlEl) this.spreadProps(controlEl, this.getControlProps(controlEl));
|
|
524
|
+
const helperTextEl = this.hydrator.query("helperText");
|
|
525
|
+
if (helperTextEl) this.spreadProps(helperTextEl, this.api.getHelperTextProps());
|
|
526
|
+
if (isPointerPressed()) {
|
|
527
|
+
this.cancelDeferredRender ??= whenPointerReleased(() => {
|
|
528
|
+
this.cancelDeferredRender = void 0;
|
|
529
|
+
this.render();
|
|
530
|
+
});
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
this.spreadPropsByOptionalValue("errorText", ({ el, value }) => {
|
|
534
|
+
if (value === void 0 && this.api.errors.length > 0) el.textContent = this.api.errors.join(" ");
|
|
535
|
+
return this.api.getErrorTextProps({ match: value });
|
|
536
|
+
});
|
|
537
|
+
this.spreadPropsByValue("indicator", ({ value }) => this.api.getIndicatorProps({ type: value }));
|
|
538
|
+
}
|
|
539
|
+
destroy() {
|
|
540
|
+
const rootEl = this.fieldMachine.refs.get("rootEl");
|
|
541
|
+
if (rootEl) unregisterFieldMachine(rootEl);
|
|
542
|
+
unregisterFieldMachineForForm(rootEl, this.fieldMachine);
|
|
543
|
+
this.cancelDeferredRender?.();
|
|
544
|
+
for (const cleanup of this.cleanups.splice(0)) cleanup();
|
|
545
|
+
super.destroy();
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Zag's props differ per element only in `readOnly`, and the control part is the one place a
|
|
549
|
+
* bare native element is wired up by the field itself (primitives bring their own control).
|
|
550
|
+
*/
|
|
551
|
+
getControlProps(controlEl) {
|
|
552
|
+
switch (controlEl.localName) {
|
|
553
|
+
case "input": return this.api.getInputProps();
|
|
554
|
+
case "textarea": return this.api.getTextareaProps();
|
|
555
|
+
case "select": return this.api.getSelectProps();
|
|
556
|
+
default: return this.api.getControlProps();
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
/** What the machine validates with: the form's messages for this field, then the consumer's own. */
|
|
560
|
+
validate(details) {
|
|
561
|
+
const formMachine = getFormMachineFor(this.fieldMachine.refs.get("rootEl"));
|
|
562
|
+
const formMessages = formMachine ? getFieldMessages(formMachine, this.fieldMachine.prop("name")) : [];
|
|
563
|
+
const own = this.ownValidate?.(details);
|
|
564
|
+
if (isPromise(own)) return own.then((result) => [...formMessages, ...toErrorArray(result)]);
|
|
565
|
+
return [...formMessages, ...toErrorArray(own)];
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* `listenTo`: when a named sibling field in the same form changes value, this field announces
|
|
569
|
+
* `fluid-primitives:field:dependencychange` for consumer DOM reactions and treats it like a
|
|
570
|
+
* change of its own, so it revalidates under the same rules as when its user types.
|
|
571
|
+
*
|
|
572
|
+
* A sibling's registration may come later than this field's (the form can hydrate after its
|
|
573
|
+
* fields), so subscribing is retried whenever a form registers, until every name resolved.
|
|
574
|
+
*/
|
|
575
|
+
listenToDependencies() {
|
|
576
|
+
const listenTo = this.fieldMachine.prop("listenTo");
|
|
577
|
+
const rootEl = this.fieldMachine.refs.get("rootEl");
|
|
578
|
+
const formEl = rootEl?.closest("form");
|
|
579
|
+
if (!listenTo?.length || !rootEl || !formEl) return;
|
|
580
|
+
const names = listenTo.map(trimArraySuffix);
|
|
581
|
+
const pending = new Set(names);
|
|
582
|
+
const readSibling = (sibling) => getCurrentFieldValue(sibling.refs.get("rootEl"), sibling.prop("name"), sibling.prop("defaultValue"));
|
|
583
|
+
const announce = () => {
|
|
584
|
+
const dependencies = {};
|
|
585
|
+
listenTo.forEach((name, index) => {
|
|
586
|
+
const sibling = getFieldMachinesFor(rootEl).get(names[index]);
|
|
587
|
+
dependencies[name] = sibling ? readSibling(sibling) : null;
|
|
588
|
+
});
|
|
589
|
+
const detail = {
|
|
590
|
+
name: this.fieldMachine.prop("name"),
|
|
591
|
+
dependencies,
|
|
592
|
+
values: createFormValues(new FormData(formEl))
|
|
593
|
+
};
|
|
594
|
+
rootEl.dispatchEvent(new CustomEvent(FIELD_DEPENDENCY_CHANGE_EVENT, {
|
|
595
|
+
bubbles: true,
|
|
596
|
+
detail
|
|
597
|
+
}));
|
|
598
|
+
this.fieldMachine.send({
|
|
599
|
+
type: "CONTROL.CHANGE",
|
|
600
|
+
value: getComparableFieldValue(rootEl, this.fieldMachine.prop("name"), this.fieldMachine.prop("defaultValue"))
|
|
601
|
+
});
|
|
602
|
+
};
|
|
603
|
+
const trySubscribe = () => {
|
|
604
|
+
for (const name of Array.from(pending)) {
|
|
605
|
+
const siblingRootEl = getFieldMachinesFor(rootEl).get(name)?.refs.get("rootEl");
|
|
606
|
+
if (!siblingRootEl) continue;
|
|
607
|
+
siblingRootEl.addEventListener(FIELD_VALUE_CHANGE_EVENT, announce);
|
|
608
|
+
this.cleanups.push(() => siblingRootEl.removeEventListener(FIELD_VALUE_CHANGE_EVENT, announce));
|
|
609
|
+
pending.delete(name);
|
|
610
|
+
}
|
|
611
|
+
if (pending.size === 0) formEl.removeEventListener("fluid-primitives:form:registered", trySubscribe);
|
|
612
|
+
};
|
|
613
|
+
trySubscribe();
|
|
614
|
+
if (pending.size > 0) {
|
|
615
|
+
formEl.addEventListener("fluid-primitives:form:registered", trySubscribe);
|
|
616
|
+
this.cleanups.push(() => formEl.removeEventListener("fluid-primitives:form:registered", trySubscribe));
|
|
206
617
|
}
|
|
207
618
|
}
|
|
208
619
|
};
|
|
620
|
+
function isPromise(value) {
|
|
621
|
+
return typeof value === "object" && value !== null && "then" in value;
|
|
622
|
+
}
|
|
209
623
|
|
|
210
624
|
//#endregion
|
|
211
625
|
export { Field };
|