@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.
Files changed (123) hide show
  1. package/index.js +51 -0
  2. package/package.json +38 -0
  3. package/src/action_private_properties.js +11 -0
  4. package/src/action_proxy_test.html +353 -0
  5. package/src/action_run_states.js +5 -0
  6. package/src/actions.js +1377 -0
  7. package/src/browser_integration/browser_integration.js +191 -0
  8. package/src/browser_integration/document_back_and_forward.js +17 -0
  9. package/src/browser_integration/document_loading_signal.js +100 -0
  10. package/src/browser_integration/document_state_signal.js +9 -0
  11. package/src/browser_integration/document_url_signal.js +9 -0
  12. package/src/browser_integration/use_is_visited.js +19 -0
  13. package/src/browser_integration/via_history.js +199 -0
  14. package/src/browser_integration/via_navigation.js +168 -0
  15. package/src/components/action_execution/form_context.js +8 -0
  16. package/src/components/action_execution/render_actionable_component.jsx +27 -0
  17. package/src/components/action_execution/use_action.js +330 -0
  18. package/src/components/action_execution/use_execute_action.js +161 -0
  19. package/src/components/action_renderer.jsx +136 -0
  20. package/src/components/collect_form_element_values.js +79 -0
  21. package/src/components/demos/0_button_demo.html +155 -0
  22. package/src/components/demos/1_checkbox_demo.html +257 -0
  23. package/src/components/demos/2_input_textual_demo.html +354 -0
  24. package/src/components/demos/3_radio_demo.html +222 -0
  25. package/src/components/demos/4_select_demo.html +104 -0
  26. package/src/components/demos/5_list_scrollable_demo.html +153 -0
  27. package/src/components/demos/action/0_button_demo.html +204 -0
  28. package/src/components/demos/action/10_shortcuts_demo.html +189 -0
  29. package/src/components/demos/action/11_nested_shortcuts_demo.html +401 -0
  30. package/src/components/demos/action/1_input_text_demo.html +461 -0
  31. package/src/components/demos/action/2_form_multiple.html +303 -0
  32. package/src/components/demos/action/3_details_demo.html +172 -0
  33. package/src/components/demos/action/4_input_checkbox_demo.html +611 -0
  34. package/src/components/demos/action/6_checkbox_list_demo.html +109 -0
  35. package/src/components/demos/action/7_radio_list_demo.html +217 -0
  36. package/src/components/demos/action/8_editable_text_demo.html +442 -0
  37. package/src/components/demos/action/9_link_demo.html +172 -0
  38. package/src/components/demos/demo.md +0 -0
  39. package/src/components/demos/route/basic/basic.html +14 -0
  40. package/src/components/demos/route/basic/basic_route_demo.jsx +224 -0
  41. package/src/components/demos/route/multi/multi.html +14 -0
  42. package/src/components/demos/route/multi/multi_route_demo.jsx +277 -0
  43. package/src/components/details/details.jsx +248 -0
  44. package/src/components/details/summary_marker.jsx +141 -0
  45. package/src/components/editable_text/editable_text.jsx +96 -0
  46. package/src/components/error_boundary_context.js +9 -0
  47. package/src/components/form.jsx +144 -0
  48. package/src/components/input/button.jsx +333 -0
  49. package/src/components/input/checkbox_list.jsx +294 -0
  50. package/src/components/input/field.jsx +61 -0
  51. package/src/components/input/field_css.js +118 -0
  52. package/src/components/input/input.jsx +15 -0
  53. package/src/components/input/input_checkbox.jsx +370 -0
  54. package/src/components/input/input_radio.jsx +299 -0
  55. package/src/components/input/input_textual.jsx +338 -0
  56. package/src/components/input/radio_list.jsx +283 -0
  57. package/src/components/input/select.jsx +273 -0
  58. package/src/components/input/use_form_event.js +20 -0
  59. package/src/components/input/use_on_change.js +12 -0
  60. package/src/components/link/link.jsx +291 -0
  61. package/src/components/loader/loader_background.jsx +324 -0
  62. package/src/components/loader/loading_spinner.jsx +68 -0
  63. package/src/components/loader/network_speed.js +83 -0
  64. package/src/components/loader/rectangle_loading.jsx +225 -0
  65. package/src/components/route.jsx +15 -0
  66. package/src/components/selection/selection.js +5 -0
  67. package/src/components/selection/selection_context.jsx +262 -0
  68. package/src/components/shortcut/os.js +9 -0
  69. package/src/components/shortcut/shortcut_context.jsx +390 -0
  70. package/src/components/use_action_events.js +37 -0
  71. package/src/components/use_auto_focus.js +43 -0
  72. package/src/components/use_debounce_true.js +31 -0
  73. package/src/components/use_focus_group.js +19 -0
  74. package/src/components/use_initial_value.js +104 -0
  75. package/src/components/use_is_visited.js +19 -0
  76. package/src/components/use_ref_array.js +38 -0
  77. package/src/components/use_signal_sync.js +50 -0
  78. package/src/components/use_state_array.js +40 -0
  79. package/src/docs/actions.md +228 -0
  80. package/src/docs/demos/resource/action_status.jsx +42 -0
  81. package/src/docs/demos/resource/demo.md +1 -0
  82. package/src/docs/demos/resource/resource_demo_0.html +84 -0
  83. package/src/docs/demos/resource/resource_demo_10_post_gc.html +364 -0
  84. package/src/docs/demos/resource/resource_demo_11_describe_many.html +362 -0
  85. package/src/docs/demos/resource/resource_demo_2.html +173 -0
  86. package/src/docs/demos/resource/resource_demo_3_filtered_users.html +415 -0
  87. package/src/docs/demos/resource/resource_demo_4_details.html +284 -0
  88. package/src/docs/demos/resource/resource_demo_5_renderer_lazy.html +115 -0
  89. package/src/docs/demos/resource/resource_demo_6_gc.html +217 -0
  90. package/src/docs/demos/resource/resource_demo_7_child_gc.html +240 -0
  91. package/src/docs/demos/resource/resource_demo_8_proxy_gc.html +319 -0
  92. package/src/docs/demos/resource/resource_demo_9_describe_one.html +472 -0
  93. package/src/docs/demos/resource/tata.jsx +3 -0
  94. package/src/docs/demos/resource/toto.jsx +3 -0
  95. package/src/docs/demos/user_nav/user_nav.html +12 -0
  96. package/src/docs/demos/user_nav/user_nav.jsx +330 -0
  97. package/src/docs/resource_dependencies.md +103 -0
  98. package/src/docs/resource_with_params.md +80 -0
  99. package/src/notes.md +13 -0
  100. package/src/route/route.js +518 -0
  101. package/src/route/route.test.html +228 -0
  102. package/src/store/array_signal_store.js +537 -0
  103. package/src/store/local_storage_signal.js +17 -0
  104. package/src/store/resource_graph.js +1303 -0
  105. package/src/store/tests/resource_graph_autoreload_demo.html +12 -0
  106. package/src/store/tests/resource_graph_autoreload_demo.jsx +964 -0
  107. package/src/store/tests/resource_graph_dependencies.test.js +95 -0
  108. package/src/store/value_in_local_storage.js +187 -0
  109. package/src/symbol_object_signal.js +1 -0
  110. package/src/use_action_data.js +10 -0
  111. package/src/use_action_status.js +47 -0
  112. package/src/utils/add_many_event_listeners.js +15 -0
  113. package/src/utils/array_add_remove.js +61 -0
  114. package/src/utils/array_signal.js +15 -0
  115. package/src/utils/compare_two_js_values.js +172 -0
  116. package/src/utils/execute_with_cleanup.js +21 -0
  117. package/src/utils/get_caller_info.js +85 -0
  118. package/src/utils/iterable_weak_set.js +62 -0
  119. package/src/utils/js_value_weak_map.js +162 -0
  120. package/src/utils/js_value_weak_map_demo.html +690 -0
  121. package/src/utils/merge_two_js_values.js +53 -0
  122. package/src/utils/stringify_for_display.js +150 -0
  123. 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
+ };