@jsenv/navi 0.10.2 → 0.11.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 +13838 -23291
- package/dist/jsenv_navi.js.map +1281 -0
- package/package.json +5 -7
- package/index.js +0 -122
- package/src/action_private_properties.js +0 -11
- package/src/action_proxy_test.html +0 -353
- package/src/action_run_states.js +0 -5
- package/src/actions.js +0 -1401
- package/src/browser_integration/browser_integration.js +0 -216
- package/src/browser_integration/document_back_and_forward.js +0 -17
- package/src/browser_integration/document_loading_signal.js +0 -100
- package/src/browser_integration/document_state_signal.js +0 -9
- package/src/browser_integration/document_url_signal.js +0 -9
- package/src/browser_integration/use_is_visited.js +0 -19
- package/src/browser_integration/via_history.js +0 -232
- package/src/browser_integration/via_navigation.js +0 -168
- package/src/components/action_execution/form_context.js +0 -5
- package/src/components/action_execution/render_actionable_component.jsx +0 -29
- package/src/components/action_execution/use_action.js +0 -99
- package/src/components/action_execution/use_execute_action.js +0 -193
- package/src/components/action_execution/use_run_on_mount.js +0 -9
- package/src/components/action_renderer.jsx +0 -125
- package/src/components/callout/callout.js +0 -990
- package/src/components/callout/callout_demo.html +0 -201
- package/src/components/callout/test_dynamic_positioning.html +0 -161
- package/src/components/callout/test_html_document_iframe.html +0 -182
- package/src/components/demos/0_button_demo.html +0 -707
- package/src/components/demos/10_column_reordering_debug.html +0 -277
- package/src/components/demos/11_table_selection_debug.html +0 -432
- package/src/components/demos/1_checkbox_demo.html +0 -754
- package/src/components/demos/2_input_textual_demo.html +0 -286
- package/src/components/demos/3_radio_demo.html +0 -874
- package/src/components/demos/4_select_demo.html +0 -100
- package/src/components/demos/5_list_scrollable_demo.html +0 -153
- package/src/components/demos/6_tablist_demo.html +0 -77
- package/src/components/demos/7_table_selection_demo.html +0 -176
- package/src/components/demos/8_table_fixed_headers_demo.html +0 -584
- package/src/components/demos/9_table_column_drag_demo.html +0 -325
- package/src/components/demos/action/0_button_demo.html +0 -204
- package/src/components/demos/action/10_shortcuts_demo.html +0 -189
- package/src/components/demos/action/11_nested_shortcuts_demo.xhtml +0 -401
- package/src/components/demos/action/1_input_text_demo.html +0 -876
- package/src/components/demos/action/2_form_multiple.html +0 -303
- package/src/components/demos/action/3_details_demo.html +0 -203
- package/src/components/demos/action/4_input_checkbox_demo.html +0 -731
- package/src/components/demos/action/5_input_checkbox_state_demo.html +0 -270
- package/src/components/demos/action/6_checkbox_list_demo.html +0 -341
- package/src/components/demos/action/7_radio_list_demo.html +0 -357
- package/src/components/demos/action/8_editable_demo.html +0 -431
- package/src/components/demos/action/9_link_demo.html +0 -194
- package/src/components/demos/demo.md +0 -0
- package/src/components/demos/route/basic/basic.html +0 -14
- package/src/components/demos/route/basic/basic_route_demo.jsx +0 -224
- package/src/components/demos/route/multi/multi.html +0 -14
- package/src/components/demos/route/multi/multi_route_demo.jsx +0 -277
- package/src/components/demos/ui_transition/0_action_renderer_ui_transition_demo.html +0 -695
- package/src/components/demos/ui_transition/1_nested_ui_transition_demo.html +0 -429
- package/src/components/demos/ui_transition/2_height_transition_test.html +0 -295
- package/src/components/details/details.jsx +0 -245
- package/src/components/details/summary_marker.jsx +0 -141
- package/src/components/edition/editable.jsx +0 -186
- package/src/components/error_boundary_context.js +0 -9
- package/src/components/field/README.md +0 -247
- package/src/components/field/button.jsx +0 -429
- package/src/components/field/checkbox_list.jsx +0 -185
- package/src/components/field/collect_form_element_values.js +0 -82
- package/src/components/field/custom_field.js +0 -106
- package/src/components/field/form.jsx +0 -209
- package/src/components/field/input.jsx +0 -16
- package/src/components/field/input_checkbox.jsx +0 -434
- package/src/components/field/input_radio.jsx +0 -432
- package/src/components/field/input_textual.jsx +0 -389
- package/src/components/field/label.jsx +0 -46
- package/src/components/field/radio_list.jsx +0 -183
- package/src/components/field/select.jsx +0 -256
- package/src/components/field/use_action_events.js +0 -132
- package/src/components/field/use_form_events.js +0 -59
- package/src/components/field/use_ui_state_controller.js +0 -506
- package/src/components/item_tracker/README.md +0 -461
- package/src/components/item_tracker/use_isolated_item_tracker.jsx +0 -209
- package/src/components/item_tracker/use_isolated_item_tracker_demo.html +0 -148
- package/src/components/item_tracker/use_isolated_item_tracker_demo.jsx +0 -460
- package/src/components/item_tracker/use_item_tracker.jsx +0 -143
- package/src/components/item_tracker/use_item_tracker_demo.html +0 -207
- package/src/components/item_tracker/use_item_tracker_demo.jsx +0 -216
- package/src/components/keyboard_shortcuts/active_keyboard_shortcuts.jsx +0 -87
- package/src/components/keyboard_shortcuts/aria_key_shortcuts.js +0 -61
- package/src/components/keyboard_shortcuts/keyboard_key_meta.js +0 -17
- package/src/components/keyboard_shortcuts/keyboard_shortcuts.js +0 -371
- package/src/components/keyboard_shortcuts/os.js +0 -9
- package/src/components/layout/demos/demo_flex.html +0 -638
- package/src/components/layout/demos/demo_layout_style_buttons.html +0 -351
- package/src/components/layout/demos/demo_layout_style_input.html +0 -226
- package/src/components/layout/demos/demo_layout_style_text.html +0 -514
- package/src/components/layout/flex.jsx +0 -109
- package/src/components/layout/layout_context.jsx +0 -3
- package/src/components/layout/spacing.jsx +0 -20
- package/src/components/layout/use_layout_style.js +0 -249
- package/src/components/link/link.jsx +0 -267
- package/src/components/link/link_with_icon.jsx +0 -52
- package/src/components/loader/loader_background.jsx +0 -372
- package/src/components/loader/loading_spinner.jsx +0 -68
- package/src/components/loader/network_speed.js +0 -83
- package/src/components/loader/rectangle_loading.jsx +0 -244
- package/src/components/props_composition/demos/demo_with_props_style.html +0 -81
- package/src/components/props_composition/with_props_class_name.js +0 -37
- package/src/components/props_composition/with_props_style.js +0 -26
- package/src/components/route.jsx +0 -19
- package/src/components/selection/selection.jsx +0 -1583
- package/src/components/svg/font_sized_svg.jsx +0 -59
- package/src/components/svg/icon_and_text.jsx +0 -21
- package/src/components/svg/svg_mask_overlay.jsx +0 -105
- package/src/components/table/drag/table_drag.jsx +0 -506
- package/src/components/table/resize/table_resize.jsx +0 -650
- package/src/components/table/resize/table_size.js +0 -43
- package/src/components/table/selection/table_selection.js +0 -106
- package/src/components/table/selection/table_selection.jsx +0 -203
- package/src/components/table/sticky/sticky_group.js +0 -354
- package/src/components/table/sticky/table_sticky.js +0 -25
- package/src/components/table/sticky/table_sticky.jsx +0 -501
- package/src/components/table/table.jsx +0 -721
- package/src/components/table/table_css.js +0 -211
- package/src/components/table/table_ui.jsx +0 -49
- package/src/components/table/use_cells_and_columns.js +0 -90
- package/src/components/table/use_object_array_to_cells.js +0 -46
- package/src/components/table/z_indexes.js +0 -23
- package/src/components/tablist/tablist.jsx +0 -99
- package/src/components/text/demos/demo_text_and_icon.html +0 -421
- package/src/components/text/overflow.jsx +0 -15
- package/src/components/text/text.jsx +0 -83
- package/src/components/text/text_and_count.jsx +0 -28
- package/src/components/ui_transition.jsx +0 -128
- package/src/components/use_auto_focus.js +0 -94
- package/src/components/use_batch_during_render.js +0 -33
- package/src/components/use_debounce_true.js +0 -31
- package/src/components/use_dependencies_diff.js +0 -35
- package/src/components/use_focus_group.js +0 -20
- package/src/components/use_initial_value.js +0 -78
- package/src/components/use_is_visited.js +0 -19
- package/src/components/use_ref_array.js +0 -38
- package/src/components/use_signal_sync.js +0 -50
- package/src/components/use_stable_callback.js +0 -68
- package/src/components/use_state_array.js +0 -47
- package/src/docs/actions.md +0 -250
- package/src/docs/demos/resource/action_status.jsx +0 -42
- package/src/docs/demos/resource/demo.md +0 -1
- package/src/docs/demos/resource/resource_demo_0.html +0 -84
- package/src/docs/demos/resource/resource_demo_10_post_gc.html +0 -364
- package/src/docs/demos/resource/resource_demo_11_describe_many.html +0 -362
- package/src/docs/demos/resource/resource_demo_2.html +0 -173
- package/src/docs/demos/resource/resource_demo_3_filtered_users.html +0 -415
- package/src/docs/demos/resource/resource_demo_4_details.html +0 -284
- package/src/docs/demos/resource/resource_demo_5_renderer_lazy.html +0 -115
- package/src/docs/demos/resource/resource_demo_6_gc.html +0 -217
- package/src/docs/demos/resource/resource_demo_7_child_gc.html +0 -240
- package/src/docs/demos/resource/resource_demo_8_proxy_gc.html +0 -319
- package/src/docs/demos/resource/resource_demo_9_describe_one.html +0 -472
- package/src/docs/demos/resource/tata.jsx +0 -3
- package/src/docs/demos/resource/toto.jsx +0 -3
- package/src/docs/demos/user_nav/user_nav.html +0 -12
- package/src/docs/demos/user_nav/user_nav.jsx +0 -330
- package/src/docs/resource_dependencies.md +0 -103
- package/src/docs/resource_with_params.md +0 -80
- package/src/navi_css_vars.js +0 -14
- package/src/notes.md +0 -34
- package/src/route/route.js +0 -596
- package/src/route/route.xtest.html +0 -228
- package/src/store/array_signal_store.js +0 -537
- package/src/store/local_storage_signal.js +0 -17
- package/src/store/resource_graph.js +0 -1304
- package/src/store/tests/resource_graph_autoreload_demo.html +0 -12
- package/src/store/tests/resource_graph_autoreload_demo.jsx +0 -964
- package/src/store/tests/resource_graph_dependencies.test_manual.js +0 -95
- package/src/store/value_in_local_storage.js +0 -187
- package/src/symbol_object_signal.js +0 -1
- package/src/use_action_data.js +0 -10
- package/src/use_action_status.js +0 -47
- package/src/utils/add_many_event_listeners.js +0 -15
- package/src/utils/array_add_remove.js +0 -61
- package/src/utils/array_signal.js +0 -15
- package/src/utils/compare_two_js_values.js +0 -172
- package/src/utils/execute_with_cleanup.js +0 -21
- package/src/utils/get_caller_info.js +0 -85
- package/src/utils/is_signal.js +0 -20
- package/src/utils/js_value_weak_map.js +0 -162
- package/src/utils/js_value_weak_map_demo.html +0 -690
- package/src/utils/merge_two_js_values.js +0 -53
- package/src/utils/stringify_for_display.js +0 -131
- package/src/utils/weak_effect.js +0 -48
- package/src/validation/constraints/confirm_constraint.js +0 -14
- package/src/validation/constraints/create_unique_value_constraint.js +0 -27
- package/src/validation/constraints/native_constraints.js +0 -338
- package/src/validation/constraints/readonly_constraint.js +0 -41
- package/src/validation/constraints/same_as_constraint.js +0 -42
- package/src/validation/constraints/single_space_constraint.js +0 -13
- package/src/validation/custom_constraint_validation.js +0 -793
- package/src/validation/custom_message.js +0 -18
- package/src/validation/demos/browser_style.png +0 -0
- package/src/validation/demos/demo_same_as_constraint.html +0 -259
- package/src/validation/demos/form_validation_demo.html +0 -142
- package/src/validation/demos/form_validation_demo_preact.html +0 -87
- package/src/validation/demos/form_validation_native_popover_demo.html +0 -168
- package/src/validation/demos/form_validation_vs_native_demo.html +0 -172
- package/src/validation/hooks/use_constraints.js +0 -23
- package/src/validation/hooks/use_custom_validation_ref.js +0 -73
- package/src/validation/hooks/use_validation_message.js +0 -19
- package/src/validation/input_change_effect.js +0 -106
|
@@ -1,793 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Custom form validation implementation
|
|
3
|
-
*
|
|
4
|
-
* This implementation addresses several limitations of the browser's native validation API:
|
|
5
|
-
*
|
|
6
|
-
* Limitations of native validation:
|
|
7
|
-
* - Cannot programmatically detect if validation message is currently displayed
|
|
8
|
-
* - No ability to dismiss messages with keyboard (e.g., Escape key)
|
|
9
|
-
* - Requires complex event handling to manage validation message display
|
|
10
|
-
* - Limited support for storing/managing multiple validation messages
|
|
11
|
-
* - No customization of validation message appearance
|
|
12
|
-
*
|
|
13
|
-
* Design approach:
|
|
14
|
-
* - Works alongside native validation (which acts as a fallback)
|
|
15
|
-
* - Proactively detects validation issues before native validation triggers
|
|
16
|
-
* - Provides complete control over validation message UX
|
|
17
|
-
* - Supports keyboard navigation and dismissal
|
|
18
|
-
* - Allows custom styling and positioning of validation messages
|
|
19
|
-
*
|
|
20
|
-
* Features:
|
|
21
|
-
* - Constraint-based validation system with built-in and custom constraints
|
|
22
|
-
* - Custom validation messages with different severity levels
|
|
23
|
-
* - Form submission prevention on validation failure
|
|
24
|
-
* - Validation on Enter key in forms or standalone inputs
|
|
25
|
-
* - Escape key to dismiss validation messages
|
|
26
|
-
* - Support for standard HTML validation attributes (required, pattern, type="email")
|
|
27
|
-
* - Validation messages that follow the input element and adapt to viewport
|
|
28
|
-
*/
|
|
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";
|
|
44
|
-
import { openCallout } from "../components/callout/callout.js";
|
|
45
|
-
import {
|
|
46
|
-
DISABLED_CONSTRAINT,
|
|
47
|
-
MAX_CONSTRAINT,
|
|
48
|
-
MAX_LENGTH_CONSTRAINT,
|
|
49
|
-
MIN_CONSTRAINT,
|
|
50
|
-
MIN_LENGTH_CONSTRAINT,
|
|
51
|
-
PATTERN_CONSTRAINT,
|
|
52
|
-
REQUIRED_CONSTRAINT,
|
|
53
|
-
TYPE_EMAIL_CONSTRAINT,
|
|
54
|
-
TYPE_NUMBER_CONSTRAINT,
|
|
55
|
-
} from "./constraints/native_constraints.js";
|
|
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";
|
|
59
|
-
|
|
60
|
-
let debug = false;
|
|
61
|
-
|
|
62
|
-
const validationInProgressWeakSet = new WeakSet();
|
|
63
|
-
|
|
64
|
-
export const requestAction = (
|
|
65
|
-
target,
|
|
66
|
-
action,
|
|
67
|
-
{
|
|
68
|
-
actionOrigin,
|
|
69
|
-
event,
|
|
70
|
-
requester = target,
|
|
71
|
-
method = "rerun",
|
|
72
|
-
meta = {},
|
|
73
|
-
confirmMessage,
|
|
74
|
-
} = {},
|
|
75
|
-
) => {
|
|
76
|
-
if (!actionOrigin) {
|
|
77
|
-
console.warn("requestAction: actionOrigin is required");
|
|
78
|
-
}
|
|
79
|
-
let elementToValidate = requester;
|
|
80
|
-
|
|
81
|
-
let validationInterface = elementToValidate.__validationInterface__;
|
|
82
|
-
if (!validationInterface) {
|
|
83
|
-
validationInterface = installCustomConstraintValidation(elementToValidate);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const customEventDetail = {
|
|
87
|
-
action,
|
|
88
|
-
actionOrigin,
|
|
89
|
-
method,
|
|
90
|
-
event,
|
|
91
|
-
requester,
|
|
92
|
-
meta,
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
if (debug) {
|
|
96
|
-
console.debug(
|
|
97
|
-
`action requested by`,
|
|
98
|
-
requester,
|
|
99
|
-
`(event: "${event?.type}")`,
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Determine what needs to be validated and how to handle the result
|
|
104
|
-
const isForm = elementToValidate.tagName === "FORM";
|
|
105
|
-
const formToValidate = isForm ? elementToValidate : elementToValidate.form;
|
|
106
|
-
|
|
107
|
-
let isValid = false;
|
|
108
|
-
let elementForConfirmation = elementToValidate;
|
|
109
|
-
let elementForDispatch = elementToValidate;
|
|
110
|
-
|
|
111
|
-
if (formToValidate) {
|
|
112
|
-
// Form validation case
|
|
113
|
-
if (validationInProgressWeakSet.has(formToValidate)) {
|
|
114
|
-
if (debug) {
|
|
115
|
-
console.debug(`validation already in progress for`, formToValidate);
|
|
116
|
-
}
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
|
-
validationInProgressWeakSet.add(formToValidate);
|
|
120
|
-
setTimeout(() => {
|
|
121
|
-
validationInProgressWeakSet.delete(formToValidate);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
// Validate all form elements
|
|
125
|
-
const formElements = formToValidate.elements;
|
|
126
|
-
isValid = true; // Assume valid until proven otherwise
|
|
127
|
-
for (const formElement of formElements) {
|
|
128
|
-
const elementValidationInterface = formElement.__validationInterface__;
|
|
129
|
-
if (!elementValidationInterface) {
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const elementIsValid = elementValidationInterface.checkValidity({
|
|
134
|
-
fromRequestAction: true,
|
|
135
|
-
skipReadonly:
|
|
136
|
-
formElement.tagName === "BUTTON" && formElement !== requester,
|
|
137
|
-
});
|
|
138
|
-
if (!elementIsValid) {
|
|
139
|
-
elementValidationInterface.reportValidity();
|
|
140
|
-
isValid = false;
|
|
141
|
-
break;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
elementForConfirmation = formToValidate;
|
|
146
|
-
elementForDispatch = target;
|
|
147
|
-
} else {
|
|
148
|
-
// Single element validation case
|
|
149
|
-
isValid = validationInterface.checkValidity({ fromRequestAction: true });
|
|
150
|
-
if (!isValid) {
|
|
151
|
-
validationInterface.reportValidity();
|
|
152
|
-
}
|
|
153
|
-
elementForConfirmation = target;
|
|
154
|
-
elementForDispatch = target;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// If validation failed, dispatch actionprevented and return
|
|
158
|
-
if (!isValid) {
|
|
159
|
-
const actionPreventedCustomEvent = new CustomEvent("actionprevented", {
|
|
160
|
-
detail: customEventDetail,
|
|
161
|
-
});
|
|
162
|
-
elementForDispatch.dispatchEvent(actionPreventedCustomEvent);
|
|
163
|
-
return false;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Validation passed, check for confirmation
|
|
167
|
-
confirmMessage =
|
|
168
|
-
confirmMessage ||
|
|
169
|
-
elementForConfirmation.getAttribute("data-confirm-message");
|
|
170
|
-
if (confirmMessage) {
|
|
171
|
-
// eslint-disable-next-line no-alert
|
|
172
|
-
if (!window.confirm(confirmMessage)) {
|
|
173
|
-
const actionPreventedCustomEvent = new CustomEvent("actionprevented", {
|
|
174
|
-
detail: customEventDetail,
|
|
175
|
-
});
|
|
176
|
-
elementForDispatch.dispatchEvent(actionPreventedCustomEvent);
|
|
177
|
-
return false;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// All good, dispatch the action
|
|
182
|
-
const actionCustomEvent = new CustomEvent("action", {
|
|
183
|
-
detail: customEventDetail,
|
|
184
|
-
});
|
|
185
|
-
if (debug) {
|
|
186
|
-
console.debug(
|
|
187
|
-
`element is valid -> dispatch "action" on`,
|
|
188
|
-
elementForDispatch,
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
elementForDispatch.dispatchEvent(actionCustomEvent);
|
|
192
|
-
return true;
|
|
193
|
-
};
|
|
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
|
-
|
|
204
|
-
export const closeValidationMessage = (element, reason) => {
|
|
205
|
-
const validationInterface = element.__validationInterface__;
|
|
206
|
-
if (!validationInterface) {
|
|
207
|
-
return false;
|
|
208
|
-
}
|
|
209
|
-
const { validationMessage } = validationInterface;
|
|
210
|
-
if (!validationMessage) {
|
|
211
|
-
return false;
|
|
212
|
-
}
|
|
213
|
-
return validationMessage.close(reason);
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
export const checkValidity = (element) => {
|
|
217
|
-
const validationInterface = element.__validationInterface__;
|
|
218
|
-
if (!validationInterface) {
|
|
219
|
-
return false;
|
|
220
|
-
}
|
|
221
|
-
return validationInterface.checkValidity();
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
const formInstrumentedWeakSet = new WeakSet();
|
|
225
|
-
export const installCustomConstraintValidation = (
|
|
226
|
-
element,
|
|
227
|
-
elementReceivingValidationMessage = element,
|
|
228
|
-
) => {
|
|
229
|
-
if (element.tagName === "INPUT" && element.type === "hidden") {
|
|
230
|
-
elementReceivingValidationMessage = element.form || document.body;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const validationInterface = {
|
|
234
|
-
uninstall: undefined,
|
|
235
|
-
registerConstraint: undefined,
|
|
236
|
-
addCustomMessage: undefined,
|
|
237
|
-
removeCustomMessage: undefined,
|
|
238
|
-
checkValidity: undefined,
|
|
239
|
-
reportValidity: undefined,
|
|
240
|
-
validationMessage: null,
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
const [teardown, addTeardown] = createPubSub();
|
|
244
|
-
cleanup: {
|
|
245
|
-
const uninstall = () => {
|
|
246
|
-
teardown();
|
|
247
|
-
};
|
|
248
|
-
validationInterface.uninstall = uninstall;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
const isForm = element.tagName === "FORM";
|
|
252
|
-
if (isForm) {
|
|
253
|
-
formInstrumentedWeakSet.add(element);
|
|
254
|
-
addTeardown(() => {
|
|
255
|
-
formInstrumentedWeakSet.delete(element);
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
expose_as_node_property: {
|
|
260
|
-
element.__validationInterface__ = validationInterface;
|
|
261
|
-
addTeardown(() => {
|
|
262
|
-
delete element.__validationInterface__;
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
const dispatchCancelCustomEvent = (options) => {
|
|
267
|
-
const cancelEvent = new CustomEvent("cancel", options);
|
|
268
|
-
element.dispatchEvent(cancelEvent);
|
|
269
|
-
};
|
|
270
|
-
const closeElementValidationMessage = (reason) => {
|
|
271
|
-
if (validationInterface.validationMessage) {
|
|
272
|
-
validationInterface.validationMessage.close(reason);
|
|
273
|
-
return true;
|
|
274
|
-
}
|
|
275
|
-
return false;
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
const constraintSet = new Set();
|
|
279
|
-
constraintSet.add(DISABLED_CONSTRAINT);
|
|
280
|
-
constraintSet.add(REQUIRED_CONSTRAINT);
|
|
281
|
-
constraintSet.add(PATTERN_CONSTRAINT);
|
|
282
|
-
constraintSet.add(TYPE_EMAIL_CONSTRAINT);
|
|
283
|
-
constraintSet.add(TYPE_NUMBER_CONSTRAINT);
|
|
284
|
-
constraintSet.add(MIN_LENGTH_CONSTRAINT);
|
|
285
|
-
constraintSet.add(MAX_LENGTH_CONSTRAINT);
|
|
286
|
-
constraintSet.add(MIN_CONSTRAINT);
|
|
287
|
-
constraintSet.add(MAX_CONSTRAINT);
|
|
288
|
-
constraintSet.add(READONLY_CONSTRAINT);
|
|
289
|
-
constraintSet.add(SAME_AS_CONSTRAINT);
|
|
290
|
-
register_constraint: {
|
|
291
|
-
validationInterface.registerConstraint = (constraint) => {
|
|
292
|
-
if (typeof constraint === "function") {
|
|
293
|
-
constraint = {
|
|
294
|
-
name: constraint.name || "custom_function",
|
|
295
|
-
check: constraint,
|
|
296
|
-
};
|
|
297
|
-
}
|
|
298
|
-
constraintSet.add(constraint);
|
|
299
|
-
return () => {
|
|
300
|
-
constraintSet.delete(constraint);
|
|
301
|
-
};
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
let failedConstraintInfo = null;
|
|
306
|
-
const validityInfoMap = new Map();
|
|
307
|
-
|
|
308
|
-
const hasTitleAttribute = element.hasAttribute("title");
|
|
309
|
-
|
|
310
|
-
const resetValidity = ({ fromRequestAction } = {}) => {
|
|
311
|
-
if (fromRequestAction && failedConstraintInfo) {
|
|
312
|
-
for (const [key, customMessage] of customMessageMap) {
|
|
313
|
-
if (customMessage.removeOnRequestAction) {
|
|
314
|
-
customMessageMap.delete(key);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
for (const [, validityInfo] of validityInfoMap) {
|
|
320
|
-
if (validityInfo.cleanup) {
|
|
321
|
-
validityInfo.cleanup();
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
validityInfoMap.clear();
|
|
325
|
-
failedConstraintInfo = null;
|
|
326
|
-
};
|
|
327
|
-
addTeardown(resetValidity);
|
|
328
|
-
|
|
329
|
-
const checkValidity = ({ fromRequestAction, skipReadonly } = {}) => {
|
|
330
|
-
resetValidity({ fromRequestAction });
|
|
331
|
-
for (const constraint of constraintSet) {
|
|
332
|
-
const constraintCleanupSet = new Set();
|
|
333
|
-
const registerChange = (register) => {
|
|
334
|
-
const registerResult = register(() => {
|
|
335
|
-
checkValidity();
|
|
336
|
-
});
|
|
337
|
-
if (typeof registerResult === "function") {
|
|
338
|
-
constraintCleanupSet.add(registerResult);
|
|
339
|
-
}
|
|
340
|
-
};
|
|
341
|
-
const cleanup = () => {
|
|
342
|
-
for (const cleanupCallback of constraintCleanupSet) {
|
|
343
|
-
cleanupCallback();
|
|
344
|
-
}
|
|
345
|
-
constraintCleanupSet.clear();
|
|
346
|
-
};
|
|
347
|
-
|
|
348
|
-
const checkResult = constraint.check(element, {
|
|
349
|
-
fromRequestAction,
|
|
350
|
-
skipReadonly,
|
|
351
|
-
registerChange,
|
|
352
|
-
});
|
|
353
|
-
if (!checkResult) {
|
|
354
|
-
cleanup();
|
|
355
|
-
continue;
|
|
356
|
-
}
|
|
357
|
-
const constraintValidityInfo =
|
|
358
|
-
typeof checkResult === "string"
|
|
359
|
-
? { message: checkResult }
|
|
360
|
-
: checkResult;
|
|
361
|
-
|
|
362
|
-
failedConstraintInfo = {
|
|
363
|
-
name: constraint.name,
|
|
364
|
-
constraint,
|
|
365
|
-
...constraintValidityInfo,
|
|
366
|
-
cleanup,
|
|
367
|
-
reportStatus: "not_reported",
|
|
368
|
-
};
|
|
369
|
-
validityInfoMap.set(constraint, failedConstraintInfo);
|
|
370
|
-
}
|
|
371
|
-
|
|
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
|
-
}
|
|
382
|
-
closeElementValidationMessage("becomes_valid");
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
return !failedConstraintInfo;
|
|
386
|
-
};
|
|
387
|
-
const reportValidity = ({ skipFocus } = {}) => {
|
|
388
|
-
if (!failedConstraintInfo) {
|
|
389
|
-
closeElementValidationMessage("becomes_valid");
|
|
390
|
-
return;
|
|
391
|
-
}
|
|
392
|
-
if (failedConstraintInfo.silent) {
|
|
393
|
-
closeElementValidationMessage("invalid_silent");
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
if (validationInterface.validationMessage) {
|
|
397
|
-
const { message, level, closeOnClickOutside } = failedConstraintInfo;
|
|
398
|
-
validationInterface.validationMessage.update(message, {
|
|
399
|
-
level,
|
|
400
|
-
closeOnClickOutside,
|
|
401
|
-
});
|
|
402
|
-
return;
|
|
403
|
-
}
|
|
404
|
-
if (!skipFocus) {
|
|
405
|
-
element.focus();
|
|
406
|
-
}
|
|
407
|
-
const removeCloseOnCleanup = addTeardown(() => {
|
|
408
|
-
closeElementValidationMessage("cleanup");
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
const anchorElement =
|
|
412
|
-
failedConstraintInfo.target || elementReceivingValidationMessage;
|
|
413
|
-
validationInterface.validationMessage = openCallout(
|
|
414
|
-
failedConstraintInfo.message,
|
|
415
|
-
{
|
|
416
|
-
anchorElement,
|
|
417
|
-
level: failedConstraintInfo.level,
|
|
418
|
-
closeOnClickOutside: failedConstraintInfo.closeOnClickOutside,
|
|
419
|
-
onClose: () => {
|
|
420
|
-
removeCloseOnCleanup();
|
|
421
|
-
validationInterface.validationMessage = null;
|
|
422
|
-
if (failedConstraintInfo) {
|
|
423
|
-
failedConstraintInfo.reportStatus = "closed";
|
|
424
|
-
}
|
|
425
|
-
if (!skipFocus) {
|
|
426
|
-
element.focus();
|
|
427
|
-
}
|
|
428
|
-
},
|
|
429
|
-
},
|
|
430
|
-
);
|
|
431
|
-
failedConstraintInfo.reportStatus = "reported";
|
|
432
|
-
};
|
|
433
|
-
validationInterface.checkValidity = checkValidity;
|
|
434
|
-
validationInterface.reportValidity = reportValidity;
|
|
435
|
-
|
|
436
|
-
const customMessageMap = new Map();
|
|
437
|
-
custom_message: {
|
|
438
|
-
constraintSet.add({
|
|
439
|
-
name: "custom_message",
|
|
440
|
-
check: () => {
|
|
441
|
-
for (const [, { message, level }] of customMessageMap) {
|
|
442
|
-
return { message, level };
|
|
443
|
-
}
|
|
444
|
-
return null;
|
|
445
|
-
},
|
|
446
|
-
});
|
|
447
|
-
const addCustomMessage = (
|
|
448
|
-
key,
|
|
449
|
-
message,
|
|
450
|
-
{ level = "error", removeOnRequestAction = false } = {},
|
|
451
|
-
) => {
|
|
452
|
-
customMessageMap.set(key, { message, level, removeOnRequestAction });
|
|
453
|
-
checkValidity();
|
|
454
|
-
reportValidity();
|
|
455
|
-
return () => {
|
|
456
|
-
removeCustomMessage(key);
|
|
457
|
-
};
|
|
458
|
-
};
|
|
459
|
-
const removeCustomMessage = (key) => {
|
|
460
|
-
if (customMessageMap.has(key)) {
|
|
461
|
-
customMessageMap.delete(key);
|
|
462
|
-
checkValidity();
|
|
463
|
-
reportValidity();
|
|
464
|
-
}
|
|
465
|
-
};
|
|
466
|
-
addTeardown(() => {
|
|
467
|
-
customMessageMap.clear();
|
|
468
|
-
});
|
|
469
|
-
Object.assign(validationInterface, {
|
|
470
|
-
addCustomMessage,
|
|
471
|
-
removeCustomMessage,
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
checkValidity();
|
|
476
|
-
close_and_check_on_input: {
|
|
477
|
-
const oninput = () => {
|
|
478
|
-
customMessageMap.clear();
|
|
479
|
-
closeElementValidationMessage("input_event");
|
|
480
|
-
checkValidity();
|
|
481
|
-
};
|
|
482
|
-
element.addEventListener("input", oninput);
|
|
483
|
-
addTeardown(() => {
|
|
484
|
-
element.removeEventListener("input", oninput);
|
|
485
|
-
});
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
check_on_actionend: {
|
|
489
|
-
// this ensure we re-check validity (and remove message no longer relevant)
|
|
490
|
-
// once the action ends (used to remove the NOT_BUSY_CONSTRAINT message)
|
|
491
|
-
const onactionend = () => {
|
|
492
|
-
checkValidity();
|
|
493
|
-
};
|
|
494
|
-
element.addEventListener("actionend", onactionend);
|
|
495
|
-
addTeardown(() => {
|
|
496
|
-
element.removeEventListener("actionend", onactionend);
|
|
497
|
-
});
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
report_on_report_validity_call: {
|
|
501
|
-
const nativeReportValidity = element.reportValidity;
|
|
502
|
-
element.reportValidity = () => {
|
|
503
|
-
reportValidity();
|
|
504
|
-
};
|
|
505
|
-
addTeardown(() => {
|
|
506
|
-
element.reportValidity = nativeReportValidity;
|
|
507
|
-
});
|
|
508
|
-
}
|
|
509
|
-
|
|
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) {
|
|
517
|
-
return;
|
|
518
|
-
}
|
|
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,
|
|
540
|
-
});
|
|
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;
|
|
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
|
-
});
|
|
577
|
-
};
|
|
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
|
-
});
|
|
599
|
-
addTeardown(() => {
|
|
600
|
-
stop();
|
|
601
|
-
});
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
execute_on_form_submit: {
|
|
605
|
-
if (!isForm) {
|
|
606
|
-
break execute_on_form_submit;
|
|
607
|
-
}
|
|
608
|
-
// We will dispatch "action" when "submit" occurs (code called from.submit() to bypass validation)
|
|
609
|
-
const form = element;
|
|
610
|
-
const removeListener = addEventListener(form, "submit", (e) => {
|
|
611
|
-
e.preventDefault();
|
|
612
|
-
if (debug) {
|
|
613
|
-
console.debug(`"submit" called -> dispatch "action" on`, form);
|
|
614
|
-
}
|
|
615
|
-
const actionCustomEvent = new CustomEvent("action", {
|
|
616
|
-
detail: {
|
|
617
|
-
action: null,
|
|
618
|
-
event: e,
|
|
619
|
-
method: "rerun",
|
|
620
|
-
requester: form,
|
|
621
|
-
meta: {
|
|
622
|
-
isSubmit: true,
|
|
623
|
-
},
|
|
624
|
-
},
|
|
625
|
-
});
|
|
626
|
-
form.dispatchEvent(actionCustomEvent);
|
|
627
|
-
});
|
|
628
|
-
addTeardown(() => {
|
|
629
|
-
removeListener();
|
|
630
|
-
});
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
close_on_escape: {
|
|
634
|
-
const onkeydown = (e) => {
|
|
635
|
-
if (e.key === "Escape") {
|
|
636
|
-
if (!closeElementValidationMessage("escape_key")) {
|
|
637
|
-
dispatchCancelCustomEvent({ detail: { reason: "escape_key" } });
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
};
|
|
641
|
-
element.addEventListener("keydown", onkeydown);
|
|
642
|
-
addTeardown(() => {
|
|
643
|
-
element.removeEventListener("keydown", onkeydown);
|
|
644
|
-
});
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
cancel_on_blur: {
|
|
648
|
-
const onblur = () => {
|
|
649
|
-
if (element.value === "") {
|
|
650
|
-
dispatchCancelCustomEvent({
|
|
651
|
-
detail: {
|
|
652
|
-
reason: "blur_empty",
|
|
653
|
-
},
|
|
654
|
-
});
|
|
655
|
-
return;
|
|
656
|
-
}
|
|
657
|
-
// if we have failed constraint, we cancel too
|
|
658
|
-
if (failedConstraintInfo) {
|
|
659
|
-
dispatchCancelCustomEvent({
|
|
660
|
-
detail: {
|
|
661
|
-
reason: "blur_invalid",
|
|
662
|
-
failedConstraintInfo,
|
|
663
|
-
},
|
|
664
|
-
});
|
|
665
|
-
return;
|
|
666
|
-
}
|
|
667
|
-
};
|
|
668
|
-
element.addEventListener("blur", onblur);
|
|
669
|
-
addTeardown(() => {
|
|
670
|
-
element.removeEventListener("blur", onblur);
|
|
671
|
-
});
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
return validationInterface;
|
|
675
|
-
};
|
|
676
|
-
|
|
677
|
-
const wouldClickSubmitForm = (clickEvent) => {
|
|
678
|
-
if (clickEvent.defaultPrevented) {
|
|
679
|
-
return false;
|
|
680
|
-
}
|
|
681
|
-
const clickTarget = clickEvent.target;
|
|
682
|
-
const { form } = clickTarget;
|
|
683
|
-
if (!form) {
|
|
684
|
-
return false;
|
|
685
|
-
}
|
|
686
|
-
const button = clickTarget.closest("button");
|
|
687
|
-
if (!button) {
|
|
688
|
-
return false;
|
|
689
|
-
}
|
|
690
|
-
const wouldSubmitFormByType =
|
|
691
|
-
button.type === "submit" || button.type === "image";
|
|
692
|
-
if (wouldSubmitFormByType) {
|
|
693
|
-
return true;
|
|
694
|
-
}
|
|
695
|
-
if (button.type) {
|
|
696
|
-
return false;
|
|
697
|
-
}
|
|
698
|
-
if (getFirstButtonSubmittingForm(form)) {
|
|
699
|
-
// an other button is explicitly submitting the form, this one would not submit it
|
|
700
|
-
return false;
|
|
701
|
-
}
|
|
702
|
-
// this is the only button inside the form without type attribute, so it defaults to type="submit"
|
|
703
|
-
return true;
|
|
704
|
-
};
|
|
705
|
-
const getFirstButtonSubmittingForm = (form) => {
|
|
706
|
-
return form.querySelector(
|
|
707
|
-
`button[type="submit"], input[type="submit"], input[type="image"]`,
|
|
708
|
-
);
|
|
709
|
-
};
|
|
710
|
-
|
|
711
|
-
const wouldKeydownSubmitForm = (keydownEvent) => {
|
|
712
|
-
if (keydownEvent.defaultPrevented) {
|
|
713
|
-
return false;
|
|
714
|
-
}
|
|
715
|
-
const keydownTarget = keydownEvent.target;
|
|
716
|
-
const { form } = keydownTarget;
|
|
717
|
-
if (!form) {
|
|
718
|
-
return false;
|
|
719
|
-
}
|
|
720
|
-
if (keydownEvent.key !== "Enter") {
|
|
721
|
-
return false;
|
|
722
|
-
}
|
|
723
|
-
const isTextInput =
|
|
724
|
-
keydownTarget.tagName === "INPUT" || keydownTarget.tagName === "TEXTAREA";
|
|
725
|
-
if (!isTextInput) {
|
|
726
|
-
return false;
|
|
727
|
-
}
|
|
728
|
-
return true;
|
|
729
|
-
};
|
|
730
|
-
|
|
731
|
-
const dispatchActionRequestedCustomEvent = (
|
|
732
|
-
fieldOrForm,
|
|
733
|
-
{ actionOrigin = "action_prop", event, requester },
|
|
734
|
-
) => {
|
|
735
|
-
const actionRequestedCustomEvent = new CustomEvent("actionrequested", {
|
|
736
|
-
cancelable: true,
|
|
737
|
-
detail: {
|
|
738
|
-
actionOrigin,
|
|
739
|
-
event,
|
|
740
|
-
requester,
|
|
741
|
-
},
|
|
742
|
-
});
|
|
743
|
-
fieldOrForm.dispatchEvent(actionRequestedCustomEvent);
|
|
744
|
-
};
|
|
745
|
-
// https://developer.mozilla.org/en-US/docs/Web/HTML/Guides/Constraint_validation
|
|
746
|
-
const requestSubmit = HTMLFormElement.prototype.requestSubmit;
|
|
747
|
-
HTMLFormElement.prototype.requestSubmit = function (submitter) {
|
|
748
|
-
const form = this;
|
|
749
|
-
const isInstrumented = formInstrumentedWeakSet.has(form);
|
|
750
|
-
if (!isInstrumented) {
|
|
751
|
-
requestSubmit.call(form, submitter);
|
|
752
|
-
return;
|
|
753
|
-
}
|
|
754
|
-
const programmaticEvent = new CustomEvent("programmatic_requestsubmit", {
|
|
755
|
-
cancelable: true,
|
|
756
|
-
detail: {
|
|
757
|
-
submitter,
|
|
758
|
-
},
|
|
759
|
-
});
|
|
760
|
-
dispatchActionRequestedCustomEvent(form, {
|
|
761
|
-
event: programmaticEvent,
|
|
762
|
-
requester: submitter,
|
|
763
|
-
});
|
|
764
|
-
|
|
765
|
-
// When all fields are valid calling the native requestSubmit would let browser go through the
|
|
766
|
-
// standard form validation steps leading to form submission.
|
|
767
|
-
// We don't want that because we have our own action system to handle forms
|
|
768
|
-
// If we did that the form submission would happen in parallel of our action system
|
|
769
|
-
// and because we listen to "submit" event to dispatch "action" event
|
|
770
|
-
// we would end up with two actions being executed.
|
|
771
|
-
//
|
|
772
|
-
// In case we have discrepencies in our implementation compared to the browser standard
|
|
773
|
-
// this also prevent the native validation message to show up.
|
|
774
|
-
|
|
775
|
-
// requestSubmit.call(this, submitter);
|
|
776
|
-
};
|
|
777
|
-
|
|
778
|
-
// const submit = HTMLFormElement.prototype.submit;
|
|
779
|
-
// HTMLFormElement.prototype.submit = function (...args) {
|
|
780
|
-
// const form = this;
|
|
781
|
-
// if (form.hasAttribute("data-method")) {
|
|
782
|
-
// console.warn("You must use form.requestSubmit() instead of form.submit()");
|
|
783
|
-
// return form.requestSubmit();
|
|
784
|
-
// }
|
|
785
|
-
// return submit.apply(this, args);
|
|
786
|
-
// };
|
|
787
|
-
|
|
788
|
-
const addEventListener = (element, event, callback) => {
|
|
789
|
-
element.addEventListener(event, callback);
|
|
790
|
-
return () => {
|
|
791
|
-
element.removeEventListener(event, callback);
|
|
792
|
-
};
|
|
793
|
-
};
|