@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,401 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" href="data:," />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Nested Keyboard Shortcuts Demo</title>
8
+ <style>
9
+ body {
10
+ font-family:
11
+ -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
12
+ max-width: 900px;
13
+ margin: 0 auto;
14
+ padding: 40px 20px;
15
+ line-height: 1.6;
16
+ background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
17
+ min-height: 100vh;
18
+ color: #333;
19
+ }
20
+
21
+ .demo-container {
22
+ background: white;
23
+ border-radius: 12px;
24
+ padding: 40px;
25
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
26
+ }
27
+
28
+ .demo-title {
29
+ font-size: 2rem;
30
+ font-weight: 700;
31
+ margin: 0 0 30px 0;
32
+ color: #2d3748;
33
+ text-align: center;
34
+ }
35
+
36
+ .demo-description {
37
+ background: #e6fffa;
38
+ border-left: 4px solid #38b2ac;
39
+ padding: 20px;
40
+ margin: 0 0 30px 0;
41
+ border-radius: 0 8px 8px 0;
42
+ }
43
+
44
+ .demo-description h3 {
45
+ margin: 0 0 15px 0;
46
+ color: #2d3748;
47
+ font-size: 1.2rem;
48
+ }
49
+
50
+ .shortcut-section {
51
+ margin: 20px 0;
52
+ }
53
+
54
+ .shortcut-section h4 {
55
+ margin: 0 0 10px 0;
56
+ color: #2c7a7b;
57
+ font-size: 1rem;
58
+ }
59
+
60
+ .shortcut-list {
61
+ list-style: none;
62
+ padding: 0;
63
+ margin: 0;
64
+ }
65
+
66
+ .shortcut-item {
67
+ display: flex;
68
+ align-items: center;
69
+ margin: 8px 0;
70
+ padding: 6px 0;
71
+ }
72
+
73
+ .shortcut-key {
74
+ background: #2d3748;
75
+ color: white;
76
+ padding: 3px 10px;
77
+ border-radius: 5px;
78
+ font-family: "Monaco", "Menlo", monospace;
79
+ font-size: 0.8rem;
80
+ font-weight: 600;
81
+ margin-right: 12px;
82
+ min-width: 50px;
83
+ text-align: center;
84
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
85
+ }
86
+
87
+ .shortcut-description {
88
+ color: #4a5568;
89
+ flex: 1;
90
+ font-size: 0.9rem;
91
+ }
92
+
93
+ .file-explorer {
94
+ background: #f8f9fa;
95
+ border: 2px solid #e9ecef;
96
+ border-radius: 12px;
97
+ padding: 20px;
98
+ margin: 30px 0;
99
+ }
100
+
101
+ .explorer-title {
102
+ margin: 0 0 20px 0;
103
+ color: #2d3748;
104
+ font-size: 1.3rem;
105
+ text-align: center;
106
+ }
107
+
108
+ .file-list {
109
+ list-style: none;
110
+ padding: 0;
111
+ margin: 0;
112
+ border: 1px solid #d1d5db;
113
+ border-radius: 6px;
114
+ background: white;
115
+ }
116
+
117
+ .file-item {
118
+ border-bottom: 1px solid #e5e7eb;
119
+ padding: 0;
120
+ }
121
+
122
+ .file-item:last-child {
123
+ border-bottom: none;
124
+ }
125
+
126
+ .file-link {
127
+ display: block;
128
+ padding: 12px 16px;
129
+ text-decoration: none;
130
+ color: #374151;
131
+ transition: background-color 0.2s ease;
132
+ }
133
+
134
+ .file-link:hover {
135
+ background-color: #f3f4f6;
136
+ }
137
+
138
+ .file-link:focus {
139
+ outline: 2px solid #3b82f6;
140
+ outline-offset: -2px;
141
+ background-color: #dbeafe;
142
+ }
143
+
144
+ .file-icon {
145
+ margin-right: 10px;
146
+ font-size: 1.1rem;
147
+ }
148
+
149
+ .instructions {
150
+ background: #fef7ff;
151
+ border: 1px solid #e879f9;
152
+ border-radius: 8px;
153
+ padding: 20px;
154
+ margin-top: 30px;
155
+ }
156
+
157
+ .instructions h4 {
158
+ color: #a21caf;
159
+ margin: 0 0 10px 0;
160
+ font-size: 1rem;
161
+ }
162
+
163
+ .instructions p {
164
+ margin: 5px 0;
165
+ color: #744210;
166
+ font-size: 0.9rem;
167
+ }
168
+
169
+ .status-display {
170
+ background: #1f2937;
171
+ color: #f9fafb;
172
+ padding: 15px;
173
+ border-radius: 8px;
174
+ margin: 20px 0;
175
+ font-family: "Monaco", "Menlo", monospace;
176
+ font-size: 0.9rem;
177
+ min-height: 60px;
178
+ }
179
+
180
+ .status-title {
181
+ color: #9ca3af;
182
+ margin-bottom: 8px;
183
+ font-size: 0.8rem;
184
+ text-transform: uppercase;
185
+ letter-spacing: 0.5px;
186
+ }
187
+ </style>
188
+ </head>
189
+ <body>
190
+ <div id="root"></div>
191
+
192
+ <script type="module" jsenv-type="module/jsx">
193
+ import { render } from "preact";
194
+ import { useState, useRef } from "preact/hooks";
195
+ import {
196
+ // eslint-disable-next-line no-unused-vars
197
+ Link,
198
+ // eslint-disable-next-line no-unused-vars
199
+ ShortcutProvider,
200
+ // eslint-disable-next-line no-unused-vars
201
+ SelectionProvider,
202
+ } from "@jsenv/navi";
203
+
204
+ // eslint-disable-next-line no-unused-vars
205
+ const App = () => {
206
+ const [status, setStatus] = useState("Ready - Try the shortcuts!");
207
+ const [selection, setSelection] = useState([]);
208
+ const [deletedFiles, setDeletedFiles] = useState([]);
209
+
210
+ const containerShortcuts = [
211
+ {
212
+ key: "a",
213
+ description: "Select all files",
214
+ action: () => {
215
+ setStatus("📋 Selected all files (container shortcut)");
216
+ },
217
+ },
218
+ {
219
+ key: "delete",
220
+ description: "Delete selected files",
221
+ action: async () => {
222
+ setStatus("đŸ—‘ī¸ Start deleting files (container shortcut)");
223
+ await new Promise((resolve) => setTimeout(resolve, 2_000));
224
+ setStatus(
225
+ "đŸ—‘ī¸ Selected files deletion done! (container shortcut)",
226
+ );
227
+ },
228
+ confirmMessage: "Are you sure you want to delete selected files?",
229
+ },
230
+ {
231
+ key: "r",
232
+ description: "Refresh file list",
233
+ action: () => {
234
+ setStatus("🔄 Refreshed file list (container shortcut)");
235
+ },
236
+ },
237
+ ];
238
+
239
+ const files = [
240
+ { name: "document.pdf", icon: "📄", type: "PDF Document" },
241
+ { name: "image.jpg", icon: "đŸ–ŧī¸", type: "JPEG Image" },
242
+ { name: "video.mp4", icon: "đŸŽĨ", type: "MP4 Video" },
243
+ { name: "archive.zip", icon: "đŸ“Ļ", type: "ZIP Archive" },
244
+ ];
245
+
246
+ const elementRef = useRef();
247
+
248
+ return (
249
+ <div className="demo-container">
250
+ <h1 className="demo-title">🔗 Nested Shortcuts Demo</h1>
251
+
252
+ <div className="demo-description">
253
+ <h3>Container vs Link Shortcuts:</h3>
254
+
255
+ <div className="shortcut-section">
256
+ <h4>
257
+ 📁 Container Shortcuts (work anywhere in file explorer):
258
+ </h4>
259
+ <ul className="shortcut-list">
260
+ <li className="shortcut-item">
261
+ <span className="shortcut-key">a</span>
262
+ <span className="shortcut-description">
263
+ Select all files
264
+ </span>
265
+ </li>
266
+ <li className="shortcut-item">
267
+ <span className="shortcut-key">Delete</span>
268
+ <span className="shortcut-description">
269
+ Delete selected files
270
+ </span>
271
+ </li>
272
+ <li className="shortcut-item">
273
+ <span className="shortcut-key">r</span>
274
+ <span className="shortcut-description">
275
+ Refresh file list
276
+ </span>
277
+ </li>
278
+ </ul>
279
+ </div>
280
+
281
+ <div className="shortcut-section">
282
+ <h4>🔗 Link Shortcuts (work only when link is focused):</h4>
283
+ <ul className="shortcut-list">
284
+ <li className="shortcut-item">
285
+ <span className="shortcut-key">Enter</span>
286
+ <span className="shortcut-description">
287
+ Open/download file
288
+ </span>
289
+ </li>
290
+ <li className="shortcut-item">
291
+ <span className="shortcut-key">Space</span>
292
+ <span className="shortcut-description">Preview file</span>
293
+ </li>
294
+ <li className="shortcut-item">
295
+ <span className="shortcut-key">i</span>
296
+ <span className="shortcut-description">Show file info</span>
297
+ </li>
298
+ </ul>
299
+ </div>
300
+ </div>
301
+
302
+ <div className="status-display">
303
+ <div className="status-title">Action Status:</div>
304
+ <div>{status}</div>
305
+ </div>
306
+
307
+ <div className="file-explorer">
308
+ <h2 className="explorer-title">📁 File Explorer</h2>
309
+
310
+ <SelectionProvider
311
+ value={selection}
312
+ onChange={(value) => {
313
+ setSelection(value);
314
+ }}
315
+ >
316
+ <ShortcutProvider
317
+ elementRef={elementRef}
318
+ shortcuts={containerShortcuts}
319
+ onActionStart={() => {
320
+ setDeletedFiles(selection);
321
+ }}
322
+ onActionEnd={() => {
323
+ setDeletedFiles([]);
324
+ }}
325
+ >
326
+ <ul className="file-list" ref={elementRef}>
327
+ {files.map((file) => (
328
+ <li key={file.name} className="file-item">
329
+ <Link
330
+ className="file-link"
331
+ href="#"
332
+ value={file.name}
333
+ readOnly={deletedFiles.includes(file.name)}
334
+ loading={deletedFiles.includes(file.name)}
335
+ shortcuts={[
336
+ {
337
+ key: "enter",
338
+ description: `Open ${file.name}`,
339
+ action: () => {
340
+ setStatus(
341
+ `📂 Opening ${file.name} (link shortcut)`,
342
+ );
343
+ },
344
+ },
345
+ {
346
+ key: "space",
347
+ description: `Preview ${file.name}`,
348
+ action: () => {
349
+ setStatus(
350
+ `đŸ‘ī¸ Previewing ${file.name} (link shortcut)`,
351
+ );
352
+ },
353
+ },
354
+ {
355
+ key: "i",
356
+ description: `Info for ${file.name}`,
357
+ action: () => {
358
+ setStatus(
359
+ `â„šī¸ Info: ${file.name} - ${file.type} (link shortcut)`,
360
+ );
361
+ },
362
+ },
363
+ ]}
364
+ >
365
+ <span className="file-icon">{file.icon}</span>
366
+ {file.name}
367
+ </Link>
368
+ </li>
369
+ ))}
370
+ </ul>
371
+ </ShortcutProvider>
372
+ </SelectionProvider>
373
+ </div>
374
+
375
+ <div className="instructions">
376
+ <h4>đŸŽ¯ How to Test:</h4>
377
+ <p>
378
+ <strong>Container shortcuts:</strong> Click anywhere in the file
379
+ explorer area, then press 'a', 'Delete', or 'r'
380
+ </p>
381
+ <p>
382
+ <strong>Link shortcuts:</strong> Tab to focus a specific file,
383
+ then press 'Enter', 'Space', or 'i'
384
+ </p>
385
+ <p>
386
+ <strong>Precedence:</strong> When a link is focused, link
387
+ shortcuts take priority over container shortcuts
388
+ </p>
389
+ <p>
390
+ <strong>Status display:</strong> Watch the black box above to
391
+ see which shortcuts are triggered
392
+ </p>
393
+ </div>
394
+ </div>
395
+ );
396
+ };
397
+
398
+ render(<App />, document.getElementById("root"));
399
+ </script>
400
+ </body>
401
+ </html>