@marianmeres/stuic 3.76.1 → 3.76.3
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.
|
@@ -47,6 +47,12 @@ export function onSubmitValidityCheck(node) {
|
|
|
47
47
|
e.preventDefault();
|
|
48
48
|
// this will disable all other onsubmit listeners...
|
|
49
49
|
e.stopImmediatePropagation();
|
|
50
|
+
// // [debug] kept commented for the next time Issue A regresses
|
|
51
|
+
// // eslint-disable-next-line no-console
|
|
52
|
+
// console.log(
|
|
53
|
+
// "[onSubmitValidityCheck] submit intercepted. element count:",
|
|
54
|
+
// node.elements?.length
|
|
55
|
+
// );
|
|
50
56
|
const invalid = [];
|
|
51
57
|
for (let i = 0; i < node.elements?.length; i++) {
|
|
52
58
|
const el = node.elements[i];
|
|
@@ -55,11 +61,38 @@ export function onSubmitValidityCheck(node) {
|
|
|
55
61
|
// input (last radio input), which is not desired
|
|
56
62
|
if (el.type === "radio")
|
|
57
63
|
continue;
|
|
64
|
+
// // [debug] kept commented for the next time Issue A regresses
|
|
65
|
+
// // eslint-disable-next-line no-console
|
|
66
|
+
// console.log(`[onSubmitValidityCheck] el#${i} ${el.name || el.type} BEFORE`, {
|
|
67
|
+
// value: el.value,
|
|
68
|
+
// valid: el.validity.valid,
|
|
69
|
+
// customError: el.validity.customError,
|
|
70
|
+
// valueMissing: el.validity.valueMissing,
|
|
71
|
+
// validationMessage: el.validationMessage,
|
|
72
|
+
// });
|
|
73
|
+
// Clear any stale `customError` flag from a prior submit attempt before
|
|
74
|
+
// re-dispatching the validate listeners. Without this, if the field's
|
|
75
|
+
// per-field validate $effect was torn down/re-mounted in a way that
|
|
76
|
+
// skipped re-running its `customValidator` (e.g., the parent re-rendered
|
|
77
|
+
// for an unrelated reason between submits), the previous submit's
|
|
78
|
+
// customValidity message lingers on the DOM element. Then `el.validity.valid`
|
|
79
|
+
// would return false here even though the customValidator now reports no
|
|
80
|
+
// error, silently routing the form to `submit_invalid` and never calling
|
|
81
|
+
// the consumer's onSubmit. The dispatched change event below re-runs the
|
|
82
|
+
// per-field validator which re-applies a real customValidity if needed.
|
|
83
|
+
if (typeof el.setCustomValidity === "function")
|
|
84
|
+
el.setCustomValidity("");
|
|
58
85
|
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
59
86
|
el.dispatchEvent(new Event("change", { bubbles: true }));
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
//
|
|
87
|
+
// // [debug] kept commented for the next time Issue A regresses
|
|
88
|
+
// // eslint-disable-next-line no-console
|
|
89
|
+
// console.log(`[onSubmitValidityCheck] el#${i} ${el.name || el.type} AFTER `, {
|
|
90
|
+
// value: el.value,
|
|
91
|
+
// valid: el.validity.valid,
|
|
92
|
+
// customError: el.validity.customError,
|
|
93
|
+
// valueMissing: el.validity.valueMissing,
|
|
94
|
+
// validationMessage: el.validationMessage,
|
|
95
|
+
// });
|
|
63
96
|
if (!el.validity.valid) {
|
|
64
97
|
invalid.push(el);
|
|
65
98
|
}
|
|
@@ -67,12 +100,28 @@ export function onSubmitValidityCheck(node) {
|
|
|
67
100
|
}
|
|
68
101
|
// none invalid
|
|
69
102
|
if (!invalid.length) {
|
|
103
|
+
// // [debug] kept commented for the next time Issue A regresses
|
|
104
|
+
// // eslint-disable-next-line no-console
|
|
105
|
+
// console.log("[onSubmitValidityCheck] → dispatching submit_valid");
|
|
70
106
|
node.dispatchEvent(new CustomEvent(SUBMIT_VALID_EVENT_NAME, {
|
|
71
107
|
bubbles: true,
|
|
72
108
|
detail: { formData: new FormData(node) },
|
|
73
109
|
}));
|
|
74
110
|
}
|
|
75
111
|
else {
|
|
112
|
+
// // [debug] kept commented for the next time Issue A regresses
|
|
113
|
+
// // eslint-disable-next-line no-console
|
|
114
|
+
// console.warn(
|
|
115
|
+
// "[onSubmitValidityCheck] → dispatching submit_invalid; invalid =",
|
|
116
|
+
// invalid.map((el) => ({
|
|
117
|
+
// name: el.name,
|
|
118
|
+
// type: el.type,
|
|
119
|
+
// value: el.value,
|
|
120
|
+
// customError: el.validity.customError,
|
|
121
|
+
// valueMissing: el.validity.valueMissing,
|
|
122
|
+
// validationMessage: el.validationMessage,
|
|
123
|
+
// }))
|
|
124
|
+
// );
|
|
76
125
|
node.dispatchEvent(new CustomEvent(SUBMIT_INVALID_EVENT_NAME, {
|
|
77
126
|
bubbles: true,
|
|
78
127
|
detail: { invalid },
|
|
@@ -154,6 +154,14 @@ export function validate(el, fn) {
|
|
|
154
154
|
}
|
|
155
155
|
return fallback;
|
|
156
156
|
};
|
|
157
|
+
// // [debug] kept commented for the next time Issue A regresses
|
|
158
|
+
// // eslint-disable-next-line no-console
|
|
159
|
+
// console.log(
|
|
160
|
+
// `[validate $effect] (re)mount listener on <${el.tagName.toLowerCase()} name="${
|
|
161
|
+
// (el as HTMLInputElement).name || ""
|
|
162
|
+
// }">`,
|
|
163
|
+
// { enabled, on, hasCustomValidator: typeof customValidator === "function" }
|
|
164
|
+
// );
|
|
157
165
|
const _doValidate = () => {
|
|
158
166
|
if (!enabled)
|
|
159
167
|
return;
|
|
@@ -165,8 +173,26 @@ export function validate(el, fn) {
|
|
|
165
173
|
let customValidatorMessage = "";
|
|
166
174
|
if (typeof customValidator === "function") {
|
|
167
175
|
customValidatorMessage = customValidator(el.value, context, el) || "";
|
|
168
|
-
el.setCustomValidity(customValidatorMessage);
|
|
169
176
|
}
|
|
177
|
+
// Always write — including the no-validator case (which writes "" and
|
|
178
|
+
// thus clears any stale `customError` flag the element may be carrying
|
|
179
|
+
// from a prior listener generation). Without this, a `customValidator`
|
|
180
|
+
// being removed across re-renders would leave the previous message
|
|
181
|
+
// stuck on the element forever.
|
|
182
|
+
el.setCustomValidity(customValidatorMessage);
|
|
183
|
+
// // [debug] kept commented for the next time Issue A regresses
|
|
184
|
+
// // eslint-disable-next-line no-console
|
|
185
|
+
// console.log(
|
|
186
|
+
// `[validate _doValidate] ran on <${el.tagName.toLowerCase()} name="${
|
|
187
|
+
// (el as HTMLInputElement).name || ""
|
|
188
|
+
// }">`,
|
|
189
|
+
// {
|
|
190
|
+
// value: el.value,
|
|
191
|
+
// customValidatorMessage,
|
|
192
|
+
// customError: el.validity.customError,
|
|
193
|
+
// valid: el.validity.valid,
|
|
194
|
+
// }
|
|
195
|
+
// );
|
|
170
196
|
// this triggers the bubble, which is not what we want
|
|
171
197
|
// el.reportValidity();
|
|
172
198
|
const validityState = el.validity;
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import type { Snippet } from "svelte";
|
|
3
3
|
import type { HTMLAttributes } from "svelte/elements";
|
|
4
4
|
import type { TranslateFn } from "../../types.js";
|
|
5
|
+
import type { NotificationsStack } from "../Notifications/notifications-stack.svelte.js";
|
|
5
6
|
import type {
|
|
6
7
|
LoginFormData,
|
|
7
8
|
LoginFormValidationError,
|
|
8
9
|
} from "./_internal/login-form-types.js";
|
|
9
|
-
import type { NotificationsStack } from "../Notifications/notifications-stack.svelte.js";
|
|
10
10
|
|
|
11
11
|
export interface Props extends Omit<HTMLAttributes<HTMLFormElement>, "children"> {
|
|
12
12
|
/** Bindable login data. Default: createEmptyLoginFormData() */
|
|
@@ -78,18 +78,18 @@
|
|
|
78
78
|
|
|
79
79
|
<script lang="ts">
|
|
80
80
|
import { untrack } from "svelte";
|
|
81
|
+
import { onSubmitValidityCheck } from "../../actions/on-submit-validity-check.svelte.js";
|
|
82
|
+
import { tooltip } from "../../actions/tooltip/tooltip.svelte.js";
|
|
81
83
|
import { twMerge } from "../../utils/tw-merge.js";
|
|
84
|
+
import Button from "../Button/Button.svelte";
|
|
85
|
+
import DismissibleMessage from "../DismissibleMessage/DismissibleMessage.svelte";
|
|
86
|
+
import FieldCheckbox from "../Input/FieldCheckbox.svelte";
|
|
87
|
+
import FieldInput from "../Input/FieldInput.svelte";
|
|
82
88
|
import { t_default } from "./_internal/login-form-i18n-defaults.js";
|
|
83
89
|
import {
|
|
84
90
|
createEmptyLoginFormData,
|
|
85
91
|
validateLoginForm,
|
|
86
92
|
} from "./_internal/login-form-utils.js";
|
|
87
|
-
import { tooltip } from "../../actions/tooltip/tooltip.svelte.js";
|
|
88
|
-
import Button from "../Button/Button.svelte";
|
|
89
|
-
import DismissibleMessage from "../DismissibleMessage/DismissibleMessage.svelte";
|
|
90
|
-
import FieldCheckbox from "../Input/FieldCheckbox.svelte";
|
|
91
|
-
import FieldInput from "../Input/FieldInput.svelte";
|
|
92
|
-
import { onSubmitValidityCheck } from "../../actions/on-submit-validity-check.svelte.js";
|
|
93
93
|
|
|
94
94
|
let {
|
|
95
95
|
formData = $bindable(createEmptyLoginFormData()),
|
|
@@ -131,13 +131,20 @@
|
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
function handleSubmitValid() {
|
|
134
|
+
// // [debug] kept commented for the next time Issue A regresses
|
|
135
|
+
// // eslint-disable-next-line no-console
|
|
136
|
+
// console.log("[LoginForm handleSubmitValid] entered", {
|
|
137
|
+
// formData: { ...formData },
|
|
138
|
+
// internalErrors: $state.snapshot(internalErrors),
|
|
139
|
+
// externalErrors: [...externalErrors],
|
|
140
|
+
// error,
|
|
141
|
+
// });
|
|
142
|
+
|
|
134
143
|
// Defensively clear any stale customValidity left on form fields by a prior
|
|
135
|
-
// validation pass. The
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
// submit, silently routing the form to `submit_invalid` and never calling
|
|
140
|
-
// `onSubmit`. Resetting here gives every retry a clean slate.
|
|
144
|
+
// validation pass. The canonical fix lives in `onSubmitValidityCheck` (which
|
|
145
|
+
// pre-clears before the per-field re-dispatch), but doing it here too is
|
|
146
|
+
// cheap insurance against any future regression that lets a stale flag slip
|
|
147
|
+
// past the action.
|
|
141
148
|
if (el) {
|
|
142
149
|
for (const node of Array.from(el.elements) as HTMLInputElement[]) {
|
|
143
150
|
if (typeof node.setCustomValidity === "function") node.setCustomValidity("");
|
|
@@ -147,6 +154,15 @@
|
|
|
147
154
|
const validationErrors = validateLoginForm(formData, t);
|
|
148
155
|
internalErrors = validationErrors;
|
|
149
156
|
|
|
157
|
+
// // [debug] kept commented for the next time Issue A regresses
|
|
158
|
+
// // eslint-disable-next-line no-console
|
|
159
|
+
// console.log("[LoginForm handleSubmitValid] post-validate", {
|
|
160
|
+
// validationErrors,
|
|
161
|
+
// externalErrorsLen: externalErrors.length,
|
|
162
|
+
// willCallOnSubmit:
|
|
163
|
+
// validationErrors.length === 0 && externalErrors.length === 0,
|
|
164
|
+
// });
|
|
165
|
+
|
|
150
166
|
if (validationErrors.length === 0 && externalErrors.length === 0) {
|
|
151
167
|
onSubmit(formData);
|
|
152
168
|
}
|
|
@@ -188,10 +204,10 @@
|
|
|
188
204
|
<DismissibleMessage message={error} intent="destructive" />
|
|
189
205
|
|
|
190
206
|
<!--
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
207
|
+
svelte-ignore binding_property_non_reactive:
|
|
208
|
+
formData is a $bindable prop — deep reactivity depends on the consumer
|
|
209
|
+
passing a $state() object. The bindings work correctly regardless.
|
|
210
|
+
-->
|
|
195
211
|
<!-- Email -->
|
|
196
212
|
<!-- svelte-ignore binding_property_non_reactive -->
|
|
197
213
|
<FieldInput
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Snippet } from "svelte";
|
|
2
2
|
import type { HTMLAttributes } from "svelte/elements";
|
|
3
3
|
import type { TranslateFn } from "../../types.js";
|
|
4
|
-
import type { LoginFormData, LoginFormValidationError } from "./_internal/login-form-types.js";
|
|
5
4
|
import type { NotificationsStack } from "../Notifications/notifications-stack.svelte.js";
|
|
5
|
+
import type { LoginFormData, LoginFormValidationError } from "./_internal/login-form-types.js";
|
|
6
6
|
export interface Props extends Omit<HTMLAttributes<HTMLFormElement>, "children"> {
|
|
7
7
|
/** Bindable login data. Default: createEmptyLoginFormData() */
|
|
8
8
|
formData?: LoginFormData;
|