@jsenv/navi 0.0.1 → 0.1.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 (139) hide show
  1. package/dist/jsenv_navi.js +22959 -0
  2. package/index.js +66 -16
  3. package/package.json +23 -11
  4. package/src/actions.js +50 -26
  5. package/src/browser_integration/browser_integration.js +31 -6
  6. package/src/browser_integration/via_history.js +42 -9
  7. package/src/components/action_execution/render_actionable_component.jsx +6 -4
  8. package/src/components/action_execution/use_action.js +51 -282
  9. package/src/components/action_execution/use_execute_action.js +106 -92
  10. package/src/components/action_execution/use_run_on_mount.js +9 -0
  11. package/src/components/action_renderer.jsx +21 -32
  12. package/src/components/demos/0_button_demo.html +574 -103
  13. package/src/components/demos/10_column_reordering_debug.html +277 -0
  14. package/src/components/demos/11_table_selection_debug.html +432 -0
  15. package/src/components/demos/1_checkbox_demo.html +579 -202
  16. package/src/components/demos/2_input_textual_demo.html +81 -138
  17. package/src/components/demos/3_radio_demo.html +0 -2
  18. package/src/components/demos/4_select_demo.html +19 -23
  19. package/src/components/demos/6_tablist_demo.html +77 -0
  20. package/src/components/demos/7_table_selection_demo.html +176 -0
  21. package/src/components/demos/8_table_fixed_headers_demo.html +584 -0
  22. package/src/components/demos/9_table_column_drag_demo.html +325 -0
  23. package/src/components/demos/action/0_button_demo.html +2 -4
  24. package/src/components/demos/action/1_input_text_demo.html +643 -222
  25. package/src/components/demos/action/3_details_demo.html +146 -115
  26. package/src/components/demos/action/4_input_checkbox_demo.html +442 -322
  27. package/src/components/demos/action/5_input_checkbox_state_demo.html +270 -0
  28. package/src/components/demos/action/6_checkbox_list_demo.html +304 -72
  29. package/src/components/demos/action/7_radio_list_demo.html +310 -170
  30. package/src/components/demos/action/{8_editable_text_demo.html → 8_editable_demo.html} +65 -76
  31. package/src/components/demos/action/9_link_demo.html +84 -62
  32. package/src/components/demos/ui_transition/0_action_renderer_ui_transition_demo.html +695 -0
  33. package/src/components/demos/ui_transition/1_nested_ui_transition_demo.html +429 -0
  34. package/src/components/demos/ui_transition/2_height_transition_test.html +295 -0
  35. package/src/components/details/details.jsx +62 -64
  36. package/src/components/edition/editable.jsx +186 -0
  37. package/src/components/field/README.md +247 -0
  38. package/src/components/{input → field}/button.jsx +151 -130
  39. package/src/components/field/checkbox_list.jsx +184 -0
  40. package/src/components/{collect_form_element_values.js → field/collect_form_element_values.js} +7 -4
  41. package/src/components/{input → field}/field_css.js +4 -1
  42. package/src/components/field/form.jsx +211 -0
  43. package/src/components/{input → field}/input.jsx +1 -0
  44. package/src/components/{input → field}/input_checkbox.jsx +132 -155
  45. package/src/components/{input → field}/input_radio.jsx +135 -46
  46. package/src/components/field/input_textual.jsx +418 -0
  47. package/src/components/field/label.jsx +32 -0
  48. package/src/components/field/radio_list.jsx +182 -0
  49. package/src/components/{input → field}/select.jsx +17 -32
  50. package/src/components/field/use_action_events.js +132 -0
  51. package/src/components/field/use_form_events.js +55 -0
  52. package/src/components/field/use_ui_state_controller.js +506 -0
  53. package/src/components/item_tracker/README.md +461 -0
  54. package/src/components/item_tracker/use_isolated_item_tracker.jsx +209 -0
  55. package/src/components/item_tracker/use_isolated_item_tracker_demo.html +148 -0
  56. package/src/components/item_tracker/use_isolated_item_tracker_demo.jsx +460 -0
  57. package/src/components/item_tracker/use_item_tracker.jsx +143 -0
  58. package/src/components/item_tracker/use_item_tracker_demo.html +207 -0
  59. package/src/components/item_tracker/use_item_tracker_demo.jsx +216 -0
  60. package/src/components/keyboard_shortcuts/active_keyboard_shortcuts.jsx +87 -0
  61. package/src/components/keyboard_shortcuts/aria_key_shortcuts.js +61 -0
  62. package/src/components/keyboard_shortcuts/keyboard_key_meta.js +17 -0
  63. package/src/components/keyboard_shortcuts/keyboard_shortcuts.js +371 -0
  64. package/src/components/link/link.jsx +65 -102
  65. package/src/components/link/link_with_icon.jsx +52 -0
  66. package/src/components/loader/loader_background.jsx +85 -64
  67. package/src/components/loader/rectangle_loading.jsx +38 -19
  68. package/src/components/route.jsx +8 -4
  69. package/src/components/selection/selection.jsx +1583 -0
  70. package/src/components/svg/font_sized_svg.jsx +45 -0
  71. package/src/components/svg/icon_and_text.jsx +21 -0
  72. package/src/components/svg/svg_mask_overlay.jsx +105 -0
  73. package/src/components/table/drag/table_drag.jsx +506 -0
  74. package/src/components/table/resize/table_resize.jsx +650 -0
  75. package/src/components/table/resize/table_size.js +43 -0
  76. package/src/components/table/selection/table_selection.js +106 -0
  77. package/src/components/table/selection/table_selection.jsx +203 -0
  78. package/src/components/table/sticky/sticky_group.js +354 -0
  79. package/src/components/table/sticky/table_sticky.js +25 -0
  80. package/src/components/table/sticky/table_sticky.jsx +501 -0
  81. package/src/components/table/table.jsx +721 -0
  82. package/src/components/table/table_css.js +211 -0
  83. package/src/components/table/table_ui.jsx +49 -0
  84. package/src/components/table/use_cells_and_columns.js +90 -0
  85. package/src/components/table/use_object_array_to_cells.js +46 -0
  86. package/src/components/table/z_indexes.js +23 -0
  87. package/src/components/tablist/tablist.jsx +99 -0
  88. package/src/components/text/overflow.jsx +15 -0
  89. package/src/components/text/text_and_count.jsx +28 -0
  90. package/src/components/ui_transition.jsx +128 -0
  91. package/src/components/use_auto_focus.js +58 -7
  92. package/src/components/use_batch_during_render.js +33 -0
  93. package/src/components/use_debounce_true.js +7 -7
  94. package/src/components/use_dependencies_diff.js +35 -0
  95. package/src/components/use_focus_group.js +4 -3
  96. package/src/components/use_initial_value.js +8 -34
  97. package/src/components/use_signal_sync.js +1 -1
  98. package/src/components/use_stable_callback.js +68 -0
  99. package/src/components/use_state_array.js +16 -9
  100. package/src/docs/actions.md +22 -0
  101. package/src/notes.md +33 -12
  102. package/src/route/route.js +97 -47
  103. package/src/store/resource_graph.js +2 -1
  104. package/src/store/tests/{resource_graph_dependencies.test.js → resource_graph_dependencies.test_manual.js} +13 -13
  105. package/src/utils/is_signal.js +20 -0
  106. package/src/utils/stringify_for_display.js +4 -23
  107. package/src/validation/constraints/confirm_constraint.js +14 -0
  108. package/src/validation/constraints/create_unique_value_constraint.js +27 -0
  109. package/src/validation/constraints/native_constraints.js +313 -0
  110. package/src/validation/constraints/readonly_constraint.js +36 -0
  111. package/src/validation/constraints/single_space_constraint.js +13 -0
  112. package/src/validation/custom_constraint_validation.js +599 -0
  113. package/src/validation/custom_message.js +18 -0
  114. package/src/validation/demos/browser_style.png +0 -0
  115. package/src/validation/demos/form_validation_demo.html +142 -0
  116. package/src/validation/demos/form_validation_demo_preact.html +87 -0
  117. package/src/validation/demos/form_validation_native_popover_demo.html +168 -0
  118. package/src/validation/demos/form_validation_vs_native_demo.html +172 -0
  119. package/src/validation/demos/validation_message_demo.html +203 -0
  120. package/src/validation/hooks/use_constraints.js +23 -0
  121. package/src/validation/hooks/use_custom_validation_ref.js +73 -0
  122. package/src/validation/hooks/use_validation_message.js +19 -0
  123. package/src/validation/validation_message.js +741 -0
  124. package/src/components/editable_text/editable_text.jsx +0 -96
  125. package/src/components/form.jsx +0 -144
  126. package/src/components/input/checkbox_list.jsx +0 -294
  127. package/src/components/input/field.jsx +0 -61
  128. package/src/components/input/input_textual.jsx +0 -338
  129. package/src/components/input/radio_list.jsx +0 -283
  130. package/src/components/input/use_form_event.js +0 -20
  131. package/src/components/input/use_on_change.js +0 -12
  132. package/src/components/selection/selection.js +0 -5
  133. package/src/components/selection/selection_context.jsx +0 -262
  134. package/src/components/shortcut/shortcut_context.jsx +0 -390
  135. package/src/components/use_action_events.js +0 -37
  136. package/src/utils/iterable_weak_set.js +0 -62
  137. /package/src/components/demos/action/{11_nested_shortcuts_demo.html → 11_nested_shortcuts_demo.xhtml} +0 -0
  138. /package/src/components/{shortcut → keyboard_shortcuts}/os.js +0 -0
  139. /package/src/route/{route.test.html → route.xtest.html} +0 -0
@@ -194,155 +194,98 @@
194
194
  <script type="module" jsenv-type="module/jsx">
195
195
  import { render } from "preact";
196
196
  import { useState } from "preact/hooks";
197
- import {
198
- // eslint-disable-next-line no-unused-vars
199
- Input,
200
- // eslint-disable-next-line no-unused-vars
201
- Field,
202
- } from "@jsenv/navi";
197
+ // eslint-disable-next-line no-unused-vars
198
+ import { Input } from "@jsenv/navi";
203
199
 
204
200
  // eslint-disable-next-line no-unused-vars
205
201
  const App = () => {
202
+ return (
203
+ <div className="demo-grid">
204
+ <LoadingDemo />
205
+ <ReadOnlyDemo />
206
+ <DisabledDemo />
207
+ </div>
208
+ );
209
+ };
210
+
211
+ // eslint-disable-next-line no-unused-vars
212
+ const LoadingDemo = () => {
213
+ const [value, setValue] = useState();
206
214
  const [loading, setLoading] = useState(false);
207
- const [loadingB, setLoadingB] = useState(false);
208
- const [loadingC, setLoadingC] = useState(false);
209
- const [loadingD, setLoadingD] = useState(false);
210
215
 
211
216
  return (
212
- <div className="demo-grid">
213
- <div className="demo-card">
214
- <h3 className="demo-title">💫 Loading State</h3>
215
- <div className="control-group">
216
- <Field
217
- input={
218
- <Input loading={loading} placeholder="Enter some text..." />
219
- }
220
- label="Basic Input"
221
- />
222
- </div>
223
- <div className="native-comparison">
224
- <div className="native-demo">
225
- <h4>@jsenv/navi Input</h4>
226
- <Input loading={loading} placeholder="Enter some text..." />
227
- </div>
228
- <div className="native-demo">
229
- <h4>Native HTML Input</h4>
230
- <input placeholder="Native comparison" />
231
- </div>
232
- </div>
233
- <div className="button-group">
234
- <button onClick={() => setLoading(!loading)}>
235
- {loading ? "Stop Loading" : "Start Loading"}
236
- </button>
237
- </div>
217
+ <div className="demo-card">
218
+ <h3 className="demo-title">💫 Loading State</h3>
219
+ <div className="control-group">
220
+ <h4>@jsenv/navi Input</h4>
221
+ <Input
222
+ loading={loading}
223
+ value={value}
224
+ onUIStateChange={setValue}
225
+ placeholder="Enter some text..."
226
+ />
238
227
  </div>
239
-
240
- <div className="demo-card">
241
- <h3 className="demo-title">⚡ Action Input</h3>
242
- <div className="control-group">
243
- <Field
244
- input={
245
- <Input
246
- name="action-input"
247
- action={() => {}}
248
- loading={loadingB}
249
- placeholder="Input with action..."
250
- />
251
- }
252
- label="Action Input"
253
- />
254
- </div>
255
- <div className="native-comparison">
256
- <div className="native-demo">
257
- <h4>@jsenv/navi Input with Action</h4>
258
- <Input
259
- name="action-input"
260
- action={() => {}}
261
- loading={loadingB}
262
- placeholder="Input with action..."
263
- />
264
- </div>
265
- <div className="native-demo">
266
- <h4>Native HTML Input</h4>
267
- <input placeholder="Native has no built-in action" />
268
- </div>
269
- </div>
270
- <div className="button-group">
271
- <button onClick={() => setLoadingB(!loadingB)}>
272
- {loadingB ? "Stop Loading" : "Start Loading"}
273
- </button>
274
- </div>
228
+ <div className="control-group">
229
+ <h4>Native HTML Input</h4>
230
+ <input placeholder="Enter some text..." />
275
231
  </div>
232
+ <div className="button-group">
233
+ <button onClick={() => setLoading(!loading)}>
234
+ {loading ? "Stop Loading" : "Start Loading"}
235
+ </button>
236
+ </div>
237
+ </div>
238
+ );
239
+ };
276
240
 
277
- <div className="demo-card">
278
- <h3 className="demo-title">🔒 Read-only State</h3>
279
- <div className="control-group">
280
- <Field
281
- input={
282
- <Input
283
- value="This is read-only content"
284
- readonly
285
- loading={loadingC}
286
- />
287
- }
288
- label="Read-only Input"
289
- readonly
290
- />
291
- </div>
292
- <div className="native-comparison">
293
- <div className="native-demo">
294
- <h4>@jsenv/navi Read-only</h4>
295
- <Input
296
- value="This is read-only content"
297
- readonly
298
- loading={loadingC}
299
- />
300
- </div>
301
- <div className="native-demo">
302
- <h4>Native Read-only</h4>
303
- <input readonly value="Native read-only" />
304
- </div>
305
- </div>
306
- <div className="button-group">
307
- <button onClick={() => setLoadingC(!loadingC)}>
308
- {loadingC ? "Stop Loading" : "Start Loading"}
309
- </button>
310
- </div>
241
+ // eslint-disable-next-line no-unused-vars
242
+ const ReadOnlyDemo = () => {
243
+ // not giving onValueChange param makes it read-only
244
+ const [loading, setLoading] = useState(false);
245
+
246
+ return (
247
+ <div className="demo-card">
248
+ <h3 className="demo-title">🔒 Read-only State</h3>
249
+ <div className="control-group">
250
+ <h4>@jsenv/navi Read-only</h4>
251
+ <Input value="This is read-only content" loading={loading} />
252
+ </div>
253
+ <div className="control-group">
254
+ <h4>Native Read-only</h4>
255
+ <input readOnly value="This is read-only content" />
311
256
  </div>
257
+ <div className="button-group">
258
+ <button onClick={() => setLoading(!loading)}>
259
+ {loading ? "Stop Loading" : "Start Loading"}
260
+ </button>
261
+ </div>
262
+ </div>
263
+ );
264
+ };
265
+
266
+ // eslint-disable-next-line no-unused-vars
267
+ const DisabledDemo = () => {
268
+ const [loading, setLoading] = useState(false);
312
269
 
313
- <div className="demo-card">
314
- <h3 className="demo-title">🚫 Disabled State</h3>
315
- <div className="control-group">
316
- <Field
317
- input={
318
- <Input
319
- disabled
320
- loading={loadingD}
321
- placeholder="Disabled input..."
322
- />
323
- }
324
- label="Disabled Input"
325
- />
326
- </div>
327
- <div className="native-comparison">
328
- <div className="native-demo">
329
- <h4>@jsenv/navi Disabled</h4>
330
- <Input
331
- disabled
332
- loading={loadingD}
333
- placeholder="Disabled input..."
334
- />
335
- </div>
336
- <div className="native-demo">
337
- <h4>Native Disabled</h4>
338
- <input disabled placeholder="Native disabled" />
339
- </div>
340
- </div>
341
- <div className="button-group">
342
- <button onClick={() => setLoadingD(!loadingD)}>
343
- {loadingD ? "Stop Loading" : "Start Loading"}
344
- </button>
345
- </div>
270
+ return (
271
+ <div className="demo-card">
272
+ <h3 className="demo-title">🚫 Disabled State</h3>
273
+ <div className="control-group">
274
+ <h4>@jsenv/navi Disabled</h4>
275
+ <Input
276
+ disabled
277
+ loading={loading}
278
+ placeholder="Disabled input..."
279
+ />
280
+ </div>
281
+ <div className="control-group">
282
+ <h4>Native Disabled</h4>
283
+ <input disabled placeholder="Disabled input..." />
284
+ </div>
285
+ <div className="button-group">
286
+ <button onClick={() => setLoading(!loading)}>
287
+ {loading ? "Stop Loading" : "Start Loading"}
288
+ </button>
346
289
  </div>
347
290
  </div>
348
291
  );
@@ -15,8 +15,6 @@
15
15
  import {
16
16
  // eslint-disable-next-line no-unused-vars
17
17
  Input,
18
- // eslint-disable-next-line no-unused-vars
19
- Field,
20
18
  } from "@jsenv/navi";
21
19
 
22
20
  // eslint-disable-next-line no-unused-vars
@@ -16,7 +16,7 @@
16
16
  // eslint-disable-next-line no-unused-vars
17
17
  Select,
18
18
  // eslint-disable-next-line no-unused-vars
19
- Field,
19
+ Label,
20
20
  } from "@jsenv/navi";
21
21
 
22
22
  // eslint-disable-next-line no-unused-vars
@@ -41,17 +41,15 @@
41
41
  <strong>Loading</strong>
42
42
  </p>
43
43
 
44
- <Field
45
- input={
46
- <Select name="loading" loading={loading}>
47
- {[
48
- { label: "a", value: "a" },
49
- { label: "b", value: "b" },
50
- ]}
51
- </Select>
52
- }
53
- label="Coucou"
54
- ></Field>
44
+ <Label>
45
+ Coucou
46
+ <Select name="loading" loading={loading}>
47
+ {[
48
+ { label: "a", value: "a" },
49
+ { label: "b", value: "b" },
50
+ ]}
51
+ </Select>
52
+ </Label>
55
53
 
56
54
  <div>
57
55
  <label>
@@ -69,17 +67,15 @@
69
67
  <strong>Readonly</strong>
70
68
  </p>
71
69
 
72
- <Field
73
- input={
74
- <Select name="loading" loading={loading} value="b" readOnly>
75
- {[
76
- { label: "a", value: "a" },
77
- { label: "b", value: "b" },
78
- ]}
79
- </Select>
80
- }
81
- label="Coucou"
82
- ></Field>
70
+ <Label>
71
+ Coucou
72
+ <Select name="loading" loading={loading} value="b" readOnly>
73
+ {[
74
+ { label: "a", value: "a" },
75
+ { label: "b", value: "b" },
76
+ ]}
77
+ </Select>
78
+ </Label>
83
79
 
84
80
  <div>
85
81
  <label>
@@ -0,0 +1,77 @@
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>TabList demo</title>
8
+ <style>
9
+ .tab_button {
10
+ background: none;
11
+ border: none;
12
+ color: inherit;
13
+ font: inherit;
14
+ cursor: pointer;
15
+ }
16
+ </style>
17
+ </head>
18
+ <body>
19
+ <div id="root" style="position: relative"></div>
20
+
21
+ <script type="module" jsenv-type="module/jsx">
22
+ import { render } from "preact";
23
+ import { useState } from "preact/hooks";
24
+ import {
25
+ // eslint-disable-next-line no-unused-vars
26
+ TabList,
27
+ // eslint-disable-next-line no-unused-vars
28
+ Tab,
29
+ } from "../tablist/tablist.jsx";
30
+
31
+ const tabs = [
32
+ { id: "overview", label: "Overview" },
33
+ { id: "code", label: "Code" },
34
+ { id: "issues", label: "Issues" },
35
+ { id: "pulls", label: "Pull requests" },
36
+ { id: "actions", label: "Actions" },
37
+ { id: "projects", label: "Projects" },
38
+ { id: "wiki", label: "Wiki" },
39
+ { id: "security", label: "Security" },
40
+ { id: "insights", label: "Insights" },
41
+ ];
42
+
43
+ // eslint-disable-next-line no-unused-vars
44
+ const App = () => {
45
+ const [selectedTabId, setSelectedTabId] = useState("overview");
46
+
47
+ return (
48
+ <div>
49
+ <p style="max-width: 800px; margin: 20px auto 0;">
50
+ Click the tabs to toggle the active one. The underline marker is
51
+ shown for the active tab.
52
+ </p>
53
+ <TabList aria-label="Demo tabs">
54
+ {tabs.map((tab) => (
55
+ <Tab key={tab.id} selected={selectedTabId === tab.id}>
56
+ <button
57
+ type="button"
58
+ className="tab_button"
59
+ onClick={() => setSelectedTabId(tab.id)}
60
+ >
61
+ {tab.label}
62
+ </button>
63
+ </Tab>
64
+ ))}
65
+ </TabList>
66
+ <div style="max-width: 800px; margin: 16px auto; padding: 12px; border: 1px dashed #ccc; border-radius: 8px;">
67
+ <strong>Selected tab:</strong>{" "}
68
+ {tabs.find((t) => t.id === selectedTabId).label}
69
+ </div>
70
+ </div>
71
+ );
72
+ };
73
+
74
+ render(<App />, document.querySelector("#root"));
75
+ </script>
76
+ </body>
77
+ </html>
@@ -0,0 +1,176 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Table Cell Selection Demo - Preact</title>
7
+ <style>
8
+ body {
9
+ font-family:
10
+ system-ui,
11
+ -apple-system,
12
+ sans-serif;
13
+ margin: 20px;
14
+ background: #f5f5f5;
15
+ }
16
+
17
+ .demo-container {
18
+ max-width: 800px;
19
+ margin: 0 auto;
20
+ background: white;
21
+ padding: 20px;
22
+ border-radius: 8px;
23
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
24
+ }
25
+
26
+ /* Status display */
27
+ .status {
28
+ margin-top: 20px;
29
+ padding: 10px;
30
+ background: #f0f8ff;
31
+ border-radius: 4px;
32
+ font-family: monospace;
33
+ font-size: 14px;
34
+ }
35
+
36
+ .actions {
37
+ margin-top: 15px;
38
+ }
39
+
40
+ button {
41
+ background: #0078d4;
42
+ color: white;
43
+ border: none;
44
+ padding: 8px 16px;
45
+ border-radius: 4px;
46
+ cursor: pointer;
47
+ margin-right: 8px;
48
+ }
49
+
50
+ button:hover {
51
+ background: #106ebe;
52
+ }
53
+
54
+ button:disabled {
55
+ background: #ccc;
56
+ cursor: not-allowed;
57
+ }
58
+ </style>
59
+ </head>
60
+ <body>
61
+ <div id="root"></div>
62
+
63
+ <script type="module" jsenv-type="module/jsx">
64
+ import { render } from "preact";
65
+ import { useRef, useState } from "preact/hooks";
66
+ import {
67
+ // eslint-disable-next-line no-unused-vars
68
+ Table,
69
+ } from "@jsenv/navi";
70
+
71
+ // Sample data
72
+ const data = [
73
+ {
74
+ id: "1",
75
+ index: 1,
76
+ name: "1A",
77
+ email: "2A",
78
+ status: "3A",
79
+ },
80
+ {
81
+ id: "2",
82
+ index: 2,
83
+ name: "1B",
84
+ email: "2B",
85
+ status: "3B",
86
+ },
87
+ {
88
+ id: "3",
89
+ index: 3,
90
+ name: "1C",
91
+ email: "2C",
92
+ status: "3C",
93
+ },
94
+ {
95
+ id: "4",
96
+ index: 4,
97
+ name: "4A",
98
+ email: "4B",
99
+ status: "4C",
100
+ },
101
+ ];
102
+
103
+ // Column definitions
104
+ const columns = [
105
+ { id: "name", accessorKey: "name", header: "Name" },
106
+ { id: "email", accessorKey: "email", header: "Email" },
107
+ { id: "status", accessorKey: "status", header: "Status" },
108
+ ];
109
+
110
+ // eslint-disable-next-line no-unused-vars
111
+ const TableDemo = () => {
112
+ const tableRef = useRef(null);
113
+ const [selection, setSelection] = useState([]);
114
+
115
+ return (
116
+ <div className="demo-container">
117
+ <h1>Table Cell Selection Demo - Preact</h1>
118
+ <p>
119
+ Click cells to select/deselect. Use Cmd/Ctrl+click for
120
+ multi-select. Use Tab and arrow keys to navigate.
121
+ <br />
122
+ <strong>Click the # column to select entire rows.</strong>
123
+ <br />
124
+ <strong>Click column headers to select entire columns.</strong>
125
+ </p>
126
+
127
+ <Table
128
+ ref={tableRef}
129
+ data={data}
130
+ columns={columns}
131
+ selection={selection}
132
+ onSelectionChange={(value) => {
133
+ setSelection(value);
134
+ }}
135
+ />
136
+
137
+ <div className="status">
138
+ <div>
139
+ Selection: <span>{JSON.stringify(selection)}</span>
140
+ </div>
141
+ <div>
142
+ Total selection count: <span>{selection.length}</span>
143
+ </div>
144
+ </div>
145
+
146
+ <div className="actions">
147
+ <button
148
+ onClick={() => {
149
+ tableRef.current.clearSelection();
150
+ }}
151
+ >
152
+ Clear Selection
153
+ </button>
154
+ <button
155
+ onClick={() => {
156
+ tableRef.current.selectAll();
157
+ }}
158
+ >
159
+ Select All
160
+ </button>
161
+ </div>
162
+ </div>
163
+ );
164
+ };
165
+
166
+ // Render the app
167
+ render(<TableDemo />, document.getElementById("root"));
168
+
169
+ console.log("Preact Table selection demo loaded. Try:");
170
+ console.log("- Click cells to select/deselect");
171
+ console.log("- Use Cmd/Ctrl+click for multi-select");
172
+ console.log("- Use Tab and arrow keys to navigate");
173
+ console.log("- Press Space or Enter to toggle selection");
174
+ </script>
175
+ </body>
176
+ </html>