@samline/forms 2.2.3 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/browser/global.d.ts +16 -5
- package/dist/browser/global.global.js +164 -87
- package/dist/browser/global.global.js.map +1 -1
- package/dist/index.cjs +164 -87
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -5
- package/dist/index.d.ts +16 -5
- package/dist/index.js +164 -87
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -91,33 +91,45 @@ var cssEscape = (value) => {
|
|
|
91
91
|
}
|
|
92
92
|
return value.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g, "\\$1");
|
|
93
93
|
};
|
|
94
|
-
var
|
|
94
|
+
var findVisibleField = (form2, displayName) => {
|
|
95
|
+
const node = form2.querySelector(
|
|
96
|
+
`[name="${cssEscape(displayName)}"]:not([type="hidden"])`
|
|
97
|
+
);
|
|
98
|
+
if (node instanceof HTMLInputElement || node instanceof HTMLTextAreaElement) {
|
|
99
|
+
return node;
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
};
|
|
103
|
+
var findHiddenMirror = (form2, fieldName) => {
|
|
95
104
|
const byAttr = form2.querySelector(
|
|
96
105
|
`[${FORMATTER_RAW_ATTRIBUTE}="${cssEscape(fieldName)}"]`
|
|
97
106
|
);
|
|
98
107
|
if (byAttr) return byAttr;
|
|
99
108
|
const byName = form2.querySelector(
|
|
100
|
-
`[name="${cssEscape(
|
|
109
|
+
`input[type="hidden"][name="${cssEscape(fieldName)}"]`
|
|
101
110
|
);
|
|
102
111
|
if (byName) return byName;
|
|
103
|
-
const byNameOld = form2.querySelector(
|
|
104
|
-
`[name="${cssEscape(`${fieldName}Raw`)}"]`
|
|
105
|
-
);
|
|
106
|
-
if (byNameOld) return byNameOld;
|
|
107
112
|
return null;
|
|
108
113
|
};
|
|
109
|
-
var
|
|
110
|
-
const existing =
|
|
114
|
+
var ensureHiddenMirror = (form2, fieldName) => {
|
|
115
|
+
const existing = findHiddenMirror(form2, fieldName);
|
|
111
116
|
if (existing) return existing;
|
|
112
117
|
const input = document.createElement("input");
|
|
113
118
|
input.type = "hidden";
|
|
114
|
-
input.name =
|
|
119
|
+
input.name = fieldName;
|
|
115
120
|
input.setAttribute("aria-hidden", "true");
|
|
116
121
|
input.tabIndex = -1;
|
|
117
122
|
input.setAttribute(FORMATTER_RAW_ATTRIBUTE, fieldName);
|
|
118
123
|
form2.appendChild(input);
|
|
119
124
|
return input;
|
|
120
125
|
};
|
|
126
|
+
var renameVisibleField = (visible, displayName) => {
|
|
127
|
+
if (visible.name === displayName) return;
|
|
128
|
+
visible.name = displayName;
|
|
129
|
+
};
|
|
130
|
+
var restoreVisibleName = (visible, originalName) => {
|
|
131
|
+
visible.name = originalName;
|
|
132
|
+
};
|
|
121
133
|
var applyFormattedValue = (visible, mirror, formatted, raw, inputType) => {
|
|
122
134
|
const rawInput = visible.value;
|
|
123
135
|
const caret = clamp(
|
|
@@ -235,6 +247,7 @@ var loadFormatter = () => {
|
|
|
235
247
|
};
|
|
236
248
|
|
|
237
249
|
// src/api/format.ts
|
|
250
|
+
var UNBOUND = null;
|
|
238
251
|
var registry = /* @__PURE__ */ new WeakMap();
|
|
239
252
|
var getRegistry = (state2) => {
|
|
240
253
|
let bucket = registry.get(state2);
|
|
@@ -245,97 +258,130 @@ var getRegistry = (state2) => {
|
|
|
245
258
|
return bucket;
|
|
246
259
|
};
|
|
247
260
|
var resolveFieldNames = (config) => Array.isArray(config.field) ? config.field : [config.field];
|
|
248
|
-
var addListener = (state2, target, type, handler) => {
|
|
249
|
-
|
|
250
|
-
|
|
261
|
+
var addListener = (state2, target, type, handler, useCapture) => {
|
|
262
|
+
if (useCapture) {
|
|
263
|
+
target.addEventListener(type, handler, { capture: true });
|
|
264
|
+
} else {
|
|
265
|
+
target.addEventListener(type, handler);
|
|
266
|
+
}
|
|
267
|
+
const entry = { element: target, type, handler };
|
|
268
|
+
if (useCapture !== void 0) entry.capture = useCapture;
|
|
269
|
+
state2.listeners.push(entry);
|
|
270
|
+
};
|
|
271
|
+
var readInputType = (event) => {
|
|
272
|
+
const candidate = event;
|
|
273
|
+
return typeof candidate.inputType === "string" ? candidate.inputType : void 0;
|
|
251
274
|
};
|
|
252
|
-
var buildHandler = (
|
|
275
|
+
var buildHandler = (visible, mirror, formatFn, formatType, formatOptions) => {
|
|
253
276
|
const handler = (event) => {
|
|
254
|
-
if (!
|
|
255
|
-
|
|
256
|
-
const
|
|
277
|
+
if (!visible.isConnected || !mirror.isConnected) return;
|
|
278
|
+
const isVisible = event.target === visible;
|
|
279
|
+
const isMirror = event.target === mirror;
|
|
280
|
+
if (!isVisible && !isMirror) return;
|
|
281
|
+
const rawInput = isMirror ? mirror.value : visible.value;
|
|
257
282
|
const inputType = readInputType(event);
|
|
258
283
|
const { formatted, raw } = formatFn(rawInput, formatType, formatOptions);
|
|
259
284
|
if (!formatted && !raw) {
|
|
260
|
-
|
|
261
|
-
if (mirror
|
|
285
|
+
visible.value = "";
|
|
286
|
+
if (mirror.value !== "") mirror.value = "";
|
|
262
287
|
return;
|
|
263
288
|
}
|
|
264
|
-
applyFormattedValue(
|
|
265
|
-
void mirrorFieldName;
|
|
289
|
+
applyFormattedValue(visible, mirror, formatted, raw, inputType);
|
|
266
290
|
};
|
|
267
291
|
return handler;
|
|
268
292
|
};
|
|
269
|
-
var readInputType = (event) => {
|
|
270
|
-
const candidate = event;
|
|
271
|
-
return typeof candidate.inputType === "string" ? candidate.inputType : void 0;
|
|
272
|
-
};
|
|
273
293
|
var applyFormat = async (state2, helpers, config) => {
|
|
274
294
|
if (!state2.element || !state2.api) return;
|
|
275
|
-
const formatter = await loadFormatter();
|
|
276
|
-
if (!formatter) return;
|
|
277
295
|
const formatType = config.type;
|
|
278
296
|
const formatOptions = config.options;
|
|
279
297
|
const fieldNames = resolveFieldNames(config);
|
|
280
298
|
const bucket = getRegistry(state2);
|
|
299
|
+
const phase1Entries = [];
|
|
281
300
|
for (const fieldName of fieldNames) {
|
|
282
|
-
const
|
|
283
|
-
|
|
284
|
-
const
|
|
285
|
-
(f) => f instanceof HTMLInputElement && f.type !== "
|
|
301
|
+
const displayName = config.displayField ?? `${fieldName}_displayed`;
|
|
302
|
+
const canonicalFields = helpers.getFieldsByName(fieldName);
|
|
303
|
+
const candidates = canonicalFields.filter(
|
|
304
|
+
(f) => f instanceof HTMLInputElement && f.type !== "hidden" || f instanceof HTMLTextAreaElement
|
|
286
305
|
);
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
});
|
|
304
|
-
const current = field.value;
|
|
305
|
-
if (current !== "") {
|
|
306
|
-
const { formatted, raw } = formatter.format(current, formatType, formatOptions);
|
|
307
|
-
applyFormattedValue(field, mirror, formatted, raw);
|
|
308
|
-
}
|
|
309
|
-
continue;
|
|
310
|
-
}
|
|
311
|
-
const handler = buildHandler(
|
|
312
|
-
field,
|
|
313
|
-
mirror,
|
|
314
|
-
formatter.format,
|
|
315
|
-
formatType,
|
|
316
|
-
formatOptions,
|
|
317
|
-
trackerMirrorName
|
|
318
|
-
);
|
|
319
|
-
addListener(state2, state2.element, "input", handler);
|
|
320
|
-
bucket.set(fieldName, {
|
|
321
|
-
fields: /* @__PURE__ */ new Set([field]),
|
|
322
|
-
mirrorName: trackerMirrorName,
|
|
323
|
-
handler,
|
|
306
|
+
let visible = candidates[0] ?? null;
|
|
307
|
+
if (!visible) {
|
|
308
|
+
visible = findVisibleField(state2.element, displayName);
|
|
309
|
+
}
|
|
310
|
+
if (!visible) continue;
|
|
311
|
+
const existing = bucket.get(fieldName);
|
|
312
|
+
let mirror = findHiddenMirror(state2.element, fieldName);
|
|
313
|
+
const mirrorIsOwned = !mirror;
|
|
314
|
+
if (!mirror) mirror = ensureHiddenMirror(state2.element, fieldName);
|
|
315
|
+
renameVisibleField(visible, displayName);
|
|
316
|
+
if (existing) {
|
|
317
|
+
existing.displayName = displayName;
|
|
318
|
+
existing.mirrorIsOwned = mirrorIsOwned;
|
|
319
|
+
state2.formattedFields.set(fieldName, {
|
|
320
|
+
config,
|
|
321
|
+
displayName,
|
|
324
322
|
mirrorIsOwned
|
|
325
323
|
});
|
|
324
|
+
phase1Entries.push(existing);
|
|
325
|
+
} else {
|
|
326
|
+
const entry = {
|
|
327
|
+
canonicalName: fieldName,
|
|
328
|
+
displayName,
|
|
329
|
+
visible,
|
|
330
|
+
mirror,
|
|
331
|
+
mirrorIsOwned,
|
|
332
|
+
handler: UNBOUND
|
|
333
|
+
};
|
|
334
|
+
bucket.set(fieldName, entry);
|
|
326
335
|
state2.formattedFields.set(fieldName, {
|
|
327
336
|
config,
|
|
328
|
-
|
|
337
|
+
displayName,
|
|
329
338
|
mirrorIsOwned
|
|
330
339
|
});
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
340
|
+
phase1Entries.push(entry);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
const formatter = await loadFormatter();
|
|
344
|
+
if (!formatter) {
|
|
345
|
+
rollbackPhase1(phase1Entries, bucket, state2.formattedFields);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
for (const entry of phase1Entries) {
|
|
349
|
+
if (entry.handler !== UNBOUND) {
|
|
350
|
+
const current = entry.visible.value;
|
|
351
|
+
if (current !== "") {
|
|
352
|
+
const { formatted, raw } = formatter.format(current, formatType, formatOptions);
|
|
353
|
+
applyFormattedValue(entry.visible, entry.mirror, formatted, raw);
|
|
337
354
|
}
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
const handler = buildHandler(
|
|
358
|
+
entry.visible,
|
|
359
|
+
entry.mirror,
|
|
360
|
+
formatter.format,
|
|
361
|
+
formatType,
|
|
362
|
+
formatOptions
|
|
363
|
+
);
|
|
364
|
+
addListener(state2, state2.element, "input", handler, true);
|
|
365
|
+
entry.handler = handler;
|
|
366
|
+
const initial = entry.visible.value;
|
|
367
|
+
if (initial !== "") {
|
|
368
|
+
const { formatted, raw } = formatter.format(initial, formatType, formatOptions);
|
|
369
|
+
applyFormattedValue(entry.visible, entry.mirror, formatted, raw);
|
|
370
|
+
} else if (entry.mirrorIsOwned && entry.mirror.value !== "") {
|
|
371
|
+
entry.mirror.value = "";
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
var rollbackPhase1 = (entries, bucket, registry2) => {
|
|
376
|
+
for (const entry of entries) {
|
|
377
|
+
if (entry.visible.isConnected) {
|
|
378
|
+
restoreVisibleName(entry.visible, entry.canonicalName);
|
|
379
|
+
}
|
|
380
|
+
if (entry.mirrorIsOwned && entry.mirror.isConnected) {
|
|
381
|
+
entry.mirror.remove();
|
|
338
382
|
}
|
|
383
|
+
bucket.delete(entry.canonicalName);
|
|
384
|
+
registry2.delete(entry.canonicalName);
|
|
339
385
|
}
|
|
340
386
|
};
|
|
341
387
|
var createFormat = (state2, helpers) => (config) => {
|
|
@@ -345,6 +391,14 @@ var createFormat = (state2, helpers) => (config) => {
|
|
|
345
391
|
return state2.api;
|
|
346
392
|
};
|
|
347
393
|
var createFormatAll = createFormat;
|
|
394
|
+
var resolveCanonicalForName = (state2, name) => {
|
|
395
|
+
const bucket = registry.get(state2);
|
|
396
|
+
if (!bucket) return null;
|
|
397
|
+
for (const [canonical, entry] of bucket) {
|
|
398
|
+
if (canonical === name || entry.displayName === name) return canonical;
|
|
399
|
+
}
|
|
400
|
+
return null;
|
|
401
|
+
};
|
|
348
402
|
var cleanupFormatRegistry = (state2) => {
|
|
349
403
|
if (!state2.element) {
|
|
350
404
|
registry.delete(state2);
|
|
@@ -356,10 +410,14 @@ var cleanupFormatRegistry = (state2) => {
|
|
|
356
410
|
return;
|
|
357
411
|
}
|
|
358
412
|
for (const [fieldName, entry] of bucket) {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
413
|
+
if (entry.handler !== UNBOUND) {
|
|
414
|
+
state2.element.removeEventListener("input", entry.handler, true);
|
|
415
|
+
}
|
|
416
|
+
if (entry.visible.isConnected) {
|
|
417
|
+
restoreVisibleName(entry.visible, fieldName);
|
|
418
|
+
}
|
|
419
|
+
if (entry.mirrorIsOwned && entry.mirror.isConnected) {
|
|
420
|
+
entry.mirror.remove();
|
|
363
421
|
}
|
|
364
422
|
bucket.delete(fieldName);
|
|
365
423
|
state2.formattedFields.delete(fieldName);
|
|
@@ -372,7 +430,12 @@ var createDestroy = (state2, _helpers) => () => {
|
|
|
372
430
|
if (state2.isDestroyed) return;
|
|
373
431
|
state2.isDestroyed = true;
|
|
374
432
|
for (const listener of state2.listeners) {
|
|
375
|
-
listener.
|
|
433
|
+
const options = listener.capture ? { capture: true } : void 0;
|
|
434
|
+
if (options) {
|
|
435
|
+
listener.element.removeEventListener(listener.type, listener.handler, options);
|
|
436
|
+
} else {
|
|
437
|
+
listener.element.removeEventListener(listener.type, listener.handler);
|
|
438
|
+
}
|
|
376
439
|
}
|
|
377
440
|
state2.listeners.length = 0;
|
|
378
441
|
if (state2.autoSubmitTimer) {
|
|
@@ -820,9 +883,15 @@ var createFormController = (target, options = {}) => {
|
|
|
820
883
|
const snapshot = state2.api.getState();
|
|
821
884
|
state2.subscribers.forEach((listener) => listener(snapshot));
|
|
822
885
|
};
|
|
823
|
-
const addListener2 = (node, type, handler) => {
|
|
824
|
-
|
|
825
|
-
|
|
886
|
+
const addListener2 = (node, type, handler, capture) => {
|
|
887
|
+
if (capture) {
|
|
888
|
+
node.addEventListener(type, handler, { capture: true });
|
|
889
|
+
} else {
|
|
890
|
+
node.addEventListener(type, handler);
|
|
891
|
+
}
|
|
892
|
+
const entry = { element: node, type, handler };
|
|
893
|
+
if (capture !== void 0) entry.capture = capture;
|
|
894
|
+
state2.listeners.push(entry);
|
|
826
895
|
};
|
|
827
896
|
const clearFieldCache = () => state2.fieldCache.clear();
|
|
828
897
|
const getFieldsByName = (name) => {
|
|
@@ -838,6 +907,12 @@ var createFormController = (target, options = {}) => {
|
|
|
838
907
|
for (const field of getNamedFields(state2.element)) {
|
|
839
908
|
names.add(field.name);
|
|
840
909
|
}
|
|
910
|
+
for (const [canonical, entry] of state2.formattedFields) {
|
|
911
|
+
names.add(canonical);
|
|
912
|
+
if (entry.displayName && entry.displayName !== canonical) {
|
|
913
|
+
names.add(entry.displayName);
|
|
914
|
+
}
|
|
915
|
+
}
|
|
841
916
|
return Array.from(names);
|
|
842
917
|
};
|
|
843
918
|
const getValues = () => {
|
|
@@ -923,14 +998,16 @@ var createFormController = (target, options = {}) => {
|
|
|
923
998
|
const name = target2.name;
|
|
924
999
|
if (!name) return;
|
|
925
1000
|
clearFieldCache();
|
|
1001
|
+
const canonical = resolveCanonicalForName(state2, name) ?? name;
|
|
926
1002
|
if (normalizedOptions.clearManualErrorsOnChange) {
|
|
927
|
-
delete state2.manualErrors[
|
|
1003
|
+
delete state2.manualErrors[canonical];
|
|
928
1004
|
}
|
|
929
|
-
syncVisualState([
|
|
930
|
-
if (state2.isValidated && state2.validators[
|
|
931
|
-
validateNames([
|
|
1005
|
+
syncVisualState([canonical]);
|
|
1006
|
+
if (state2.isValidated && state2.validators[canonical]) {
|
|
1007
|
+
validateNames([canonical]);
|
|
932
1008
|
}
|
|
933
|
-
emitFieldWatchers(
|
|
1009
|
+
emitFieldWatchers(canonical);
|
|
1010
|
+
if (canonical !== name) emitFieldWatchers(name);
|
|
934
1011
|
notifySubscribers();
|
|
935
1012
|
scheduleAutoSubmit();
|
|
936
1013
|
};
|