@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,53 +0,0 @@
|
|
|
1
|
-
export const MERGE_AS_PRIMITIVE_SYMBOL = Symbol("navi_merge_as_primitive");
|
|
2
|
-
|
|
3
|
-
export const mergeTwoJsValues = (firstValue, secondValue) => {
|
|
4
|
-
const firstIsPrimitive =
|
|
5
|
-
firstValue === null ||
|
|
6
|
-
typeof firstValue !== "object" ||
|
|
7
|
-
MERGE_AS_PRIMITIVE_SYMBOL in firstValue;
|
|
8
|
-
|
|
9
|
-
if (firstIsPrimitive) {
|
|
10
|
-
return secondValue;
|
|
11
|
-
}
|
|
12
|
-
const secondIsPrimitive =
|
|
13
|
-
secondValue === null ||
|
|
14
|
-
typeof secondValue !== "object" ||
|
|
15
|
-
MERGE_AS_PRIMITIVE_SYMBOL in secondValue;
|
|
16
|
-
if (secondIsPrimitive) {
|
|
17
|
-
return secondValue;
|
|
18
|
-
}
|
|
19
|
-
const objectMerge = {};
|
|
20
|
-
const firstKeys = Object.keys(firstValue);
|
|
21
|
-
const secondKeys = Object.keys(secondValue);
|
|
22
|
-
let hasChanged = false;
|
|
23
|
-
|
|
24
|
-
// First loop: check for keys in first object and recursively merge with second
|
|
25
|
-
for (const key of firstKeys) {
|
|
26
|
-
const firstValueForKey = firstValue[key];
|
|
27
|
-
const secondHasKey = secondKeys.includes(key);
|
|
28
|
-
|
|
29
|
-
if (secondHasKey) {
|
|
30
|
-
const secondValueForKey = secondValue[key];
|
|
31
|
-
const mergedValue = mergeTwoJsValues(firstValueForKey, secondValueForKey);
|
|
32
|
-
objectMerge[key] = mergedValue;
|
|
33
|
-
if (mergedValue !== firstValueForKey) {
|
|
34
|
-
hasChanged = true;
|
|
35
|
-
}
|
|
36
|
-
} else {
|
|
37
|
-
objectMerge[key] = firstValueForKey;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
for (const key of secondKeys) {
|
|
42
|
-
if (firstKeys.includes(key)) {
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
objectMerge[key] = secondValue[key];
|
|
46
|
-
hasChanged = true;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (!hasChanged) {
|
|
50
|
-
return firstValue;
|
|
51
|
-
}
|
|
52
|
-
return objectMerge;
|
|
53
|
-
};
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import { getSignalType } from "./is_signal.js";
|
|
2
|
-
|
|
3
|
-
const MAX_ENTRIES = 5;
|
|
4
|
-
|
|
5
|
-
export const stringifyForDisplay = (
|
|
6
|
-
value,
|
|
7
|
-
maxDepth = 2,
|
|
8
|
-
currentDepth = 0,
|
|
9
|
-
options = {},
|
|
10
|
-
) => {
|
|
11
|
-
const { asFunctionArgs = false } = options;
|
|
12
|
-
const indent = " ".repeat(currentDepth);
|
|
13
|
-
const nextIndent = " ".repeat(currentDepth + 1);
|
|
14
|
-
|
|
15
|
-
if (currentDepth >= maxDepth) {
|
|
16
|
-
return typeof value === "object" && value !== null
|
|
17
|
-
? "[Object]"
|
|
18
|
-
: String(value);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (value === null) {
|
|
22
|
-
return "null";
|
|
23
|
-
}
|
|
24
|
-
if (value === undefined) {
|
|
25
|
-
return "undefined";
|
|
26
|
-
}
|
|
27
|
-
if (typeof value === "string") {
|
|
28
|
-
return `"${value}"`;
|
|
29
|
-
}
|
|
30
|
-
if (typeof value === "number" || typeof value === "boolean") {
|
|
31
|
-
return String(value);
|
|
32
|
-
}
|
|
33
|
-
if (typeof value === "function") {
|
|
34
|
-
return `[Function ${value.name || "anonymous"}]`;
|
|
35
|
-
}
|
|
36
|
-
if (value instanceof Date) {
|
|
37
|
-
return `Date(${value.toISOString()})`;
|
|
38
|
-
}
|
|
39
|
-
if (value instanceof RegExp) {
|
|
40
|
-
return value.toString();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (Array.isArray(value)) {
|
|
44
|
-
const openBracket = asFunctionArgs ? "(" : "[";
|
|
45
|
-
const closeBracket = asFunctionArgs ? ")" : "]";
|
|
46
|
-
|
|
47
|
-
if (value.length === 0) return `${openBracket}${closeBracket}`;
|
|
48
|
-
|
|
49
|
-
// Display arrays with only one element on a single line
|
|
50
|
-
if (value.length === 1) {
|
|
51
|
-
const item = stringifyForDisplay(
|
|
52
|
-
value[0],
|
|
53
|
-
maxDepth,
|
|
54
|
-
currentDepth + 1,
|
|
55
|
-
// Remove asFunctionArgs for nested calls
|
|
56
|
-
{ ...options, asFunctionArgs: false },
|
|
57
|
-
);
|
|
58
|
-
return `${openBracket}${item}${closeBracket}`;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (value.length > MAX_ENTRIES) {
|
|
62
|
-
const preview = value
|
|
63
|
-
.slice(0, MAX_ENTRIES)
|
|
64
|
-
.map(
|
|
65
|
-
(v) =>
|
|
66
|
-
`${nextIndent}${stringifyForDisplay(v, maxDepth, currentDepth + 1, { ...options, asFunctionArgs: false })}`,
|
|
67
|
-
);
|
|
68
|
-
return `${openBracket}\n${preview.join(",\n")},\n${nextIndent}...${value.length - MAX_ENTRIES} more\n${indent}${closeBracket}`;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const items = value.map(
|
|
72
|
-
(v) =>
|
|
73
|
-
`${nextIndent}${stringifyForDisplay(v, maxDepth, currentDepth + 1, { ...options, asFunctionArgs: false })}`,
|
|
74
|
-
);
|
|
75
|
-
return `${openBracket}\n${items.join(",\n")}\n${indent}${closeBracket}`;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (typeof value === "object") {
|
|
79
|
-
const signalType = getSignalType(value);
|
|
80
|
-
if (signalType) {
|
|
81
|
-
const signalValue = value.peek();
|
|
82
|
-
const prefix = signalType === "computed" ? "computed" : "signal";
|
|
83
|
-
return `${prefix}(${stringifyForDisplay(signalValue, maxDepth, currentDepth, { ...options, asFunctionArgs: false })})`;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const entries = Object.entries(value);
|
|
87
|
-
if (entries.length === 0) return "{}";
|
|
88
|
-
|
|
89
|
-
// ✅ Inclure les clés avec valeurs undefined/null
|
|
90
|
-
const allEntries = [];
|
|
91
|
-
for (const [key, val] of entries) {
|
|
92
|
-
allEntries.push([key, val]);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Ajouter les clés avec undefined (que Object.entries omet)
|
|
96
|
-
const descriptor = Object.getOwnPropertyDescriptors(value);
|
|
97
|
-
for (const [key, desc] of Object.entries(descriptor)) {
|
|
98
|
-
if (desc.value === undefined && !entries.some(([k]) => k === key)) {
|
|
99
|
-
allEntries.push([key, undefined]);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Display objects with only one key on a single line
|
|
104
|
-
if (allEntries.length === 1) {
|
|
105
|
-
const [key, val] = allEntries[0];
|
|
106
|
-
const valueStr = stringifyForDisplay(val, maxDepth, currentDepth + 1, {
|
|
107
|
-
...options,
|
|
108
|
-
asFunctionArgs: false,
|
|
109
|
-
});
|
|
110
|
-
return `{ ${key}: ${valueStr} }`;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (allEntries.length > MAX_ENTRIES) {
|
|
114
|
-
const preview = allEntries
|
|
115
|
-
.slice(0, MAX_ENTRIES)
|
|
116
|
-
.map(
|
|
117
|
-
([k, v]) =>
|
|
118
|
-
`${nextIndent}${k}: ${stringifyForDisplay(v, maxDepth, currentDepth + 1, { ...options, asFunctionArgs: false })}`,
|
|
119
|
-
);
|
|
120
|
-
return `{\n${preview.join(",\n")},\n${nextIndent}...${allEntries.length - MAX_ENTRIES} more\n${indent}}`;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const pairs = allEntries.map(
|
|
124
|
-
([k, v]) =>
|
|
125
|
-
`${nextIndent}${k}: ${stringifyForDisplay(v, maxDepth, currentDepth + 1, { ...options, asFunctionArgs: false })}`,
|
|
126
|
-
);
|
|
127
|
-
return `{\n${pairs.join(",\n")}\n${indent}}`;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return String(value);
|
|
131
|
-
};
|
package/src/utils/weak_effect.js
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { effect } from "@preact/signals";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Creates an effect that uses WeakRef to prevent garbage collection of referenced values.
|
|
5
|
-
*
|
|
6
|
-
* This utility is useful when you want to create reactive effects that watch objects
|
|
7
|
-
* without preventing those objects from being garbage collected. If any of the referenced
|
|
8
|
-
* values is collected, the effect automatically disposes itself.
|
|
9
|
-
*
|
|
10
|
-
* @param {Array} values - Array of values to create weak references for
|
|
11
|
-
* @param {Function} callback - Function to call when the effect runs, receives dereferenced values as arguments
|
|
12
|
-
* @returns {Function} dispose - Function to manually dispose the effect
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```js
|
|
16
|
-
* const objectA = { name: "A" };
|
|
17
|
-
* const objectB = { name: "B" };
|
|
18
|
-
* const prefixSignal = signal('demo');
|
|
19
|
-
*
|
|
20
|
-
* const dispose = weakEffect([objectA, objectB], (a, b) => {
|
|
21
|
-
* const prefix = prefixSignal.value
|
|
22
|
-
* console.log(prefix, a.name, b.name);
|
|
23
|
-
* });
|
|
24
|
-
*
|
|
25
|
-
* // Effect will auto-dispose if objectA or objectB where garbage collected
|
|
26
|
-
* // or can be manually disposed:
|
|
27
|
-
* dispose();
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
export const weakEffect = (values, callback) => {
|
|
31
|
-
const weakRefSet = new Set();
|
|
32
|
-
for (const value of values) {
|
|
33
|
-
weakRefSet.add(new WeakRef(value));
|
|
34
|
-
}
|
|
35
|
-
const dispose = effect(() => {
|
|
36
|
-
const values = [];
|
|
37
|
-
for (const weakRef of weakRefSet) {
|
|
38
|
-
const value = weakRef.deref();
|
|
39
|
-
if (value === undefined) {
|
|
40
|
-
dispose();
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
values.push(value);
|
|
44
|
-
}
|
|
45
|
-
callback(...values);
|
|
46
|
-
});
|
|
47
|
-
return dispose;
|
|
48
|
-
};
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export const CONFIRM_CONSTRAINT = {
|
|
2
|
-
name: "confirm",
|
|
3
|
-
check: (element) => {
|
|
4
|
-
const confirmMessage = element.getAttribute("data-confirm");
|
|
5
|
-
if (!confirmMessage) {
|
|
6
|
-
return "";
|
|
7
|
-
}
|
|
8
|
-
// eslint-disable-next-line no-alert
|
|
9
|
-
if (window.confirm(confirmMessage)) {
|
|
10
|
-
return "";
|
|
11
|
-
}
|
|
12
|
-
return "";
|
|
13
|
-
},
|
|
14
|
-
};
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
export const createUniqueValueConstraint = (
|
|
2
|
-
// the set might be incomplete (the front usually don't have the full copy of all the items from the backend)
|
|
3
|
-
// but this is already nice to help user with what we know
|
|
4
|
-
// it's also possible that front is unsync with backend, preventing user to choose a value
|
|
5
|
-
// that is actually free.
|
|
6
|
-
// But this is unlikely to happen and user could reload the page to be able to choose that name
|
|
7
|
-
// that suddenly became available
|
|
8
|
-
existingValueSet,
|
|
9
|
-
message = `"{value}" already exists. Please choose another value.`,
|
|
10
|
-
) => {
|
|
11
|
-
return {
|
|
12
|
-
name: "unique_value",
|
|
13
|
-
check: (input) => {
|
|
14
|
-
const inputValue = input.value;
|
|
15
|
-
const hasConflict = existingValueSet.has(inputValue);
|
|
16
|
-
// console.log({
|
|
17
|
-
// inputValue,
|
|
18
|
-
// names: Array.from(otherNameSet.values()),
|
|
19
|
-
// hasConflict,
|
|
20
|
-
// });
|
|
21
|
-
if (hasConflict) {
|
|
22
|
-
return message.replace("{value}", inputValue);
|
|
23
|
-
}
|
|
24
|
-
return "";
|
|
25
|
-
},
|
|
26
|
-
};
|
|
27
|
-
};
|
|
@@ -1,338 +0,0 @@
|
|
|
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 null;
|
|
76
|
-
}
|
|
77
|
-
if (requiredMessage) {
|
|
78
|
-
return requiredMessage;
|
|
79
|
-
}
|
|
80
|
-
if (element.type === "password") {
|
|
81
|
-
return element.hasAttribute("data-same-as")
|
|
82
|
-
? `Veuillez confirmer le mot de passe.`
|
|
83
|
-
: `Veuillez saisir un mot de passe.`;
|
|
84
|
-
}
|
|
85
|
-
if (element.type === "email") {
|
|
86
|
-
return element.hasAttribute("data-same-as")
|
|
87
|
-
? `Veuillez confirmer l'adresse e-mail`
|
|
88
|
-
: `Veuillez saisir une adresse e-mail.`;
|
|
89
|
-
}
|
|
90
|
-
return element.hasAttribute("data-same-as")
|
|
91
|
-
? `Veuillez confirmer le champ précédent`
|
|
92
|
-
: `Veuillez remplir ce champ.`;
|
|
93
|
-
},
|
|
94
|
-
};
|
|
95
|
-
export const PATTERN_CONSTRAINT = {
|
|
96
|
-
name: "pattern",
|
|
97
|
-
check: (input) => {
|
|
98
|
-
const pattern = input.pattern;
|
|
99
|
-
if (!pattern) {
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
const value = input.value;
|
|
103
|
-
if (!value) {
|
|
104
|
-
return null;
|
|
105
|
-
}
|
|
106
|
-
const regex = new RegExp(pattern);
|
|
107
|
-
if (regex.test(value)) {
|
|
108
|
-
return null;
|
|
109
|
-
}
|
|
110
|
-
const patternMessage = input.getAttribute("data-pattern-message");
|
|
111
|
-
if (patternMessage) {
|
|
112
|
-
return patternMessage;
|
|
113
|
-
}
|
|
114
|
-
let message = `Veuillez respecter le format requis.`;
|
|
115
|
-
const title = input.title;
|
|
116
|
-
if (title) {
|
|
117
|
-
message += `<br />${title}`;
|
|
118
|
-
}
|
|
119
|
-
return message;
|
|
120
|
-
},
|
|
121
|
-
};
|
|
122
|
-
// https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input/email#validation
|
|
123
|
-
const emailregex =
|
|
124
|
-
/^[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])?)*$/;
|
|
125
|
-
export const TYPE_EMAIL_CONSTRAINT = {
|
|
126
|
-
name: "type_email",
|
|
127
|
-
check: (input) => {
|
|
128
|
-
if (input.type !== "email") {
|
|
129
|
-
return null;
|
|
130
|
-
}
|
|
131
|
-
const value = input.value;
|
|
132
|
-
if (!value) {
|
|
133
|
-
return null;
|
|
134
|
-
}
|
|
135
|
-
if (!value.includes("@")) {
|
|
136
|
-
return `Veuillez inclure "@" dans l'adresse e-mail. Il manque un symbole "@" dans ${value}.`;
|
|
137
|
-
}
|
|
138
|
-
if (!emailregex.test(value)) {
|
|
139
|
-
return `Veuillez saisir une adresse e-mail valide.`;
|
|
140
|
-
}
|
|
141
|
-
return null;
|
|
142
|
-
},
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
export const MIN_LENGTH_CONSTRAINT = {
|
|
146
|
-
name: "min_length",
|
|
147
|
-
check: (element) => {
|
|
148
|
-
if (element.tagName === "INPUT") {
|
|
149
|
-
if (!INPUT_TYPE_SUPPORTING_MIN_LENGTH_SET.has(element.type)) {
|
|
150
|
-
return null;
|
|
151
|
-
}
|
|
152
|
-
} else if (element.tagName !== "TEXTAREA") {
|
|
153
|
-
return null;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const minLength = element.minLength;
|
|
157
|
-
if (minLength === -1) {
|
|
158
|
-
return null;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const value = element.value;
|
|
162
|
-
const valueLength = value.length;
|
|
163
|
-
if (valueLength === 0) {
|
|
164
|
-
return null;
|
|
165
|
-
}
|
|
166
|
-
if (valueLength < minLength) {
|
|
167
|
-
const thisField = generateThisFieldText(element);
|
|
168
|
-
if (valueLength === 1) {
|
|
169
|
-
return `${thisField} doit contenir au moins ${minLength} caractère (il contient actuellement un seul caractère).`;
|
|
170
|
-
}
|
|
171
|
-
return `${thisField} doit contenir au moins ${minLength} caractères (il contient actuellement ${valueLength} caractères).`;
|
|
172
|
-
}
|
|
173
|
-
return null;
|
|
174
|
-
},
|
|
175
|
-
};
|
|
176
|
-
const INPUT_TYPE_SUPPORTING_MIN_LENGTH_SET = new Set([
|
|
177
|
-
"text",
|
|
178
|
-
"search",
|
|
179
|
-
"url",
|
|
180
|
-
"tel",
|
|
181
|
-
"email",
|
|
182
|
-
"password",
|
|
183
|
-
]);
|
|
184
|
-
|
|
185
|
-
const generateThisFieldText = (field) => {
|
|
186
|
-
return field.type === "password"
|
|
187
|
-
? "Ce mot de passe"
|
|
188
|
-
: field.type === "email"
|
|
189
|
-
? "Cette adresse e-mail"
|
|
190
|
-
: "Ce champ";
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
export const MAX_LENGTH_CONSTRAINT = {
|
|
194
|
-
name: "max_length",
|
|
195
|
-
check: (element) => {
|
|
196
|
-
if (element.tagName === "INPUT") {
|
|
197
|
-
if (!INPUT_TYPE_SUPPORTING_MAX_LENGTH_SET.has(element.type)) {
|
|
198
|
-
return null;
|
|
199
|
-
}
|
|
200
|
-
} else if (element.tagName !== "TEXTAREA") {
|
|
201
|
-
return null;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
const maxLength = element.maxLength;
|
|
205
|
-
if (maxLength === -1) {
|
|
206
|
-
return null;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const value = element.value;
|
|
210
|
-
const valueLength = value.length;
|
|
211
|
-
if (valueLength > maxLength) {
|
|
212
|
-
const thisField = generateThisFieldText(element);
|
|
213
|
-
return `${thisField} doit contenir au maximum ${maxLength} caractères (il contient actuellement ${valueLength} caractères).`;
|
|
214
|
-
}
|
|
215
|
-
return null;
|
|
216
|
-
},
|
|
217
|
-
};
|
|
218
|
-
const INPUT_TYPE_SUPPORTING_MAX_LENGTH_SET = new Set(
|
|
219
|
-
INPUT_TYPE_SUPPORTING_MIN_LENGTH_SET,
|
|
220
|
-
);
|
|
221
|
-
|
|
222
|
-
export const TYPE_NUMBER_CONSTRAINT = {
|
|
223
|
-
name: "type_number",
|
|
224
|
-
check: (element) => {
|
|
225
|
-
if (element.tagName !== "INPUT") {
|
|
226
|
-
return null;
|
|
227
|
-
}
|
|
228
|
-
if (element.type !== "number") {
|
|
229
|
-
return null;
|
|
230
|
-
}
|
|
231
|
-
if (element.value === "") {
|
|
232
|
-
return null;
|
|
233
|
-
}
|
|
234
|
-
const value = element.valueAsNumber;
|
|
235
|
-
if (isNaN(value)) {
|
|
236
|
-
return `Doit être un nombre.`;
|
|
237
|
-
}
|
|
238
|
-
return null;
|
|
239
|
-
},
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
export const MIN_CONSTRAINT = {
|
|
243
|
-
name: "min",
|
|
244
|
-
check: (element) => {
|
|
245
|
-
if (element.tagName !== "INPUT") {
|
|
246
|
-
return null;
|
|
247
|
-
}
|
|
248
|
-
if (element.type === "number") {
|
|
249
|
-
const minString = element.min;
|
|
250
|
-
if (minString === "") {
|
|
251
|
-
return null;
|
|
252
|
-
}
|
|
253
|
-
const minNumber = parseFloat(minString);
|
|
254
|
-
if (isNaN(minNumber)) {
|
|
255
|
-
return null;
|
|
256
|
-
}
|
|
257
|
-
const valueAsNumber = element.valueAsNumber;
|
|
258
|
-
if (isNaN(valueAsNumber)) {
|
|
259
|
-
return null;
|
|
260
|
-
}
|
|
261
|
-
if (valueAsNumber < minNumber) {
|
|
262
|
-
const minMessage = element.getAttribute("data-min-message");
|
|
263
|
-
return (
|
|
264
|
-
minMessage ||
|
|
265
|
-
`Doit être supérieur ou égal à <strong>${minString}</strong>.`
|
|
266
|
-
);
|
|
267
|
-
}
|
|
268
|
-
return null;
|
|
269
|
-
}
|
|
270
|
-
if (element.type === "time") {
|
|
271
|
-
const min = element.min;
|
|
272
|
-
if (min === undefined) {
|
|
273
|
-
return null;
|
|
274
|
-
}
|
|
275
|
-
const [minHours, minMinutes] = min.split(":").map(Number);
|
|
276
|
-
const value = element.value;
|
|
277
|
-
const [hours, minutes] = value.split(":").map(Number);
|
|
278
|
-
if (hours < minHours) {
|
|
279
|
-
return `Doit être <strong>${min}</strong> ou plus.`;
|
|
280
|
-
}
|
|
281
|
-
if (hours === minHours && minMinutes < minutes) {
|
|
282
|
-
return `Doit être <strong>${min}</strong> ou plus.`;
|
|
283
|
-
}
|
|
284
|
-
return null;
|
|
285
|
-
}
|
|
286
|
-
// "range"
|
|
287
|
-
// - user interface do not let user enter anything outside the boundaries
|
|
288
|
-
// - when setting value via js browser enforce boundaries too
|
|
289
|
-
// "date", "month", "week", "datetime-local"
|
|
290
|
-
// - same as "range"
|
|
291
|
-
return null;
|
|
292
|
-
},
|
|
293
|
-
};
|
|
294
|
-
|
|
295
|
-
export const MAX_CONSTRAINT = {
|
|
296
|
-
name: "max",
|
|
297
|
-
check: (element) => {
|
|
298
|
-
if (element.tagName !== "INPUT") {
|
|
299
|
-
return null;
|
|
300
|
-
}
|
|
301
|
-
if (element.type === "number") {
|
|
302
|
-
const maxString = element.max;
|
|
303
|
-
if (maxString === "") {
|
|
304
|
-
return null;
|
|
305
|
-
}
|
|
306
|
-
const maxNumber = parseFloat(maxString);
|
|
307
|
-
if (isNaN(maxNumber)) {
|
|
308
|
-
return null;
|
|
309
|
-
}
|
|
310
|
-
const valueAsNumber = element.valueAsNumber;
|
|
311
|
-
if (isNaN(valueAsNumber)) {
|
|
312
|
-
return null;
|
|
313
|
-
}
|
|
314
|
-
if (valueAsNumber > maxNumber) {
|
|
315
|
-
const maxMessage = element.getAttribute("data-max-message");
|
|
316
|
-
return maxMessage || `Doit être <strong>${maxString}</strong> ou plus.`;
|
|
317
|
-
}
|
|
318
|
-
return null;
|
|
319
|
-
}
|
|
320
|
-
if (element.type === "time") {
|
|
321
|
-
const max = element.min;
|
|
322
|
-
if (max === undefined) {
|
|
323
|
-
return null;
|
|
324
|
-
}
|
|
325
|
-
const [maxHours, maxMinutes] = max.split(":").map(Number);
|
|
326
|
-
const value = element.value;
|
|
327
|
-
const [hours, minutes] = value.split(":").map(Number);
|
|
328
|
-
if (hours > maxHours) {
|
|
329
|
-
return `Doit être <strong>${max}</strong> ou moins.`;
|
|
330
|
-
}
|
|
331
|
-
if (hours === maxHours && maxMinutes > minutes) {
|
|
332
|
-
return `Doit être <strong>${max}</strong> ou moins.`;
|
|
333
|
-
}
|
|
334
|
-
return null;
|
|
335
|
-
}
|
|
336
|
-
return null;
|
|
337
|
-
},
|
|
338
|
-
};
|
|
@@ -1,41 +0,0 @@
|
|
|
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
|
-
if (element.type === "hidden") {
|
|
11
|
-
return null;
|
|
12
|
-
}
|
|
13
|
-
const readonlySilent = element.hasAttribute("data-readonly-silent");
|
|
14
|
-
if (readonlySilent) {
|
|
15
|
-
return { silent: true };
|
|
16
|
-
}
|
|
17
|
-
const readonlyMessage = element.getAttribute("data-readonly-message");
|
|
18
|
-
if (readonlyMessage) {
|
|
19
|
-
return {
|
|
20
|
-
message: readonlyMessage,
|
|
21
|
-
level: "info",
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
const isBusy = element.getAttribute("aria-busy") === "true";
|
|
25
|
-
if (isBusy) {
|
|
26
|
-
return {
|
|
27
|
-
target: element,
|
|
28
|
-
message: `Cette action est en cours. Veuillez patienter.`,
|
|
29
|
-
level: "info",
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
return {
|
|
33
|
-
target: element,
|
|
34
|
-
message:
|
|
35
|
-
element.tagName === "BUTTON"
|
|
36
|
-
? `Cet action n'est pas disponible pour l'instant.`
|
|
37
|
-
: `Cet élément est en lecture seule et ne peut pas être modifié.`,
|
|
38
|
-
level: "info",
|
|
39
|
-
};
|
|
40
|
-
},
|
|
41
|
-
};
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
export const SAME_AS_CONSTRAINT = {
|
|
2
|
-
name: "same_as",
|
|
3
|
-
check: (element) => {
|
|
4
|
-
const sameAs = element.getAttribute("data-same-as");
|
|
5
|
-
if (!sameAs) {
|
|
6
|
-
return null;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const otherElement = document.querySelector(sameAs);
|
|
10
|
-
if (!otherElement) {
|
|
11
|
-
console.warn(
|
|
12
|
-
`Same as constraint: could not find element for selector ${sameAs}`,
|
|
13
|
-
);
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const value = element.value;
|
|
18
|
-
const otherValue = otherElement.value;
|
|
19
|
-
if (value === "" || otherValue === "") {
|
|
20
|
-
// don't validate if one of the two values is empty
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (value === otherValue) {
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const message = element.getAttribute("data-same-as-message");
|
|
29
|
-
if (message) {
|
|
30
|
-
return message;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const type = element.type;
|
|
34
|
-
if (type === "password") {
|
|
35
|
-
return `Ce mot de passe doit être identique au précédent.`;
|
|
36
|
-
}
|
|
37
|
-
if (type === "email") {
|
|
38
|
-
return `Cette adresse e-mail doit être identique a la précédente.`;
|
|
39
|
-
}
|
|
40
|
-
return `Ce champ doit être identique au précédent.`;
|
|
41
|
-
},
|
|
42
|
-
};
|