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,9 +1,347 @@
|
|
|
1
|
-
import { reactive, markRaw, effectScope, watch, nextTick } from 'vue';
|
|
1
|
+
import { toRaw, reactive, markRaw, effectScope, watch, nextTick } from 'vue';
|
|
2
2
|
import { i as isRegisterValue, a as isTransforming, S as SSR_COMPONENT_HOST_MODIFIER, R as REGISTER_OWNER_MARKER, b as V_REGISTER_MARKER } from './attaform.KvlEedlF.mjs';
|
|
3
3
|
import { e as canonicalizePath } from './attaform.CdZIEf7_.mjs';
|
|
4
4
|
|
|
5
|
+
function activeElementOf(node) {
|
|
6
|
+
const rootNode = node.getRootNode();
|
|
7
|
+
return rootNode instanceof Document || rootNode instanceof ShadowRoot ? rootNode.activeElement : null;
|
|
8
|
+
}
|
|
9
|
+
|
|
5
10
|
const INTERACTIVE_TAG_NAMES = /* @__PURE__ */ new Set(["INPUT", "SELECT", "TEXTAREA"]);
|
|
6
11
|
|
|
12
|
+
const isArray = Array.isArray;
|
|
13
|
+
function isFunction(value) {
|
|
14
|
+
return typeof value === "function";
|
|
15
|
+
}
|
|
16
|
+
function toTypeString(value) {
|
|
17
|
+
return Object.prototype.toString.call(value);
|
|
18
|
+
}
|
|
19
|
+
function isSet(value) {
|
|
20
|
+
return toTypeString(value) === "[object Set]";
|
|
21
|
+
}
|
|
22
|
+
function isDate(value) {
|
|
23
|
+
return toTypeString(value) === "[object Date]";
|
|
24
|
+
}
|
|
25
|
+
function isSymbol(value) {
|
|
26
|
+
return typeof value === "symbol";
|
|
27
|
+
}
|
|
28
|
+
function isObject(value) {
|
|
29
|
+
return value !== null && typeof value === "object";
|
|
30
|
+
}
|
|
31
|
+
function looseToNumber(val) {
|
|
32
|
+
const n = parseFloat(val);
|
|
33
|
+
return isNaN(n) ? val : n;
|
|
34
|
+
}
|
|
35
|
+
function looseCompareArrays(a, b) {
|
|
36
|
+
if (a.length !== b.length) return false;
|
|
37
|
+
let equal = true;
|
|
38
|
+
for (let i = 0; equal && i < a.length; i++) {
|
|
39
|
+
equal = looseEqual(a[i], b[i]);
|
|
40
|
+
}
|
|
41
|
+
return equal;
|
|
42
|
+
}
|
|
43
|
+
function looseEqual(a, b) {
|
|
44
|
+
if (a === b) return true;
|
|
45
|
+
const aValidType = isDate(a);
|
|
46
|
+
const bValidType = isDate(b);
|
|
47
|
+
if (aValidType || bValidType) {
|
|
48
|
+
return aValidType && bValidType ? a.getTime() === b.getTime() : false;
|
|
49
|
+
}
|
|
50
|
+
const aSymbol = isSymbol(a);
|
|
51
|
+
const bSymbol = isSymbol(b);
|
|
52
|
+
if (aSymbol || bSymbol) return a === b;
|
|
53
|
+
const aIsArray = isArray(a);
|
|
54
|
+
const bIsArray = isArray(b);
|
|
55
|
+
if (aIsArray || bIsArray) {
|
|
56
|
+
return aIsArray && bIsArray ? looseCompareArrays(a, b) : false;
|
|
57
|
+
}
|
|
58
|
+
const aIsObject = isObject(a);
|
|
59
|
+
const bIsObject = isObject(b);
|
|
60
|
+
if (aIsObject !== bIsObject) return false;
|
|
61
|
+
if (aIsObject && bIsObject) {
|
|
62
|
+
const keysA = Object.keys(a);
|
|
63
|
+
if (keysA.length !== Object.keys(b).length) return false;
|
|
64
|
+
for (const key of keysA) {
|
|
65
|
+
const hasA = Object.prototype.hasOwnProperty.call(a, key);
|
|
66
|
+
const hasB = Object.prototype.hasOwnProperty.call(b, key);
|
|
67
|
+
if (!hasA || !hasB || !looseEqual(a[key], b[key])) return false;
|
|
68
|
+
}
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
return String(a) === String(b);
|
|
72
|
+
}
|
|
73
|
+
function looseIndexOf(arr, val) {
|
|
74
|
+
return arr.findIndex((item) => looseEqual(item, val));
|
|
75
|
+
}
|
|
76
|
+
function invokeArrayFns(fns, ...args) {
|
|
77
|
+
for (let i = 0; i < fns.length; i++) {
|
|
78
|
+
const fn = fns[i];
|
|
79
|
+
if (fn) fn(...args);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const assignKey = /* @__PURE__ */ Symbol.for("attaform:assign-key");
|
|
84
|
+
const DEFAULT_ASSIGNER_TAG = /* @__PURE__ */ Symbol.for("attaform:default-assigner-tag");
|
|
85
|
+
function isDefaultAssigner(fn) {
|
|
86
|
+
return typeof fn === "function" && fn[DEFAULT_ASSIGNER_TAG] === true;
|
|
87
|
+
}
|
|
88
|
+
const CONSUMER_WRAPPED_TAG = /* @__PURE__ */ Symbol.for("attaform:consumer-wrapped-assigner");
|
|
89
|
+
function isConsumerWrapped(fn) {
|
|
90
|
+
return typeof fn === "function" && fn[CONSUMER_WRAPPED_TAG] === true;
|
|
91
|
+
}
|
|
92
|
+
function fireAssigner(el, registerValue, value) {
|
|
93
|
+
const fn = el[assignKey];
|
|
94
|
+
if (fn === void 0) return void 0;
|
|
95
|
+
if (isDefaultAssigner(fn) || isConsumerWrapped(fn)) {
|
|
96
|
+
return fn(value);
|
|
97
|
+
}
|
|
98
|
+
if (!isRegisterValue(registerValue)) {
|
|
99
|
+
return fn(value, void 0);
|
|
100
|
+
}
|
|
101
|
+
return wrapWithTransforms(
|
|
102
|
+
value,
|
|
103
|
+
registerValue,
|
|
104
|
+
(coerced) => fn(coerced, registerValue),
|
|
105
|
+
void 0
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
function isThenable(x) {
|
|
109
|
+
return x !== null && (typeof x === "object" || typeof x === "function") && typeof x.then === "function";
|
|
110
|
+
}
|
|
111
|
+
function makeTransformContext() {
|
|
112
|
+
const holder = { controller: null, aborted: false };
|
|
113
|
+
const ctx = {
|
|
114
|
+
get signal() {
|
|
115
|
+
if (holder.controller === null) {
|
|
116
|
+
holder.controller = new AbortController();
|
|
117
|
+
if (holder.aborted) holder.controller.abort();
|
|
118
|
+
}
|
|
119
|
+
return holder.controller.signal;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
return { ctx, holder };
|
|
123
|
+
}
|
|
124
|
+
function runTransforms(initial, registerValue) {
|
|
125
|
+
const transforms = registerValue.transforms;
|
|
126
|
+
if (transforms === void 0 || transforms.length === 0) {
|
|
127
|
+
return { kind: "sync", ok: true, value: initial };
|
|
128
|
+
}
|
|
129
|
+
const { ctx, holder } = makeTransformContext();
|
|
130
|
+
let v = initial;
|
|
131
|
+
for (let i = 0; i < transforms.length; i++) {
|
|
132
|
+
const fn = transforms[i];
|
|
133
|
+
let out;
|
|
134
|
+
try {
|
|
135
|
+
out = fn(v, ctx);
|
|
136
|
+
} catch (err) {
|
|
137
|
+
logTransformFailure(registerValue.path);
|
|
138
|
+
return { kind: "sync", ok: false };
|
|
139
|
+
}
|
|
140
|
+
if (isThenable(out)) {
|
|
141
|
+
const rest = transforms.slice(i + 1);
|
|
142
|
+
const seed = out;
|
|
143
|
+
const run = () => rest.reduce(
|
|
144
|
+
(acc, next) => acc.then((value) => next(value, ctx)),
|
|
145
|
+
Promise.resolve(seed)
|
|
146
|
+
);
|
|
147
|
+
return { kind: "async", run, holder };
|
|
148
|
+
}
|
|
149
|
+
v = out;
|
|
150
|
+
}
|
|
151
|
+
return { kind: "sync", ok: true, value: v };
|
|
152
|
+
}
|
|
153
|
+
function kickoffAsyncTransform(rv, holder, run, commit, syncDom) {
|
|
154
|
+
const token = rv.beginTransform(holder);
|
|
155
|
+
void run().then(
|
|
156
|
+
(value) => {
|
|
157
|
+
const live = rv.isCurrentTransform(token);
|
|
158
|
+
rv.endTransform(token);
|
|
159
|
+
if (!live) return;
|
|
160
|
+
const coerced = applyCoerce(value, rv);
|
|
161
|
+
const wrote = commit(coerced);
|
|
162
|
+
if (wrote === false) rv.setTransformError(transformGateRejectedError(rv.path));
|
|
163
|
+
else syncDom?.();
|
|
164
|
+
},
|
|
165
|
+
(err) => {
|
|
166
|
+
if (rv.isCurrentTransform(token)) rv.setTransformError(toTransformError(err));
|
|
167
|
+
rv.endTransform(token);
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
function logTransformFailure(path, index, fn, err) {
|
|
172
|
+
{
|
|
173
|
+
console.error("[attaform] AF14 attaform.dev/e/af14");
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function applyCoerce(value, registerValue) {
|
|
177
|
+
return registerValue.coerce !== void 0 ? registerValue.coerce(value) : value;
|
|
178
|
+
}
|
|
179
|
+
function wrapWithTransforms(value, registerValue, commit, syncDom) {
|
|
180
|
+
const r = runTransforms(value, registerValue);
|
|
181
|
+
if (r.kind === "async") {
|
|
182
|
+
kickoffAsyncTransform(registerValue, r.holder, r.run, commit, syncDom);
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
if (!r.ok) return false;
|
|
186
|
+
const coerced = applyCoerce(r.value, registerValue);
|
|
187
|
+
return commit(coerced);
|
|
188
|
+
}
|
|
189
|
+
function toTransformError(value) {
|
|
190
|
+
if (value instanceof Error) return value;
|
|
191
|
+
const message = typeof value === "string" && value.length > 0 ? value : `Transform rejected with a non-Error value (${typeof value})`;
|
|
192
|
+
return new Error(message, { cause: value });
|
|
193
|
+
}
|
|
194
|
+
function transformGateRejectedError(path) {
|
|
195
|
+
return new Error(
|
|
196
|
+
`[attaform] transform result for path '${path}' was rejected by the field's type gate (the resolved value did not fit the schema slot).`
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
const getModelAssigner = (el, vnode, registerValue) => {
|
|
200
|
+
const fn = vnode.props?.["onUpdate:registerValue"] ?? vnode.props?.["on:update:registerValue"];
|
|
201
|
+
if (isArray(fn)) {
|
|
202
|
+
const fnArr = fn.filter((x) => isFunction(x));
|
|
203
|
+
const wrapped = (value) => {
|
|
204
|
+
return wrapWithTransforms(
|
|
205
|
+
value,
|
|
206
|
+
registerValue,
|
|
207
|
+
(coerced) => {
|
|
208
|
+
invokeArrayFns(fnArr, coerced, registerValue);
|
|
209
|
+
return void 0;
|
|
210
|
+
},
|
|
211
|
+
void 0
|
|
212
|
+
);
|
|
213
|
+
};
|
|
214
|
+
wrapped[CONSUMER_WRAPPED_TAG] = true;
|
|
215
|
+
return wrapped;
|
|
216
|
+
}
|
|
217
|
+
if (isFunction(fn)) {
|
|
218
|
+
const handler = fn;
|
|
219
|
+
const wrapped = (value) => {
|
|
220
|
+
return wrapWithTransforms(
|
|
221
|
+
value,
|
|
222
|
+
registerValue,
|
|
223
|
+
(coerced) => handler(coerced, registerValue),
|
|
224
|
+
void 0
|
|
225
|
+
);
|
|
226
|
+
};
|
|
227
|
+
wrapped[CONSUMER_WRAPPED_TAG] = true;
|
|
228
|
+
return wrapped;
|
|
229
|
+
}
|
|
230
|
+
const defaultAssigner = (value) => {
|
|
231
|
+
if (value === void 0 && registerValue.acceptsUndefined) {
|
|
232
|
+
return registerValue.setValueWithInternalPath(void 0);
|
|
233
|
+
}
|
|
234
|
+
return wrapWithTransforms(
|
|
235
|
+
value,
|
|
236
|
+
registerValue,
|
|
237
|
+
(coerced) => registerValue.setValueWithInternalPath(coerced),
|
|
238
|
+
el._syncFromStorage
|
|
239
|
+
);
|
|
240
|
+
};
|
|
241
|
+
defaultAssigner[DEFAULT_ASSIGNER_TAG] = true;
|
|
242
|
+
return defaultAssigner;
|
|
243
|
+
};
|
|
244
|
+
function makeNoopAssigner() {
|
|
245
|
+
const noop = (_) => void 0;
|
|
246
|
+
noop[DEFAULT_ASSIGNER_TAG] = true;
|
|
247
|
+
return noop;
|
|
248
|
+
}
|
|
249
|
+
function setAssignFunction(el, vnode, value) {
|
|
250
|
+
const current = el[assignKey];
|
|
251
|
+
if (current !== void 0 && !isDefaultAssigner(current) && !isConsumerWrapped(current)) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
if (value === void 0) {
|
|
255
|
+
el[assignKey] = makeNoopAssigner();
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
if (!isRegisterValue(value)) {
|
|
259
|
+
el[assignKey] = makeNoopAssigner();
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
el[assignKey] = getModelAssigner(el, vnode, value);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const inherited = /* @__PURE__ */ new WeakMap();
|
|
266
|
+
function recordInheritedModifiers(rv, modifiers) {
|
|
267
|
+
const lazy = modifiers.lazy === true;
|
|
268
|
+
const trim = modifiers.trim === true;
|
|
269
|
+
const number = modifiers.number === true;
|
|
270
|
+
if ((lazy || trim || number) && typeof rv === "object" && rv !== null) {
|
|
271
|
+
inherited.set(toRaw(rv), { lazy, trim, number });
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
function withInheritedModifiers(rv, own) {
|
|
275
|
+
const found = typeof rv === "object" && rv !== null ? inherited.get(toRaw(rv)) : void 0;
|
|
276
|
+
return found === void 0 ? own : { ...found, ...own };
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function commit(rv, raw, modifiers) {
|
|
280
|
+
let value = raw;
|
|
281
|
+
if (modifiers.number === true && typeof value === "string") {
|
|
282
|
+
const cast = looseToNumber(value);
|
|
283
|
+
if (typeof cast !== "number") return rv.markBlank();
|
|
284
|
+
if (!Number.isFinite(cast)) return false;
|
|
285
|
+
value = cast;
|
|
286
|
+
}
|
|
287
|
+
if (value == null || value === "" && !rv.acceptsString) return rv.commitFromHost(value);
|
|
288
|
+
return wrapWithTransforms(value, rv, (coerced) => rv.commitFromHost(coerced), void 0) !== false;
|
|
289
|
+
}
|
|
290
|
+
function createHostChannel() {
|
|
291
|
+
const editing = /* @__PURE__ */ new Set();
|
|
292
|
+
const pending = /* @__PURE__ */ new Map();
|
|
293
|
+
const heldDraft = (rv) => {
|
|
294
|
+
const draft = pending.get(rv.path);
|
|
295
|
+
return draft?.basis === rv.innerRef.value ? draft : void 0;
|
|
296
|
+
};
|
|
297
|
+
return {
|
|
298
|
+
write(rv, value, ownModifiers) {
|
|
299
|
+
const modifiers = withInheritedModifiers(rv, ownModifiers);
|
|
300
|
+
if (modifiers.lazy === true && editing.has(rv.path)) {
|
|
301
|
+
pending.set(rv.path, { value, basis: rv.innerRef.value });
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
return commit(rv, value, modifiers);
|
|
305
|
+
},
|
|
306
|
+
writeInput(rv, payload, modifiers) {
|
|
307
|
+
if (typeof payload !== "object" || payload === null || payload instanceof Event || !("value" in payload) || !Object.hasOwn(payload, "value")) {
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
return rv.setValueFromHost(payload.value, modifiers);
|
|
311
|
+
},
|
|
312
|
+
markEditing(rv, isEditing, modifiers) {
|
|
313
|
+
if (isEditing) {
|
|
314
|
+
editing.add(rv.path);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
editing.delete(rv.path);
|
|
318
|
+
const draft = heldDraft(rv);
|
|
319
|
+
if (pending.delete(rv.path)) {
|
|
320
|
+
if (draft !== void 0) {
|
|
321
|
+
const buffered = draft.value;
|
|
322
|
+
commit(
|
|
323
|
+
rv,
|
|
324
|
+
modifiers.trim === true && typeof buffered === "string" ? buffered.trim() : buffered,
|
|
325
|
+
modifiers
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
if (modifiers.trim === true) {
|
|
331
|
+
const current = rv.innerRef.value;
|
|
332
|
+
if (typeof current === "string" && current !== current.trim()) {
|
|
333
|
+
commit(rv, current.trim(), modifiers);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
},
|
|
337
|
+
holdsDraft: (rv) => heldDraft(rv) !== void 0,
|
|
338
|
+
release(rv) {
|
|
339
|
+
editing.delete(rv.path);
|
|
340
|
+
pending.delete(rv.path);
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
7
345
|
const attaformListenersSymbol = /* @__PURE__ */ Symbol.for("attaform:focus-listeners");
|
|
8
346
|
const HOST_FOCUS_TARGET_SELECTOR = [
|
|
9
347
|
'input:not([type="hidden"])',
|
|
@@ -33,9 +371,7 @@ function attachFocusListeners(kernel, segments, element, instanceMeta) {
|
|
|
33
371
|
element.addEventListener("focus", handleFocus);
|
|
34
372
|
element.addEventListener("blur", handleBlur);
|
|
35
373
|
target[attaformListenersSymbol] = { handleFocus, handleBlur };
|
|
36
|
-
|
|
37
|
-
const activeElement = rootNode instanceof Document || rootNode instanceof ShadowRoot ? rootNode.activeElement : null;
|
|
38
|
-
if (activeElement === element) {
|
|
374
|
+
if (activeElementOf(element) === element) {
|
|
39
375
|
kernel.markFocused(segments, true, focusMeta);
|
|
40
376
|
}
|
|
41
377
|
}
|
|
@@ -101,124 +437,54 @@ function createDomBinding(kernel) {
|
|
|
101
437
|
}
|
|
102
438
|
}
|
|
103
439
|
function rebuildSortedRegistrations() {
|
|
104
|
-
const flat = [];
|
|
105
|
-
for (const [, record] of elements) {
|
|
106
|
-
for (const el of record.elements) {
|
|
107
|
-
flat.push({
|
|
108
|
-
path: record.path,
|
|
109
|
-
element: el,
|
|
110
|
-
host: false,
|
|
111
|
-
formInstanceId: elementToFormInstance.get(el) ?? ""
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
for (const [key, target] of hostTargets) {
|
|
116
|
-
if (elements.has(key)) continue;
|
|
117
|
-
flat.push({
|
|
118
|
-
path: target.path,
|
|
119
|
-
element: target.hostRoot,
|
|
120
|
-
host: true,
|
|
121
|
-
formInstanceId: target.formInstanceId
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
flat.sort(
|
|
125
|
-
(a, b) => a.element.compareDocumentPosition(b.element) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1
|
|
126
|
-
);
|
|
127
|
-
return flat;
|
|
128
|
-
}
|
|
129
|
-
function getFirstErrorElement(formInstanceId) {
|
|
130
|
-
sortedRegistrationsCache ?? (sortedRegistrationsCache = rebuildSortedRegistrations());
|
|
131
|
-
for (const entry of sortedRegistrationsCache) {
|
|
132
|
-
if (entry.formInstanceId !== formInstanceId) continue;
|
|
133
|
-
if (!entry.element.isConnected) continue;
|
|
134
|
-
if (entry.element.offsetParent === null) continue;
|
|
135
|
-
if (kernel.getErrorsForPath(entry.path).length === 0) continue;
|
|
136
|
-
const element = entry.host ? resolveHostFocusTarget(entry.element) : entry.element;
|
|
137
|
-
return { path: entry.path, element };
|
|
138
|
-
}
|
|
139
|
-
return null;
|
|
140
|
-
}
|
|
141
|
-
return {
|
|
142
|
-
elements,
|
|
143
|
-
attach,
|
|
144
|
-
detach,
|
|
145
|
-
markHostConnected,
|
|
146
|
-
getFirstErrorElement
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
function armDomBinding(value) {
|
|
150
|
-
value.ensureDomBinding?.(createDomBinding);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const isArray = Array.isArray;
|
|
154
|
-
function isFunction(value) {
|
|
155
|
-
return typeof value === "function";
|
|
156
|
-
}
|
|
157
|
-
function toTypeString(value) {
|
|
158
|
-
return Object.prototype.toString.call(value);
|
|
159
|
-
}
|
|
160
|
-
function isSet(value) {
|
|
161
|
-
return toTypeString(value) === "[object Set]";
|
|
162
|
-
}
|
|
163
|
-
function isDate(value) {
|
|
164
|
-
return toTypeString(value) === "[object Date]";
|
|
165
|
-
}
|
|
166
|
-
function isSymbol(value) {
|
|
167
|
-
return typeof value === "symbol";
|
|
168
|
-
}
|
|
169
|
-
function isObject(value) {
|
|
170
|
-
return value !== null && typeof value === "object";
|
|
171
|
-
}
|
|
172
|
-
function looseToNumber(val) {
|
|
173
|
-
const n = parseFloat(val);
|
|
174
|
-
return isNaN(n) ? val : n;
|
|
175
|
-
}
|
|
176
|
-
function looseCompareArrays(a, b) {
|
|
177
|
-
if (a.length !== b.length) return false;
|
|
178
|
-
let equal = true;
|
|
179
|
-
for (let i = 0; equal && i < a.length; i++) {
|
|
180
|
-
equal = looseEqual(a[i], b[i]);
|
|
181
|
-
}
|
|
182
|
-
return equal;
|
|
183
|
-
}
|
|
184
|
-
function looseEqual(a, b) {
|
|
185
|
-
if (a === b) return true;
|
|
186
|
-
const aValidType = isDate(a);
|
|
187
|
-
const bValidType = isDate(b);
|
|
188
|
-
if (aValidType || bValidType) {
|
|
189
|
-
return aValidType && bValidType ? a.getTime() === b.getTime() : false;
|
|
190
|
-
}
|
|
191
|
-
const aSymbol = isSymbol(a);
|
|
192
|
-
const bSymbol = isSymbol(b);
|
|
193
|
-
if (aSymbol || bSymbol) return a === b;
|
|
194
|
-
const aIsArray = isArray(a);
|
|
195
|
-
const bIsArray = isArray(b);
|
|
196
|
-
if (aIsArray || bIsArray) {
|
|
197
|
-
return aIsArray && bIsArray ? looseCompareArrays(a, b) : false;
|
|
440
|
+
const flat = [];
|
|
441
|
+
for (const [, record] of elements) {
|
|
442
|
+
for (const el of record.elements) {
|
|
443
|
+
flat.push({
|
|
444
|
+
path: record.path,
|
|
445
|
+
element: el,
|
|
446
|
+
host: false,
|
|
447
|
+
formInstanceId: elementToFormInstance.get(el) ?? ""
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
for (const [key, target] of hostTargets) {
|
|
452
|
+
if (elements.has(key)) continue;
|
|
453
|
+
flat.push({
|
|
454
|
+
path: target.path,
|
|
455
|
+
element: target.hostRoot,
|
|
456
|
+
host: true,
|
|
457
|
+
formInstanceId: target.formInstanceId
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
flat.sort(
|
|
461
|
+
(a, b) => a.element.compareDocumentPosition(b.element) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1
|
|
462
|
+
);
|
|
463
|
+
return flat;
|
|
198
464
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
if (!hasA || !hasB || !looseEqual(a[key], b[key])) return false;
|
|
465
|
+
function getFirstErrorElement(formInstanceId) {
|
|
466
|
+
sortedRegistrationsCache ?? (sortedRegistrationsCache = rebuildSortedRegistrations());
|
|
467
|
+
for (const entry of sortedRegistrationsCache) {
|
|
468
|
+
if (entry.formInstanceId !== formInstanceId) continue;
|
|
469
|
+
if (!entry.element.isConnected) continue;
|
|
470
|
+
if (entry.element.offsetParent === null) continue;
|
|
471
|
+
if (kernel.getErrorsForPath(entry.path).length === 0) continue;
|
|
472
|
+
const element = entry.host ? resolveHostFocusTarget(entry.element) : entry.element;
|
|
473
|
+
return { path: entry.path, element };
|
|
209
474
|
}
|
|
210
|
-
return
|
|
475
|
+
return null;
|
|
211
476
|
}
|
|
212
|
-
return
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
477
|
+
return {
|
|
478
|
+
elements,
|
|
479
|
+
attach,
|
|
480
|
+
detach,
|
|
481
|
+
markHostConnected,
|
|
482
|
+
getFirstErrorElement,
|
|
483
|
+
hostChannel: createHostChannel()
|
|
484
|
+
};
|
|
216
485
|
}
|
|
217
|
-
function
|
|
218
|
-
|
|
219
|
-
const fn = fns[i];
|
|
220
|
-
if (fn) fn(...args);
|
|
221
|
-
}
|
|
486
|
+
function armDomBinding(value) {
|
|
487
|
+
return value.ensureDomBinding?.(createDomBinding);
|
|
222
488
|
}
|
|
223
489
|
|
|
224
490
|
const MANAGED_ARIA_ATTRS = [
|
|
@@ -369,188 +635,6 @@ function isRegisterValueLike(val) {
|
|
|
369
635
|
return typeof val === "object" && val !== null && "markInteracted" in val && typeof val.markInteracted === "function";
|
|
370
636
|
}
|
|
371
637
|
|
|
372
|
-
const assignKey = /* @__PURE__ */ Symbol.for("attaform:assign-key");
|
|
373
|
-
const DEFAULT_ASSIGNER_TAG = /* @__PURE__ */ Symbol.for("attaform:default-assigner-tag");
|
|
374
|
-
function isDefaultAssigner(fn) {
|
|
375
|
-
return typeof fn === "function" && fn[DEFAULT_ASSIGNER_TAG] === true;
|
|
376
|
-
}
|
|
377
|
-
const CONSUMER_WRAPPED_TAG = /* @__PURE__ */ Symbol.for("attaform:consumer-wrapped-assigner");
|
|
378
|
-
function isConsumerWrapped(fn) {
|
|
379
|
-
return typeof fn === "function" && fn[CONSUMER_WRAPPED_TAG] === true;
|
|
380
|
-
}
|
|
381
|
-
function fireAssigner(el, registerValue, value) {
|
|
382
|
-
const fn = el[assignKey];
|
|
383
|
-
if (fn === void 0) return void 0;
|
|
384
|
-
if (isDefaultAssigner(fn) || isConsumerWrapped(fn)) {
|
|
385
|
-
return fn(value);
|
|
386
|
-
}
|
|
387
|
-
if (!isRegisterValue(registerValue)) {
|
|
388
|
-
return fn(value, void 0);
|
|
389
|
-
}
|
|
390
|
-
return wrapWithTransforms(
|
|
391
|
-
value,
|
|
392
|
-
registerValue,
|
|
393
|
-
(coerced) => fn(coerced, registerValue),
|
|
394
|
-
void 0
|
|
395
|
-
);
|
|
396
|
-
}
|
|
397
|
-
function isThenable(x) {
|
|
398
|
-
return x !== null && (typeof x === "object" || typeof x === "function") && typeof x.then === "function";
|
|
399
|
-
}
|
|
400
|
-
function makeTransformContext() {
|
|
401
|
-
const holder = { controller: null, aborted: false };
|
|
402
|
-
const ctx = {
|
|
403
|
-
get signal() {
|
|
404
|
-
if (holder.controller === null) {
|
|
405
|
-
holder.controller = new AbortController();
|
|
406
|
-
if (holder.aborted) holder.controller.abort();
|
|
407
|
-
}
|
|
408
|
-
return holder.controller.signal;
|
|
409
|
-
}
|
|
410
|
-
};
|
|
411
|
-
return { ctx, holder };
|
|
412
|
-
}
|
|
413
|
-
function runTransforms(initial, registerValue) {
|
|
414
|
-
const transforms = registerValue.transforms;
|
|
415
|
-
if (transforms === void 0 || transforms.length === 0) {
|
|
416
|
-
return { kind: "sync", ok: true, value: initial };
|
|
417
|
-
}
|
|
418
|
-
const { ctx, holder } = makeTransformContext();
|
|
419
|
-
let v = initial;
|
|
420
|
-
for (let i = 0; i < transforms.length; i++) {
|
|
421
|
-
const fn = transforms[i];
|
|
422
|
-
let out;
|
|
423
|
-
try {
|
|
424
|
-
out = fn(v, ctx);
|
|
425
|
-
} catch (err) {
|
|
426
|
-
logTransformFailure(registerValue.path);
|
|
427
|
-
return { kind: "sync", ok: false };
|
|
428
|
-
}
|
|
429
|
-
if (isThenable(out)) {
|
|
430
|
-
const rest = transforms.slice(i + 1);
|
|
431
|
-
const seed = out;
|
|
432
|
-
const run = () => rest.reduce(
|
|
433
|
-
(acc, next) => acc.then((value) => next(value, ctx)),
|
|
434
|
-
Promise.resolve(seed)
|
|
435
|
-
);
|
|
436
|
-
return { kind: "async", run, holder };
|
|
437
|
-
}
|
|
438
|
-
v = out;
|
|
439
|
-
}
|
|
440
|
-
return { kind: "sync", ok: true, value: v };
|
|
441
|
-
}
|
|
442
|
-
function kickoffAsyncTransform(rv, holder, run, commit, syncDom) {
|
|
443
|
-
const token = rv.beginTransform(holder);
|
|
444
|
-
void run().then(
|
|
445
|
-
(value) => {
|
|
446
|
-
const live = rv.isCurrentTransform(token);
|
|
447
|
-
rv.endTransform(token);
|
|
448
|
-
if (!live) return;
|
|
449
|
-
const coerced = applyCoerce(value, rv);
|
|
450
|
-
const wrote = commit(coerced);
|
|
451
|
-
if (wrote === false) rv.setTransformError(transformGateRejectedError(rv.path));
|
|
452
|
-
else syncDom?.();
|
|
453
|
-
},
|
|
454
|
-
(err) => {
|
|
455
|
-
if (rv.isCurrentTransform(token)) rv.setTransformError(toTransformError(err));
|
|
456
|
-
rv.endTransform(token);
|
|
457
|
-
}
|
|
458
|
-
);
|
|
459
|
-
}
|
|
460
|
-
function logTransformFailure(path, index, fn, err) {
|
|
461
|
-
{
|
|
462
|
-
console.error("[attaform] AF14 attaform.dev/e/af14");
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
function applyCoerce(value, registerValue) {
|
|
466
|
-
return registerValue.coerce !== void 0 ? registerValue.coerce(value) : value;
|
|
467
|
-
}
|
|
468
|
-
function wrapWithTransforms(value, registerValue, commit, syncDom) {
|
|
469
|
-
const r = runTransforms(value, registerValue);
|
|
470
|
-
if (r.kind === "async") {
|
|
471
|
-
kickoffAsyncTransform(registerValue, r.holder, r.run, commit, syncDom);
|
|
472
|
-
return true;
|
|
473
|
-
}
|
|
474
|
-
if (!r.ok) return false;
|
|
475
|
-
const coerced = applyCoerce(r.value, registerValue);
|
|
476
|
-
return commit(coerced);
|
|
477
|
-
}
|
|
478
|
-
function toTransformError(value) {
|
|
479
|
-
if (value instanceof Error) return value;
|
|
480
|
-
const message = typeof value === "string" && value.length > 0 ? value : `Transform rejected with a non-Error value (${typeof value})`;
|
|
481
|
-
return new Error(message, { cause: value });
|
|
482
|
-
}
|
|
483
|
-
function transformGateRejectedError(path) {
|
|
484
|
-
return new Error(
|
|
485
|
-
`[attaform] transform result for path '${path}' was rejected by the field's type gate (the resolved value did not fit the schema slot).`
|
|
486
|
-
);
|
|
487
|
-
}
|
|
488
|
-
const getModelAssigner = (el, vnode, registerValue) => {
|
|
489
|
-
const fn = vnode.props?.["onUpdate:registerValue"] ?? vnode.props?.["on:update:registerValue"];
|
|
490
|
-
if (isArray(fn)) {
|
|
491
|
-
const fnArr = fn.filter((x) => isFunction(x));
|
|
492
|
-
const wrapped = (value) => {
|
|
493
|
-
return wrapWithTransforms(
|
|
494
|
-
value,
|
|
495
|
-
registerValue,
|
|
496
|
-
(coerced) => {
|
|
497
|
-
invokeArrayFns(fnArr, coerced, registerValue);
|
|
498
|
-
return void 0;
|
|
499
|
-
},
|
|
500
|
-
void 0
|
|
501
|
-
);
|
|
502
|
-
};
|
|
503
|
-
wrapped[CONSUMER_WRAPPED_TAG] = true;
|
|
504
|
-
return wrapped;
|
|
505
|
-
}
|
|
506
|
-
if (isFunction(fn)) {
|
|
507
|
-
const handler = fn;
|
|
508
|
-
const wrapped = (value) => {
|
|
509
|
-
return wrapWithTransforms(
|
|
510
|
-
value,
|
|
511
|
-
registerValue,
|
|
512
|
-
(coerced) => handler(coerced, registerValue),
|
|
513
|
-
void 0
|
|
514
|
-
);
|
|
515
|
-
};
|
|
516
|
-
wrapped[CONSUMER_WRAPPED_TAG] = true;
|
|
517
|
-
return wrapped;
|
|
518
|
-
}
|
|
519
|
-
const defaultAssigner = (value) => {
|
|
520
|
-
if (value === void 0 && registerValue.acceptsUndefined) {
|
|
521
|
-
return registerValue.setValueWithInternalPath(void 0);
|
|
522
|
-
}
|
|
523
|
-
return wrapWithTransforms(
|
|
524
|
-
value,
|
|
525
|
-
registerValue,
|
|
526
|
-
(coerced) => registerValue.setValueWithInternalPath(coerced),
|
|
527
|
-
el._syncFromStorage
|
|
528
|
-
);
|
|
529
|
-
};
|
|
530
|
-
defaultAssigner[DEFAULT_ASSIGNER_TAG] = true;
|
|
531
|
-
return defaultAssigner;
|
|
532
|
-
};
|
|
533
|
-
function makeNoopAssigner() {
|
|
534
|
-
const noop = (_) => void 0;
|
|
535
|
-
noop[DEFAULT_ASSIGNER_TAG] = true;
|
|
536
|
-
return noop;
|
|
537
|
-
}
|
|
538
|
-
function setAssignFunction(el, vnode, value) {
|
|
539
|
-
const current = el[assignKey];
|
|
540
|
-
if (current !== void 0 && !isDefaultAssigner(current) && !isConsumerWrapped(current)) {
|
|
541
|
-
return;
|
|
542
|
-
}
|
|
543
|
-
if (value === void 0) {
|
|
544
|
-
el[assignKey] = makeNoopAssigner();
|
|
545
|
-
return;
|
|
546
|
-
}
|
|
547
|
-
if (!isRegisterValue(value)) {
|
|
548
|
-
el[assignKey] = makeNoopAssigner();
|
|
549
|
-
return;
|
|
550
|
-
}
|
|
551
|
-
el[assignKey] = getModelAssigner(el, vnode, value);
|
|
552
|
-
}
|
|
553
|
-
|
|
554
638
|
function isBlankFileValue(value) {
|
|
555
639
|
if (value === null || value === void 0) return true;
|
|
556
640
|
if (Array.isArray(value) && value.length === 0) return true;
|
|
@@ -642,11 +726,6 @@ function syncElementRegistration(el, value, oldValue) {
|
|
|
642
726
|
}
|
|
643
727
|
|
|
644
728
|
const valueSyncScopeKey = /* @__PURE__ */ Symbol.for("attaform:value-sync-scope");
|
|
645
|
-
function isElementFocused(el) {
|
|
646
|
-
const rootNode = el.getRootNode();
|
|
647
|
-
const activeElement = rootNode instanceof Document || rootNode instanceof ShadowRoot ? rootNode.activeElement : null;
|
|
648
|
-
return activeElement === el;
|
|
649
|
-
}
|
|
650
729
|
function setupValueSync(el, source, apply, options = {}) {
|
|
651
730
|
const skipWhileFocused = options.skipWhileFocused === true;
|
|
652
731
|
const scope = effectScope(true);
|
|
@@ -655,7 +734,7 @@ function setupValueSync(el, source, apply, options = {}) {
|
|
|
655
734
|
source,
|
|
656
735
|
() => {
|
|
657
736
|
if (el.composing === true) return;
|
|
658
|
-
if (skipWhileFocused &&
|
|
737
|
+
if (skipWhileFocused && activeElementOf(el) === el) return;
|
|
659
738
|
apply();
|
|
660
739
|
},
|
|
661
740
|
{ flush: "post" }
|
|
@@ -714,6 +793,28 @@ function onCompositionEnd(e) {
|
|
|
714
793
|
target.dispatchEvent(new Event("input"));
|
|
715
794
|
}
|
|
716
795
|
}
|
|
796
|
+
const heldDrafts = /* @__PURE__ */ new WeakMap();
|
|
797
|
+
function holdDraft(owner, control, stale) {
|
|
798
|
+
heldDrafts.set(owner, {
|
|
799
|
+
control,
|
|
800
|
+
text: control.value,
|
|
801
|
+
stale,
|
|
802
|
+
start: control.selectionStart,
|
|
803
|
+
end: control.selectionEnd
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
function restoreDraft(owner) {
|
|
807
|
+
const held = heldDrafts.get(owner);
|
|
808
|
+
if (held === void 0) return;
|
|
809
|
+
heldDrafts.delete(owner);
|
|
810
|
+
const { control, text, stale, start, end } = held;
|
|
811
|
+
if (control.value !== stale || stale === text) return;
|
|
812
|
+
control.value = text;
|
|
813
|
+
if (start !== null && end !== null) control.setSelectionRange(start, end);
|
|
814
|
+
}
|
|
815
|
+
function textControl(node) {
|
|
816
|
+
return node instanceof HTMLInputElement || node instanceof HTMLTextAreaElement ? node : null;
|
|
817
|
+
}
|
|
717
818
|
const vRegisterText = {
|
|
718
819
|
created(el, { value, modifiers: { lazy, trim, number } }, vnode) {
|
|
719
820
|
const castToNumberAtCreated = number === true || vnode.props?.["type"] === "number";
|
|
@@ -836,12 +937,14 @@ const vRegisterText = {
|
|
|
836
937
|
mounted(el, { value }) {
|
|
837
938
|
if (!isRegisterValue(value)) return;
|
|
838
939
|
el.value = value.displayValue.value;
|
|
940
|
+
el._lastAppliedModel = value.innerRef.value;
|
|
839
941
|
setupValueSync(
|
|
840
942
|
el,
|
|
841
943
|
value.displayValue,
|
|
842
944
|
() => {
|
|
843
945
|
const next = value.displayValue.value;
|
|
844
946
|
if (el.value !== next) el.value = next;
|
|
947
|
+
el._lastAppliedModel = value.innerRef.value;
|
|
845
948
|
},
|
|
846
949
|
{ skipWhileFocused: true }
|
|
847
950
|
);
|
|
@@ -850,17 +953,16 @@ const vRegisterText = {
|
|
|
850
953
|
setAssignFunction(el, vnode, value);
|
|
851
954
|
if (el.composing === true) return;
|
|
852
955
|
if (!isRegisterValue(value)) return;
|
|
956
|
+
const model = value.innerRef.value;
|
|
957
|
+
const modelMoved = model !== el._lastAppliedModel || !isRegisterValue(oldValue) || oldValue.path !== value.path;
|
|
958
|
+
el._lastAppliedModel = model;
|
|
853
959
|
const target = value.displayValue.value;
|
|
854
960
|
if (el.value === target) {
|
|
855
961
|
return;
|
|
856
962
|
}
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
if (lazy === true && value.innerRef.value === oldValue) {
|
|
861
|
-
return;
|
|
862
|
-
}
|
|
863
|
-
if (trim === true && el.value.trim() === target) {
|
|
963
|
+
if (activeElementOf(el) === el && el.type !== "range") {
|
|
964
|
+
if (lazy === true && !modelMoved || trim === true && el.value.trim() === target) {
|
|
965
|
+
holdDraft(el, el, target);
|
|
864
966
|
return;
|
|
865
967
|
}
|
|
866
968
|
}
|
|
@@ -931,20 +1033,15 @@ const vRegisterCheckbox = {
|
|
|
931
1033
|
el._lastAppliedModel = value.innerRef.value;
|
|
932
1034
|
});
|
|
933
1035
|
},
|
|
934
|
-
// Skip the DOM sync when the model is identity-unchanged
|
|
935
|
-
//
|
|
936
|
-
//
|
|
937
|
-
//
|
|
938
|
-
//
|
|
939
|
-
//
|
|
940
|
-
//
|
|
941
|
-
//
|
|
942
|
-
//
|
|
943
|
-
// the browser fires `change`. Identity comparison on
|
|
944
|
-
// `innerRef.value` is sound for the same reason as multi-select —
|
|
945
|
-
// every form write produces a fresh value at the path (new
|
|
946
|
-
// primitives; new array/Set references along the spine), so
|
|
947
|
-
// reference equality tracks "did the model move" exactly.
|
|
1036
|
+
// Skip the DOM sync when the model is identity-unchanged since the last
|
|
1037
|
+
// application. Without the guard, a sibling's reactive write triggers
|
|
1038
|
+
// `beforeUpdate` mid-click, `setChecked` re-applies the prior model state,
|
|
1039
|
+
// and the in-flight toggle is clobbered before the browser fires `change`.
|
|
1040
|
+
// Compare `innerRef.value` by identity: every form write produces a fresh
|
|
1041
|
+
// value at the path, new primitives and new array or Set references along
|
|
1042
|
+
// the spine, so reference equality tracks "did the model move" exactly. The
|
|
1043
|
+
// guard must NOT compare against `oldValue`, which is the previous binding's
|
|
1044
|
+
// wrapper RegisterValue and never equals a model scalar.
|
|
948
1045
|
beforeUpdate(el, binding, vnode) {
|
|
949
1046
|
setAssignFunction(el, vnode, binding.value);
|
|
950
1047
|
if (!isRegisterValue(binding.value)) return;
|
|
@@ -994,13 +1091,11 @@ const vRegisterRadio = {
|
|
|
994
1091
|
}
|
|
995
1092
|
});
|
|
996
1093
|
},
|
|
997
|
-
//
|
|
998
|
-
//
|
|
999
|
-
//
|
|
1000
|
-
//
|
|
1001
|
-
//
|
|
1002
|
-
// model. Checkbox already uses `mounted: setChecked` for the same
|
|
1003
|
-
// reason.
|
|
1094
|
+
// The initial checked sync runs in `mounted`, NOT `created`: Vue fires
|
|
1095
|
+
// `created` before the element's attributes are patched, so `type`, `value`
|
|
1096
|
+
// and `_value` are absent, `getValue(el)` returns undefined, and every radio
|
|
1097
|
+
// in a group mounts unchecked whatever the model says. Checkbox uses
|
|
1098
|
+
// `mounted` for the same reason.
|
|
1004
1099
|
mounted(el, { value }) {
|
|
1005
1100
|
if (!isRegisterValue(value)) return;
|
|
1006
1101
|
el.checked = looseEqual(value.innerRef.value, applyCoerce(getValue(el), value));
|
|
@@ -1010,15 +1105,12 @@ const vRegisterRadio = {
|
|
|
1010
1105
|
el._lastAppliedModel = value.innerRef.value;
|
|
1011
1106
|
});
|
|
1012
1107
|
},
|
|
1013
|
-
// Skip the DOM sync when the model is identity-unchanged
|
|
1014
|
-
//
|
|
1015
|
-
//
|
|
1016
|
-
//
|
|
1017
|
-
//
|
|
1018
|
-
//
|
|
1019
|
-
// `vRegisterSelect` and `setChecked` bugs: a sibling's reactive
|
|
1020
|
-
// write triggers `beforeUpdate` mid-click and writes back the
|
|
1021
|
-
// prior model state, clobbering the in-flight selection.
|
|
1108
|
+
// Skip the DOM sync when the model is identity-unchanged since the last
|
|
1109
|
+
// application, tracked on `_lastAppliedModel`. Comparing against `oldValue`
|
|
1110
|
+
// instead compares a primitive scalar to the previous binding's wrapper
|
|
1111
|
+
// RegisterValue, never equal, so `el.checked` re-applies on every parent
|
|
1112
|
+
// re-render and a sibling's reactive write clobbers an in-flight selection
|
|
1113
|
+
// mid-click.
|
|
1022
1114
|
beforeUpdate(el, { value }, vnode) {
|
|
1023
1115
|
if (!isRegisterValue(value)) return;
|
|
1024
1116
|
setAssignFunction(el, vnode, value);
|
|
@@ -1077,22 +1169,20 @@ const vRegisterSelect = {
|
|
|
1077
1169
|
beforeUpdate(el, binding, vnode) {
|
|
1078
1170
|
setAssignFunction(el, vnode, binding.value);
|
|
1079
1171
|
},
|
|
1080
|
-
// Skip the DOM sync when the model is identity-unchanged
|
|
1081
|
-
//
|
|
1082
|
-
//
|
|
1083
|
-
//
|
|
1084
|
-
//
|
|
1085
|
-
//
|
|
1086
|
-
//
|
|
1087
|
-
//
|
|
1088
|
-
//
|
|
1089
|
-
//
|
|
1090
|
-
//
|
|
1091
|
-
//
|
|
1092
|
-
//
|
|
1093
|
-
//
|
|
1094
|
-
// — it short-circuits the immediate post-write render where the
|
|
1095
|
-
// DOM is already in sync from the user's click.
|
|
1172
|
+
// Skip the DOM sync when the model is identity-unchanged since the last
|
|
1173
|
+
// application. A parent re-render fires `updated` whether or not the bound
|
|
1174
|
+
// model moved: a typed character in a sibling, an async-validation tick, any
|
|
1175
|
+
// reactive read elsewhere on the page. Re-applying `setSelected` against the
|
|
1176
|
+
// prior model on each of those clobbers an in-progress `<select multiple>`
|
|
1177
|
+
// selection between mousedown and the browser's change decision, after which
|
|
1178
|
+
// the browser sees no net change, never fires `change`, and the model never
|
|
1179
|
+
// updates.
|
|
1180
|
+
//
|
|
1181
|
+
// Identity comparison is sound: every form write produces a new array or Set
|
|
1182
|
+
// reference at the path, the replacement of `form.value` rolling fresh
|
|
1183
|
+
// structures forward along the spine. The `_assigning` gate stays as well,
|
|
1184
|
+
// short-circuiting the immediate post-write render where the DOM is already
|
|
1185
|
+
// in sync from the user's click.
|
|
1096
1186
|
updated(el, { value }) {
|
|
1097
1187
|
if (el._assigning === true) return;
|
|
1098
1188
|
if (!isRegisterValue(value)) return;
|
|
@@ -1229,6 +1319,29 @@ function observeForLateHostControl(el, rv) {
|
|
|
1229
1319
|
observer.observe(el, { childList: true, subtree: true });
|
|
1230
1320
|
hostHealObservers.set(el, observer);
|
|
1231
1321
|
}
|
|
1322
|
+
const hostEdits = /* @__PURE__ */ new WeakMap();
|
|
1323
|
+
function trackHostEdits(el, rv, modifiers) {
|
|
1324
|
+
if (modifiers.lazy !== true && modifiers.trim !== true) return;
|
|
1325
|
+
if (!INTERACTIVE_TAG_NAMES.has(el.tagName) && rv.hasRegisteredDescendant(el)) return;
|
|
1326
|
+
const channel = armDomBinding(rv)?.hostChannel;
|
|
1327
|
+
if (channel === void 0) return;
|
|
1328
|
+
const edits = { channel, rv, base: void 0 };
|
|
1329
|
+
const edge = (editing) => (event) => {
|
|
1330
|
+
if (editing && !channel.holdsDraft(rv)) edits.base = textControl(event.target)?.value;
|
|
1331
|
+
const other = event.relatedTarget;
|
|
1332
|
+
if (other instanceof Node && el.contains(other)) return;
|
|
1333
|
+
channel.markEditing(rv, editing, modifiers);
|
|
1334
|
+
};
|
|
1335
|
+
addTrackedListener(el, "focus", edge(true), { capture: true });
|
|
1336
|
+
addTrackedListener(el, "blur", edge(false), { capture: true });
|
|
1337
|
+
hostEdits.set(el, edits);
|
|
1338
|
+
}
|
|
1339
|
+
function holdHostDraft(el) {
|
|
1340
|
+
const edits = hostEdits.get(el);
|
|
1341
|
+
if (edits?.base === void 0 || !edits.channel.holdsDraft(edits.rv)) return;
|
|
1342
|
+
const control = textControl(activeElementOf(el));
|
|
1343
|
+
if (control !== null && el.contains(control)) holdDraft(el, control, edits.base);
|
|
1344
|
+
}
|
|
1232
1345
|
function activateComponentHost(el, rv) {
|
|
1233
1346
|
if (rv.hasRegisteredDescendant(el)) return;
|
|
1234
1347
|
el[REGISTER_OWNER_MARKER] = true;
|
|
@@ -1243,25 +1356,34 @@ function activateComponentHost(el, rv) {
|
|
|
1243
1356
|
setupNoLatchHost(el, rv);
|
|
1244
1357
|
scheduleHostSelfHeal(el, rv);
|
|
1245
1358
|
}
|
|
1359
|
+
function inheritModifiers(binding) {
|
|
1360
|
+
const modifiers = withInheritedModifiers(binding.value, binding.modifiers);
|
|
1361
|
+
return modifiers === binding.modifiers ? binding : { ...binding, modifiers };
|
|
1362
|
+
}
|
|
1246
1363
|
const vRegisterDynamic = {
|
|
1247
|
-
created(el,
|
|
1364
|
+
created(el, ownBinding, vnode) {
|
|
1365
|
+
const binding = inheritModifiers(ownBinding);
|
|
1248
1366
|
if (isRegisterValue(binding.value)) armDomBinding(binding.value);
|
|
1249
1367
|
callModelHook(el, binding, vnode, null, "created");
|
|
1250
1368
|
if (isRegisterValue(binding.value)) setupAria(el, binding.value, vnode);
|
|
1251
1369
|
},
|
|
1252
|
-
mounted(el,
|
|
1370
|
+
mounted(el, ownBinding, vnode) {
|
|
1371
|
+
const binding = inheritModifiers(ownBinding);
|
|
1253
1372
|
callModelHook(el, binding, vnode, null, "mounted");
|
|
1254
1373
|
if (isRegisterValue(binding.value) && INTERACTIVE_TAG_NAMES.has(el.tagName) && binding.modifiers[SSR_COMPONENT_HOST_MODIFIER] !== true && vnode.props?.["disabled"] === void 0) {
|
|
1255
1374
|
setupDisabledSync(el, binding.value.disabled);
|
|
1256
1375
|
}
|
|
1257
1376
|
if (binding.modifiers[SSR_COMPONENT_HOST_MODIFIER] === true && isRegisterValue(binding.value)) {
|
|
1377
|
+
trackHostEdits(el, binding.value, binding.modifiers);
|
|
1258
1378
|
activateComponentHost(el, binding.value);
|
|
1259
1379
|
}
|
|
1260
1380
|
},
|
|
1261
|
-
beforeUpdate(el,
|
|
1381
|
+
beforeUpdate(el, ownBinding, vnode, prevVNode) {
|
|
1382
|
+
const binding = inheritModifiers(ownBinding);
|
|
1262
1383
|
if (isRegisterValue(binding.value)) armDomBinding(binding.value);
|
|
1263
1384
|
syncElementRegistration(el, binding.value, binding.oldValue);
|
|
1264
1385
|
callModelHook(el, binding, vnode, prevVNode, "beforeUpdate");
|
|
1386
|
+
holdHostDraft(el);
|
|
1265
1387
|
const ariaEl = el;
|
|
1266
1388
|
const value = binding.value;
|
|
1267
1389
|
if (!isRegisterValue(value) || value.ariaDisplayState === void 0) {
|
|
@@ -1279,13 +1401,17 @@ const vRegisterDynamic = {
|
|
|
1279
1401
|
}
|
|
1280
1402
|
},
|
|
1281
1403
|
updated(el, binding, vnode, prevVNode) {
|
|
1282
|
-
callModelHook(el, binding, vnode, prevVNode, "updated");
|
|
1404
|
+
callModelHook(el, inheritModifiers(binding), vnode, prevVNode, "updated");
|
|
1405
|
+
restoreDraft(el);
|
|
1283
1406
|
},
|
|
1284
1407
|
beforeUnmount(el, { value }) {
|
|
1285
1408
|
removeTrackedListeners(el);
|
|
1286
1409
|
teardownAria(el);
|
|
1287
1410
|
teardownValueSync(el);
|
|
1288
1411
|
teardownDisabledSync(el);
|
|
1412
|
+
const edits = hostEdits.get(el);
|
|
1413
|
+
edits?.channel.release(edits.rv);
|
|
1414
|
+
hostEdits.delete(el);
|
|
1289
1415
|
if (!isRegisterValue(value)) return;
|
|
1290
1416
|
value.deregisterElement(el);
|
|
1291
1417
|
if (componentHostLatch.has(el)) {
|
|
@@ -1306,13 +1432,12 @@ const vRegisterDynamic = {
|
|
|
1306
1432
|
delete el._syncFromStorage;
|
|
1307
1433
|
delete el[assignKey];
|
|
1308
1434
|
},
|
|
1309
|
-
//
|
|
1310
|
-
//
|
|
1311
|
-
//
|
|
1312
|
-
//
|
|
1313
|
-
//
|
|
1314
|
-
//
|
|
1315
|
-
// describedby matches the client after hydration.
|
|
1435
|
+
// Vue skips directive lifecycle during SSR, so the hooks above never run on
|
|
1436
|
+
// the server and the same aria attributes are emitted here from the SSR-time
|
|
1437
|
+
// gated display state. Honours authored attrs through the vnode-level
|
|
1438
|
+
// lockout, touches no DOM, and shares `resolveAriaValue` with the client
|
|
1439
|
+
// path. Ids are SSR-stable, `formInstanceId` deriving from Vue's `useId`, so
|
|
1440
|
+
// a server-rendered describedby matches the client after hydration.
|
|
1316
1441
|
getSSRProps(binding, vnode) {
|
|
1317
1442
|
const rv = binding.value;
|
|
1318
1443
|
if (!isRegisterValue(rv)) return void 0;
|
|
@@ -1377,4 +1502,4 @@ function installVRegister(app) {
|
|
|
1377
1502
|
}
|
|
1378
1503
|
}
|
|
1379
1504
|
|
|
1380
|
-
export { assignKey as a, vRegisterFile as b, armDomBinding as c, installVRegister as i, vRegister as v };
|
|
1505
|
+
export { assignKey as a, vRegisterFile as b, armDomBinding as c, installVRegister as i, recordInheritedModifiers as r, vRegister as v, withInheritedModifiers as w };
|