@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.js
CHANGED
|
@@ -50,33 +50,45 @@ var cssEscape = (value) => {
|
|
|
50
50
|
}
|
|
51
51
|
return value.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g, "\\$1");
|
|
52
52
|
};
|
|
53
|
-
var
|
|
53
|
+
var findVisibleField = (form2, displayName) => {
|
|
54
|
+
const node = form2.querySelector(
|
|
55
|
+
`[name="${cssEscape(displayName)}"]:not([type="hidden"])`
|
|
56
|
+
);
|
|
57
|
+
if (node instanceof HTMLInputElement || node instanceof HTMLTextAreaElement) {
|
|
58
|
+
return node;
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
};
|
|
62
|
+
var findHiddenMirror = (form2, fieldName) => {
|
|
54
63
|
const byAttr = form2.querySelector(
|
|
55
64
|
`[${FORMATTER_RAW_ATTRIBUTE}="${cssEscape(fieldName)}"]`
|
|
56
65
|
);
|
|
57
66
|
if (byAttr) return byAttr;
|
|
58
67
|
const byName = form2.querySelector(
|
|
59
|
-
`[name="${cssEscape(
|
|
68
|
+
`input[type="hidden"][name="${cssEscape(fieldName)}"]`
|
|
60
69
|
);
|
|
61
70
|
if (byName) return byName;
|
|
62
|
-
const byNameOld = form2.querySelector(
|
|
63
|
-
`[name="${cssEscape(`${fieldName}Raw`)}"]`
|
|
64
|
-
);
|
|
65
|
-
if (byNameOld) return byNameOld;
|
|
66
71
|
return null;
|
|
67
72
|
};
|
|
68
|
-
var
|
|
69
|
-
const existing =
|
|
73
|
+
var ensureHiddenMirror = (form2, fieldName) => {
|
|
74
|
+
const existing = findHiddenMirror(form2, fieldName);
|
|
70
75
|
if (existing) return existing;
|
|
71
76
|
const input = document.createElement("input");
|
|
72
77
|
input.type = "hidden";
|
|
73
|
-
input.name =
|
|
78
|
+
input.name = fieldName;
|
|
74
79
|
input.setAttribute("aria-hidden", "true");
|
|
75
80
|
input.tabIndex = -1;
|
|
76
81
|
input.setAttribute(FORMATTER_RAW_ATTRIBUTE, fieldName);
|
|
77
82
|
form2.appendChild(input);
|
|
78
83
|
return input;
|
|
79
84
|
};
|
|
85
|
+
var renameVisibleField = (visible, displayName) => {
|
|
86
|
+
if (visible.name === displayName) return;
|
|
87
|
+
visible.name = displayName;
|
|
88
|
+
};
|
|
89
|
+
var restoreVisibleName = (visible, originalName) => {
|
|
90
|
+
visible.name = originalName;
|
|
91
|
+
};
|
|
80
92
|
var applyFormattedValue = (visible, mirror, formatted, raw, inputType) => {
|
|
81
93
|
const rawInput = visible.value;
|
|
82
94
|
const caret = clamp(
|
|
@@ -194,6 +206,7 @@ var loadFormatter = () => {
|
|
|
194
206
|
};
|
|
195
207
|
|
|
196
208
|
// src/api/format.ts
|
|
209
|
+
var UNBOUND = null;
|
|
197
210
|
var registry = /* @__PURE__ */ new WeakMap();
|
|
198
211
|
var getRegistry = (state2) => {
|
|
199
212
|
let bucket = registry.get(state2);
|
|
@@ -204,97 +217,130 @@ var getRegistry = (state2) => {
|
|
|
204
217
|
return bucket;
|
|
205
218
|
};
|
|
206
219
|
var resolveFieldNames = (config) => Array.isArray(config.field) ? config.field : [config.field];
|
|
207
|
-
var addListener = (state2, target, type, handler) => {
|
|
208
|
-
|
|
209
|
-
|
|
220
|
+
var addListener = (state2, target, type, handler, useCapture) => {
|
|
221
|
+
if (useCapture) {
|
|
222
|
+
target.addEventListener(type, handler, { capture: true });
|
|
223
|
+
} else {
|
|
224
|
+
target.addEventListener(type, handler);
|
|
225
|
+
}
|
|
226
|
+
const entry = { element: target, type, handler };
|
|
227
|
+
if (useCapture !== void 0) entry.capture = useCapture;
|
|
228
|
+
state2.listeners.push(entry);
|
|
229
|
+
};
|
|
230
|
+
var readInputType = (event) => {
|
|
231
|
+
const candidate = event;
|
|
232
|
+
return typeof candidate.inputType === "string" ? candidate.inputType : void 0;
|
|
210
233
|
};
|
|
211
|
-
var buildHandler = (
|
|
234
|
+
var buildHandler = (visible, mirror, formatFn, formatType, formatOptions) => {
|
|
212
235
|
const handler = (event) => {
|
|
213
|
-
if (!
|
|
214
|
-
|
|
215
|
-
const
|
|
236
|
+
if (!visible.isConnected || !mirror.isConnected) return;
|
|
237
|
+
const isVisible = event.target === visible;
|
|
238
|
+
const isMirror = event.target === mirror;
|
|
239
|
+
if (!isVisible && !isMirror) return;
|
|
240
|
+
const rawInput = isMirror ? mirror.value : visible.value;
|
|
216
241
|
const inputType = readInputType(event);
|
|
217
242
|
const { formatted, raw } = formatFn(rawInput, formatType, formatOptions);
|
|
218
243
|
if (!formatted && !raw) {
|
|
219
|
-
|
|
220
|
-
if (mirror
|
|
244
|
+
visible.value = "";
|
|
245
|
+
if (mirror.value !== "") mirror.value = "";
|
|
221
246
|
return;
|
|
222
247
|
}
|
|
223
|
-
applyFormattedValue(
|
|
224
|
-
void mirrorFieldName;
|
|
248
|
+
applyFormattedValue(visible, mirror, formatted, raw, inputType);
|
|
225
249
|
};
|
|
226
250
|
return handler;
|
|
227
251
|
};
|
|
228
|
-
var readInputType = (event) => {
|
|
229
|
-
const candidate = event;
|
|
230
|
-
return typeof candidate.inputType === "string" ? candidate.inputType : void 0;
|
|
231
|
-
};
|
|
232
252
|
var applyFormat = async (state2, helpers, config) => {
|
|
233
253
|
if (!state2.element || !state2.api) return;
|
|
234
|
-
const formatter = await loadFormatter();
|
|
235
|
-
if (!formatter) return;
|
|
236
254
|
const formatType = config.type;
|
|
237
255
|
const formatOptions = config.options;
|
|
238
256
|
const fieldNames = resolveFieldNames(config);
|
|
239
257
|
const bucket = getRegistry(state2);
|
|
258
|
+
const phase1Entries = [];
|
|
240
259
|
for (const fieldName of fieldNames) {
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
const
|
|
244
|
-
(f) => f instanceof HTMLInputElement && f.type !== "
|
|
260
|
+
const displayName = config.displayField ?? `${fieldName}_displayed`;
|
|
261
|
+
const canonicalFields = helpers.getFieldsByName(fieldName);
|
|
262
|
+
const candidates = canonicalFields.filter(
|
|
263
|
+
(f) => f instanceof HTMLInputElement && f.type !== "hidden" || f instanceof HTMLTextAreaElement
|
|
245
264
|
);
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
});
|
|
263
|
-
const current = field.value;
|
|
264
|
-
if (current !== "") {
|
|
265
|
-
const { formatted, raw } = formatter.format(current, formatType, formatOptions);
|
|
266
|
-
applyFormattedValue(field, mirror, formatted, raw);
|
|
267
|
-
}
|
|
268
|
-
continue;
|
|
269
|
-
}
|
|
270
|
-
const handler = buildHandler(
|
|
271
|
-
field,
|
|
272
|
-
mirror,
|
|
273
|
-
formatter.format,
|
|
274
|
-
formatType,
|
|
275
|
-
formatOptions,
|
|
276
|
-
trackerMirrorName
|
|
277
|
-
);
|
|
278
|
-
addListener(state2, state2.element, "input", handler);
|
|
279
|
-
bucket.set(fieldName, {
|
|
280
|
-
fields: /* @__PURE__ */ new Set([field]),
|
|
281
|
-
mirrorName: trackerMirrorName,
|
|
282
|
-
handler,
|
|
265
|
+
let visible = candidates[0] ?? null;
|
|
266
|
+
if (!visible) {
|
|
267
|
+
visible = findVisibleField(state2.element, displayName);
|
|
268
|
+
}
|
|
269
|
+
if (!visible) continue;
|
|
270
|
+
const existing = bucket.get(fieldName);
|
|
271
|
+
let mirror = findHiddenMirror(state2.element, fieldName);
|
|
272
|
+
const mirrorIsOwned = !mirror;
|
|
273
|
+
if (!mirror) mirror = ensureHiddenMirror(state2.element, fieldName);
|
|
274
|
+
renameVisibleField(visible, displayName);
|
|
275
|
+
if (existing) {
|
|
276
|
+
existing.displayName = displayName;
|
|
277
|
+
existing.mirrorIsOwned = mirrorIsOwned;
|
|
278
|
+
state2.formattedFields.set(fieldName, {
|
|
279
|
+
config,
|
|
280
|
+
displayName,
|
|
283
281
|
mirrorIsOwned
|
|
284
282
|
});
|
|
283
|
+
phase1Entries.push(existing);
|
|
284
|
+
} else {
|
|
285
|
+
const entry = {
|
|
286
|
+
canonicalName: fieldName,
|
|
287
|
+
displayName,
|
|
288
|
+
visible,
|
|
289
|
+
mirror,
|
|
290
|
+
mirrorIsOwned,
|
|
291
|
+
handler: UNBOUND
|
|
292
|
+
};
|
|
293
|
+
bucket.set(fieldName, entry);
|
|
285
294
|
state2.formattedFields.set(fieldName, {
|
|
286
295
|
config,
|
|
287
|
-
|
|
296
|
+
displayName,
|
|
288
297
|
mirrorIsOwned
|
|
289
298
|
});
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
299
|
+
phase1Entries.push(entry);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
const formatter = await loadFormatter();
|
|
303
|
+
if (!formatter) {
|
|
304
|
+
rollbackPhase1(phase1Entries, bucket, state2.formattedFields);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
for (const entry of phase1Entries) {
|
|
308
|
+
if (entry.handler !== UNBOUND) {
|
|
309
|
+
const current = entry.visible.value;
|
|
310
|
+
if (current !== "") {
|
|
311
|
+
const { formatted, raw } = formatter.format(current, formatType, formatOptions);
|
|
312
|
+
applyFormattedValue(entry.visible, entry.mirror, formatted, raw);
|
|
296
313
|
}
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
const handler = buildHandler(
|
|
317
|
+
entry.visible,
|
|
318
|
+
entry.mirror,
|
|
319
|
+
formatter.format,
|
|
320
|
+
formatType,
|
|
321
|
+
formatOptions
|
|
322
|
+
);
|
|
323
|
+
addListener(state2, state2.element, "input", handler, true);
|
|
324
|
+
entry.handler = handler;
|
|
325
|
+
const initial = entry.visible.value;
|
|
326
|
+
if (initial !== "") {
|
|
327
|
+
const { formatted, raw } = formatter.format(initial, formatType, formatOptions);
|
|
328
|
+
applyFormattedValue(entry.visible, entry.mirror, formatted, raw);
|
|
329
|
+
} else if (entry.mirrorIsOwned && entry.mirror.value !== "") {
|
|
330
|
+
entry.mirror.value = "";
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
var rollbackPhase1 = (entries, bucket, registry2) => {
|
|
335
|
+
for (const entry of entries) {
|
|
336
|
+
if (entry.visible.isConnected) {
|
|
337
|
+
restoreVisibleName(entry.visible, entry.canonicalName);
|
|
338
|
+
}
|
|
339
|
+
if (entry.mirrorIsOwned && entry.mirror.isConnected) {
|
|
340
|
+
entry.mirror.remove();
|
|
297
341
|
}
|
|
342
|
+
bucket.delete(entry.canonicalName);
|
|
343
|
+
registry2.delete(entry.canonicalName);
|
|
298
344
|
}
|
|
299
345
|
};
|
|
300
346
|
var createFormat = (state2, helpers) => (config) => {
|
|
@@ -304,6 +350,14 @@ var createFormat = (state2, helpers) => (config) => {
|
|
|
304
350
|
return state2.api;
|
|
305
351
|
};
|
|
306
352
|
var createFormatAll = createFormat;
|
|
353
|
+
var resolveCanonicalForName = (state2, name) => {
|
|
354
|
+
const bucket = registry.get(state2);
|
|
355
|
+
if (!bucket) return null;
|
|
356
|
+
for (const [canonical, entry] of bucket) {
|
|
357
|
+
if (canonical === name || entry.displayName === name) return canonical;
|
|
358
|
+
}
|
|
359
|
+
return null;
|
|
360
|
+
};
|
|
307
361
|
var cleanupFormatRegistry = (state2) => {
|
|
308
362
|
if (!state2.element) {
|
|
309
363
|
registry.delete(state2);
|
|
@@ -315,10 +369,14 @@ var cleanupFormatRegistry = (state2) => {
|
|
|
315
369
|
return;
|
|
316
370
|
}
|
|
317
371
|
for (const [fieldName, entry] of bucket) {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
372
|
+
if (entry.handler !== UNBOUND) {
|
|
373
|
+
state2.element.removeEventListener("input", entry.handler, true);
|
|
374
|
+
}
|
|
375
|
+
if (entry.visible.isConnected) {
|
|
376
|
+
restoreVisibleName(entry.visible, fieldName);
|
|
377
|
+
}
|
|
378
|
+
if (entry.mirrorIsOwned && entry.mirror.isConnected) {
|
|
379
|
+
entry.mirror.remove();
|
|
322
380
|
}
|
|
323
381
|
bucket.delete(fieldName);
|
|
324
382
|
state2.formattedFields.delete(fieldName);
|
|
@@ -331,7 +389,12 @@ var createDestroy = (state2, _helpers) => () => {
|
|
|
331
389
|
if (state2.isDestroyed) return;
|
|
332
390
|
state2.isDestroyed = true;
|
|
333
391
|
for (const listener of state2.listeners) {
|
|
334
|
-
listener.
|
|
392
|
+
const options = listener.capture ? { capture: true } : void 0;
|
|
393
|
+
if (options) {
|
|
394
|
+
listener.element.removeEventListener(listener.type, listener.handler, options);
|
|
395
|
+
} else {
|
|
396
|
+
listener.element.removeEventListener(listener.type, listener.handler);
|
|
397
|
+
}
|
|
335
398
|
}
|
|
336
399
|
state2.listeners.length = 0;
|
|
337
400
|
if (state2.autoSubmitTimer) {
|
|
@@ -779,9 +842,15 @@ var createFormController = (target, options = {}) => {
|
|
|
779
842
|
const snapshot = state2.api.getState();
|
|
780
843
|
state2.subscribers.forEach((listener) => listener(snapshot));
|
|
781
844
|
};
|
|
782
|
-
const addListener2 = (node, type, handler) => {
|
|
783
|
-
|
|
784
|
-
|
|
845
|
+
const addListener2 = (node, type, handler, capture) => {
|
|
846
|
+
if (capture) {
|
|
847
|
+
node.addEventListener(type, handler, { capture: true });
|
|
848
|
+
} else {
|
|
849
|
+
node.addEventListener(type, handler);
|
|
850
|
+
}
|
|
851
|
+
const entry = { element: node, type, handler };
|
|
852
|
+
if (capture !== void 0) entry.capture = capture;
|
|
853
|
+
state2.listeners.push(entry);
|
|
785
854
|
};
|
|
786
855
|
const clearFieldCache = () => state2.fieldCache.clear();
|
|
787
856
|
const getFieldsByName = (name) => {
|
|
@@ -797,6 +866,12 @@ var createFormController = (target, options = {}) => {
|
|
|
797
866
|
for (const field of getNamedFields(state2.element)) {
|
|
798
867
|
names.add(field.name);
|
|
799
868
|
}
|
|
869
|
+
for (const [canonical, entry] of state2.formattedFields) {
|
|
870
|
+
names.add(canonical);
|
|
871
|
+
if (entry.displayName && entry.displayName !== canonical) {
|
|
872
|
+
names.add(entry.displayName);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
800
875
|
return Array.from(names);
|
|
801
876
|
};
|
|
802
877
|
const getValues = () => {
|
|
@@ -882,14 +957,16 @@ var createFormController = (target, options = {}) => {
|
|
|
882
957
|
const name = target2.name;
|
|
883
958
|
if (!name) return;
|
|
884
959
|
clearFieldCache();
|
|
960
|
+
const canonical = resolveCanonicalForName(state2, name) ?? name;
|
|
885
961
|
if (normalizedOptions.clearManualErrorsOnChange) {
|
|
886
|
-
delete state2.manualErrors[
|
|
962
|
+
delete state2.manualErrors[canonical];
|
|
887
963
|
}
|
|
888
|
-
syncVisualState([
|
|
889
|
-
if (state2.isValidated && state2.validators[
|
|
890
|
-
validateNames([
|
|
964
|
+
syncVisualState([canonical]);
|
|
965
|
+
if (state2.isValidated && state2.validators[canonical]) {
|
|
966
|
+
validateNames([canonical]);
|
|
891
967
|
}
|
|
892
|
-
emitFieldWatchers(
|
|
968
|
+
emitFieldWatchers(canonical);
|
|
969
|
+
if (canonical !== name) emitFieldWatchers(name);
|
|
893
970
|
notifySubscribers();
|
|
894
971
|
scheduleAutoSubmit();
|
|
895
972
|
};
|