@samline/forms 2.7.2 → 2.9.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/CHANGELOG.md +316 -0
- package/README.md +119 -119
- package/dist/browser/global.d.ts +50 -16
- package/dist/browser/global.global.js +12 -6188
- package/dist/browser/global.global.js.map +1 -1
- package/dist/browser/index.cjs +340 -84
- package/dist/browser/index.cjs.map +1 -1
- package/dist/browser/index.d.cts +1 -1
- package/dist/browser/index.d.ts +1 -1
- package/dist/browser/index.js +1 -1
- package/dist/{chunk-MMHX34WB.js → chunk-6MA5TF6R.js} +341 -85
- package/dist/chunk-6MA5TF6R.js.map +1 -0
- package/dist/index.cjs +340 -84
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/{registry-GzopYPCR.d.ts → registry-CPe_lf8-.d.cts} +50 -4
- package/dist/{registry-GzopYPCR.d.cts → registry-CPe_lf8-.d.ts} +50 -4
- package/package.json +39 -8
- package/dist/chunk-MMHX34WB.js.map +0 -1
- package/docs/api/add-cleanup.md +0 -52
- package/docs/api/append.md +0 -96
- package/docs/api/auto-submit.md +0 -91
- package/docs/api/clear-errors.md +0 -69
- package/docs/api/destroy.md +0 -80
- package/docs/api/disable-auto-submit.md +0 -65
- package/docs/api/element.md +0 -78
- package/docs/api/form.md +0 -156
- package/docs/api/format.md +0 -270
- package/docs/api/get-data.md +0 -90
- package/docs/api/get-field.md +0 -87
- package/docs/api/get-state.md +0 -111
- package/docs/api/get-value.md +0 -114
- package/docs/api/index.md +0 -55
- package/docs/api/observe.md +0 -126
- package/docs/api/on-submit.md +0 -94
- package/docs/api/parse-form-data.md +0 -94
- package/docs/api/prefill.md +0 -89
- package/docs/api/regex.md +0 -155
- package/docs/api/reset.md +0 -61
- package/docs/api/revalidate.md +0 -52
- package/docs/api/set-errors.md +0 -97
- package/docs/api/set-value.md +0 -120
- package/docs/api/subscribe.md +0 -107
- package/docs/api/unwatch.md +0 -74
- package/docs/api/validate-field-value.md +0 -144
- package/docs/api/validate-values.md +0 -103
- package/docs/api/validate.md +0 -102
- package/docs/api/watch.md +0 -117
- package/docs/browser.md +0 -156
- package/docs/css-styling.md +0 -191
- package/docs/getting-started.md +0 -222
- package/docs/options.md +0 -326
- package/docs/recipes.md +0 -531
- package/docs/typescript.md +0 -615
- package/docs/vanilla.md +0 -12
- /package/{LICENSE → LICENSE.md} +0 -0
package/dist/browser/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { v as FormsApi, w as FormsAvailable, F as FormTarget, a as FormControllerOptions, b as FormController, N as NewFormInput } from '../registry-CPe_lf8-.cjs';
|
|
2
2
|
|
|
3
3
|
declare const Forms: FormsApi;
|
|
4
4
|
declare const form: (target: FormTarget, options?: FormControllerOptions) => FormController;
|
package/dist/browser/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { v as FormsApi, w as FormsAvailable, F as FormTarget, a as FormControllerOptions, b as FormController, N as NewFormInput } from '../registry-CPe_lf8-.js';
|
|
2
2
|
|
|
3
3
|
declare const Forms: FormsApi;
|
|
4
4
|
declare const form: (target: FormTarget, options?: FormControllerOptions) => FormController;
|
package/dist/browser/index.js
CHANGED
|
@@ -172,7 +172,7 @@ var validateValues = (values, schema) => {
|
|
|
172
172
|
|
|
173
173
|
// src/api/append.ts
|
|
174
174
|
var createAppend = (state2, helpers) => ({ tag, content, class: className, atStart = false }) => {
|
|
175
|
-
if (!state2.element) return null;
|
|
175
|
+
if (!state2.element || state2.isDestroyed) return null;
|
|
176
176
|
helpers.clearFieldCache();
|
|
177
177
|
if (className) {
|
|
178
178
|
const classSelector = className.trim().split(/\s+/)[0];
|
|
@@ -208,6 +208,7 @@ var createAddCleanup = (state2, _helpers) => (cleanup) => {
|
|
|
208
208
|
|
|
209
209
|
// src/api/auto-submit.ts
|
|
210
210
|
var createAutoSubmit = (state2, helpers) => (next = true) => {
|
|
211
|
+
if (state2.isDestroyed) return state2.api;
|
|
211
212
|
state2.autoSubmitEnabled = next !== false;
|
|
212
213
|
if (!state2.autoSubmitEnabled && state2.autoSubmitTimer) {
|
|
213
214
|
clearTimeout(state2.autoSubmitTimer);
|
|
@@ -220,6 +221,7 @@ var createAutoSubmit = (state2, helpers) => (next = true) => {
|
|
|
220
221
|
|
|
221
222
|
// src/api/clear-errors.ts
|
|
222
223
|
var createClearErrors = (state2, helpers) => (fields) => {
|
|
224
|
+
if (state2.isDestroyed) return state2.api;
|
|
223
225
|
if (!fields) {
|
|
224
226
|
state2.manualErrors = {};
|
|
225
227
|
helpers.syncVisualState();
|
|
@@ -346,6 +348,7 @@ var isFormatChar = (char) => {
|
|
|
346
348
|
return true;
|
|
347
349
|
};
|
|
348
350
|
var restoreCursor = (field, position) => {
|
|
351
|
+
if (field.ownerDocument.activeElement !== field) return;
|
|
349
352
|
const max = field.value.length;
|
|
350
353
|
const safe = clamp(position, 0, max);
|
|
351
354
|
const apply = () => {
|
|
@@ -648,6 +651,7 @@ var createDestroy = (state2, _helpers) => () => {
|
|
|
648
651
|
}
|
|
649
652
|
}
|
|
650
653
|
state2.watchedFields.clear();
|
|
654
|
+
state2.groupWatchers.clear();
|
|
651
655
|
state2.subscribers.clear();
|
|
652
656
|
state2.submitHandlers.clear();
|
|
653
657
|
state2.fieldCache.clear();
|
|
@@ -709,7 +713,7 @@ var mergeErrors = (left, right) => {
|
|
|
709
713
|
var createGetState = (state2, helpers) => () => {
|
|
710
714
|
const values = helpers.getValues();
|
|
711
715
|
const errors = cloneErrors(helpers.getMergedErrors());
|
|
712
|
-
const filledFields =
|
|
716
|
+
const filledFields = Object.keys(values).filter((name) => {
|
|
713
717
|
const value = values[name];
|
|
714
718
|
if (Array.isArray(value)) return value.length > 0;
|
|
715
719
|
if (typeof value === "string") return value.trim().length > 0;
|
|
@@ -844,9 +848,9 @@ var writeFieldValue = (fields, value) => {
|
|
|
844
848
|
continue;
|
|
845
849
|
}
|
|
846
850
|
if (field instanceof HTMLSelectElement && field.multiple) {
|
|
847
|
-
const
|
|
851
|
+
const selectedValues2 = normalizedArray ?? [normalizedValue];
|
|
848
852
|
for (const option of field.options) {
|
|
849
|
-
option.selected =
|
|
853
|
+
option.selected = selectedValues2.includes(option.value);
|
|
850
854
|
}
|
|
851
855
|
continue;
|
|
852
856
|
}
|
|
@@ -881,7 +885,7 @@ var submitForm = (form2) => {
|
|
|
881
885
|
var createGetValue = (state2, helpers) => (name) => readFieldValue(helpers.getFieldsByName(name));
|
|
882
886
|
|
|
883
887
|
// src/api/observe.ts
|
|
884
|
-
var
|
|
888
|
+
var observeField = (state2, field, callback) => {
|
|
885
889
|
const callbacks = state2.watchedFields.get(field) ?? /* @__PURE__ */ new Set();
|
|
886
890
|
callbacks.add(callback);
|
|
887
891
|
state2.watchedFields.set(field, callbacks);
|
|
@@ -896,47 +900,72 @@ var createObserve = (state2, helpers) => (field, callback) => {
|
|
|
896
900
|
}
|
|
897
901
|
};
|
|
898
902
|
};
|
|
903
|
+
var observeFields = (state2, fields, callback) => {
|
|
904
|
+
const names = Array.from(new Set(fields));
|
|
905
|
+
if (names.length === 0) return () => void 0;
|
|
906
|
+
const entry = { fields: names, callback };
|
|
907
|
+
state2.groupWatchers.add(entry);
|
|
908
|
+
if (state2.element) {
|
|
909
|
+
const values = Object.fromEntries(names.map((name) => [name, state2.api.getValue(name)]));
|
|
910
|
+
callback(values, null, state2.element, state2.api.getState());
|
|
911
|
+
}
|
|
912
|
+
return () => {
|
|
913
|
+
state2.groupWatchers.delete(entry);
|
|
914
|
+
};
|
|
915
|
+
};
|
|
916
|
+
var createObserve = (state2, _helpers) => {
|
|
917
|
+
function observe(field, callback) {
|
|
918
|
+
if (state2.isDestroyed) return () => void 0;
|
|
919
|
+
return Array.isArray(field) ? observeFields(state2, field, callback) : observeField(state2, field, callback);
|
|
920
|
+
}
|
|
921
|
+
return observe;
|
|
922
|
+
};
|
|
899
923
|
|
|
900
924
|
// src/api/on-submit.ts
|
|
901
925
|
var createOnSubmit = (state2, _helpers) => (callback, preventDefault = true) => {
|
|
926
|
+
if (state2.isDestroyed) return state2.api;
|
|
902
927
|
state2.submitHandlers.add({ callback, preventDefault });
|
|
903
928
|
return state2.api;
|
|
904
929
|
};
|
|
905
930
|
|
|
906
931
|
// src/api/prefill.ts
|
|
907
|
-
var createPrefill = (state2,
|
|
908
|
-
if (!state2.element) return state2.api;
|
|
932
|
+
var createPrefill = (state2, helpers) => (fieldName) => {
|
|
933
|
+
if (!state2.element || state2.isDestroyed) return state2.api;
|
|
909
934
|
const params = new URLSearchParams(window.location.search);
|
|
910
|
-
params.
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
935
|
+
const keys = Array.from(new Set(params.keys())).filter(
|
|
936
|
+
(key) => !fieldName || fieldName === key
|
|
937
|
+
);
|
|
938
|
+
if (keys.length === 0) return state2.api;
|
|
939
|
+
state2.autoSubmitSuspended = true;
|
|
940
|
+
try {
|
|
941
|
+
for (const key of keys) {
|
|
942
|
+
const values = params.getAll(key);
|
|
943
|
+
state2.api.setValue(key, values.length > 1 ? values : values[0]);
|
|
944
|
+
}
|
|
945
|
+
} finally {
|
|
946
|
+
state2.autoSubmitSuspended = false;
|
|
947
|
+
}
|
|
948
|
+
helpers.scheduleAutoSubmit();
|
|
914
949
|
return state2.api;
|
|
915
950
|
};
|
|
916
951
|
|
|
917
952
|
// src/api/reset.ts
|
|
918
953
|
var createReset = (state2, helpers) => () => {
|
|
919
|
-
if (!state2.element) return state2.api;
|
|
920
|
-
state2.
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
state2.attributes.error,
|
|
928
|
-
state2.attributes.filled,
|
|
929
|
-
"aria-invalid"
|
|
930
|
-
]);
|
|
931
|
-
helpers.syncVisualState();
|
|
932
|
-
helpers.notifySubscribers();
|
|
954
|
+
if (!state2.element || state2.isDestroyed) return state2.api;
|
|
955
|
+
state2.isResetting = true;
|
|
956
|
+
try {
|
|
957
|
+
state2.element.reset();
|
|
958
|
+
} finally {
|
|
959
|
+
state2.isResetting = false;
|
|
960
|
+
}
|
|
961
|
+
helpers.applyReset();
|
|
933
962
|
return state2.api;
|
|
934
963
|
};
|
|
935
964
|
|
|
936
965
|
// src/api/validate.ts
|
|
937
966
|
var createValidate = (state2, helpers) => (fields) => {
|
|
967
|
+
if (state2.isDestroyed) return { isValid: true, errors: {} };
|
|
938
968
|
state2.isValidated = true;
|
|
939
|
-
helpers.syncVisualState(fields);
|
|
940
969
|
return helpers.validateNames(fields);
|
|
941
970
|
};
|
|
942
971
|
|
|
@@ -945,6 +974,7 @@ var createRevalidate = createValidate;
|
|
|
945
974
|
|
|
946
975
|
// src/api/set-errors.ts
|
|
947
976
|
var createSetErrors = (state2, helpers) => (fields) => {
|
|
977
|
+
if (state2.isDestroyed) return state2.api;
|
|
948
978
|
if (Array.isArray(fields)) {
|
|
949
979
|
state2.manualErrors = {
|
|
950
980
|
...state2.manualErrors,
|
|
@@ -961,8 +991,78 @@ var createSetErrors = (state2, helpers) => (fields) => {
|
|
|
961
991
|
return state2.api;
|
|
962
992
|
};
|
|
963
993
|
|
|
994
|
+
// src/api/set-options.ts
|
|
995
|
+
var isGroup = (entry) => typeof entry === "object" && entry !== null && Array.isArray(entry.options);
|
|
996
|
+
var createOption = (entry) => {
|
|
997
|
+
const option = document.createElement("option");
|
|
998
|
+
if (typeof entry === "object") {
|
|
999
|
+
option.value = String(entry.value);
|
|
1000
|
+
option.textContent = entry.label ?? String(entry.value);
|
|
1001
|
+
option.disabled = Boolean(entry.disabled);
|
|
1002
|
+
if (entry.selected) option.defaultSelected = true;
|
|
1003
|
+
} else {
|
|
1004
|
+
option.value = String(entry);
|
|
1005
|
+
option.textContent = String(entry);
|
|
1006
|
+
}
|
|
1007
|
+
return option;
|
|
1008
|
+
};
|
|
1009
|
+
var buildNodes = (options) => {
|
|
1010
|
+
const nodes = [];
|
|
1011
|
+
let hasExplicitSelection = false;
|
|
1012
|
+
const track = (option) => {
|
|
1013
|
+
if (option.defaultSelected) hasExplicitSelection = true;
|
|
1014
|
+
return option;
|
|
1015
|
+
};
|
|
1016
|
+
for (const entry of options) {
|
|
1017
|
+
if (entry === null || entry === void 0) continue;
|
|
1018
|
+
if (isGroup(entry)) {
|
|
1019
|
+
const group = document.createElement("optgroup");
|
|
1020
|
+
group.label = entry.label;
|
|
1021
|
+
group.disabled = Boolean(entry.disabled);
|
|
1022
|
+
for (const child of entry.options) {
|
|
1023
|
+
if (child === null || child === void 0) continue;
|
|
1024
|
+
group.appendChild(track(createOption(child)));
|
|
1025
|
+
}
|
|
1026
|
+
nodes.push(group);
|
|
1027
|
+
continue;
|
|
1028
|
+
}
|
|
1029
|
+
nodes.push(track(createOption(entry)));
|
|
1030
|
+
}
|
|
1031
|
+
return { nodes, hasExplicitSelection };
|
|
1032
|
+
};
|
|
1033
|
+
var selectedValues = (select) => Array.from(select.selectedOptions, (option) => option.value);
|
|
1034
|
+
var restoreSelection = (select, previous) => {
|
|
1035
|
+
if (previous.length === 0) return;
|
|
1036
|
+
const options = Array.from(select.options);
|
|
1037
|
+
if (select.multiple) {
|
|
1038
|
+
for (const option of options) option.selected = previous.includes(option.value);
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
const match = options.find((option) => option.value === previous[0]);
|
|
1042
|
+
if (match) match.selected = true;
|
|
1043
|
+
};
|
|
1044
|
+
var sameValues = (left, right) => left.length === right.length && left.every((value, index) => value === right[index]);
|
|
1045
|
+
var createSetOptions = (state2, helpers) => (name, options, config = {}) => {
|
|
1046
|
+
if (!state2.element || state2.isDestroyed) return state2.api;
|
|
1047
|
+
const selects = helpers.getFieldsByName(name).filter((field) => field instanceof HTMLSelectElement);
|
|
1048
|
+
if (selects.length === 0) return state2.api;
|
|
1049
|
+
const keepValue = config.keepValue ?? true;
|
|
1050
|
+
const changed = [];
|
|
1051
|
+
for (const select of selects) {
|
|
1052
|
+
const previous = selectedValues(select);
|
|
1053
|
+
const { nodes, hasExplicitSelection } = buildNodes(options);
|
|
1054
|
+
select.replaceChildren(...nodes);
|
|
1055
|
+
if (keepValue && !hasExplicitSelection) restoreSelection(select, previous);
|
|
1056
|
+
if (!sameValues(previous, selectedValues(select))) changed.push(select);
|
|
1057
|
+
}
|
|
1058
|
+
helpers.commitOwnMutations(selects);
|
|
1059
|
+
changed[0]?.dispatchEvent(new Event("input", { bubbles: true }));
|
|
1060
|
+
return state2.api;
|
|
1061
|
+
};
|
|
1062
|
+
|
|
964
1063
|
// src/api/set-value.ts
|
|
965
1064
|
var createSetValue = (state2, helpers) => (name, value) => {
|
|
1065
|
+
if (state2.isDestroyed) return state2.api;
|
|
966
1066
|
const fields = helpers.getFieldsByName(name);
|
|
967
1067
|
if (fields.length === 0) return state2.api;
|
|
968
1068
|
writeFieldValue(fields, value);
|
|
@@ -974,6 +1074,7 @@ var createSetValue = (state2, helpers) => (name, value) => {
|
|
|
974
1074
|
|
|
975
1075
|
// src/api/subscribe.ts
|
|
976
1076
|
var createSubscribe = (state2, _helpers) => (listener) => {
|
|
1077
|
+
if (state2.isDestroyed) return () => void 0;
|
|
977
1078
|
state2.subscribers.add(listener);
|
|
978
1079
|
listener(state2.api.getState());
|
|
979
1080
|
return () => {
|
|
@@ -982,28 +1083,46 @@ var createSubscribe = (state2, _helpers) => (listener) => {
|
|
|
982
1083
|
};
|
|
983
1084
|
|
|
984
1085
|
// src/api/unwatch.ts
|
|
985
|
-
var
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1086
|
+
var sameFields = (left, right) => left.length === right.size && left.every((name) => right.has(name));
|
|
1087
|
+
var createUnwatch = (state2, _helpers) => {
|
|
1088
|
+
function unwatch(field, callback) {
|
|
1089
|
+
if (field === void 0 || field === "") {
|
|
1090
|
+
state2.watchedFields.clear();
|
|
1091
|
+
state2.groupWatchers.clear();
|
|
1092
|
+
return state2.api;
|
|
1093
|
+
}
|
|
1094
|
+
if (Array.isArray(field)) {
|
|
1095
|
+
const names = new Set(field);
|
|
1096
|
+
for (const entry of Array.from(state2.groupWatchers)) {
|
|
1097
|
+
if (!sameFields(entry.fields, names)) continue;
|
|
1098
|
+
if (callback && entry.callback !== callback) continue;
|
|
1099
|
+
state2.groupWatchers.delete(entry);
|
|
1100
|
+
}
|
|
1101
|
+
return state2.api;
|
|
1102
|
+
}
|
|
1103
|
+
if (!callback) {
|
|
1104
|
+
state2.watchedFields.delete(field);
|
|
1105
|
+
return state2.api;
|
|
1106
|
+
}
|
|
1107
|
+
const callbacks = state2.watchedFields.get(field);
|
|
1108
|
+
callbacks?.delete(callback);
|
|
1109
|
+
if (callbacks && callbacks.size === 0) {
|
|
1110
|
+
state2.watchedFields.delete(field);
|
|
1111
|
+
}
|
|
992
1112
|
return state2.api;
|
|
993
1113
|
}
|
|
994
|
-
|
|
995
|
-
callbacks?.delete(callback);
|
|
996
|
-
if (callbacks && callbacks.size === 0) {
|
|
997
|
-
state2.watchedFields.delete(field);
|
|
998
|
-
}
|
|
999
|
-
return state2.api;
|
|
1114
|
+
return unwatch;
|
|
1000
1115
|
};
|
|
1001
1116
|
|
|
1002
1117
|
// src/api/watch.ts
|
|
1003
|
-
var createWatch = (state2, helpers) =>
|
|
1118
|
+
var createWatch = (state2, helpers) => {
|
|
1004
1119
|
const observe = createObserve(state2, helpers);
|
|
1005
|
-
|
|
1006
|
-
|
|
1120
|
+
function watch(field, callback) {
|
|
1121
|
+
if (Array.isArray(field)) observe(field, callback);
|
|
1122
|
+
else observe(field, callback);
|
|
1123
|
+
return state2.api;
|
|
1124
|
+
}
|
|
1125
|
+
return watch;
|
|
1007
1126
|
};
|
|
1008
1127
|
|
|
1009
1128
|
// src/core/controller.ts
|
|
@@ -1030,6 +1149,7 @@ var createFormController = (target, options = {}) => {
|
|
|
1030
1149
|
attributes,
|
|
1031
1150
|
validators: normalizedOptions.validators ?? {},
|
|
1032
1151
|
watchedFields: /* @__PURE__ */ new Map(),
|
|
1152
|
+
groupWatchers: /* @__PURE__ */ new Set(),
|
|
1033
1153
|
subscribers: /* @__PURE__ */ new Set(),
|
|
1034
1154
|
submitHandlers: /* @__PURE__ */ new Set(),
|
|
1035
1155
|
fieldCache: /* @__PURE__ */ new Map(),
|
|
@@ -1043,6 +1163,9 @@ var createFormController = (target, options = {}) => {
|
|
|
1043
1163
|
submitCount: 0,
|
|
1044
1164
|
pendingSubmitCount: 0,
|
|
1045
1165
|
autoSubmitTimer: null,
|
|
1166
|
+
isAutoSubmitting: false,
|
|
1167
|
+
autoSubmitSuspended: false,
|
|
1168
|
+
isResetting: false,
|
|
1046
1169
|
isDestroyed: false,
|
|
1047
1170
|
listeners: [],
|
|
1048
1171
|
mutationObserver: null,
|
|
@@ -1087,12 +1210,20 @@ var createFormController = (target, options = {}) => {
|
|
|
1087
1210
|
state2.fieldCache.set(name, fields);
|
|
1088
1211
|
return fields;
|
|
1089
1212
|
};
|
|
1090
|
-
const
|
|
1091
|
-
|
|
1092
|
-
|
|
1213
|
+
const indexFieldsByName = () => {
|
|
1214
|
+
const index = /* @__PURE__ */ new Map();
|
|
1215
|
+
if (!state2.element) return index;
|
|
1093
1216
|
for (const field of getNamedFields(state2.element)) {
|
|
1094
|
-
|
|
1217
|
+
const bucket = index.get(field.name);
|
|
1218
|
+
if (bucket) bucket.push(field);
|
|
1219
|
+
else index.set(field.name, [field]);
|
|
1095
1220
|
}
|
|
1221
|
+
return index;
|
|
1222
|
+
};
|
|
1223
|
+
const getTrackedFieldNames = (index = indexFieldsByName()) => {
|
|
1224
|
+
if (!state2.element) return Object.keys(state2.validators);
|
|
1225
|
+
const names = new Set(Object.keys(state2.validators));
|
|
1226
|
+
for (const name of index.keys()) names.add(name);
|
|
1096
1227
|
for (const [canonical, entry] of state2.formattedFields) {
|
|
1097
1228
|
names.add(canonical);
|
|
1098
1229
|
if (entry.displayName && entry.displayName !== canonical) {
|
|
@@ -1103,8 +1234,9 @@ var createFormController = (target, options = {}) => {
|
|
|
1103
1234
|
};
|
|
1104
1235
|
const getValues = () => {
|
|
1105
1236
|
const values = {};
|
|
1106
|
-
|
|
1107
|
-
|
|
1237
|
+
const index = indexFieldsByName();
|
|
1238
|
+
for (const name of getTrackedFieldNames(index)) {
|
|
1239
|
+
values[name] = readFieldValue(index.get(name) ?? []);
|
|
1108
1240
|
}
|
|
1109
1241
|
return values;
|
|
1110
1242
|
};
|
|
@@ -1188,7 +1320,7 @@ var createFormController = (target, options = {}) => {
|
|
|
1188
1320
|
}
|
|
1189
1321
|
return affected;
|
|
1190
1322
|
};
|
|
1191
|
-
const
|
|
1323
|
+
const emitSingleWatchers = (name) => {
|
|
1192
1324
|
if (!state2.element || !state2.api) return;
|
|
1193
1325
|
const callbacks = state2.watchedFields.get(name);
|
|
1194
1326
|
if (!callbacks || callbacks.size === 0) return;
|
|
@@ -1197,29 +1329,85 @@ var createFormController = (target, options = {}) => {
|
|
|
1197
1329
|
const snapshot = state2.api.getState();
|
|
1198
1330
|
callbacks.forEach((callback) => callback(value, fieldElement, state2.element, snapshot));
|
|
1199
1331
|
};
|
|
1332
|
+
const emitGroupWatchers = (names) => {
|
|
1333
|
+
if (!state2.element || !state2.api || state2.groupWatchers.size === 0) return;
|
|
1334
|
+
let snapshot;
|
|
1335
|
+
for (const entry of Array.from(state2.groupWatchers)) {
|
|
1336
|
+
const changed = names ? names.find((name) => entry.fields.includes(name)) : null;
|
|
1337
|
+
if (changed === void 0) continue;
|
|
1338
|
+
snapshot ?? (snapshot = state2.api.getState());
|
|
1339
|
+
const values = Object.fromEntries(
|
|
1340
|
+
entry.fields.map((field) => [field, state2.api.getValue(field)])
|
|
1341
|
+
);
|
|
1342
|
+
entry.callback(values, changed, state2.element, snapshot);
|
|
1343
|
+
}
|
|
1344
|
+
};
|
|
1345
|
+
const emitFieldWatchers = (name) => {
|
|
1346
|
+
emitSingleWatchers(name);
|
|
1347
|
+
emitGroupWatchers([name]);
|
|
1348
|
+
};
|
|
1349
|
+
const runAutoSubmit = () => {
|
|
1350
|
+
state2.autoSubmitTimer = null;
|
|
1351
|
+
if (!state2.element || state2.isDestroyed || !state2.autoSubmitEnabled) return;
|
|
1352
|
+
state2.isAutoSubmitting = true;
|
|
1353
|
+
try {
|
|
1354
|
+
submitForm(state2.element);
|
|
1355
|
+
} finally {
|
|
1356
|
+
state2.isAutoSubmitting = false;
|
|
1357
|
+
}
|
|
1358
|
+
};
|
|
1200
1359
|
const scheduleAutoSubmit = () => {
|
|
1201
|
-
if (!state2.element || !state2.autoSubmitEnabled) return;
|
|
1360
|
+
if (!state2.element || !state2.autoSubmitEnabled || state2.autoSubmitSuspended) return;
|
|
1202
1361
|
if (state2.autoSubmitTimer) clearTimeout(state2.autoSubmitTimer);
|
|
1362
|
+
state2.autoSubmitTimer = null;
|
|
1203
1363
|
if (state2.autoSubmitDebounce > 0) {
|
|
1204
|
-
state2.autoSubmitTimer = setTimeout(
|
|
1205
|
-
() => submitForm(state2.element),
|
|
1206
|
-
state2.autoSubmitDebounce
|
|
1207
|
-
);
|
|
1364
|
+
state2.autoSubmitTimer = setTimeout(runAutoSubmit, state2.autoSubmitDebounce);
|
|
1208
1365
|
return;
|
|
1209
1366
|
}
|
|
1210
|
-
|
|
1367
|
+
runAutoSubmit();
|
|
1368
|
+
};
|
|
1369
|
+
const selectSnapshots = /* @__PURE__ */ new WeakMap();
|
|
1370
|
+
const readSelectSnapshot = (select) => Array.from(select.selectedOptions, (option) => option.value).join("\0");
|
|
1371
|
+
const snapshotSelect = (select) => {
|
|
1372
|
+
selectSnapshots.set(select, readSelectSnapshot(select));
|
|
1373
|
+
};
|
|
1374
|
+
const snapshotAllSelects = () => {
|
|
1375
|
+
if (!state2.element) return;
|
|
1376
|
+
for (const field of getNamedFields(state2.element)) {
|
|
1377
|
+
if (field instanceof HTMLSelectElement) snapshotSelect(field);
|
|
1378
|
+
}
|
|
1379
|
+
};
|
|
1380
|
+
const emitAllFieldWatchers = () => {
|
|
1381
|
+
for (const name of Array.from(state2.watchedFields.keys())) emitSingleWatchers(name);
|
|
1382
|
+
emitGroupWatchers(null);
|
|
1383
|
+
};
|
|
1384
|
+
const applyReset = () => {
|
|
1385
|
+
if (!state2.element || state2.isDestroyed) return;
|
|
1386
|
+
clearFieldCache();
|
|
1387
|
+
resetFormattedFields(state2);
|
|
1388
|
+
state2.manualErrors = {};
|
|
1389
|
+
state2.validationErrors = {};
|
|
1390
|
+
state2.validationGroupErrors = {};
|
|
1391
|
+
state2.validationElementErrors.clear();
|
|
1392
|
+
clearAttributes(state2.element, [attributes.error, attributes.filled, "aria-invalid"]);
|
|
1393
|
+
syncVisualState();
|
|
1394
|
+
snapshotAllSelects();
|
|
1395
|
+
emitAllFieldWatchers();
|
|
1396
|
+
notifySubscribers();
|
|
1211
1397
|
};
|
|
1212
1398
|
const helpers = {
|
|
1213
1399
|
notifySubscribers,
|
|
1214
1400
|
clearFieldCache,
|
|
1215
1401
|
getFieldsByName,
|
|
1216
|
-
getTrackedFieldNames,
|
|
1402
|
+
getTrackedFieldNames: () => getTrackedFieldNames(),
|
|
1217
1403
|
getValues,
|
|
1218
1404
|
getMergedErrors,
|
|
1219
1405
|
syncVisualState,
|
|
1220
1406
|
validateNames,
|
|
1221
1407
|
emitFieldWatchers,
|
|
1222
|
-
scheduleAutoSubmit
|
|
1408
|
+
scheduleAutoSubmit,
|
|
1409
|
+
applyReset,
|
|
1410
|
+
commitOwnMutations: (selects) => commitOwnMutations(selects)
|
|
1223
1411
|
};
|
|
1224
1412
|
const handleDelegatedEvent = (event) => {
|
|
1225
1413
|
const target2 = event.target;
|
|
@@ -1227,6 +1415,7 @@ var createFormController = (target, options = {}) => {
|
|
|
1227
1415
|
const name = target2.name;
|
|
1228
1416
|
if (!name) return;
|
|
1229
1417
|
clearFieldCache();
|
|
1418
|
+
if (target2 instanceof HTMLSelectElement) snapshotSelect(target2);
|
|
1230
1419
|
const canonical = resolveCanonicalForName(state2, name) ?? name;
|
|
1231
1420
|
const displayName = resolveDisplayNameForName(state2, name);
|
|
1232
1421
|
if (normalizedOptions.clearManualErrorsOnChange) {
|
|
@@ -1237,8 +1426,9 @@ var createFormController = (target, options = {}) => {
|
|
|
1237
1426
|
if (state2.isValidated && affectedValidationNames.some((field) => state2.validators[field] !== void 0)) {
|
|
1238
1427
|
validateNames(affectedValidationNames);
|
|
1239
1428
|
}
|
|
1240
|
-
|
|
1241
|
-
if (canonical !== name)
|
|
1429
|
+
emitSingleWatchers(canonical);
|
|
1430
|
+
if (canonical !== name) emitSingleWatchers(name);
|
|
1431
|
+
emitGroupWatchers(canonical !== name ? [canonical, name] : [canonical]);
|
|
1242
1432
|
notifySubscribers();
|
|
1243
1433
|
scheduleAutoSubmit();
|
|
1244
1434
|
};
|
|
@@ -1248,6 +1438,23 @@ var createFormController = (target, options = {}) => {
|
|
|
1248
1438
|
if (target2.form !== state2.element || state2.element?.contains(target2)) return;
|
|
1249
1439
|
handleDelegatedEvent(event);
|
|
1250
1440
|
};
|
|
1441
|
+
const isFocusable = (candidate) => {
|
|
1442
|
+
if (!candidate.isConnected || candidate.disabled || candidate.hidden) return false;
|
|
1443
|
+
return !(candidate instanceof HTMLInputElement && candidate.type === "hidden");
|
|
1444
|
+
};
|
|
1445
|
+
const focusFirstInvalidField = (errors) => {
|
|
1446
|
+
const candidates = [];
|
|
1447
|
+
for (const name of Object.keys(errors)) {
|
|
1448
|
+
const fields = getFieldsByName(resolveDisplayNameForName(state2, name)).filter(isFocusable);
|
|
1449
|
+
const elementErrors = state2.validationElementErrors.get(name);
|
|
1450
|
+
const field = fields.find((candidate) => elementErrors?.has(candidate)) ?? fields[0];
|
|
1451
|
+
if (field) candidates.push(field);
|
|
1452
|
+
}
|
|
1453
|
+
candidates.sort(
|
|
1454
|
+
(left, right) => left.compareDocumentPosition(right) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1
|
|
1455
|
+
);
|
|
1456
|
+
candidates[0]?.focus();
|
|
1457
|
+
};
|
|
1251
1458
|
const handleSubmitEvent = (event) => {
|
|
1252
1459
|
if (!state2.element || state2.isDestroyed || !state2.api) return;
|
|
1253
1460
|
if (normalizedOptions.clearErrorsOnSubmit) state2.manualErrors = {};
|
|
@@ -1258,23 +1465,7 @@ var createFormController = (target, options = {}) => {
|
|
|
1258
1465
|
state2.submitCount += 1;
|
|
1259
1466
|
notifySubscribers();
|
|
1260
1467
|
if (!validation.isValid) {
|
|
1261
|
-
|
|
1262
|
-
const displayName = resolveDisplayNameForName(state2, name);
|
|
1263
|
-
const fields = getFieldsByName(displayName);
|
|
1264
|
-
const elementErrors = state2.validationElementErrors.get(name);
|
|
1265
|
-
const field = fields.find((candidate2) => {
|
|
1266
|
-
if (!candidate2.isConnected || candidate2.disabled || candidate2.hidden) return false;
|
|
1267
|
-
if (candidate2 instanceof HTMLInputElement && candidate2.type === "hidden") return false;
|
|
1268
|
-
return elementErrors?.has(candidate2);
|
|
1269
|
-
}) ?? fields.find((candidate2) => {
|
|
1270
|
-
if (!candidate2.isConnected || candidate2.disabled || candidate2.hidden) return false;
|
|
1271
|
-
return !(candidate2 instanceof HTMLInputElement && candidate2.type === "hidden");
|
|
1272
|
-
});
|
|
1273
|
-
if (field) {
|
|
1274
|
-
field.focus();
|
|
1275
|
-
break;
|
|
1276
|
-
}
|
|
1277
|
-
}
|
|
1468
|
+
if (!state2.isAutoSubmitting) focusFirstInvalidField(validation.errors);
|
|
1278
1469
|
return;
|
|
1279
1470
|
}
|
|
1280
1471
|
const candidate = event;
|
|
@@ -1287,7 +1478,13 @@ var createFormController = (target, options = {}) => {
|
|
|
1287
1478
|
try {
|
|
1288
1479
|
for (const handler of handlers) {
|
|
1289
1480
|
const result = handler.callback(state2.element, data, formData, snapshot);
|
|
1290
|
-
if (result && typeof result.then === "function")
|
|
1481
|
+
if (result && typeof result.then === "function") {
|
|
1482
|
+
pending.push(
|
|
1483
|
+
Promise.resolve(result).catch((error) => {
|
|
1484
|
+
console.error("[forms] submit handler failed", error);
|
|
1485
|
+
})
|
|
1486
|
+
);
|
|
1487
|
+
}
|
|
1291
1488
|
}
|
|
1292
1489
|
} catch (error) {
|
|
1293
1490
|
synchronousError = error;
|
|
@@ -1303,15 +1500,71 @@ var createFormController = (target, options = {}) => {
|
|
|
1303
1500
|
}
|
|
1304
1501
|
if (hasSynchronousError) throw synchronousError;
|
|
1305
1502
|
};
|
|
1503
|
+
const handleResetEvent = (event) => {
|
|
1504
|
+
if (state2.isResetting) return;
|
|
1505
|
+
const timer = setTimeout(() => {
|
|
1506
|
+
state2.cleanups.delete(cancel);
|
|
1507
|
+
if (!event.defaultPrevented) applyReset();
|
|
1508
|
+
}, 0);
|
|
1509
|
+
const cancel = () => clearTimeout(timer);
|
|
1510
|
+
state2.cleanups.add(cancel);
|
|
1511
|
+
};
|
|
1512
|
+
const touchesFields = (node) => node instanceof Element && (isFieldElement(node) || node.querySelector("input, select, textarea") !== null);
|
|
1513
|
+
const isStructural = (record) => record.type === "attributes" || Array.from(record.addedNodes).some(touchesFields) || Array.from(record.removedNodes).some(touchesFields);
|
|
1514
|
+
const isOptionNode = (node) => node instanceof HTMLOptionElement || node instanceof HTMLOptGroupElement;
|
|
1515
|
+
const optionListOwner = (record) => {
|
|
1516
|
+
if (record.type !== "childList") return null;
|
|
1517
|
+
const touchesOptions = Array.from(record.addedNodes).some(isOptionNode) || Array.from(record.removedNodes).some(isOptionNode);
|
|
1518
|
+
if (!touchesOptions) return null;
|
|
1519
|
+
const target2 = record.target;
|
|
1520
|
+
if (target2 instanceof HTMLSelectElement) return target2;
|
|
1521
|
+
if (target2 instanceof HTMLOptGroupElement && target2.parentElement instanceof HTMLSelectElement) {
|
|
1522
|
+
return target2.parentElement;
|
|
1523
|
+
}
|
|
1524
|
+
return null;
|
|
1525
|
+
};
|
|
1526
|
+
const handleMutations = (records) => {
|
|
1527
|
+
if (state2.isDestroyed) return;
|
|
1528
|
+
const structural = records.some(isStructural);
|
|
1529
|
+
const changedNames = /* @__PURE__ */ new Set();
|
|
1530
|
+
for (const record of records) {
|
|
1531
|
+
const select = optionListOwner(record);
|
|
1532
|
+
if (!select || !select.name) continue;
|
|
1533
|
+
const next = readSelectSnapshot(select);
|
|
1534
|
+
if (selectSnapshots.get(select) !== next) changedNames.add(select.name);
|
|
1535
|
+
selectSnapshots.set(select, next);
|
|
1536
|
+
}
|
|
1537
|
+
if (!structural && changedNames.size === 0) return;
|
|
1538
|
+
clearFieldCache();
|
|
1539
|
+
if (structural) {
|
|
1540
|
+
snapshotAllSelects();
|
|
1541
|
+
if (state2.isValidated) validateNames();
|
|
1542
|
+
else syncVisualState();
|
|
1543
|
+
} else {
|
|
1544
|
+
const affected = /* @__PURE__ */ new Set();
|
|
1545
|
+
for (const name of changedNames) {
|
|
1546
|
+
for (const field of getAffectedValidationNames(name)) affected.add(field);
|
|
1547
|
+
}
|
|
1548
|
+
const names = Array.from(affected);
|
|
1549
|
+
syncVisualState(Array.from(changedNames));
|
|
1550
|
+
if (state2.isValidated && names.some((field) => state2.validators[field] !== void 0)) {
|
|
1551
|
+
validateNames(names);
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
for (const name of changedNames) emitFieldWatchers(name);
|
|
1555
|
+
notifySubscribers();
|
|
1556
|
+
};
|
|
1557
|
+
const commitOwnMutations = (selects) => {
|
|
1558
|
+
selects.forEach(snapshotSelect);
|
|
1559
|
+
const pending = state2.mutationObserver?.takeRecords() ?? [];
|
|
1560
|
+
const foreign = pending.filter(
|
|
1561
|
+
(record) => !selects.some((select) => select.contains(record.target))
|
|
1562
|
+
);
|
|
1563
|
+
if (foreign.length > 0) handleMutations(foreign);
|
|
1564
|
+
};
|
|
1306
1565
|
const startMutationObserver = () => {
|
|
1307
1566
|
if (!state2.element || typeof MutationObserver === "undefined") return;
|
|
1308
|
-
state2.mutationObserver = new MutationObserver(
|
|
1309
|
-
clearFieldCache();
|
|
1310
|
-
if (state2.isValidated) {
|
|
1311
|
-
validateNames();
|
|
1312
|
-
} else syncVisualState();
|
|
1313
|
-
notifySubscribers();
|
|
1314
|
-
});
|
|
1567
|
+
state2.mutationObserver = new MutationObserver(handleMutations);
|
|
1315
1568
|
state2.mutationObserver.observe(state2.element, {
|
|
1316
1569
|
childList: true,
|
|
1317
1570
|
subtree: true,
|
|
@@ -1340,6 +1593,7 @@ var createFormController = (target, options = {}) => {
|
|
|
1340
1593
|
setErrors: createSetErrors(state2, helpers),
|
|
1341
1594
|
clearErrors: createClearErrors(state2, helpers),
|
|
1342
1595
|
setValue: createSetValue(state2, helpers),
|
|
1596
|
+
setOptions: createSetOptions(state2, helpers),
|
|
1343
1597
|
getValue: createGetValue(state2, helpers),
|
|
1344
1598
|
getField: createGetField(state2, helpers),
|
|
1345
1599
|
validate: createValidate(state2, helpers),
|
|
@@ -1358,6 +1612,8 @@ var createFormController = (target, options = {}) => {
|
|
|
1358
1612
|
addListener2(state2.element, "input", handleDelegatedEvent);
|
|
1359
1613
|
addListener2(document, "input", handleExternalDelegatedEvent);
|
|
1360
1614
|
addListener2(state2.element, "submit", handleSubmitEvent);
|
|
1615
|
+
addListener2(state2.element, "reset", handleResetEvent);
|
|
1616
|
+
snapshotAllSelects();
|
|
1361
1617
|
startMutationObserver();
|
|
1362
1618
|
}
|
|
1363
1619
|
if (normalizedOptions.autoSubmit) {
|
|
@@ -1417,4 +1673,4 @@ export {
|
|
|
1417
1673
|
form,
|
|
1418
1674
|
browser
|
|
1419
1675
|
};
|
|
1420
|
-
//# sourceMappingURL=chunk-
|
|
1676
|
+
//# sourceMappingURL=chunk-6MA5TF6R.js.map
|