@jsenv/navi 0.0.1 → 0.1.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 +22954 -0
- package/index.js +66 -16
- package/package.json +22 -11
- package/src/actions.js +50 -26
- package/src/browser_integration/browser_integration.js +31 -6
- package/src/browser_integration/via_history.js +42 -9
- package/src/components/action_execution/render_actionable_component.jsx +6 -4
- package/src/components/action_execution/use_action.js +51 -282
- package/src/components/action_execution/use_execute_action.js +106 -92
- package/src/components/action_execution/use_run_on_mount.js +9 -0
- package/src/components/action_renderer.jsx +21 -32
- package/src/components/demos/0_button_demo.html +574 -103
- package/src/components/demos/10_column_reordering_debug.html +277 -0
- package/src/components/demos/11_table_selection_debug.html +432 -0
- package/src/components/demos/1_checkbox_demo.html +579 -202
- package/src/components/demos/2_input_textual_demo.html +81 -138
- package/src/components/demos/3_radio_demo.html +0 -2
- package/src/components/demos/4_select_demo.html +19 -23
- package/src/components/demos/6_tablist_demo.html +77 -0
- package/src/components/demos/7_table_selection_demo.html +176 -0
- package/src/components/demos/8_table_fixed_headers_demo.html +584 -0
- package/src/components/demos/9_table_column_drag_demo.html +325 -0
- package/src/components/demos/action/0_button_demo.html +2 -4
- package/src/components/demos/action/1_input_text_demo.html +643 -222
- package/src/components/demos/action/3_details_demo.html +146 -115
- package/src/components/demos/action/4_input_checkbox_demo.html +442 -322
- package/src/components/demos/action/5_input_checkbox_state_demo.html +270 -0
- package/src/components/demos/action/6_checkbox_list_demo.html +304 -72
- package/src/components/demos/action/7_radio_list_demo.html +310 -170
- package/src/components/demos/action/{8_editable_text_demo.html → 8_editable_demo.html} +65 -76
- package/src/components/demos/action/9_link_demo.html +84 -62
- package/src/components/demos/ui_transition/0_action_renderer_ui_transition_demo.html +695 -0
- package/src/components/demos/ui_transition/1_nested_ui_transition_demo.html +429 -0
- package/src/components/demos/ui_transition/2_height_transition_test.html +295 -0
- package/src/components/details/details.jsx +62 -64
- package/src/components/edition/editable.jsx +186 -0
- package/src/components/field/README.md +247 -0
- package/src/components/{input → field}/button.jsx +151 -130
- package/src/components/field/checkbox_list.jsx +184 -0
- package/src/components/{collect_form_element_values.js → field/collect_form_element_values.js} +7 -4
- package/src/components/{input → field}/field_css.js +4 -1
- package/src/components/field/form.jsx +211 -0
- package/src/components/{input → field}/input.jsx +1 -0
- package/src/components/{input → field}/input_checkbox.jsx +132 -155
- package/src/components/{input → field}/input_radio.jsx +135 -46
- package/src/components/{input → field}/input_textual.jsx +247 -173
- package/src/components/field/label.jsx +32 -0
- package/src/components/field/radio_list.jsx +182 -0
- package/src/components/{input → field}/select.jsx +17 -32
- package/src/components/field/use_action_events.js +132 -0
- package/src/components/field/use_form_events.js +55 -0
- package/src/components/field/use_ui_state_controller.js +506 -0
- package/src/components/item_tracker/README.md +461 -0
- package/src/components/item_tracker/use_isolated_item_tracker.jsx +209 -0
- package/src/components/item_tracker/use_isolated_item_tracker_demo.html +148 -0
- package/src/components/item_tracker/use_isolated_item_tracker_demo.jsx +460 -0
- package/src/components/item_tracker/use_item_tracker.jsx +143 -0
- package/src/components/item_tracker/use_item_tracker_demo.html +207 -0
- package/src/components/item_tracker/use_item_tracker_demo.jsx +216 -0
- package/src/components/keyboard_shortcuts/active_keyboard_shortcuts.jsx +87 -0
- package/src/components/keyboard_shortcuts/aria_key_shortcuts.js +61 -0
- package/src/components/keyboard_shortcuts/keyboard_key_meta.js +17 -0
- package/src/components/keyboard_shortcuts/keyboard_shortcuts.js +371 -0
- package/src/components/link/link.jsx +65 -102
- package/src/components/link/link_with_icon.jsx +52 -0
- package/src/components/loader/loader_background.jsx +85 -64
- package/src/components/loader/rectangle_loading.jsx +38 -19
- package/src/components/route.jsx +8 -4
- package/src/components/selection/selection.jsx +1583 -0
- package/src/components/svg/font_sized_svg.jsx +45 -0
- package/src/components/svg/icon_and_text.jsx +21 -0
- package/src/components/svg/svg_mask_overlay.jsx +105 -0
- package/src/components/table/drag/table_drag.jsx +506 -0
- package/src/components/table/resize/table_resize.jsx +650 -0
- package/src/components/table/resize/table_size.js +43 -0
- package/src/components/table/selection/table_selection.js +106 -0
- package/src/components/table/selection/table_selection.jsx +203 -0
- package/src/components/table/sticky/sticky_group.js +354 -0
- package/src/components/table/sticky/table_sticky.js +25 -0
- package/src/components/table/sticky/table_sticky.jsx +501 -0
- package/src/components/table/table.jsx +721 -0
- package/src/components/table/table_css.js +211 -0
- package/src/components/table/table_ui.jsx +49 -0
- package/src/components/table/use_cells_and_columns.js +90 -0
- package/src/components/table/use_object_array_to_cells.js +46 -0
- package/src/components/table/z_indexes.js +23 -0
- package/src/components/tablist/tablist.jsx +99 -0
- package/src/components/text/overflow.jsx +15 -0
- package/src/components/text/text_and_count.jsx +28 -0
- package/src/components/ui_transition.jsx +128 -0
- package/src/components/use_auto_focus.js +58 -7
- package/src/components/use_batch_during_render.js +33 -0
- package/src/components/use_debounce_true.js +7 -7
- package/src/components/use_dependencies_diff.js +35 -0
- package/src/components/use_focus_group.js +4 -3
- package/src/components/use_initial_value.js +8 -34
- package/src/components/use_signal_sync.js +1 -1
- package/src/components/use_stable_callback.js +68 -0
- package/src/components/use_state_array.js +16 -9
- package/src/docs/actions.md +22 -0
- package/src/notes.md +33 -12
- package/src/route/route.js +97 -47
- package/src/store/resource_graph.js +2 -1
- package/src/store/tests/{resource_graph_dependencies.test.js → resource_graph_dependencies.test_manual.js} +13 -13
- package/src/utils/is_signal.js +20 -0
- package/src/utils/stringify_for_display.js +4 -23
- package/src/validation/constraints/confirm_constraint.js +14 -0
- package/src/validation/constraints/create_unique_value_constraint.js +27 -0
- package/src/validation/constraints/native_constraints.js +313 -0
- package/src/validation/constraints/readonly_constraint.js +36 -0
- package/src/validation/constraints/single_space_constraint.js +13 -0
- package/src/validation/custom_constraint_validation.js +599 -0
- package/src/validation/custom_message.js +18 -0
- package/src/validation/demos/browser_style.png +0 -0
- package/src/validation/demos/form_validation_demo.html +142 -0
- package/src/validation/demos/form_validation_demo_preact.html +87 -0
- package/src/validation/demos/form_validation_native_popover_demo.html +168 -0
- package/src/validation/demos/form_validation_vs_native_demo.html +172 -0
- package/src/validation/demos/validation_message_demo.html +203 -0
- package/src/validation/hooks/use_constraints.js +23 -0
- package/src/validation/hooks/use_custom_validation_ref.js +73 -0
- package/src/validation/hooks/use_validation_message.js +19 -0
- package/src/validation/validation_message.js +741 -0
- package/src/components/editable_text/editable_text.jsx +0 -96
- package/src/components/form.jsx +0 -144
- package/src/components/input/checkbox_list.jsx +0 -294
- package/src/components/input/field.jsx +0 -61
- package/src/components/input/radio_list.jsx +0 -283
- package/src/components/input/use_form_event.js +0 -20
- package/src/components/input/use_on_change.js +0 -12
- package/src/components/selection/selection.js +0 -5
- package/src/components/selection/selection_context.jsx +0 -262
- package/src/components/shortcut/shortcut_context.jsx +0 -390
- package/src/components/use_action_events.js +0 -37
- package/src/utils/iterable_weak_set.js +0 -62
- /package/src/components/demos/action/{11_nested_shortcuts_demo.html → 11_nested_shortcuts_demo.xhtml} +0 -0
- /package/src/components/{shortcut → keyboard_shortcuts}/os.js +0 -0
- /package/src/route/{route.test.html → route.xtest.html} +0 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* https://developer.mozilla.org/en-US/docs/Web/HTML/Guides/Constraint_validation
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// this constraint is not really a native constraint and browser just not let this happen at all
|
|
6
|
+
// in our case it's just here in case some code is wrongly calling "requestAction" or "checkValidity" on a disabled element
|
|
7
|
+
export const DISABLED_CONSTRAINT = {
|
|
8
|
+
name: "disabled",
|
|
9
|
+
check: (element) => {
|
|
10
|
+
if (element.disabled) {
|
|
11
|
+
return `Ce champ est désactivé.`;
|
|
12
|
+
}
|
|
13
|
+
return null;
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const REQUIRED_CONSTRAINT = {
|
|
18
|
+
name: "required",
|
|
19
|
+
check: (element, { registerChange }) => {
|
|
20
|
+
if (!element.required) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const requiredMessage = element.getAttribute("data-required-message");
|
|
24
|
+
|
|
25
|
+
if (element.type === "checkbox") {
|
|
26
|
+
if (!element.checked) {
|
|
27
|
+
return requiredMessage || `Veuillez cocher cette case.`;
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
if (element.type === "radio") {
|
|
32
|
+
// For radio buttons, check if any radio with the same name is selected
|
|
33
|
+
const name = element.name;
|
|
34
|
+
if (!name) {
|
|
35
|
+
// If no name, check just this radio
|
|
36
|
+
if (!element.checked) {
|
|
37
|
+
return requiredMessage || `Veuillez sélectionner une option.`;
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const closestFieldset = element.closest("fieldset");
|
|
43
|
+
const fieldsetRequiredMessage = closestFieldset
|
|
44
|
+
? closestFieldset.getAttribute("data-required-message")
|
|
45
|
+
: null;
|
|
46
|
+
|
|
47
|
+
// Find the container (form or closest fieldset)
|
|
48
|
+
const container = element.form || closestFieldset || document;
|
|
49
|
+
// Check if any radio with the same name is checked
|
|
50
|
+
const radioSelector = `input[type="radio"][name="${CSS.escape(name)}"]`;
|
|
51
|
+
const radiosWithSameName = container.querySelectorAll(radioSelector);
|
|
52
|
+
for (const radio of radiosWithSameName) {
|
|
53
|
+
if (radio.checked) {
|
|
54
|
+
return null; // At least one radio is selected
|
|
55
|
+
}
|
|
56
|
+
registerChange((onChange) => {
|
|
57
|
+
radio.addEventListener("change", onChange);
|
|
58
|
+
return () => {
|
|
59
|
+
radio.removeEventListener("change", onChange);
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
message:
|
|
66
|
+
requiredMessage ||
|
|
67
|
+
fieldsetRequiredMessage ||
|
|
68
|
+
`Veuillez sélectionner une option.`,
|
|
69
|
+
target: closestFieldset
|
|
70
|
+
? closestFieldset.querySelector("legend")
|
|
71
|
+
: undefined,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (!element.value) {
|
|
75
|
+
return requiredMessage || `Veuillez remplir ce champ.`;
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
export const PATTERN_CONSTRAINT = {
|
|
81
|
+
name: "pattern",
|
|
82
|
+
check: (input) => {
|
|
83
|
+
const pattern = input.pattern;
|
|
84
|
+
if (!pattern) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
const value = input.value;
|
|
88
|
+
if (!value) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
const regex = new RegExp(pattern);
|
|
92
|
+
if (!regex.test(value)) {
|
|
93
|
+
const patternMessage = input.getAttribute("data-pattern-message");
|
|
94
|
+
if (patternMessage) {
|
|
95
|
+
return patternMessage;
|
|
96
|
+
}
|
|
97
|
+
let message = `Veuillez respecter le format requis.`;
|
|
98
|
+
const title = input.title;
|
|
99
|
+
if (title) {
|
|
100
|
+
message += `<br />${title}`;
|
|
101
|
+
}
|
|
102
|
+
return message;
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input/email#validation
|
|
108
|
+
const emailregex =
|
|
109
|
+
/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
|
110
|
+
export const TYPE_EMAIL_CONSTRAINT = {
|
|
111
|
+
name: "type_email",
|
|
112
|
+
check: (input) => {
|
|
113
|
+
if (input.type !== "email") {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
const value = input.value;
|
|
117
|
+
if (!value) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
if (!value.includes("@")) {
|
|
121
|
+
return `Veuillez inclure "@" dans l'adresse e-mail. Il manque un symbole "@" dans ${value}.`;
|
|
122
|
+
}
|
|
123
|
+
if (!emailregex.test(value)) {
|
|
124
|
+
return `Veuillez saisir une adresse e-mail valide.`;
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export const MIN_LENGTH_CONSTRAINT = {
|
|
131
|
+
name: "min_length",
|
|
132
|
+
check: (element) => {
|
|
133
|
+
if (element.tagName === "INPUT") {
|
|
134
|
+
if (!INPUT_TYPE_SUPPORTING_MIN_LENGTH_SET.has(element.type)) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
} else if (element.tagName !== "TEXTAREA") {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const minLength = element.minLength;
|
|
142
|
+
if (minLength === -1) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const value = element.value;
|
|
147
|
+
const valueLength = value.length;
|
|
148
|
+
if (valueLength === 0) {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
if (valueLength < minLength) {
|
|
152
|
+
if (valueLength === 1) {
|
|
153
|
+
return `Ce champ doit contenir au moins ${minLength} caractère (il contient actuellement un seul caractère).`;
|
|
154
|
+
}
|
|
155
|
+
return `Ce champ doit contenir au moins ${minLength} caractères (il contient actuellement ${valueLength} caractères).`;
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
const INPUT_TYPE_SUPPORTING_MIN_LENGTH_SET = new Set([
|
|
161
|
+
"text",
|
|
162
|
+
"search",
|
|
163
|
+
"url",
|
|
164
|
+
"tel",
|
|
165
|
+
"email",
|
|
166
|
+
"password",
|
|
167
|
+
]);
|
|
168
|
+
|
|
169
|
+
export const MAX_LENGTH_CONSTRAINT = {
|
|
170
|
+
name: "max_length",
|
|
171
|
+
check: (element) => {
|
|
172
|
+
if (element.tagName === "INPUT") {
|
|
173
|
+
if (!INPUT_TYPE_SUPPORTING_MAX_LENGTH_SET.has(element.type)) {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
} else if (element.tagName !== "TEXTAREA") {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const maxLength = element.maxLength;
|
|
181
|
+
if (maxLength === -1) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const value = element.value;
|
|
186
|
+
const valueLength = value.length;
|
|
187
|
+
if (valueLength > maxLength) {
|
|
188
|
+
return `Ce champ doit contenir au maximum ${maxLength} caractères (il contient actuellement ${valueLength} caractères).`;
|
|
189
|
+
}
|
|
190
|
+
return null;
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
const INPUT_TYPE_SUPPORTING_MAX_LENGTH_SET = new Set(
|
|
194
|
+
INPUT_TYPE_SUPPORTING_MIN_LENGTH_SET,
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
export const TYPE_NUMBER_CONSTRAINT = {
|
|
198
|
+
name: "type_number",
|
|
199
|
+
check: (element) => {
|
|
200
|
+
if (element.tagName !== "INPUT") {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
if (element.type !== "number") {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
if (element.value === "") {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
const value = element.valueAsNumber;
|
|
210
|
+
if (isNaN(value)) {
|
|
211
|
+
return `Doit être un nombre.`;
|
|
212
|
+
}
|
|
213
|
+
return null;
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
export const MIN_CONSTRAINT = {
|
|
218
|
+
name: "min",
|
|
219
|
+
check: (element) => {
|
|
220
|
+
if (element.tagName !== "INPUT") {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
if (element.type === "number") {
|
|
224
|
+
const minString = element.min;
|
|
225
|
+
if (minString === "") {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
const minNumber = parseFloat(minString);
|
|
229
|
+
if (isNaN(minNumber)) {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
const valueAsNumber = element.valueAsNumber;
|
|
233
|
+
if (isNaN(valueAsNumber)) {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
if (valueAsNumber < minNumber) {
|
|
237
|
+
const minMessage = element.getAttribute("data-min-message");
|
|
238
|
+
return (
|
|
239
|
+
minMessage ||
|
|
240
|
+
`Doit être supérieur ou égal à <strong>${minString}</strong>.`
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
if (element.type === "time") {
|
|
246
|
+
const min = element.min;
|
|
247
|
+
if (min === undefined) {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
const [minHours, minMinutes] = min.split(":").map(Number);
|
|
251
|
+
const value = element.value;
|
|
252
|
+
const [hours, minutes] = value.split(":").map(Number);
|
|
253
|
+
if (hours < minHours) {
|
|
254
|
+
return `Doit être <strong>${min}</strong> ou plus.`;
|
|
255
|
+
}
|
|
256
|
+
if (hours === minHours && minMinutes < minutes) {
|
|
257
|
+
return `Doit être <strong>${min}</strong> ou plus.`;
|
|
258
|
+
}
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
// "range"
|
|
262
|
+
// - user interface do not let user enter anything outside the boundaries
|
|
263
|
+
// - when setting value via js browser enforce boundaries too
|
|
264
|
+
// "date", "month", "week", "datetime-local"
|
|
265
|
+
// - same as "range"
|
|
266
|
+
return null;
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
export const MAX_CONSTRAINT = {
|
|
271
|
+
name: "max",
|
|
272
|
+
check: (element) => {
|
|
273
|
+
if (element.tagName !== "INPUT") {
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
if (element.type === "number") {
|
|
277
|
+
const maxString = element.max;
|
|
278
|
+
if (maxString === "") {
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
const maxNumber = parseFloat(maxString);
|
|
282
|
+
if (isNaN(maxNumber)) {
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
const valueAsNumber = element.valueAsNumber;
|
|
286
|
+
if (isNaN(valueAsNumber)) {
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
if (valueAsNumber > maxNumber) {
|
|
290
|
+
const maxMessage = element.getAttribute("data-max-message");
|
|
291
|
+
return maxMessage || `Doit être <strong>${maxString}</strong> ou plus.`;
|
|
292
|
+
}
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
if (element.type === "time") {
|
|
296
|
+
const max = element.min;
|
|
297
|
+
if (max === undefined) {
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
const [maxHours, maxMinutes] = max.split(":").map(Number);
|
|
301
|
+
const value = element.value;
|
|
302
|
+
const [hours, minutes] = value.split(":").map(Number);
|
|
303
|
+
if (hours > maxHours) {
|
|
304
|
+
return `Doit être <strong>${max}</strong> ou moins.`;
|
|
305
|
+
}
|
|
306
|
+
if (hours === maxHours && maxMinutes > minutes) {
|
|
307
|
+
return `Doit être <strong>${max}</strong> ou moins.`;
|
|
308
|
+
}
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
return null;
|
|
312
|
+
},
|
|
313
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export const READONLY_CONSTRAINT = {
|
|
2
|
+
name: "readonly",
|
|
3
|
+
check: (element, { skipReadonly }) => {
|
|
4
|
+
if (skipReadonly) {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
if (!element.readonly && !element.hasAttribute("data-readonly")) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
const readonlySilent = element.hasAttribute("data-readonly-silent");
|
|
11
|
+
if (readonlySilent) {
|
|
12
|
+
return { silent: true };
|
|
13
|
+
}
|
|
14
|
+
const readonlyMessage = element.getAttribute("data-readonly-message");
|
|
15
|
+
if (readonlyMessage) {
|
|
16
|
+
return {
|
|
17
|
+
message: readonlyMessage,
|
|
18
|
+
level: "info",
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const isBusy = element.getAttribute("aria-busy") === "true";
|
|
22
|
+
if (isBusy) {
|
|
23
|
+
return {
|
|
24
|
+
message: `Cette action est en cours. Veuillez patienter.`,
|
|
25
|
+
level: "info",
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
message:
|
|
30
|
+
element.tagName === "BUTTON"
|
|
31
|
+
? `Cet action n'est pas disponible pour l'instant.`
|
|
32
|
+
: `Cet élément est en lecture seule et ne peut pas être modifié.`,
|
|
33
|
+
level: "info",
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const SINGLE_SPACE_CONSTRAINT = {
|
|
2
|
+
name: "single_space",
|
|
3
|
+
check: (input) => {
|
|
4
|
+
const inputValue = input.value;
|
|
5
|
+
const hasLeadingSpace = inputValue.startsWith(" ");
|
|
6
|
+
const hasTrailingSpace = inputValue.endsWith(" ");
|
|
7
|
+
const hasDoubleSpace = inputValue.includes(" ");
|
|
8
|
+
if (hasLeadingSpace || hasDoubleSpace || hasTrailingSpace) {
|
|
9
|
+
return "Spaces at the beginning, end, or consecutive spaces are not allowed";
|
|
10
|
+
}
|
|
11
|
+
return "";
|
|
12
|
+
},
|
|
13
|
+
};
|