attaform 0.30.0 → 0.31.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/abstract.d.mts +15 -21
- package/dist/abstract.mjs +3 -3
- package/dist/dev/abstract.mjs +4 -4
- package/dist/dev/chunks/devtools.mjs +4 -4
- package/dist/dev/directive.mjs +1 -1
- package/dist/dev/history.mjs +2 -2
- package/dist/dev/index.mjs +5 -5
- package/dist/dev/runtime/plugins/attaform.mjs +1 -1
- package/dist/dev/shared/{attaform-dev.DUZckyV8.mjs → attaform-dev.B1q-x9R9.mjs} +4 -4
- package/dist/dev/shared/{attaform-dev.BcYT6tFh.mjs → attaform-dev.CZz4GYZ6.mjs} +21 -9
- package/dist/dev/shared/{attaform-dev.BisFstxh.mjs → attaform-dev.Caibt7sb.mjs} +16 -15
- package/dist/dev/shared/{attaform-dev.DTrHonpc.mjs → attaform-dev.CtyqGia7.mjs} +176 -260
- package/dist/dev/shared/{attaform-dev.BK6Ge2qR.mjs → attaform-dev.DJ3nsWw6.mjs} +86 -122
- package/dist/dev/shared/{attaform-dev.BcNtyOmf.mjs → attaform-dev.DlaT4xGd.mjs} +4 -4
- package/dist/dev/shared/{attaform-dev.D6CLvaQT.mjs → attaform-dev.k5X47GMD.mjs} +520 -395
- package/dist/dev/shared/{attaform-dev.CQ2rJiAX.mjs → attaform-dev.y9x9Vp9F.mjs} +324 -234
- package/dist/dev/shared/{attaform-dev.BQc5QV3Y.mjs → attaform-dev.zlvDr0WB.mjs} +2 -2
- package/dist/dev/zod-v3.mjs +6 -6
- package/dist/dev/zod-v4.mjs +6 -6
- package/dist/dev/zod.mjs +5 -5
- package/dist/directive.d.mts +4 -4
- package/dist/directive.mjs +1 -1
- package/dist/esbuild.d.mts +11 -0
- package/dist/esbuild.mjs +1 -1
- package/dist/history.d.mts +3 -32
- package/dist/history.mjs +2 -2
- package/dist/index.d.mts +67 -70
- package/dist/index.mjs +4 -4
- package/dist/nuxt.d.mts +4 -3
- package/dist/nuxt.mjs +3 -6
- package/dist/rollup.d.mts +11 -0
- package/dist/rollup.mjs +1 -1
- package/dist/rspack.d.mts +19 -11
- package/dist/rspack.mjs +1 -1
- package/dist/runtime/components/AttaformDevtoolsPanel.vue +15 -15
- package/dist/runtime/components/DevtoolsValueTree.vue +2 -2
- package/dist/runtime/plugins/attaform.mjs +1 -1
- package/dist/shared/{attaform.BRDDQeHV.mjs → attaform.8mfKZCwH.mjs} +2 -2
- package/dist/shared/{attaform.DRUT703x.d.mts → attaform.B-Tb62qV.d.mts} +12 -12
- package/dist/shared/{attaform.v0fQtWyA.mjs → attaform.B1rH9ht6.mjs} +1 -1
- package/dist/shared/{attaform.DhnPb6nK.mjs → attaform.BAf3UNn4.mjs} +1 -1
- package/dist/shared/{attaform.BXneBxEj.mjs → attaform.BWUn8Kjy.mjs} +85 -121
- package/dist/shared/{attaform.WiGdIqgx.mjs → attaform.C4shmwyJ.mjs} +2 -2
- package/dist/shared/{attaform.Rc9u4Vfw.d.mts → attaform.CRVe8s82.d.mts} +83 -95
- package/dist/shared/attaform.D9RqPgGu.d.mts +42 -0
- package/dist/shared/attaform.DAo0QBmS.d.mts +2173 -0
- package/dist/shared/{attaform.BK0cgu2K.mjs → attaform.DBCbxlyk.mjs} +62 -4
- package/dist/shared/{attaform.CUfrMK7t.mjs → attaform.DL2nl_QR.mjs} +15 -14
- package/dist/shared/{attaform.DNJOWjQw.mjs → attaform.DNPJPOO7.mjs} +299 -218
- package/dist/shared/attaform.DZnrDrNb.d.mts +4657 -0
- package/dist/shared/{attaform.B3resMQ1.mjs → attaform.DxKQsqFZ.mjs} +496 -371
- package/dist/shared/{attaform.ByWP6kSx.mjs → attaform.L011tHgJ.mjs} +3 -3
- package/dist/shared/{attaform.CJ8DpRGj.mjs → attaform.LUPC-2Qg.mjs} +175 -257
- package/dist/transforms.d.mts +153 -26
- package/dist/transforms.mjs +1 -1
- package/dist/vite.d.mts +29 -52
- package/dist/vite.mjs +3 -3
- package/dist/webpack.d.mts +20 -12
- package/dist/webpack.mjs +1 -1
- package/dist/zod-v3.d.mts +90 -96
- package/dist/zod-v3.mjs +5 -5
- package/dist/zod-v4.d.mts +120 -118
- package/dist/zod-v4.mjs +5 -5
- package/dist/zod.d.mts +5 -5
- package/dist/zod.mjs +4 -4
- package/package.json +7 -6
- package/skills/attaform/SKILL.md +1 -1
- package/skills/attaform/references/custom-components.md +4 -0
- package/dist/shared/attaform.Clz0ArIi.d.mts +0 -44
- package/dist/shared/attaform.UGyOK83T.d.mts +0 -2366
- package/dist/shared/attaform.bKLzMU0o.d.mts +0 -4761
|
@@ -1,8 +1,373 @@
|
|
|
1
|
-
import { reactive, markRaw,
|
|
2
|
-
import { e as canonicalizePath } from './attaform-dev.
|
|
1
|
+
import { isRef, warn, toRaw, reactive, markRaw, effectScope, watch, nextTick } from 'vue';
|
|
2
|
+
import { e as canonicalizePath } from './attaform-dev.zlvDr0WB.mjs';
|
|
3
|
+
|
|
4
|
+
function activeElementOf(node) {
|
|
5
|
+
const rootNode = node.getRootNode();
|
|
6
|
+
return rootNode instanceof Document || rootNode instanceof ShadowRoot ? rootNode.activeElement : null;
|
|
7
|
+
}
|
|
3
8
|
|
|
4
9
|
const INTERACTIVE_TAG_NAMES = /* @__PURE__ */ new Set(["INPUT", "SELECT", "TEXTAREA"]);
|
|
5
10
|
|
|
11
|
+
const isArray = Array.isArray;
|
|
12
|
+
function isFunction(value) {
|
|
13
|
+
return typeof value === "function";
|
|
14
|
+
}
|
|
15
|
+
function toTypeString(value) {
|
|
16
|
+
return Object.prototype.toString.call(value);
|
|
17
|
+
}
|
|
18
|
+
function isSet(value) {
|
|
19
|
+
return toTypeString(value) === "[object Set]";
|
|
20
|
+
}
|
|
21
|
+
function isDate(value) {
|
|
22
|
+
return toTypeString(value) === "[object Date]";
|
|
23
|
+
}
|
|
24
|
+
function isSymbol(value) {
|
|
25
|
+
return typeof value === "symbol";
|
|
26
|
+
}
|
|
27
|
+
function isObject(value) {
|
|
28
|
+
return value !== null && typeof value === "object";
|
|
29
|
+
}
|
|
30
|
+
function looseToNumber(val) {
|
|
31
|
+
const n = parseFloat(val);
|
|
32
|
+
return isNaN(n) ? val : n;
|
|
33
|
+
}
|
|
34
|
+
function looseCompareArrays(a, b) {
|
|
35
|
+
if (a.length !== b.length) return false;
|
|
36
|
+
let equal = true;
|
|
37
|
+
for (let i = 0; equal && i < a.length; i++) {
|
|
38
|
+
equal = looseEqual(a[i], b[i]);
|
|
39
|
+
}
|
|
40
|
+
return equal;
|
|
41
|
+
}
|
|
42
|
+
function looseEqual(a, b) {
|
|
43
|
+
if (a === b) return true;
|
|
44
|
+
const aValidType = isDate(a);
|
|
45
|
+
const bValidType = isDate(b);
|
|
46
|
+
if (aValidType || bValidType) {
|
|
47
|
+
return aValidType && bValidType ? a.getTime() === b.getTime() : false;
|
|
48
|
+
}
|
|
49
|
+
const aSymbol = isSymbol(a);
|
|
50
|
+
const bSymbol = isSymbol(b);
|
|
51
|
+
if (aSymbol || bSymbol) return a === b;
|
|
52
|
+
const aIsArray = isArray(a);
|
|
53
|
+
const bIsArray = isArray(b);
|
|
54
|
+
if (aIsArray || bIsArray) {
|
|
55
|
+
return aIsArray && bIsArray ? looseCompareArrays(a, b) : false;
|
|
56
|
+
}
|
|
57
|
+
const aIsObject = isObject(a);
|
|
58
|
+
const bIsObject = isObject(b);
|
|
59
|
+
if (aIsObject !== bIsObject) return false;
|
|
60
|
+
if (aIsObject && bIsObject) {
|
|
61
|
+
const keysA = Object.keys(a);
|
|
62
|
+
if (keysA.length !== Object.keys(b).length) return false;
|
|
63
|
+
for (const key of keysA) {
|
|
64
|
+
const hasA = Object.prototype.hasOwnProperty.call(a, key);
|
|
65
|
+
const hasB = Object.prototype.hasOwnProperty.call(b, key);
|
|
66
|
+
if (!hasA || !hasB || !looseEqual(a[key], b[key])) return false;
|
|
67
|
+
}
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
return String(a) === String(b);
|
|
71
|
+
}
|
|
72
|
+
function looseIndexOf(arr, val) {
|
|
73
|
+
return arr.findIndex((item) => looseEqual(item, val));
|
|
74
|
+
}
|
|
75
|
+
function invokeArrayFns(fns, ...args) {
|
|
76
|
+
for (let i = 0; i < fns.length; i++) {
|
|
77
|
+
const fn = fns[i];
|
|
78
|
+
if (fn) fn(...args);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const V_REGISTER_MARKER = /* @__PURE__ */ Symbol.for("attaform:v-register-directive");
|
|
83
|
+
const REGISTER_OWNER_MARKER = /* @__PURE__ */ Symbol.for("attaform:register-owner-marker");
|
|
84
|
+
const SSR_COMPONENT_HOST_MODIFIER = "attaformComponentHost";
|
|
85
|
+
const V_REGISTER_COMPILED_MODIFIER = "attaformCompiled";
|
|
86
|
+
function isRegisterValue(val) {
|
|
87
|
+
if (typeof val !== "object" || val === null) return false;
|
|
88
|
+
if (!("innerRef" in val)) return false;
|
|
89
|
+
if (!isRef(val.innerRef)) return false;
|
|
90
|
+
if (!("registerElement" in val)) return false;
|
|
91
|
+
if (typeof val.registerElement !== "function") return false;
|
|
92
|
+
if (!("setValueWithInternalPath" in val)) return false;
|
|
93
|
+
if (typeof val.setValueWithInternalPath !== "function") return false;
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
function isTransforming(value) {
|
|
97
|
+
return isRegisterValue(value) && value.transforming;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const assignKey = /* @__PURE__ */ Symbol.for("attaform:assign-key");
|
|
101
|
+
const DEFAULT_ASSIGNER_TAG = /* @__PURE__ */ Symbol.for("attaform:default-assigner-tag");
|
|
102
|
+
function isDefaultAssigner(fn) {
|
|
103
|
+
return typeof fn === "function" && fn[DEFAULT_ASSIGNER_TAG] === true;
|
|
104
|
+
}
|
|
105
|
+
const CONSUMER_WRAPPED_TAG = /* @__PURE__ */ Symbol.for("attaform:consumer-wrapped-assigner");
|
|
106
|
+
function isConsumerWrapped(fn) {
|
|
107
|
+
return typeof fn === "function" && fn[CONSUMER_WRAPPED_TAG] === true;
|
|
108
|
+
}
|
|
109
|
+
function fireAssigner(el, registerValue, value) {
|
|
110
|
+
const fn = el[assignKey];
|
|
111
|
+
if (fn === void 0) return void 0;
|
|
112
|
+
if (isDefaultAssigner(fn) || isConsumerWrapped(fn)) {
|
|
113
|
+
return fn(value);
|
|
114
|
+
}
|
|
115
|
+
if (!isRegisterValue(registerValue)) {
|
|
116
|
+
return fn(value, void 0);
|
|
117
|
+
}
|
|
118
|
+
return wrapWithTransforms(
|
|
119
|
+
value,
|
|
120
|
+
registerValue,
|
|
121
|
+
(coerced) => fn(coerced, registerValue),
|
|
122
|
+
void 0
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
function isThenable(x) {
|
|
126
|
+
return x !== null && (typeof x === "object" || typeof x === "function") && typeof x.then === "function";
|
|
127
|
+
}
|
|
128
|
+
function makeTransformContext() {
|
|
129
|
+
const holder = { controller: null, aborted: false };
|
|
130
|
+
const ctx = {
|
|
131
|
+
get signal() {
|
|
132
|
+
if (holder.controller === null) {
|
|
133
|
+
holder.controller = new AbortController();
|
|
134
|
+
if (holder.aborted) holder.controller.abort();
|
|
135
|
+
}
|
|
136
|
+
return holder.controller.signal;
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
return { ctx, holder };
|
|
140
|
+
}
|
|
141
|
+
function runTransforms(initial, registerValue) {
|
|
142
|
+
const transforms = registerValue.transforms;
|
|
143
|
+
if (transforms === void 0 || transforms.length === 0) {
|
|
144
|
+
return { kind: "sync", ok: true, value: initial };
|
|
145
|
+
}
|
|
146
|
+
const { ctx, holder } = makeTransformContext();
|
|
147
|
+
let v = initial;
|
|
148
|
+
for (let i = 0; i < transforms.length; i++) {
|
|
149
|
+
const fn = transforms[i];
|
|
150
|
+
let out;
|
|
151
|
+
try {
|
|
152
|
+
out = fn(v, ctx);
|
|
153
|
+
} catch (err) {
|
|
154
|
+
logTransformFailure(registerValue.path, i, fn, err);
|
|
155
|
+
return { kind: "sync", ok: false };
|
|
156
|
+
}
|
|
157
|
+
if (isThenable(out)) {
|
|
158
|
+
const rest = transforms.slice(i + 1);
|
|
159
|
+
const seed = out;
|
|
160
|
+
const run = () => rest.reduce(
|
|
161
|
+
(acc, next) => acc.then((value) => next(value, ctx)),
|
|
162
|
+
Promise.resolve(seed)
|
|
163
|
+
);
|
|
164
|
+
return { kind: "async", run, holder };
|
|
165
|
+
}
|
|
166
|
+
v = out;
|
|
167
|
+
}
|
|
168
|
+
return { kind: "sync", ok: true, value: v };
|
|
169
|
+
}
|
|
170
|
+
function kickoffAsyncTransform(rv, holder, run, commit, syncDom) {
|
|
171
|
+
const token = rv.beginTransform(holder);
|
|
172
|
+
void run().then(
|
|
173
|
+
(value) => {
|
|
174
|
+
const live = rv.isCurrentTransform(token);
|
|
175
|
+
rv.endTransform(token);
|
|
176
|
+
if (!live) return;
|
|
177
|
+
const coerced = applyCoerce(value, rv);
|
|
178
|
+
const wrote = commit(coerced);
|
|
179
|
+
if (wrote === false) rv.setTransformError(transformGateRejectedError(rv.path));
|
|
180
|
+
else syncDom?.();
|
|
181
|
+
},
|
|
182
|
+
(err) => {
|
|
183
|
+
if (rv.isCurrentTransform(token)) rv.setTransformError(toTransformError(err));
|
|
184
|
+
rv.endTransform(token);
|
|
185
|
+
}
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
function logTransformFailure(path, index, fn, err) {
|
|
189
|
+
{
|
|
190
|
+
const namePart = fn.name !== "" ? `, '${fn.name}'` : "";
|
|
191
|
+
console.error(
|
|
192
|
+
`[attaform] transform threw for path '${path}' (index ${index}${namePart}). Write aborted. Transforms must not throw; wrap your own try/catch if the throw is recoverable. Original error:`,
|
|
193
|
+
err
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function applyCoerce(value, registerValue) {
|
|
198
|
+
return registerValue.coerce !== void 0 ? registerValue.coerce(value) : value;
|
|
199
|
+
}
|
|
200
|
+
function wrapWithTransforms(value, registerValue, commit, syncDom) {
|
|
201
|
+
const r = runTransforms(value, registerValue);
|
|
202
|
+
if (r.kind === "async") {
|
|
203
|
+
kickoffAsyncTransform(registerValue, r.holder, r.run, commit, syncDom);
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
if (!r.ok) return false;
|
|
207
|
+
const coerced = applyCoerce(r.value, registerValue);
|
|
208
|
+
return commit(coerced);
|
|
209
|
+
}
|
|
210
|
+
function toTransformError(value) {
|
|
211
|
+
if (value instanceof Error) return value;
|
|
212
|
+
const message = typeof value === "string" && value.length > 0 ? value : `Transform rejected with a non-Error value (${typeof value})`;
|
|
213
|
+
return new Error(message, { cause: value });
|
|
214
|
+
}
|
|
215
|
+
function transformGateRejectedError(path) {
|
|
216
|
+
return new Error(
|
|
217
|
+
`[attaform] transform result for path '${path}' was rejected by the field's type gate (the resolved value did not fit the schema slot).`
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
const getModelAssigner = (el, vnode, registerValue) => {
|
|
221
|
+
const fn = vnode.props?.["onUpdate:registerValue"] ?? vnode.props?.["on:update:registerValue"];
|
|
222
|
+
if (isArray(fn)) {
|
|
223
|
+
const fnArr = fn.filter((x) => isFunction(x));
|
|
224
|
+
const wrapped = (value) => {
|
|
225
|
+
return wrapWithTransforms(
|
|
226
|
+
value,
|
|
227
|
+
registerValue,
|
|
228
|
+
(coerced) => {
|
|
229
|
+
invokeArrayFns(fnArr, coerced, registerValue);
|
|
230
|
+
return void 0;
|
|
231
|
+
},
|
|
232
|
+
void 0
|
|
233
|
+
);
|
|
234
|
+
};
|
|
235
|
+
wrapped[CONSUMER_WRAPPED_TAG] = true;
|
|
236
|
+
return wrapped;
|
|
237
|
+
}
|
|
238
|
+
if (isFunction(fn)) {
|
|
239
|
+
const handler = fn;
|
|
240
|
+
const wrapped = (value) => {
|
|
241
|
+
return wrapWithTransforms(
|
|
242
|
+
value,
|
|
243
|
+
registerValue,
|
|
244
|
+
(coerced) => handler(coerced, registerValue),
|
|
245
|
+
void 0
|
|
246
|
+
);
|
|
247
|
+
};
|
|
248
|
+
wrapped[CONSUMER_WRAPPED_TAG] = true;
|
|
249
|
+
return wrapped;
|
|
250
|
+
}
|
|
251
|
+
const defaultAssigner = (value) => {
|
|
252
|
+
if (value === void 0 && registerValue.acceptsUndefined) {
|
|
253
|
+
return registerValue.setValueWithInternalPath(void 0);
|
|
254
|
+
}
|
|
255
|
+
return wrapWithTransforms(
|
|
256
|
+
value,
|
|
257
|
+
registerValue,
|
|
258
|
+
(coerced) => registerValue.setValueWithInternalPath(coerced),
|
|
259
|
+
el._syncFromStorage
|
|
260
|
+
);
|
|
261
|
+
};
|
|
262
|
+
defaultAssigner[DEFAULT_ASSIGNER_TAG] = true;
|
|
263
|
+
return defaultAssigner;
|
|
264
|
+
};
|
|
265
|
+
function makeNoopAssigner() {
|
|
266
|
+
const noop = (_) => void 0;
|
|
267
|
+
noop[DEFAULT_ASSIGNER_TAG] = true;
|
|
268
|
+
return noop;
|
|
269
|
+
}
|
|
270
|
+
function setAssignFunction(el, vnode, value) {
|
|
271
|
+
const current = el[assignKey];
|
|
272
|
+
if (current !== void 0 && !isDefaultAssigner(current) && !isConsumerWrapped(current)) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
if (value === void 0) {
|
|
276
|
+
el[assignKey] = makeNoopAssigner();
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
if (!isRegisterValue(value)) {
|
|
280
|
+
{
|
|
281
|
+
warn(
|
|
282
|
+
`v-register expected a RegisterValue, got '${typeof value}'. Bind to form.register('field'), not the field's ref, value, or path string.`
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
el[assignKey] = makeNoopAssigner();
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
el[assignKey] = getModelAssigner(el, vnode, value);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const inherited = /* @__PURE__ */ new WeakMap();
|
|
292
|
+
function recordInheritedModifiers(rv, modifiers) {
|
|
293
|
+
const lazy = modifiers.lazy === true;
|
|
294
|
+
const trim = modifiers.trim === true;
|
|
295
|
+
const number = modifiers.number === true;
|
|
296
|
+
if ((lazy || trim || number) && typeof rv === "object" && rv !== null) {
|
|
297
|
+
inherited.set(toRaw(rv), { lazy, trim, number });
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
function withInheritedModifiers(rv, own) {
|
|
301
|
+
const found = typeof rv === "object" && rv !== null ? inherited.get(toRaw(rv)) : void 0;
|
|
302
|
+
return found === void 0 ? own : { ...found, ...own };
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function commit(rv, raw, modifiers) {
|
|
306
|
+
let value = raw;
|
|
307
|
+
if (modifiers.number === true && typeof value === "string") {
|
|
308
|
+
const cast = looseToNumber(value);
|
|
309
|
+
if (typeof cast !== "number") return rv.markBlank();
|
|
310
|
+
if (!Number.isFinite(cast)) return false;
|
|
311
|
+
value = cast;
|
|
312
|
+
}
|
|
313
|
+
if (value == null || value === "" && !rv.acceptsString) return rv.commitFromHost(value);
|
|
314
|
+
return wrapWithTransforms(value, rv, (coerced) => rv.commitFromHost(coerced), void 0) !== false;
|
|
315
|
+
}
|
|
316
|
+
function createHostChannel() {
|
|
317
|
+
const editing = /* @__PURE__ */ new Set();
|
|
318
|
+
const pending = /* @__PURE__ */ new Map();
|
|
319
|
+
const heldDraft = (rv) => {
|
|
320
|
+
const draft = pending.get(rv.path);
|
|
321
|
+
return draft?.basis === rv.innerRef.value ? draft : void 0;
|
|
322
|
+
};
|
|
323
|
+
return {
|
|
324
|
+
write(rv, value, ownModifiers) {
|
|
325
|
+
const modifiers = withInheritedModifiers(rv, ownModifiers);
|
|
326
|
+
if (modifiers.lazy === true && editing.has(rv.path)) {
|
|
327
|
+
pending.set(rv.path, { value, basis: rv.innerRef.value });
|
|
328
|
+
return true;
|
|
329
|
+
}
|
|
330
|
+
return commit(rv, value, modifiers);
|
|
331
|
+
},
|
|
332
|
+
writeInput(rv, payload, modifiers) {
|
|
333
|
+
if (typeof payload !== "object" || payload === null || payload instanceof Event || !("value" in payload) || !Object.hasOwn(payload, "value")) {
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
return rv.setValueFromHost(payload.value, modifiers);
|
|
337
|
+
},
|
|
338
|
+
markEditing(rv, isEditing, modifiers) {
|
|
339
|
+
if (isEditing) {
|
|
340
|
+
editing.add(rv.path);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
editing.delete(rv.path);
|
|
344
|
+
const draft = heldDraft(rv);
|
|
345
|
+
if (pending.delete(rv.path)) {
|
|
346
|
+
if (draft !== void 0) {
|
|
347
|
+
const buffered = draft.value;
|
|
348
|
+
commit(
|
|
349
|
+
rv,
|
|
350
|
+
modifiers.trim === true && typeof buffered === "string" ? buffered.trim() : buffered,
|
|
351
|
+
modifiers
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
if (modifiers.trim === true) {
|
|
357
|
+
const current = rv.innerRef.value;
|
|
358
|
+
if (typeof current === "string" && current !== current.trim()) {
|
|
359
|
+
commit(rv, current.trim(), modifiers);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
},
|
|
363
|
+
holdsDraft: (rv) => heldDraft(rv) !== void 0,
|
|
364
|
+
release(rv) {
|
|
365
|
+
editing.delete(rv.path);
|
|
366
|
+
pending.delete(rv.path);
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
6
371
|
const attaformListenersSymbol = /* @__PURE__ */ Symbol.for("attaform:focus-listeners");
|
|
7
372
|
const HOST_FOCUS_TARGET_SELECTOR = [
|
|
8
373
|
'input:not([type="hidden"])',
|
|
@@ -32,9 +397,7 @@ function attachFocusListeners(kernel, segments, element, instanceMeta) {
|
|
|
32
397
|
element.addEventListener("focus", handleFocus);
|
|
33
398
|
element.addEventListener("blur", handleBlur);
|
|
34
399
|
target[attaformListenersSymbol] = { handleFocus, handleBlur };
|
|
35
|
-
|
|
36
|
-
const activeElement = rootNode instanceof Document || rootNode instanceof ShadowRoot ? rootNode.activeElement : null;
|
|
37
|
-
if (activeElement === element) {
|
|
400
|
+
if (activeElementOf(element) === element) {
|
|
38
401
|
kernel.markFocused(segments, true, focusMeta);
|
|
39
402
|
}
|
|
40
403
|
}
|
|
@@ -107,135 +470,47 @@ function createDomBinding(kernel) {
|
|
|
107
470
|
path: record.path,
|
|
108
471
|
element: el,
|
|
109
472
|
host: false,
|
|
110
|
-
formInstanceId: elementToFormInstance.get(el) ?? ""
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
for (const [key, target] of hostTargets) {
|
|
115
|
-
if (elements.has(key)) continue;
|
|
116
|
-
flat.push({
|
|
117
|
-
path: target.path,
|
|
118
|
-
element: target.hostRoot,
|
|
119
|
-
host: true,
|
|
120
|
-
formInstanceId: target.formInstanceId
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
flat.sort(
|
|
124
|
-
(a, b) => a.element.compareDocumentPosition(b.element) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1
|
|
125
|
-
);
|
|
126
|
-
return flat;
|
|
127
|
-
}
|
|
128
|
-
function getFirstErrorElement(formInstanceId) {
|
|
129
|
-
sortedRegistrationsCache ?? (sortedRegistrationsCache = rebuildSortedRegistrations());
|
|
130
|
-
for (const entry of sortedRegistrationsCache) {
|
|
131
|
-
if (entry.formInstanceId !== formInstanceId) continue;
|
|
132
|
-
if (!entry.element.isConnected) continue;
|
|
133
|
-
if (entry.element.offsetParent === null) continue;
|
|
134
|
-
if (kernel.getErrorsForPath(entry.path).length === 0) continue;
|
|
135
|
-
const element = entry.host ? resolveHostFocusTarget(entry.element) : entry.element;
|
|
136
|
-
return { path: entry.path, element };
|
|
137
|
-
}
|
|
138
|
-
return null;
|
|
139
|
-
}
|
|
140
|
-
return {
|
|
141
|
-
elements,
|
|
142
|
-
attach,
|
|
143
|
-
detach,
|
|
144
|
-
markHostConnected,
|
|
145
|
-
getFirstErrorElement
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
function armDomBinding(value) {
|
|
149
|
-
value.ensureDomBinding?.(createDomBinding);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const V_REGISTER_MARKER = /* @__PURE__ */ Symbol.for("attaform:v-register-directive");
|
|
153
|
-
const REGISTER_OWNER_MARKER = /* @__PURE__ */ Symbol.for("attaform:register-owner-marker");
|
|
154
|
-
const SSR_COMPONENT_HOST_MODIFIER = "attaformComponentHost";
|
|
155
|
-
const V_REGISTER_COMPILED_MODIFIER = "attaformCompiled";
|
|
156
|
-
function isRegisterValue(val) {
|
|
157
|
-
if (typeof val !== "object" || val === null) return false;
|
|
158
|
-
if (!("innerRef" in val)) return false;
|
|
159
|
-
if (!isRef(val.innerRef)) return false;
|
|
160
|
-
if (!("registerElement" in val)) return false;
|
|
161
|
-
if (typeof val.registerElement !== "function") return false;
|
|
162
|
-
if (!("setValueWithInternalPath" in val)) return false;
|
|
163
|
-
if (typeof val.setValueWithInternalPath !== "function") return false;
|
|
164
|
-
return true;
|
|
165
|
-
}
|
|
166
|
-
function isTransforming(value) {
|
|
167
|
-
return isRegisterValue(value) && value.transforming;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const isArray = Array.isArray;
|
|
171
|
-
function isFunction(value) {
|
|
172
|
-
return typeof value === "function";
|
|
173
|
-
}
|
|
174
|
-
function toTypeString(value) {
|
|
175
|
-
return Object.prototype.toString.call(value);
|
|
176
|
-
}
|
|
177
|
-
function isSet(value) {
|
|
178
|
-
return toTypeString(value) === "[object Set]";
|
|
179
|
-
}
|
|
180
|
-
function isDate(value) {
|
|
181
|
-
return toTypeString(value) === "[object Date]";
|
|
182
|
-
}
|
|
183
|
-
function isSymbol(value) {
|
|
184
|
-
return typeof value === "symbol";
|
|
185
|
-
}
|
|
186
|
-
function isObject(value) {
|
|
187
|
-
return value !== null && typeof value === "object";
|
|
188
|
-
}
|
|
189
|
-
function looseToNumber(val) {
|
|
190
|
-
const n = parseFloat(val);
|
|
191
|
-
return isNaN(n) ? val : n;
|
|
192
|
-
}
|
|
193
|
-
function looseCompareArrays(a, b) {
|
|
194
|
-
if (a.length !== b.length) return false;
|
|
195
|
-
let equal = true;
|
|
196
|
-
for (let i = 0; equal && i < a.length; i++) {
|
|
197
|
-
equal = looseEqual(a[i], b[i]);
|
|
198
|
-
}
|
|
199
|
-
return equal;
|
|
200
|
-
}
|
|
201
|
-
function looseEqual(a, b) {
|
|
202
|
-
if (a === b) return true;
|
|
203
|
-
const aValidType = isDate(a);
|
|
204
|
-
const bValidType = isDate(b);
|
|
205
|
-
if (aValidType || bValidType) {
|
|
206
|
-
return aValidType && bValidType ? a.getTime() === b.getTime() : false;
|
|
207
|
-
}
|
|
208
|
-
const aSymbol = isSymbol(a);
|
|
209
|
-
const bSymbol = isSymbol(b);
|
|
210
|
-
if (aSymbol || bSymbol) return a === b;
|
|
211
|
-
const aIsArray = isArray(a);
|
|
212
|
-
const bIsArray = isArray(b);
|
|
213
|
-
if (aIsArray || bIsArray) {
|
|
214
|
-
return aIsArray && bIsArray ? looseCompareArrays(a, b) : false;
|
|
473
|
+
formInstanceId: elementToFormInstance.get(el) ?? ""
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
for (const [key, target] of hostTargets) {
|
|
478
|
+
if (elements.has(key)) continue;
|
|
479
|
+
flat.push({
|
|
480
|
+
path: target.path,
|
|
481
|
+
element: target.hostRoot,
|
|
482
|
+
host: true,
|
|
483
|
+
formInstanceId: target.formInstanceId
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
flat.sort(
|
|
487
|
+
(a, b) => a.element.compareDocumentPosition(b.element) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1
|
|
488
|
+
);
|
|
489
|
+
return flat;
|
|
215
490
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
if (!hasA || !hasB || !looseEqual(a[key], b[key])) return false;
|
|
491
|
+
function getFirstErrorElement(formInstanceId) {
|
|
492
|
+
sortedRegistrationsCache ?? (sortedRegistrationsCache = rebuildSortedRegistrations());
|
|
493
|
+
for (const entry of sortedRegistrationsCache) {
|
|
494
|
+
if (entry.formInstanceId !== formInstanceId) continue;
|
|
495
|
+
if (!entry.element.isConnected) continue;
|
|
496
|
+
if (entry.element.offsetParent === null) continue;
|
|
497
|
+
if (kernel.getErrorsForPath(entry.path).length === 0) continue;
|
|
498
|
+
const element = entry.host ? resolveHostFocusTarget(entry.element) : entry.element;
|
|
499
|
+
return { path: entry.path, element };
|
|
226
500
|
}
|
|
227
|
-
return
|
|
501
|
+
return null;
|
|
228
502
|
}
|
|
229
|
-
return
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
503
|
+
return {
|
|
504
|
+
elements,
|
|
505
|
+
attach,
|
|
506
|
+
detach,
|
|
507
|
+
markHostConnected,
|
|
508
|
+
getFirstErrorElement,
|
|
509
|
+
hostChannel: createHostChannel()
|
|
510
|
+
};
|
|
233
511
|
}
|
|
234
|
-
function
|
|
235
|
-
|
|
236
|
-
const fn = fns[i];
|
|
237
|
-
if (fn) fn(...args);
|
|
238
|
-
}
|
|
512
|
+
function armDomBinding(value) {
|
|
513
|
+
return value.ensureDomBinding?.(createDomBinding);
|
|
239
514
|
}
|
|
240
515
|
|
|
241
516
|
const MANAGED_ARIA_ATTRS = [
|
|
@@ -386,197 +661,6 @@ function isRegisterValueLike(val) {
|
|
|
386
661
|
return typeof val === "object" && val !== null && "markInteracted" in val && typeof val.markInteracted === "function";
|
|
387
662
|
}
|
|
388
663
|
|
|
389
|
-
const assignKey = /* @__PURE__ */ Symbol.for("attaform:assign-key");
|
|
390
|
-
const DEFAULT_ASSIGNER_TAG = /* @__PURE__ */ Symbol.for("attaform:default-assigner-tag");
|
|
391
|
-
function isDefaultAssigner(fn) {
|
|
392
|
-
return typeof fn === "function" && fn[DEFAULT_ASSIGNER_TAG] === true;
|
|
393
|
-
}
|
|
394
|
-
const CONSUMER_WRAPPED_TAG = /* @__PURE__ */ Symbol.for("attaform:consumer-wrapped-assigner");
|
|
395
|
-
function isConsumerWrapped(fn) {
|
|
396
|
-
return typeof fn === "function" && fn[CONSUMER_WRAPPED_TAG] === true;
|
|
397
|
-
}
|
|
398
|
-
function fireAssigner(el, registerValue, value) {
|
|
399
|
-
const fn = el[assignKey];
|
|
400
|
-
if (fn === void 0) return void 0;
|
|
401
|
-
if (isDefaultAssigner(fn) || isConsumerWrapped(fn)) {
|
|
402
|
-
return fn(value);
|
|
403
|
-
}
|
|
404
|
-
if (!isRegisterValue(registerValue)) {
|
|
405
|
-
return fn(value, void 0);
|
|
406
|
-
}
|
|
407
|
-
return wrapWithTransforms(
|
|
408
|
-
value,
|
|
409
|
-
registerValue,
|
|
410
|
-
(coerced) => fn(coerced, registerValue),
|
|
411
|
-
void 0
|
|
412
|
-
);
|
|
413
|
-
}
|
|
414
|
-
function isThenable(x) {
|
|
415
|
-
return x !== null && (typeof x === "object" || typeof x === "function") && typeof x.then === "function";
|
|
416
|
-
}
|
|
417
|
-
function makeTransformContext() {
|
|
418
|
-
const holder = { controller: null, aborted: false };
|
|
419
|
-
const ctx = {
|
|
420
|
-
get signal() {
|
|
421
|
-
if (holder.controller === null) {
|
|
422
|
-
holder.controller = new AbortController();
|
|
423
|
-
if (holder.aborted) holder.controller.abort();
|
|
424
|
-
}
|
|
425
|
-
return holder.controller.signal;
|
|
426
|
-
}
|
|
427
|
-
};
|
|
428
|
-
return { ctx, holder };
|
|
429
|
-
}
|
|
430
|
-
function runTransforms(initial, registerValue) {
|
|
431
|
-
const transforms = registerValue.transforms;
|
|
432
|
-
if (transforms === void 0 || transforms.length === 0) {
|
|
433
|
-
return { kind: "sync", ok: true, value: initial };
|
|
434
|
-
}
|
|
435
|
-
const { ctx, holder } = makeTransformContext();
|
|
436
|
-
let v = initial;
|
|
437
|
-
for (let i = 0; i < transforms.length; i++) {
|
|
438
|
-
const fn = transforms[i];
|
|
439
|
-
let out;
|
|
440
|
-
try {
|
|
441
|
-
out = fn(v, ctx);
|
|
442
|
-
} catch (err) {
|
|
443
|
-
logTransformFailure(registerValue.path, i, fn, err);
|
|
444
|
-
return { kind: "sync", ok: false };
|
|
445
|
-
}
|
|
446
|
-
if (isThenable(out)) {
|
|
447
|
-
const rest = transforms.slice(i + 1);
|
|
448
|
-
const seed = out;
|
|
449
|
-
const run = () => rest.reduce(
|
|
450
|
-
(acc, next) => acc.then((value) => next(value, ctx)),
|
|
451
|
-
Promise.resolve(seed)
|
|
452
|
-
);
|
|
453
|
-
return { kind: "async", run, holder };
|
|
454
|
-
}
|
|
455
|
-
v = out;
|
|
456
|
-
}
|
|
457
|
-
return { kind: "sync", ok: true, value: v };
|
|
458
|
-
}
|
|
459
|
-
function kickoffAsyncTransform(rv, holder, run, commit, syncDom) {
|
|
460
|
-
const token = rv.beginTransform(holder);
|
|
461
|
-
void run().then(
|
|
462
|
-
(value) => {
|
|
463
|
-
const live = rv.isCurrentTransform(token);
|
|
464
|
-
rv.endTransform(token);
|
|
465
|
-
if (!live) return;
|
|
466
|
-
const coerced = applyCoerce(value, rv);
|
|
467
|
-
const wrote = commit(coerced);
|
|
468
|
-
if (wrote === false) rv.setTransformError(transformGateRejectedError(rv.path));
|
|
469
|
-
else syncDom?.();
|
|
470
|
-
},
|
|
471
|
-
(err) => {
|
|
472
|
-
if (rv.isCurrentTransform(token)) rv.setTransformError(toTransformError(err));
|
|
473
|
-
rv.endTransform(token);
|
|
474
|
-
}
|
|
475
|
-
);
|
|
476
|
-
}
|
|
477
|
-
function logTransformFailure(path, index, fn, err) {
|
|
478
|
-
{
|
|
479
|
-
const namePart = fn.name !== "" ? `, '${fn.name}'` : "";
|
|
480
|
-
console.error(
|
|
481
|
-
`[attaform] transform threw for path '${path}' (index ${index}${namePart}) \u2014 write aborted. Transforms must not throw; wrap your own try/catch if the throw is recoverable. Original error:`,
|
|
482
|
-
err
|
|
483
|
-
);
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
function applyCoerce(value, registerValue) {
|
|
487
|
-
return registerValue.coerce !== void 0 ? registerValue.coerce(value) : value;
|
|
488
|
-
}
|
|
489
|
-
function wrapWithTransforms(value, registerValue, commit, syncDom) {
|
|
490
|
-
const r = runTransforms(value, registerValue);
|
|
491
|
-
if (r.kind === "async") {
|
|
492
|
-
kickoffAsyncTransform(registerValue, r.holder, r.run, commit, syncDom);
|
|
493
|
-
return true;
|
|
494
|
-
}
|
|
495
|
-
if (!r.ok) return false;
|
|
496
|
-
const coerced = applyCoerce(r.value, registerValue);
|
|
497
|
-
return commit(coerced);
|
|
498
|
-
}
|
|
499
|
-
function toTransformError(value) {
|
|
500
|
-
if (value instanceof Error) return value;
|
|
501
|
-
const message = typeof value === "string" && value.length > 0 ? value : `Transform rejected with a non-Error value (${typeof value})`;
|
|
502
|
-
return new Error(message, { cause: value });
|
|
503
|
-
}
|
|
504
|
-
function transformGateRejectedError(path) {
|
|
505
|
-
return new Error(
|
|
506
|
-
`[attaform] transform result for path '${path}' was rejected by the field's type gate (the resolved value did not fit the schema slot).`
|
|
507
|
-
);
|
|
508
|
-
}
|
|
509
|
-
const getModelAssigner = (el, vnode, registerValue) => {
|
|
510
|
-
const fn = vnode.props?.["onUpdate:registerValue"] ?? vnode.props?.["on:update:registerValue"];
|
|
511
|
-
if (isArray(fn)) {
|
|
512
|
-
const fnArr = fn.filter((x) => isFunction(x));
|
|
513
|
-
const wrapped = (value) => {
|
|
514
|
-
return wrapWithTransforms(
|
|
515
|
-
value,
|
|
516
|
-
registerValue,
|
|
517
|
-
(coerced) => {
|
|
518
|
-
invokeArrayFns(fnArr, coerced, registerValue);
|
|
519
|
-
return void 0;
|
|
520
|
-
},
|
|
521
|
-
void 0
|
|
522
|
-
);
|
|
523
|
-
};
|
|
524
|
-
wrapped[CONSUMER_WRAPPED_TAG] = true;
|
|
525
|
-
return wrapped;
|
|
526
|
-
}
|
|
527
|
-
if (isFunction(fn)) {
|
|
528
|
-
const handler = fn;
|
|
529
|
-
const wrapped = (value) => {
|
|
530
|
-
return wrapWithTransforms(
|
|
531
|
-
value,
|
|
532
|
-
registerValue,
|
|
533
|
-
(coerced) => handler(coerced, registerValue),
|
|
534
|
-
void 0
|
|
535
|
-
);
|
|
536
|
-
};
|
|
537
|
-
wrapped[CONSUMER_WRAPPED_TAG] = true;
|
|
538
|
-
return wrapped;
|
|
539
|
-
}
|
|
540
|
-
const defaultAssigner = (value) => {
|
|
541
|
-
if (value === void 0 && registerValue.acceptsUndefined) {
|
|
542
|
-
return registerValue.setValueWithInternalPath(void 0);
|
|
543
|
-
}
|
|
544
|
-
return wrapWithTransforms(
|
|
545
|
-
value,
|
|
546
|
-
registerValue,
|
|
547
|
-
(coerced) => registerValue.setValueWithInternalPath(coerced),
|
|
548
|
-
el._syncFromStorage
|
|
549
|
-
);
|
|
550
|
-
};
|
|
551
|
-
defaultAssigner[DEFAULT_ASSIGNER_TAG] = true;
|
|
552
|
-
return defaultAssigner;
|
|
553
|
-
};
|
|
554
|
-
function makeNoopAssigner() {
|
|
555
|
-
const noop = (_) => void 0;
|
|
556
|
-
noop[DEFAULT_ASSIGNER_TAG] = true;
|
|
557
|
-
return noop;
|
|
558
|
-
}
|
|
559
|
-
function setAssignFunction(el, vnode, value) {
|
|
560
|
-
const current = el[assignKey];
|
|
561
|
-
if (current !== void 0 && !isDefaultAssigner(current) && !isConsumerWrapped(current)) {
|
|
562
|
-
return;
|
|
563
|
-
}
|
|
564
|
-
if (value === void 0) {
|
|
565
|
-
el[assignKey] = makeNoopAssigner();
|
|
566
|
-
return;
|
|
567
|
-
}
|
|
568
|
-
if (!isRegisterValue(value)) {
|
|
569
|
-
{
|
|
570
|
-
warn(
|
|
571
|
-
`v-register expected a RegisterValue, got '${typeof value}'. Bind to form.register('field') \u2014 not the field's ref, value, or path string.`
|
|
572
|
-
);
|
|
573
|
-
}
|
|
574
|
-
el[assignKey] = makeNoopAssigner();
|
|
575
|
-
return;
|
|
576
|
-
}
|
|
577
|
-
el[assignKey] = getModelAssigner(el, vnode, value);
|
|
578
|
-
}
|
|
579
|
-
|
|
580
664
|
function isBlankFileValue(value) {
|
|
581
665
|
if (value === null || value === void 0) return true;
|
|
582
666
|
if (Array.isArray(value) && value.length === 0) return true;
|
|
@@ -668,11 +752,6 @@ function syncElementRegistration(el, value, oldValue) {
|
|
|
668
752
|
}
|
|
669
753
|
|
|
670
754
|
const valueSyncScopeKey = /* @__PURE__ */ Symbol.for("attaform:value-sync-scope");
|
|
671
|
-
function isElementFocused(el) {
|
|
672
|
-
const rootNode = el.getRootNode();
|
|
673
|
-
const activeElement = rootNode instanceof Document || rootNode instanceof ShadowRoot ? rootNode.activeElement : null;
|
|
674
|
-
return activeElement === el;
|
|
675
|
-
}
|
|
676
755
|
function setupValueSync(el, source, apply, options = {}) {
|
|
677
756
|
const skipWhileFocused = options.skipWhileFocused === true;
|
|
678
757
|
const scope = effectScope(true);
|
|
@@ -681,7 +760,7 @@ function setupValueSync(el, source, apply, options = {}) {
|
|
|
681
760
|
source,
|
|
682
761
|
() => {
|
|
683
762
|
if (el.composing === true) return;
|
|
684
|
-
if (skipWhileFocused &&
|
|
763
|
+
if (skipWhileFocused && activeElementOf(el) === el) return;
|
|
685
764
|
apply();
|
|
686
765
|
},
|
|
687
766
|
{ flush: "post" }
|
|
@@ -740,6 +819,28 @@ function onCompositionEnd(e) {
|
|
|
740
819
|
target.dispatchEvent(new Event("input"));
|
|
741
820
|
}
|
|
742
821
|
}
|
|
822
|
+
const heldDrafts = /* @__PURE__ */ new WeakMap();
|
|
823
|
+
function holdDraft(owner, control, stale) {
|
|
824
|
+
heldDrafts.set(owner, {
|
|
825
|
+
control,
|
|
826
|
+
text: control.value,
|
|
827
|
+
stale,
|
|
828
|
+
start: control.selectionStart,
|
|
829
|
+
end: control.selectionEnd
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
function restoreDraft(owner) {
|
|
833
|
+
const held = heldDrafts.get(owner);
|
|
834
|
+
if (held === void 0) return;
|
|
835
|
+
heldDrafts.delete(owner);
|
|
836
|
+
const { control, text, stale, start, end } = held;
|
|
837
|
+
if (control.value !== stale || stale === text) return;
|
|
838
|
+
control.value = text;
|
|
839
|
+
if (start !== null && end !== null) control.setSelectionRange(start, end);
|
|
840
|
+
}
|
|
841
|
+
function textControl(node) {
|
|
842
|
+
return node instanceof HTMLInputElement || node instanceof HTMLTextAreaElement ? node : null;
|
|
843
|
+
}
|
|
743
844
|
const vRegisterText = {
|
|
744
845
|
created(el, { value, modifiers: { lazy, trim, number } }, vnode) {
|
|
745
846
|
const castToNumberAtCreated = number === true || vnode.props?.["type"] === "number";
|
|
@@ -862,12 +963,14 @@ const vRegisterText = {
|
|
|
862
963
|
mounted(el, { value }) {
|
|
863
964
|
if (!isRegisterValue(value)) return;
|
|
864
965
|
el.value = value.displayValue.value;
|
|
966
|
+
el._lastAppliedModel = value.innerRef.value;
|
|
865
967
|
setupValueSync(
|
|
866
968
|
el,
|
|
867
969
|
value.displayValue,
|
|
868
970
|
() => {
|
|
869
971
|
const next = value.displayValue.value;
|
|
870
972
|
if (el.value !== next) el.value = next;
|
|
973
|
+
el._lastAppliedModel = value.innerRef.value;
|
|
871
974
|
},
|
|
872
975
|
{ skipWhileFocused: true }
|
|
873
976
|
);
|
|
@@ -876,17 +979,16 @@ const vRegisterText = {
|
|
|
876
979
|
setAssignFunction(el, vnode, value);
|
|
877
980
|
if (el.composing === true) return;
|
|
878
981
|
if (!isRegisterValue(value)) return;
|
|
982
|
+
const model = value.innerRef.value;
|
|
983
|
+
const modelMoved = model !== el._lastAppliedModel || !isRegisterValue(oldValue) || oldValue.path !== value.path;
|
|
984
|
+
el._lastAppliedModel = model;
|
|
879
985
|
const target = value.displayValue.value;
|
|
880
986
|
if (el.value === target) {
|
|
881
987
|
return;
|
|
882
988
|
}
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
if (lazy === true && value.innerRef.value === oldValue) {
|
|
887
|
-
return;
|
|
888
|
-
}
|
|
889
|
-
if (trim === true && el.value.trim() === target) {
|
|
989
|
+
if (activeElementOf(el) === el && el.type !== "range") {
|
|
990
|
+
if (lazy === true && !modelMoved || trim === true && el.value.trim() === target) {
|
|
991
|
+
holdDraft(el, el, target);
|
|
890
992
|
return;
|
|
891
993
|
}
|
|
892
994
|
}
|
|
@@ -916,7 +1018,7 @@ const vRegisterCheckbox = {
|
|
|
916
1018
|
if (rawElementValue === void 0) {
|
|
917
1019
|
{
|
|
918
1020
|
warn(
|
|
919
|
-
'Checkbox bound to an array model is missing a `value` attribute
|
|
1021
|
+
'Checkbox bound to an array model is missing a `value` attribute, so Attaform cannot determine which item to add or remove. Add value="..." to each <input type="checkbox">.'
|
|
920
1022
|
);
|
|
921
1023
|
}
|
|
922
1024
|
return;
|
|
@@ -935,7 +1037,7 @@ const vRegisterCheckbox = {
|
|
|
935
1037
|
if (rawElementValue === void 0) {
|
|
936
1038
|
{
|
|
937
1039
|
warn(
|
|
938
|
-
'Checkbox bound to a Set model is missing a `value` attribute
|
|
1040
|
+
'Checkbox bound to a Set model is missing a `value` attribute, so Attaform cannot determine which item to add or remove. Add value="..." to each <input type="checkbox">.'
|
|
939
1041
|
);
|
|
940
1042
|
}
|
|
941
1043
|
return;
|
|
@@ -967,20 +1069,15 @@ const vRegisterCheckbox = {
|
|
|
967
1069
|
el._lastAppliedModel = value.innerRef.value;
|
|
968
1070
|
});
|
|
969
1071
|
},
|
|
970
|
-
// Skip the DOM sync when the model is identity-unchanged
|
|
971
|
-
//
|
|
972
|
-
//
|
|
973
|
-
//
|
|
974
|
-
//
|
|
975
|
-
//
|
|
976
|
-
//
|
|
977
|
-
//
|
|
978
|
-
//
|
|
979
|
-
// the browser fires `change`. Identity comparison on
|
|
980
|
-
// `innerRef.value` is sound for the same reason as multi-select —
|
|
981
|
-
// every form write produces a fresh value at the path (new
|
|
982
|
-
// primitives; new array/Set references along the spine), so
|
|
983
|
-
// reference equality tracks "did the model move" exactly.
|
|
1072
|
+
// Skip the DOM sync when the model is identity-unchanged since the last
|
|
1073
|
+
// application. Without the guard, a sibling's reactive write triggers
|
|
1074
|
+
// `beforeUpdate` mid-click, `setChecked` re-applies the prior model state,
|
|
1075
|
+
// and the in-flight toggle is clobbered before the browser fires `change`.
|
|
1076
|
+
// Compare `innerRef.value` by identity: every form write produces a fresh
|
|
1077
|
+
// value at the path, new primitives and new array or Set references along
|
|
1078
|
+
// the spine, so reference equality tracks "did the model move" exactly. The
|
|
1079
|
+
// guard must NOT compare against `oldValue`, which is the previous binding's
|
|
1080
|
+
// wrapper RegisterValue and never equals a model scalar.
|
|
984
1081
|
beforeUpdate(el, binding, vnode) {
|
|
985
1082
|
setAssignFunction(el, vnode, binding.value);
|
|
986
1083
|
if (!isRegisterValue(binding.value)) return;
|
|
@@ -1030,13 +1127,11 @@ const vRegisterRadio = {
|
|
|
1030
1127
|
}
|
|
1031
1128
|
});
|
|
1032
1129
|
},
|
|
1033
|
-
//
|
|
1034
|
-
//
|
|
1035
|
-
//
|
|
1036
|
-
//
|
|
1037
|
-
//
|
|
1038
|
-
// model. Checkbox already uses `mounted: setChecked` for the same
|
|
1039
|
-
// reason.
|
|
1130
|
+
// The initial checked sync runs in `mounted`, NOT `created`: Vue fires
|
|
1131
|
+
// `created` before the element's attributes are patched, so `type`, `value`
|
|
1132
|
+
// and `_value` are absent, `getValue(el)` returns undefined, and every radio
|
|
1133
|
+
// in a group mounts unchecked whatever the model says. Checkbox uses
|
|
1134
|
+
// `mounted` for the same reason.
|
|
1040
1135
|
mounted(el, { value }) {
|
|
1041
1136
|
if (!isRegisterValue(value)) return;
|
|
1042
1137
|
el.checked = looseEqual(value.innerRef.value, applyCoerce(getValue(el), value));
|
|
@@ -1046,15 +1141,12 @@ const vRegisterRadio = {
|
|
|
1046
1141
|
el._lastAppliedModel = value.innerRef.value;
|
|
1047
1142
|
});
|
|
1048
1143
|
},
|
|
1049
|
-
// Skip the DOM sync when the model is identity-unchanged
|
|
1050
|
-
//
|
|
1051
|
-
//
|
|
1052
|
-
//
|
|
1053
|
-
//
|
|
1054
|
-
//
|
|
1055
|
-
// `vRegisterSelect` and `setChecked` bugs: a sibling's reactive
|
|
1056
|
-
// write triggers `beforeUpdate` mid-click and writes back the
|
|
1057
|
-
// prior model state, clobbering the in-flight selection.
|
|
1144
|
+
// Skip the DOM sync when the model is identity-unchanged since the last
|
|
1145
|
+
// application, tracked on `_lastAppliedModel`. Comparing against `oldValue`
|
|
1146
|
+
// instead compares a primitive scalar to the previous binding's wrapper
|
|
1147
|
+
// RegisterValue, never equal, so `el.checked` re-applies on every parent
|
|
1148
|
+
// re-render and a sibling's reactive write clobbers an in-flight selection
|
|
1149
|
+
// mid-click.
|
|
1058
1150
|
beforeUpdate(el, { value }, vnode) {
|
|
1059
1151
|
if (!isRegisterValue(value)) return;
|
|
1060
1152
|
setAssignFunction(el, vnode, value);
|
|
@@ -1113,22 +1205,20 @@ const vRegisterSelect = {
|
|
|
1113
1205
|
beforeUpdate(el, binding, vnode) {
|
|
1114
1206
|
setAssignFunction(el, vnode, binding.value);
|
|
1115
1207
|
},
|
|
1116
|
-
// Skip the DOM sync when the model is identity-unchanged
|
|
1117
|
-
//
|
|
1118
|
-
//
|
|
1119
|
-
//
|
|
1120
|
-
//
|
|
1121
|
-
//
|
|
1122
|
-
//
|
|
1123
|
-
//
|
|
1124
|
-
//
|
|
1125
|
-
//
|
|
1126
|
-
//
|
|
1127
|
-
//
|
|
1128
|
-
//
|
|
1129
|
-
//
|
|
1130
|
-
// — it short-circuits the immediate post-write render where the
|
|
1131
|
-
// DOM is already in sync from the user's click.
|
|
1208
|
+
// Skip the DOM sync when the model is identity-unchanged since the last
|
|
1209
|
+
// application. A parent re-render fires `updated` whether or not the bound
|
|
1210
|
+
// model moved: a typed character in a sibling, an async-validation tick, any
|
|
1211
|
+
// reactive read elsewhere on the page. Re-applying `setSelected` against the
|
|
1212
|
+
// prior model on each of those clobbers an in-progress `<select multiple>`
|
|
1213
|
+
// selection between mousedown and the browser's change decision, after which
|
|
1214
|
+
// the browser sees no net change, never fires `change`, and the model never
|
|
1215
|
+
// updates.
|
|
1216
|
+
//
|
|
1217
|
+
// Identity comparison is sound: every form write produces a new array or Set
|
|
1218
|
+
// reference at the path, the replacement of `form.value` rolling fresh
|
|
1219
|
+
// structures forward along the spine. The `_assigning` gate stays as well,
|
|
1220
|
+
// short-circuiting the immediate post-write render where the DOM is already
|
|
1221
|
+
// in sync from the user's click.
|
|
1132
1222
|
updated(el, { value }) {
|
|
1133
1223
|
if (el._assigning === true) return;
|
|
1134
1224
|
if (!isRegisterValue(value)) return;
|
|
@@ -1277,6 +1367,29 @@ function observeForLateHostControl(el, rv) {
|
|
|
1277
1367
|
observer.observe(el, { childList: true, subtree: true });
|
|
1278
1368
|
hostHealObservers.set(el, observer);
|
|
1279
1369
|
}
|
|
1370
|
+
const hostEdits = /* @__PURE__ */ new WeakMap();
|
|
1371
|
+
function trackHostEdits(el, rv, modifiers) {
|
|
1372
|
+
if (modifiers.lazy !== true && modifiers.trim !== true) return;
|
|
1373
|
+
if (!INTERACTIVE_TAG_NAMES.has(el.tagName) && rv.hasRegisteredDescendant(el)) return;
|
|
1374
|
+
const channel = armDomBinding(rv)?.hostChannel;
|
|
1375
|
+
if (channel === void 0) return;
|
|
1376
|
+
const edits = { channel, rv, base: void 0 };
|
|
1377
|
+
const edge = (editing) => (event) => {
|
|
1378
|
+
if (editing && !channel.holdsDraft(rv)) edits.base = textControl(event.target)?.value;
|
|
1379
|
+
const other = event.relatedTarget;
|
|
1380
|
+
if (other instanceof Node && el.contains(other)) return;
|
|
1381
|
+
channel.markEditing(rv, editing, modifiers);
|
|
1382
|
+
};
|
|
1383
|
+
addTrackedListener(el, "focus", edge(true), { capture: true });
|
|
1384
|
+
addTrackedListener(el, "blur", edge(false), { capture: true });
|
|
1385
|
+
hostEdits.set(el, edits);
|
|
1386
|
+
}
|
|
1387
|
+
function holdHostDraft(el) {
|
|
1388
|
+
const edits = hostEdits.get(el);
|
|
1389
|
+
if (edits?.base === void 0 || !edits.channel.holdsDraft(edits.rv)) return;
|
|
1390
|
+
const control = textControl(activeElementOf(el));
|
|
1391
|
+
if (control !== null && el.contains(control)) holdDraft(el, control, edits.base);
|
|
1392
|
+
}
|
|
1280
1393
|
function activateComponentHost(el, rv) {
|
|
1281
1394
|
if (rv.hasRegisteredDescendant(el)) return;
|
|
1282
1395
|
el[REGISTER_OWNER_MARKER] = true;
|
|
@@ -1291,19 +1404,26 @@ function activateComponentHost(el, rv) {
|
|
|
1291
1404
|
setupNoLatchHost(el, rv);
|
|
1292
1405
|
scheduleHostSelfHeal(el, rv);
|
|
1293
1406
|
}
|
|
1407
|
+
function inheritModifiers(binding) {
|
|
1408
|
+
const modifiers = withInheritedModifiers(binding.value, binding.modifiers);
|
|
1409
|
+
return modifiers === binding.modifiers ? binding : { ...binding, modifiers };
|
|
1410
|
+
}
|
|
1294
1411
|
const vRegisterDynamic = {
|
|
1295
|
-
created(el,
|
|
1412
|
+
created(el, ownBinding, vnode) {
|
|
1413
|
+
const binding = inheritModifiers(ownBinding);
|
|
1296
1414
|
if (isRegisterValue(binding.value)) armDomBinding(binding.value);
|
|
1297
1415
|
callModelHook(el, binding, vnode, null, "created");
|
|
1298
1416
|
if (isRegisterValue(binding.value)) setupAria(el, binding.value, vnode);
|
|
1299
1417
|
warnRedundantStateBinding(el, binding, vnode);
|
|
1300
1418
|
},
|
|
1301
|
-
mounted(el,
|
|
1419
|
+
mounted(el, ownBinding, vnode) {
|
|
1420
|
+
const binding = inheritModifiers(ownBinding);
|
|
1302
1421
|
callModelHook(el, binding, vnode, null, "mounted");
|
|
1303
1422
|
if (isRegisterValue(binding.value) && INTERACTIVE_TAG_NAMES.has(el.tagName) && binding.modifiers[SSR_COMPONENT_HOST_MODIFIER] !== true && vnode.props?.["disabled"] === void 0) {
|
|
1304
1423
|
setupDisabledSync(el, binding.value.disabled);
|
|
1305
1424
|
}
|
|
1306
1425
|
if (binding.modifiers[SSR_COMPONENT_HOST_MODIFIER] === true && isRegisterValue(binding.value)) {
|
|
1426
|
+
trackHostEdits(el, binding.value, binding.modifiers);
|
|
1307
1427
|
activateComponentHost(el, binding.value);
|
|
1308
1428
|
}
|
|
1309
1429
|
if (warnedUnsupportedElements !== null && !INTERACTIVE_TAG_NAMES.has(el.tagName) && !warnedUnsupportedElements.has(el)) {
|
|
@@ -1316,15 +1436,17 @@ const vRegisterDynamic = {
|
|
|
1316
1436
|
if (hasMarker || hasUserAssigner) return;
|
|
1317
1437
|
warnedUnsupportedElements.add(el);
|
|
1318
1438
|
warn(
|
|
1319
|
-
`[attaform] v-register on <${el.tagName.toLowerCase()}> is a no-op
|
|
1439
|
+
`[attaform] v-register on <${el.tagName.toLowerCase()}> is a no-op: non-input roots aren't bound to text-input semantics. For custom components: call \`useRegister()\` in the child's setup and re-bind v-register to an inner native element. Lower-level: install a custom assigner via the \`assignKey\` symbol on the element.`
|
|
1320
1440
|
);
|
|
1321
1441
|
});
|
|
1322
1442
|
}
|
|
1323
1443
|
},
|
|
1324
|
-
beforeUpdate(el,
|
|
1444
|
+
beforeUpdate(el, ownBinding, vnode, prevVNode) {
|
|
1445
|
+
const binding = inheritModifiers(ownBinding);
|
|
1325
1446
|
if (isRegisterValue(binding.value)) armDomBinding(binding.value);
|
|
1326
1447
|
syncElementRegistration(el, binding.value, binding.oldValue);
|
|
1327
1448
|
callModelHook(el, binding, vnode, prevVNode, "beforeUpdate");
|
|
1449
|
+
holdHostDraft(el);
|
|
1328
1450
|
const ariaEl = el;
|
|
1329
1451
|
const value = binding.value;
|
|
1330
1452
|
if (!isRegisterValue(value) || value.ariaDisplayState === void 0) {
|
|
@@ -1342,13 +1464,17 @@ const vRegisterDynamic = {
|
|
|
1342
1464
|
}
|
|
1343
1465
|
},
|
|
1344
1466
|
updated(el, binding, vnode, prevVNode) {
|
|
1345
|
-
callModelHook(el, binding, vnode, prevVNode, "updated");
|
|
1467
|
+
callModelHook(el, inheritModifiers(binding), vnode, prevVNode, "updated");
|
|
1468
|
+
restoreDraft(el);
|
|
1346
1469
|
},
|
|
1347
1470
|
beforeUnmount(el, { value }) {
|
|
1348
1471
|
removeTrackedListeners(el);
|
|
1349
1472
|
teardownAria(el);
|
|
1350
1473
|
teardownValueSync(el);
|
|
1351
1474
|
teardownDisabledSync(el);
|
|
1475
|
+
const edits = hostEdits.get(el);
|
|
1476
|
+
edits?.channel.release(edits.rv);
|
|
1477
|
+
hostEdits.delete(el);
|
|
1352
1478
|
if (!isRegisterValue(value)) return;
|
|
1353
1479
|
value.deregisterElement(el);
|
|
1354
1480
|
if (componentHostLatch.has(el)) {
|
|
@@ -1369,13 +1495,12 @@ const vRegisterDynamic = {
|
|
|
1369
1495
|
delete el._syncFromStorage;
|
|
1370
1496
|
delete el[assignKey];
|
|
1371
1497
|
},
|
|
1372
|
-
//
|
|
1373
|
-
//
|
|
1374
|
-
//
|
|
1375
|
-
//
|
|
1376
|
-
//
|
|
1377
|
-
//
|
|
1378
|
-
// describedby matches the client after hydration.
|
|
1498
|
+
// Vue skips directive lifecycle during SSR, so the hooks above never run on
|
|
1499
|
+
// the server and the same aria attributes are emitted here from the SSR-time
|
|
1500
|
+
// gated display state. Honours authored attrs through the vnode-level
|
|
1501
|
+
// lockout, touches no DOM, and shares `resolveAriaValue` with the client
|
|
1502
|
+
// path. Ids are SSR-stable, `formInstanceId` deriving from Vue's `useId`, so
|
|
1503
|
+
// a server-rendered describedby matches the client after hydration.
|
|
1379
1504
|
getSSRProps(binding, vnode) {
|
|
1380
1505
|
const rv = binding.value;
|
|
1381
1506
|
if (!isRegisterValue(rv)) return void 0;
|
|
@@ -1462,4 +1587,4 @@ function installVRegister(app) {
|
|
|
1462
1587
|
}
|
|
1463
1588
|
}
|
|
1464
1589
|
|
|
1465
|
-
export { REGISTER_OWNER_MARKER as R, V_REGISTER_MARKER as V, assignKey as a, installVRegister as b, vRegisterFile as c, armDomBinding as d, isRegisterValue as i, vRegister as v };
|
|
1590
|
+
export { REGISTER_OWNER_MARKER as R, V_REGISTER_MARKER as V, assignKey as a, installVRegister as b, vRegisterFile as c, armDomBinding as d, isRegisterValue as i, recordInheritedModifiers as r, vRegister as v, withInheritedModifiers as w };
|