@jsenv/navi 0.9.3 → 0.10.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/jsenv_navi.js +478 -309
- package/index.js +5 -0
- package/package.json +1 -1
- package/src/components/callout/callout.js +3 -1
- package/src/components/details/details.jsx +7 -8
- package/src/components/field/button.jsx +6 -63
- package/src/components/field/checkbox_list.jsx +0 -1
- package/src/components/field/form.jsx +2 -17
- package/src/components/field/input_checkbox.jsx +0 -1
- package/src/components/field/input_textual.jsx +5 -142
- package/src/components/field/radio_list.jsx +0 -1
- package/src/components/field/select.jsx +0 -1
- package/src/components/field/use_form_events.js +4 -0
- package/src/components/keyboard_shortcuts/keyboard_shortcuts.js +1 -1
- package/src/validation/constraints/native_constraints.js +43 -18
- package/src/validation/constraints/same_as_constraint.js +42 -0
- package/src/validation/custom_constraint_validation.js +262 -59
- package/src/validation/demos/demo_same_as_constraint.html +259 -0
- package/src/validation/input_change_effect.js +106 -0
|
@@ -27,6 +27,20 @@
|
|
|
27
27
|
* - Validation messages that follow the input element and adapt to viewport
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* To enable this API one have to call installCustomConstraintValidation(element)
|
|
32
|
+
* on the <form> and every element within the <form> (<input>, <button>, etc.)
|
|
33
|
+
* (In practice this is done automatically by jsx components in navi package)
|
|
34
|
+
*
|
|
35
|
+
* Once installed code must now listen to specific action events on the <form>
|
|
36
|
+
* (not "submit" but "actionrequested" most notably)
|
|
37
|
+
*
|
|
38
|
+
* There is one way to fully bypass validation which is to call form.submit()
|
|
39
|
+
* just like you could do with the native validation API to bypass validation.
|
|
40
|
+
* We keep this behavior on purpose but in practice you always want to go through the form validation process
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
import { createPubSub } from "@jsenv/dom";
|
|
30
44
|
import { openCallout } from "../components/callout/callout.js";
|
|
31
45
|
import {
|
|
32
46
|
DISABLED_CONSTRAINT,
|
|
@@ -40,6 +54,8 @@ import {
|
|
|
40
54
|
TYPE_NUMBER_CONSTRAINT,
|
|
41
55
|
} from "./constraints/native_constraints.js";
|
|
42
56
|
import { READONLY_CONSTRAINT } from "./constraints/readonly_constraint.js";
|
|
57
|
+
import { SAME_AS_CONSTRAINT } from "./constraints/same_as_constraint.js";
|
|
58
|
+
import { listenInputChange } from "./input_change_effect.js";
|
|
43
59
|
|
|
44
60
|
let debug = false;
|
|
45
61
|
|
|
@@ -49,9 +65,9 @@ export const requestAction = (
|
|
|
49
65
|
target,
|
|
50
66
|
action,
|
|
51
67
|
{
|
|
68
|
+
actionOrigin,
|
|
52
69
|
event,
|
|
53
70
|
requester = target,
|
|
54
|
-
actionOrigin,
|
|
55
71
|
method = "rerun",
|
|
56
72
|
meta = {},
|
|
57
73
|
confirmMessage,
|
|
@@ -132,12 +148,8 @@ export const requestAction = (
|
|
|
132
148
|
// Single element validation case
|
|
133
149
|
isValid = validationInterface.checkValidity({ fromRequestAction: true });
|
|
134
150
|
if (!isValid) {
|
|
135
|
-
if (event) {
|
|
136
|
-
event.preventDefault();
|
|
137
|
-
}
|
|
138
151
|
validationInterface.reportValidity();
|
|
139
152
|
}
|
|
140
|
-
|
|
141
153
|
elementForConfirmation = target;
|
|
142
154
|
elementForDispatch = target;
|
|
143
155
|
}
|
|
@@ -180,6 +192,15 @@ export const requestAction = (
|
|
|
180
192
|
return true;
|
|
181
193
|
};
|
|
182
194
|
|
|
195
|
+
export const forwardActionRequested = (e, action, target = e.target) => {
|
|
196
|
+
requestAction(target, action, {
|
|
197
|
+
actionOrigin: e.detail?.actionOrigin,
|
|
198
|
+
event: e.detail?.event || e,
|
|
199
|
+
requester: e.detail?.requester,
|
|
200
|
+
meta: e.detail?.meta,
|
|
201
|
+
});
|
|
202
|
+
};
|
|
203
|
+
|
|
183
204
|
export const closeValidationMessage = (element, reason) => {
|
|
184
205
|
const validationInterface = element.__validationInterface__;
|
|
185
206
|
if (!validationInterface) {
|
|
@@ -200,6 +221,7 @@ export const checkValidity = (element) => {
|
|
|
200
221
|
return validationInterface.checkValidity();
|
|
201
222
|
};
|
|
202
223
|
|
|
224
|
+
const formInstrumentedWeakSet = new WeakSet();
|
|
203
225
|
export const installCustomConstraintValidation = (
|
|
204
226
|
element,
|
|
205
227
|
elementReceivingValidationMessage = element,
|
|
@@ -218,20 +240,25 @@ export const installCustomConstraintValidation = (
|
|
|
218
240
|
validationMessage: null,
|
|
219
241
|
};
|
|
220
242
|
|
|
221
|
-
const
|
|
243
|
+
const [teardown, addTeardown] = createPubSub();
|
|
222
244
|
cleanup: {
|
|
223
245
|
const uninstall = () => {
|
|
224
|
-
|
|
225
|
-
cleanupCallback();
|
|
226
|
-
}
|
|
227
|
-
cleanupCallbackSet.clear();
|
|
246
|
+
teardown();
|
|
228
247
|
};
|
|
229
248
|
validationInterface.uninstall = uninstall;
|
|
230
249
|
}
|
|
231
250
|
|
|
251
|
+
const isForm = element.tagName === "FORM";
|
|
252
|
+
if (isForm) {
|
|
253
|
+
formInstrumentedWeakSet.add(element);
|
|
254
|
+
addTeardown(() => {
|
|
255
|
+
formInstrumentedWeakSet.delete(element);
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
232
259
|
expose_as_node_property: {
|
|
233
260
|
element.__validationInterface__ = validationInterface;
|
|
234
|
-
|
|
261
|
+
addTeardown(() => {
|
|
235
262
|
delete element.__validationInterface__;
|
|
236
263
|
});
|
|
237
264
|
}
|
|
@@ -240,7 +267,6 @@ export const installCustomConstraintValidation = (
|
|
|
240
267
|
const cancelEvent = new CustomEvent("cancel", options);
|
|
241
268
|
element.dispatchEvent(cancelEvent);
|
|
242
269
|
};
|
|
243
|
-
|
|
244
270
|
const closeElementValidationMessage = (reason) => {
|
|
245
271
|
if (validationInterface.validationMessage) {
|
|
246
272
|
validationInterface.validationMessage.close(reason);
|
|
@@ -260,6 +286,7 @@ export const installCustomConstraintValidation = (
|
|
|
260
286
|
constraintSet.add(MIN_CONSTRAINT);
|
|
261
287
|
constraintSet.add(MAX_CONSTRAINT);
|
|
262
288
|
constraintSet.add(READONLY_CONSTRAINT);
|
|
289
|
+
constraintSet.add(SAME_AS_CONSTRAINT);
|
|
263
290
|
register_constraint: {
|
|
264
291
|
validationInterface.registerConstraint = (constraint) => {
|
|
265
292
|
if (typeof constraint === "function") {
|
|
@@ -278,6 +305,8 @@ export const installCustomConstraintValidation = (
|
|
|
278
305
|
let failedConstraintInfo = null;
|
|
279
306
|
const validityInfoMap = new Map();
|
|
280
307
|
|
|
308
|
+
const hasTitleAttribute = element.hasAttribute("title");
|
|
309
|
+
|
|
281
310
|
const resetValidity = ({ fromRequestAction } = {}) => {
|
|
282
311
|
if (fromRequestAction && failedConstraintInfo) {
|
|
283
312
|
for (const [key, customMessage] of customMessageMap) {
|
|
@@ -295,7 +324,7 @@ export const installCustomConstraintValidation = (
|
|
|
295
324
|
validityInfoMap.clear();
|
|
296
325
|
failedConstraintInfo = null;
|
|
297
326
|
};
|
|
298
|
-
|
|
327
|
+
addTeardown(resetValidity);
|
|
299
328
|
|
|
300
329
|
const checkValidity = ({ fromRequestAction, skipReadonly } = {}) => {
|
|
301
330
|
resetValidity({ fromRequestAction });
|
|
@@ -340,7 +369,16 @@ export const installCustomConstraintValidation = (
|
|
|
340
369
|
validityInfoMap.set(constraint, failedConstraintInfo);
|
|
341
370
|
}
|
|
342
371
|
|
|
343
|
-
if (
|
|
372
|
+
if (failedConstraintInfo) {
|
|
373
|
+
if (!hasTitleAttribute) {
|
|
374
|
+
// when a constraint is failing browser displays that constraint message if the element has no title attribute.
|
|
375
|
+
// We want to do the same with our message (overriding the browser in the process to get better messages)
|
|
376
|
+
element.setAttribute("title", failedConstraintInfo.message);
|
|
377
|
+
}
|
|
378
|
+
} else {
|
|
379
|
+
if (!hasTitleAttribute) {
|
|
380
|
+
element.removeAttribute("title");
|
|
381
|
+
}
|
|
344
382
|
closeElementValidationMessage("becomes_valid");
|
|
345
383
|
}
|
|
346
384
|
|
|
@@ -366,9 +404,9 @@ export const installCustomConstraintValidation = (
|
|
|
366
404
|
if (!skipFocus) {
|
|
367
405
|
element.focus();
|
|
368
406
|
}
|
|
369
|
-
const
|
|
407
|
+
const removeCloseOnCleanup = addTeardown(() => {
|
|
370
408
|
closeElementValidationMessage("cleanup");
|
|
371
|
-
};
|
|
409
|
+
});
|
|
372
410
|
|
|
373
411
|
const anchorElement =
|
|
374
412
|
failedConstraintInfo.target || elementReceivingValidationMessage;
|
|
@@ -379,7 +417,7 @@ export const installCustomConstraintValidation = (
|
|
|
379
417
|
level: failedConstraintInfo.level,
|
|
380
418
|
closeOnClickOutside: failedConstraintInfo.closeOnClickOutside,
|
|
381
419
|
onClose: () => {
|
|
382
|
-
|
|
420
|
+
removeCloseOnCleanup();
|
|
383
421
|
validationInterface.validationMessage = null;
|
|
384
422
|
if (failedConstraintInfo) {
|
|
385
423
|
failedConstraintInfo.reportStatus = "closed";
|
|
@@ -391,7 +429,6 @@ export const installCustomConstraintValidation = (
|
|
|
391
429
|
},
|
|
392
430
|
);
|
|
393
431
|
failedConstraintInfo.reportStatus = "reported";
|
|
394
|
-
cleanupCallbackSet.add(closeOnCleanup);
|
|
395
432
|
};
|
|
396
433
|
validationInterface.checkValidity = checkValidity;
|
|
397
434
|
validationInterface.reportValidity = reportValidity;
|
|
@@ -426,7 +463,7 @@ export const installCustomConstraintValidation = (
|
|
|
426
463
|
reportValidity();
|
|
427
464
|
}
|
|
428
465
|
};
|
|
429
|
-
|
|
466
|
+
addTeardown(() => {
|
|
430
467
|
customMessageMap.clear();
|
|
431
468
|
});
|
|
432
469
|
Object.assign(validationInterface, {
|
|
@@ -435,6 +472,7 @@ export const installCustomConstraintValidation = (
|
|
|
435
472
|
});
|
|
436
473
|
}
|
|
437
474
|
|
|
475
|
+
checkValidity();
|
|
438
476
|
close_and_check_on_input: {
|
|
439
477
|
const oninput = () => {
|
|
440
478
|
customMessageMap.clear();
|
|
@@ -442,7 +480,7 @@ export const installCustomConstraintValidation = (
|
|
|
442
480
|
checkValidity();
|
|
443
481
|
};
|
|
444
482
|
element.addEventListener("input", oninput);
|
|
445
|
-
|
|
483
|
+
addTeardown(() => {
|
|
446
484
|
element.removeEventListener("input", oninput);
|
|
447
485
|
});
|
|
448
486
|
}
|
|
@@ -454,13 +492,7 @@ export const installCustomConstraintValidation = (
|
|
|
454
492
|
checkValidity();
|
|
455
493
|
};
|
|
456
494
|
element.addEventListener("actionend", onactionend);
|
|
457
|
-
|
|
458
|
-
element.form.addEventListener("actionend", onactionend);
|
|
459
|
-
cleanupCallbackSet.add(() => {
|
|
460
|
-
element.form.removeEventListener("actionend", onactionend);
|
|
461
|
-
});
|
|
462
|
-
}
|
|
463
|
-
cleanupCallbackSet.add(() => {
|
|
495
|
+
addTeardown(() => {
|
|
464
496
|
element.removeEventListener("actionend", onactionend);
|
|
465
497
|
});
|
|
466
498
|
}
|
|
@@ -470,37 +502,119 @@ export const installCustomConstraintValidation = (
|
|
|
470
502
|
element.reportValidity = () => {
|
|
471
503
|
reportValidity();
|
|
472
504
|
};
|
|
473
|
-
|
|
505
|
+
addTeardown(() => {
|
|
474
506
|
element.reportValidity = nativeReportValidity;
|
|
475
507
|
});
|
|
476
508
|
}
|
|
477
509
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
510
|
+
request_on_enter: {
|
|
511
|
+
if (element.tagName !== "INPUT") {
|
|
512
|
+
// maybe we want it too for checkboxes etc, we'll see
|
|
513
|
+
break request_on_enter;
|
|
514
|
+
}
|
|
515
|
+
const onkeydown = (keydownEvent) => {
|
|
516
|
+
if (keydownEvent.defaultPrevented) {
|
|
481
517
|
return;
|
|
482
518
|
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
519
|
+
if (keydownEvent.key !== "Enter") {
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
if (element.hasAttribute("data-action")) {
|
|
523
|
+
if (wouldKeydownSubmitForm(keydownEvent)) {
|
|
524
|
+
keydownEvent.preventDefault();
|
|
525
|
+
}
|
|
526
|
+
dispatchActionRequestedCustomEvent(element, {
|
|
527
|
+
event: keydownEvent,
|
|
528
|
+
requester: element,
|
|
529
|
+
});
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
const { form } = element;
|
|
533
|
+
if (!form) {
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
keydownEvent.preventDefault();
|
|
537
|
+
dispatchActionRequestedCustomEvent(form, {
|
|
538
|
+
event: keydownEvent,
|
|
539
|
+
requester: getFirstButtonSubmittingForm(form) || element,
|
|
487
540
|
});
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
541
|
+
};
|
|
542
|
+
element.addEventListener("keydown", onkeydown);
|
|
543
|
+
addTeardown(() => {
|
|
544
|
+
element.removeEventListener("keydown", onkeydown);
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
request_on_button_click: {
|
|
549
|
+
const onclick = (clickEvent) => {
|
|
550
|
+
if (clickEvent.defaultPrevented) {
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
if (element.tagName !== "BUTTON") {
|
|
554
|
+
return;
|
|
491
555
|
}
|
|
556
|
+
if (element.hasAttribute("data-action")) {
|
|
557
|
+
if (wouldClickSubmitForm(clickEvent)) {
|
|
558
|
+
clickEvent.preventDefault();
|
|
559
|
+
}
|
|
560
|
+
dispatchActionRequestedCustomEvent(element, {
|
|
561
|
+
event: clickEvent,
|
|
562
|
+
requester: element,
|
|
563
|
+
});
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
const { form } = element;
|
|
567
|
+
if (!form) {
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
if (wouldClickSubmitForm(clickEvent)) {
|
|
571
|
+
clickEvent.preventDefault();
|
|
572
|
+
}
|
|
573
|
+
dispatchActionRequestedCustomEvent(form, {
|
|
574
|
+
event: clickEvent,
|
|
575
|
+
requester: element,
|
|
576
|
+
});
|
|
492
577
|
};
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
578
|
+
element.addEventListener("click", onclick);
|
|
579
|
+
addTeardown(() => {
|
|
580
|
+
element.removeEventListener("click", onclick);
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
request_on_input_change: {
|
|
585
|
+
const isInput =
|
|
586
|
+
element.tagName === "INPUT" || element.tagName === "TEXTAREA";
|
|
587
|
+
if (!isInput) {
|
|
588
|
+
break request_on_input_change;
|
|
589
|
+
}
|
|
590
|
+
const stop = listenInputChange(element, (e) => {
|
|
591
|
+
if (element.hasAttribute("data-action")) {
|
|
592
|
+
dispatchActionRequestedCustomEvent(element, {
|
|
593
|
+
event: e,
|
|
594
|
+
requester: element,
|
|
595
|
+
});
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
const { form } = element;
|
|
599
|
+
if (!form) {
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
dispatchActionRequestedCustomEvent(form, {
|
|
603
|
+
event: e,
|
|
604
|
+
requester: element,
|
|
605
|
+
});
|
|
606
|
+
});
|
|
607
|
+
addTeardown(() => {
|
|
608
|
+
stop();
|
|
496
609
|
});
|
|
497
610
|
}
|
|
498
611
|
|
|
499
612
|
execute_on_form_submit: {
|
|
500
|
-
|
|
501
|
-
if (!form) {
|
|
613
|
+
if (!isForm) {
|
|
502
614
|
break execute_on_form_submit;
|
|
503
615
|
}
|
|
616
|
+
// We will dispatch "action" when "submit" occurs (code called from.submit() to bypass validation)
|
|
617
|
+
const form = element;
|
|
504
618
|
const removeListener = addEventListener(form, "submit", (e) => {
|
|
505
619
|
e.preventDefault();
|
|
506
620
|
if (debug) {
|
|
@@ -512,12 +626,14 @@ export const installCustomConstraintValidation = (
|
|
|
512
626
|
event: e,
|
|
513
627
|
method: "rerun",
|
|
514
628
|
requester: form,
|
|
515
|
-
meta: {
|
|
629
|
+
meta: {
|
|
630
|
+
isSubmit: true,
|
|
631
|
+
},
|
|
516
632
|
},
|
|
517
633
|
});
|
|
518
634
|
form.dispatchEvent(actionCustomEvent);
|
|
519
635
|
});
|
|
520
|
-
|
|
636
|
+
addTeardown(() => {
|
|
521
637
|
removeListener();
|
|
522
638
|
});
|
|
523
639
|
}
|
|
@@ -531,7 +647,7 @@ export const installCustomConstraintValidation = (
|
|
|
531
647
|
}
|
|
532
648
|
};
|
|
533
649
|
element.addEventListener("keydown", onkeydown);
|
|
534
|
-
|
|
650
|
+
addTeardown(() => {
|
|
535
651
|
element.removeEventListener("keydown", onkeydown);
|
|
536
652
|
});
|
|
537
653
|
}
|
|
@@ -539,7 +655,11 @@ export const installCustomConstraintValidation = (
|
|
|
539
655
|
cancel_on_blur: {
|
|
540
656
|
const onblur = () => {
|
|
541
657
|
if (element.value === "") {
|
|
542
|
-
dispatchCancelCustomEvent({
|
|
658
|
+
dispatchCancelCustomEvent({
|
|
659
|
+
detail: {
|
|
660
|
+
reason: "blur_empty",
|
|
661
|
+
},
|
|
662
|
+
});
|
|
543
663
|
return;
|
|
544
664
|
}
|
|
545
665
|
// if we have failed constraint, we cancel too
|
|
@@ -554,7 +674,7 @@ export const installCustomConstraintValidation = (
|
|
|
554
674
|
}
|
|
555
675
|
};
|
|
556
676
|
element.addEventListener("blur", onblur);
|
|
557
|
-
|
|
677
|
+
addTeardown(() => {
|
|
558
678
|
element.removeEventListener("blur", onblur);
|
|
559
679
|
});
|
|
560
680
|
}
|
|
@@ -562,22 +682,105 @@ export const installCustomConstraintValidation = (
|
|
|
562
682
|
return validationInterface;
|
|
563
683
|
};
|
|
564
684
|
|
|
565
|
-
|
|
685
|
+
const wouldClickSubmitForm = (clickEvent) => {
|
|
686
|
+
if (clickEvent.defaultPrevented) {
|
|
687
|
+
return false;
|
|
688
|
+
}
|
|
689
|
+
const clickTarget = clickEvent.target;
|
|
690
|
+
const { form } = clickTarget;
|
|
691
|
+
if (!form) {
|
|
692
|
+
return false;
|
|
693
|
+
}
|
|
694
|
+
const button = clickTarget.closest("button");
|
|
695
|
+
if (!button) {
|
|
696
|
+
return false;
|
|
697
|
+
}
|
|
698
|
+
const wouldSubmitFormByType =
|
|
699
|
+
button.type === "submit" || button.type === "image";
|
|
700
|
+
if (wouldSubmitFormByType) {
|
|
701
|
+
return true;
|
|
702
|
+
}
|
|
703
|
+
if (button.type) {
|
|
704
|
+
return false;
|
|
705
|
+
}
|
|
706
|
+
if (getFirstButtonSubmittingForm(form)) {
|
|
707
|
+
// an other button is explicitly submitting the form, this one would not submit it
|
|
708
|
+
return false;
|
|
709
|
+
}
|
|
710
|
+
// this is the only button inside the form without type attribute, so it defaults to type="submit"
|
|
711
|
+
return true;
|
|
712
|
+
};
|
|
713
|
+
const getFirstButtonSubmittingForm = (form) => {
|
|
714
|
+
return form.querySelector(
|
|
715
|
+
`button[type="submit"], input[type="submit"], input[type="image"]`,
|
|
716
|
+
);
|
|
717
|
+
};
|
|
718
|
+
|
|
719
|
+
const wouldKeydownSubmitForm = (keydownEvent) => {
|
|
720
|
+
if (keydownEvent.defaultPrevented) {
|
|
721
|
+
return false;
|
|
722
|
+
}
|
|
723
|
+
const keydownTarget = keydownEvent.target;
|
|
724
|
+
const { form } = keydownTarget;
|
|
725
|
+
if (!form) {
|
|
726
|
+
return false;
|
|
727
|
+
}
|
|
728
|
+
if (keydownEvent.key !== "Enter") {
|
|
729
|
+
return false;
|
|
730
|
+
}
|
|
731
|
+
const isTextInput =
|
|
732
|
+
keydownTarget.tagName === "INPUT" || keydownTarget.tagName === "TEXTAREA";
|
|
733
|
+
if (!isTextInput) {
|
|
734
|
+
return false;
|
|
735
|
+
}
|
|
736
|
+
return true;
|
|
737
|
+
};
|
|
566
738
|
|
|
567
|
-
const
|
|
739
|
+
const dispatchActionRequestedCustomEvent = (
|
|
740
|
+
fieldOrForm,
|
|
741
|
+
{ actionOrigin = "action_prop", event, requester },
|
|
742
|
+
) => {
|
|
743
|
+
const actionRequestedCustomEvent = new CustomEvent("actionrequested", {
|
|
744
|
+
cancelable: true,
|
|
745
|
+
detail: {
|
|
746
|
+
actionOrigin,
|
|
747
|
+
event,
|
|
748
|
+
requester,
|
|
749
|
+
},
|
|
750
|
+
});
|
|
751
|
+
fieldOrForm.dispatchEvent(actionRequestedCustomEvent);
|
|
752
|
+
};
|
|
753
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTML/Guides/Constraint_validation
|
|
568
754
|
const requestSubmit = HTMLFormElement.prototype.requestSubmit;
|
|
569
755
|
HTMLFormElement.prototype.requestSubmit = function (submitter) {
|
|
570
|
-
|
|
571
|
-
const
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
for (const requestSubmitCallback of requestSubmitCallbackSet) {
|
|
575
|
-
requestSubmitCallback(this, { submitter, preventDefault });
|
|
576
|
-
}
|
|
577
|
-
if (prevented) {
|
|
756
|
+
const form = this;
|
|
757
|
+
const isInstrumented = formInstrumentedWeakSet.has(form);
|
|
758
|
+
if (!isInstrumented) {
|
|
759
|
+
requestSubmit.call(form, submitter);
|
|
578
760
|
return;
|
|
579
761
|
}
|
|
580
|
-
|
|
762
|
+
const programmaticEvent = new CustomEvent("programmatic_requestsubmit", {
|
|
763
|
+
cancelable: true,
|
|
764
|
+
detail: {
|
|
765
|
+
submitter,
|
|
766
|
+
},
|
|
767
|
+
});
|
|
768
|
+
dispatchActionRequestedCustomEvent(form, {
|
|
769
|
+
event: programmaticEvent,
|
|
770
|
+
requester: submitter,
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
// When all fields are valid calling the native requestSubmit would let browser go through the
|
|
774
|
+
// standard form validation steps leading to form submission.
|
|
775
|
+
// We don't want that because we have our own action system to handle forms
|
|
776
|
+
// If we did that the form submission would happen in parallel of our action system
|
|
777
|
+
// and because we listen to "submit" event to dispatch "action" event
|
|
778
|
+
// we would end up with two actions being executed.
|
|
779
|
+
//
|
|
780
|
+
// In case we have discrepencies in our implementation compared to the browser standard
|
|
781
|
+
// this also prevent the native validation message to show up.
|
|
782
|
+
|
|
783
|
+
// requestSubmit.call(this, submitter);
|
|
581
784
|
};
|
|
582
785
|
|
|
583
786
|
// const submit = HTMLFormElement.prototype.submit;
|