@jsenv/navi 0.0.1
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/index.js +51 -0
- package/package.json +38 -0
- package/src/action_private_properties.js +11 -0
- package/src/action_proxy_test.html +353 -0
- package/src/action_run_states.js +5 -0
- package/src/actions.js +1377 -0
- package/src/browser_integration/browser_integration.js +191 -0
- package/src/browser_integration/document_back_and_forward.js +17 -0
- package/src/browser_integration/document_loading_signal.js +100 -0
- package/src/browser_integration/document_state_signal.js +9 -0
- package/src/browser_integration/document_url_signal.js +9 -0
- package/src/browser_integration/use_is_visited.js +19 -0
- package/src/browser_integration/via_history.js +199 -0
- package/src/browser_integration/via_navigation.js +168 -0
- package/src/components/action_execution/form_context.js +8 -0
- package/src/components/action_execution/render_actionable_component.jsx +27 -0
- package/src/components/action_execution/use_action.js +330 -0
- package/src/components/action_execution/use_execute_action.js +161 -0
- package/src/components/action_renderer.jsx +136 -0
- package/src/components/collect_form_element_values.js +79 -0
- package/src/components/demos/0_button_demo.html +155 -0
- package/src/components/demos/1_checkbox_demo.html +257 -0
- package/src/components/demos/2_input_textual_demo.html +354 -0
- package/src/components/demos/3_radio_demo.html +222 -0
- package/src/components/demos/4_select_demo.html +104 -0
- package/src/components/demos/5_list_scrollable_demo.html +153 -0
- package/src/components/demos/action/0_button_demo.html +204 -0
- package/src/components/demos/action/10_shortcuts_demo.html +189 -0
- package/src/components/demos/action/11_nested_shortcuts_demo.html +401 -0
- package/src/components/demos/action/1_input_text_demo.html +461 -0
- package/src/components/demos/action/2_form_multiple.html +303 -0
- package/src/components/demos/action/3_details_demo.html +172 -0
- package/src/components/demos/action/4_input_checkbox_demo.html +611 -0
- package/src/components/demos/action/6_checkbox_list_demo.html +109 -0
- package/src/components/demos/action/7_radio_list_demo.html +217 -0
- package/src/components/demos/action/8_editable_text_demo.html +442 -0
- package/src/components/demos/action/9_link_demo.html +172 -0
- package/src/components/demos/demo.md +0 -0
- package/src/components/demos/route/basic/basic.html +14 -0
- package/src/components/demos/route/basic/basic_route_demo.jsx +224 -0
- package/src/components/demos/route/multi/multi.html +14 -0
- package/src/components/demos/route/multi/multi_route_demo.jsx +277 -0
- package/src/components/details/details.jsx +248 -0
- package/src/components/details/summary_marker.jsx +141 -0
- package/src/components/editable_text/editable_text.jsx +96 -0
- package/src/components/error_boundary_context.js +9 -0
- package/src/components/form.jsx +144 -0
- package/src/components/input/button.jsx +333 -0
- package/src/components/input/checkbox_list.jsx +294 -0
- package/src/components/input/field.jsx +61 -0
- package/src/components/input/field_css.js +118 -0
- package/src/components/input/input.jsx +15 -0
- package/src/components/input/input_checkbox.jsx +370 -0
- package/src/components/input/input_radio.jsx +299 -0
- package/src/components/input/input_textual.jsx +338 -0
- package/src/components/input/radio_list.jsx +283 -0
- package/src/components/input/select.jsx +273 -0
- package/src/components/input/use_form_event.js +20 -0
- package/src/components/input/use_on_change.js +12 -0
- package/src/components/link/link.jsx +291 -0
- package/src/components/loader/loader_background.jsx +324 -0
- package/src/components/loader/loading_spinner.jsx +68 -0
- package/src/components/loader/network_speed.js +83 -0
- package/src/components/loader/rectangle_loading.jsx +225 -0
- package/src/components/route.jsx +15 -0
- package/src/components/selection/selection.js +5 -0
- package/src/components/selection/selection_context.jsx +262 -0
- package/src/components/shortcut/os.js +9 -0
- package/src/components/shortcut/shortcut_context.jsx +390 -0
- package/src/components/use_action_events.js +37 -0
- package/src/components/use_auto_focus.js +43 -0
- package/src/components/use_debounce_true.js +31 -0
- package/src/components/use_focus_group.js +19 -0
- package/src/components/use_initial_value.js +104 -0
- package/src/components/use_is_visited.js +19 -0
- package/src/components/use_ref_array.js +38 -0
- package/src/components/use_signal_sync.js +50 -0
- package/src/components/use_state_array.js +40 -0
- package/src/docs/actions.md +228 -0
- package/src/docs/demos/resource/action_status.jsx +42 -0
- package/src/docs/demos/resource/demo.md +1 -0
- package/src/docs/demos/resource/resource_demo_0.html +84 -0
- package/src/docs/demos/resource/resource_demo_10_post_gc.html +364 -0
- package/src/docs/demos/resource/resource_demo_11_describe_many.html +362 -0
- package/src/docs/demos/resource/resource_demo_2.html +173 -0
- package/src/docs/demos/resource/resource_demo_3_filtered_users.html +415 -0
- package/src/docs/demos/resource/resource_demo_4_details.html +284 -0
- package/src/docs/demos/resource/resource_demo_5_renderer_lazy.html +115 -0
- package/src/docs/demos/resource/resource_demo_6_gc.html +217 -0
- package/src/docs/demos/resource/resource_demo_7_child_gc.html +240 -0
- package/src/docs/demos/resource/resource_demo_8_proxy_gc.html +319 -0
- package/src/docs/demos/resource/resource_demo_9_describe_one.html +472 -0
- package/src/docs/demos/resource/tata.jsx +3 -0
- package/src/docs/demos/resource/toto.jsx +3 -0
- package/src/docs/demos/user_nav/user_nav.html +12 -0
- package/src/docs/demos/user_nav/user_nav.jsx +330 -0
- package/src/docs/resource_dependencies.md +103 -0
- package/src/docs/resource_with_params.md +80 -0
- package/src/notes.md +13 -0
- package/src/route/route.js +518 -0
- package/src/route/route.test.html +228 -0
- package/src/store/array_signal_store.js +537 -0
- package/src/store/local_storage_signal.js +17 -0
- package/src/store/resource_graph.js +1303 -0
- package/src/store/tests/resource_graph_autoreload_demo.html +12 -0
- package/src/store/tests/resource_graph_autoreload_demo.jsx +964 -0
- package/src/store/tests/resource_graph_dependencies.test.js +95 -0
- package/src/store/value_in_local_storage.js +187 -0
- package/src/symbol_object_signal.js +1 -0
- package/src/use_action_data.js +10 -0
- package/src/use_action_status.js +47 -0
- package/src/utils/add_many_event_listeners.js +15 -0
- package/src/utils/array_add_remove.js +61 -0
- package/src/utils/array_signal.js +15 -0
- package/src/utils/compare_two_js_values.js +172 -0
- package/src/utils/execute_with_cleanup.js +21 -0
- package/src/utils/get_caller_info.js +85 -0
- package/src/utils/iterable_weak_set.js +62 -0
- package/src/utils/js_value_weak_map.js +162 -0
- package/src/utils/js_value_weak_map_demo.html +690 -0
- package/src/utils/merge_two_js_values.js +53 -0
- package/src/utils/stringify_for_display.js +150 -0
- package/src/utils/weak_effect.js +48 -0
|
@@ -0,0 +1,53 @@
|
|
|
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
|
+
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
export const stringifyForDisplay = (
|
|
2
|
+
value,
|
|
3
|
+
maxDepth = 2,
|
|
4
|
+
currentDepth = 0,
|
|
5
|
+
options = {},
|
|
6
|
+
) => {
|
|
7
|
+
const { asFunctionArgs = false } = options;
|
|
8
|
+
const indent = " ".repeat(currentDepth);
|
|
9
|
+
const nextIndent = " ".repeat(currentDepth + 1);
|
|
10
|
+
|
|
11
|
+
if (currentDepth >= maxDepth) {
|
|
12
|
+
return typeof value === "object" && value !== null
|
|
13
|
+
? "[Object]"
|
|
14
|
+
: String(value);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (value === null) {
|
|
18
|
+
return "null";
|
|
19
|
+
}
|
|
20
|
+
if (value === undefined) {
|
|
21
|
+
return "undefined";
|
|
22
|
+
}
|
|
23
|
+
if (typeof value === "string") {
|
|
24
|
+
return `"${value}"`;
|
|
25
|
+
}
|
|
26
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
27
|
+
return String(value);
|
|
28
|
+
}
|
|
29
|
+
if (typeof value === "function") {
|
|
30
|
+
return `[Function ${value.name || "anonymous"}]`;
|
|
31
|
+
}
|
|
32
|
+
if (value instanceof Date) {
|
|
33
|
+
return `Date(${value.toISOString()})`;
|
|
34
|
+
}
|
|
35
|
+
if (value instanceof RegExp) {
|
|
36
|
+
return value.toString();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (Array.isArray(value)) {
|
|
40
|
+
const openBracket = asFunctionArgs ? "(" : "[";
|
|
41
|
+
const closeBracket = asFunctionArgs ? ")" : "]";
|
|
42
|
+
|
|
43
|
+
if (value.length === 0) return `${openBracket}${closeBracket}`;
|
|
44
|
+
|
|
45
|
+
// Display arrays with only one element on a single line
|
|
46
|
+
if (value.length === 1) {
|
|
47
|
+
const item = stringifyForDisplay(
|
|
48
|
+
value[0],
|
|
49
|
+
maxDepth,
|
|
50
|
+
currentDepth + 1,
|
|
51
|
+
// Remove asFunctionArgs for nested calls
|
|
52
|
+
{ ...options, asFunctionArgs: false },
|
|
53
|
+
);
|
|
54
|
+
return `${openBracket}${item}${closeBracket}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (value.length > MAX_ENTRIES) {
|
|
58
|
+
const preview = value
|
|
59
|
+
.slice(0, MAX_ENTRIES)
|
|
60
|
+
.map(
|
|
61
|
+
(v) =>
|
|
62
|
+
`${nextIndent}${stringifyForDisplay(v, maxDepth, currentDepth + 1, { ...options, asFunctionArgs: false })}`,
|
|
63
|
+
);
|
|
64
|
+
return `${openBracket}\n${preview.join(",\n")},\n${nextIndent}...${value.length - MAX_ENTRIES} more\n${indent}${closeBracket}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const items = value.map(
|
|
68
|
+
(v) =>
|
|
69
|
+
`${nextIndent}${stringifyForDisplay(v, maxDepth, currentDepth + 1, { ...options, asFunctionArgs: false })}`,
|
|
70
|
+
);
|
|
71
|
+
return `${openBracket}\n${items.join(",\n")}\n${indent}${closeBracket}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (typeof value === "object") {
|
|
75
|
+
const signalType = getSignalType(value);
|
|
76
|
+
if (signalType) {
|
|
77
|
+
const signalValue = value.peek();
|
|
78
|
+
const prefix = signalType === "computed" ? "computed" : "signal";
|
|
79
|
+
return `${prefix}(${stringifyForDisplay(signalValue, maxDepth, currentDepth, { ...options, asFunctionArgs: false })})`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const entries = Object.entries(value);
|
|
83
|
+
if (entries.length === 0) return "{}";
|
|
84
|
+
|
|
85
|
+
// ✅ Inclure les clés avec valeurs undefined/null
|
|
86
|
+
const allEntries = [];
|
|
87
|
+
for (const [key, val] of entries) {
|
|
88
|
+
allEntries.push([key, val]);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Ajouter les clés avec undefined (que Object.entries omet)
|
|
92
|
+
const descriptor = Object.getOwnPropertyDescriptors(value);
|
|
93
|
+
for (const [key, desc] of Object.entries(descriptor)) {
|
|
94
|
+
if (desc.value === undefined && !entries.some(([k]) => k === key)) {
|
|
95
|
+
allEntries.push([key, undefined]);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Display objects with only one key on a single line
|
|
100
|
+
if (allEntries.length === 1) {
|
|
101
|
+
const [key, val] = allEntries[0];
|
|
102
|
+
const valueStr = stringifyForDisplay(val, maxDepth, currentDepth + 1, {
|
|
103
|
+
...options,
|
|
104
|
+
asFunctionArgs: false,
|
|
105
|
+
});
|
|
106
|
+
return `{ ${key}: ${valueStr} }`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (allEntries.length > MAX_ENTRIES) {
|
|
110
|
+
const preview = allEntries
|
|
111
|
+
.slice(0, MAX_ENTRIES)
|
|
112
|
+
.map(
|
|
113
|
+
([k, v]) =>
|
|
114
|
+
`${nextIndent}${k}: ${stringifyForDisplay(v, maxDepth, currentDepth + 1, { ...options, asFunctionArgs: false })}`,
|
|
115
|
+
);
|
|
116
|
+
return `{\n${preview.join(",\n")},\n${nextIndent}...${allEntries.length - MAX_ENTRIES} more\n${indent}}`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const pairs = allEntries.map(
|
|
120
|
+
([k, v]) =>
|
|
121
|
+
`${nextIndent}${k}: ${stringifyForDisplay(v, maxDepth, currentDepth + 1, { ...options, asFunctionArgs: false })}`,
|
|
122
|
+
);
|
|
123
|
+
return `{\n${pairs.join(",\n")}\n${indent}}`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return String(value);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export const isSignal = (value) => {
|
|
130
|
+
return getSignalType(value) !== null;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const BRAND_SYMBOL = Symbol.for("preact-signals");
|
|
134
|
+
export const getSignalType = (value) => {
|
|
135
|
+
if (!value || typeof value !== "object") {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (value.brand !== BRAND_SYMBOL) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (typeof value._fn === "function") {
|
|
144
|
+
return "computed";
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return "signal";
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const MAX_ENTRIES = 5;
|
|
@@ -0,0 +1,48 @@
|
|
|
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
|
+
};
|