@jsenv/navi 0.0.1 → 0.1.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 +22954 -0
- package/index.js +66 -16
- package/package.json +22 -11
- package/src/actions.js +50 -26
- package/src/browser_integration/browser_integration.js +31 -6
- package/src/browser_integration/via_history.js +42 -9
- package/src/components/action_execution/render_actionable_component.jsx +6 -4
- package/src/components/action_execution/use_action.js +51 -282
- package/src/components/action_execution/use_execute_action.js +106 -92
- package/src/components/action_execution/use_run_on_mount.js +9 -0
- package/src/components/action_renderer.jsx +21 -32
- package/src/components/demos/0_button_demo.html +574 -103
- package/src/components/demos/10_column_reordering_debug.html +277 -0
- package/src/components/demos/11_table_selection_debug.html +432 -0
- package/src/components/demos/1_checkbox_demo.html +579 -202
- package/src/components/demos/2_input_textual_demo.html +81 -138
- package/src/components/demos/3_radio_demo.html +0 -2
- package/src/components/demos/4_select_demo.html +19 -23
- package/src/components/demos/6_tablist_demo.html +77 -0
- package/src/components/demos/7_table_selection_demo.html +176 -0
- package/src/components/demos/8_table_fixed_headers_demo.html +584 -0
- package/src/components/demos/9_table_column_drag_demo.html +325 -0
- package/src/components/demos/action/0_button_demo.html +2 -4
- package/src/components/demos/action/1_input_text_demo.html +643 -222
- package/src/components/demos/action/3_details_demo.html +146 -115
- package/src/components/demos/action/4_input_checkbox_demo.html +442 -322
- package/src/components/demos/action/5_input_checkbox_state_demo.html +270 -0
- package/src/components/demos/action/6_checkbox_list_demo.html +304 -72
- package/src/components/demos/action/7_radio_list_demo.html +310 -170
- package/src/components/demos/action/{8_editable_text_demo.html → 8_editable_demo.html} +65 -76
- package/src/components/demos/action/9_link_demo.html +84 -62
- package/src/components/demos/ui_transition/0_action_renderer_ui_transition_demo.html +695 -0
- package/src/components/demos/ui_transition/1_nested_ui_transition_demo.html +429 -0
- package/src/components/demos/ui_transition/2_height_transition_test.html +295 -0
- package/src/components/details/details.jsx +62 -64
- package/src/components/edition/editable.jsx +186 -0
- package/src/components/field/README.md +247 -0
- package/src/components/{input → field}/button.jsx +151 -130
- package/src/components/field/checkbox_list.jsx +184 -0
- package/src/components/{collect_form_element_values.js → field/collect_form_element_values.js} +7 -4
- package/src/components/{input → field}/field_css.js +4 -1
- package/src/components/field/form.jsx +211 -0
- package/src/components/{input → field}/input.jsx +1 -0
- package/src/components/{input → field}/input_checkbox.jsx +132 -155
- package/src/components/{input → field}/input_radio.jsx +135 -46
- package/src/components/{input → field}/input_textual.jsx +247 -173
- package/src/components/field/label.jsx +32 -0
- package/src/components/field/radio_list.jsx +182 -0
- package/src/components/{input → field}/select.jsx +17 -32
- package/src/components/field/use_action_events.js +132 -0
- package/src/components/field/use_form_events.js +55 -0
- package/src/components/field/use_ui_state_controller.js +506 -0
- package/src/components/item_tracker/README.md +461 -0
- package/src/components/item_tracker/use_isolated_item_tracker.jsx +209 -0
- package/src/components/item_tracker/use_isolated_item_tracker_demo.html +148 -0
- package/src/components/item_tracker/use_isolated_item_tracker_demo.jsx +460 -0
- package/src/components/item_tracker/use_item_tracker.jsx +143 -0
- package/src/components/item_tracker/use_item_tracker_demo.html +207 -0
- package/src/components/item_tracker/use_item_tracker_demo.jsx +216 -0
- package/src/components/keyboard_shortcuts/active_keyboard_shortcuts.jsx +87 -0
- package/src/components/keyboard_shortcuts/aria_key_shortcuts.js +61 -0
- package/src/components/keyboard_shortcuts/keyboard_key_meta.js +17 -0
- package/src/components/keyboard_shortcuts/keyboard_shortcuts.js +371 -0
- package/src/components/link/link.jsx +65 -102
- package/src/components/link/link_with_icon.jsx +52 -0
- package/src/components/loader/loader_background.jsx +85 -64
- package/src/components/loader/rectangle_loading.jsx +38 -19
- package/src/components/route.jsx +8 -4
- package/src/components/selection/selection.jsx +1583 -0
- package/src/components/svg/font_sized_svg.jsx +45 -0
- package/src/components/svg/icon_and_text.jsx +21 -0
- package/src/components/svg/svg_mask_overlay.jsx +105 -0
- package/src/components/table/drag/table_drag.jsx +506 -0
- package/src/components/table/resize/table_resize.jsx +650 -0
- package/src/components/table/resize/table_size.js +43 -0
- package/src/components/table/selection/table_selection.js +106 -0
- package/src/components/table/selection/table_selection.jsx +203 -0
- package/src/components/table/sticky/sticky_group.js +354 -0
- package/src/components/table/sticky/table_sticky.js +25 -0
- package/src/components/table/sticky/table_sticky.jsx +501 -0
- package/src/components/table/table.jsx +721 -0
- package/src/components/table/table_css.js +211 -0
- package/src/components/table/table_ui.jsx +49 -0
- package/src/components/table/use_cells_and_columns.js +90 -0
- package/src/components/table/use_object_array_to_cells.js +46 -0
- package/src/components/table/z_indexes.js +23 -0
- package/src/components/tablist/tablist.jsx +99 -0
- package/src/components/text/overflow.jsx +15 -0
- package/src/components/text/text_and_count.jsx +28 -0
- package/src/components/ui_transition.jsx +128 -0
- package/src/components/use_auto_focus.js +58 -7
- package/src/components/use_batch_during_render.js +33 -0
- package/src/components/use_debounce_true.js +7 -7
- package/src/components/use_dependencies_diff.js +35 -0
- package/src/components/use_focus_group.js +4 -3
- package/src/components/use_initial_value.js +8 -34
- package/src/components/use_signal_sync.js +1 -1
- package/src/components/use_stable_callback.js +68 -0
- package/src/components/use_state_array.js +16 -9
- package/src/docs/actions.md +22 -0
- package/src/notes.md +33 -12
- package/src/route/route.js +97 -47
- package/src/store/resource_graph.js +2 -1
- package/src/store/tests/{resource_graph_dependencies.test.js → resource_graph_dependencies.test_manual.js} +13 -13
- package/src/utils/is_signal.js +20 -0
- package/src/utils/stringify_for_display.js +4 -23
- package/src/validation/constraints/confirm_constraint.js +14 -0
- package/src/validation/constraints/create_unique_value_constraint.js +27 -0
- package/src/validation/constraints/native_constraints.js +313 -0
- package/src/validation/constraints/readonly_constraint.js +36 -0
- package/src/validation/constraints/single_space_constraint.js +13 -0
- package/src/validation/custom_constraint_validation.js +599 -0
- package/src/validation/custom_message.js +18 -0
- package/src/validation/demos/browser_style.png +0 -0
- package/src/validation/demos/form_validation_demo.html +142 -0
- package/src/validation/demos/form_validation_demo_preact.html +87 -0
- package/src/validation/demos/form_validation_native_popover_demo.html +168 -0
- package/src/validation/demos/form_validation_vs_native_demo.html +172 -0
- package/src/validation/demos/validation_message_demo.html +203 -0
- package/src/validation/hooks/use_constraints.js +23 -0
- package/src/validation/hooks/use_custom_validation_ref.js +73 -0
- package/src/validation/hooks/use_validation_message.js +19 -0
- package/src/validation/validation_message.js +741 -0
- package/src/components/editable_text/editable_text.jsx +0 -96
- package/src/components/form.jsx +0 -144
- package/src/components/input/checkbox_list.jsx +0 -294
- package/src/components/input/field.jsx +0 -61
- package/src/components/input/radio_list.jsx +0 -283
- package/src/components/input/use_form_event.js +0 -20
- package/src/components/input/use_on_change.js +0 -12
- package/src/components/selection/selection.js +0 -5
- package/src/components/selection/selection_context.jsx +0 -262
- package/src/components/shortcut/shortcut_context.jsx +0 -390
- package/src/components/use_action_events.js +0 -37
- package/src/utils/iterable_weak_set.js +0 -62
- /package/src/components/demos/action/{11_nested_shortcuts_demo.html → 11_nested_shortcuts_demo.xhtml} +0 -0
- /package/src/components/{shortcut → keyboard_shortcuts}/os.js +0 -0
- /package/src/route/{route.test.html → route.xtest.html} +0 -0
|
@@ -95,6 +95,7 @@
|
|
|
95
95
|
min-height: 40px;
|
|
96
96
|
font-size: 16px;
|
|
97
97
|
line-height: 1.4;
|
|
98
|
+
position: relative;
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
.result-display {
|
|
@@ -156,11 +157,11 @@
|
|
|
156
157
|
import { render } from "preact";
|
|
157
158
|
import { useState } from "preact/hooks";
|
|
158
159
|
import { signal } from "@preact/signals";
|
|
159
|
-
import { SINGLE_SPACE_CONSTRAINT } from "@jsenv/validation";
|
|
160
160
|
import {
|
|
161
|
-
|
|
161
|
+
SINGLE_SPACE_CONSTRAINT,
|
|
162
|
+
useEditionController,
|
|
162
163
|
// eslint-disable-next-line no-unused-vars
|
|
163
|
-
|
|
164
|
+
Editable,
|
|
164
165
|
createAction,
|
|
165
166
|
} from "@jsenv/navi";
|
|
166
167
|
|
|
@@ -172,14 +173,14 @@
|
|
|
172
173
|
<LoadingDemo />
|
|
173
174
|
<ErrorDemo />
|
|
174
175
|
<CustomStyleDemo />
|
|
175
|
-
<
|
|
176
|
+
<ActionObjectDemo />
|
|
176
177
|
</div>
|
|
177
178
|
);
|
|
178
179
|
};
|
|
179
180
|
|
|
180
181
|
// eslint-disable-next-line no-unused-vars
|
|
181
182
|
const BasicDemo = () => {
|
|
182
|
-
const {
|
|
183
|
+
const { editing, startEditing, stopEditing } = useEditionController();
|
|
183
184
|
const [value, setValue] = useState("Click edit to change this text");
|
|
184
185
|
const [result, setResult] = useState("");
|
|
185
186
|
|
|
@@ -189,25 +190,24 @@
|
|
|
189
190
|
<div className="control-group">
|
|
190
191
|
<label>Editable Content:</label>
|
|
191
192
|
<div className="editable-container">
|
|
192
|
-
<
|
|
193
|
-
|
|
193
|
+
<Editable
|
|
194
|
+
editing={editing}
|
|
194
195
|
onEditEnd={() => {
|
|
195
196
|
stopEditing();
|
|
196
197
|
setResult(``);
|
|
197
198
|
}}
|
|
198
|
-
name="test"
|
|
199
|
-
action={({ test }) => {
|
|
200
|
-
setValue(test);
|
|
201
|
-
return `Updated to: "${test}"`;
|
|
202
|
-
}}
|
|
203
199
|
value={value}
|
|
200
|
+
action={(newText) => {
|
|
201
|
+
setValue(newText);
|
|
202
|
+
return `Updated to: "${newText}"`;
|
|
203
|
+
}}
|
|
204
204
|
constraints={[SINGLE_SPACE_CONSTRAINT]}
|
|
205
205
|
/>
|
|
206
206
|
</div>
|
|
207
207
|
</div>
|
|
208
208
|
<div className="button-group">
|
|
209
|
-
<button onClick={() => startEditing()} disabled={
|
|
210
|
-
{
|
|
209
|
+
<button onClick={() => startEditing()} disabled={editing}>
|
|
210
|
+
{editing ? "Editing..." : "Start Editing"}
|
|
211
211
|
</button>
|
|
212
212
|
</div>
|
|
213
213
|
<div
|
|
@@ -223,7 +223,7 @@
|
|
|
223
223
|
|
|
224
224
|
// eslint-disable-next-line no-unused-vars
|
|
225
225
|
const LoadingDemo = () => {
|
|
226
|
-
const {
|
|
226
|
+
const { editing, startEditing, stopEditing } = useEditionController();
|
|
227
227
|
const [value, setValue] = useState("This will take 2 seconds to save");
|
|
228
228
|
const [result, setResult] = useState("");
|
|
229
229
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -234,22 +234,21 @@
|
|
|
234
234
|
<div className="control-group">
|
|
235
235
|
<label>Editable Content (2s delay):</label>
|
|
236
236
|
<div className="editable-container">
|
|
237
|
-
<
|
|
238
|
-
|
|
237
|
+
<Editable
|
|
238
|
+
editing={editing}
|
|
239
239
|
onEditEnd={() => {
|
|
240
240
|
stopEditing();
|
|
241
241
|
setIsLoading(false);
|
|
242
242
|
setResult();
|
|
243
243
|
}}
|
|
244
|
-
|
|
245
|
-
action={async (
|
|
244
|
+
value={value}
|
|
245
|
+
action={async (newText) => {
|
|
246
246
|
setIsLoading(true);
|
|
247
247
|
setResult("Saving...");
|
|
248
248
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
249
|
-
setValue(
|
|
250
|
-
return `Saved: "${
|
|
249
|
+
setValue(newText);
|
|
250
|
+
return `Saved: "${newText}"`;
|
|
251
251
|
}}
|
|
252
|
-
value={value}
|
|
253
252
|
constraints={[SINGLE_SPACE_CONSTRAINT]}
|
|
254
253
|
/>
|
|
255
254
|
</div>
|
|
@@ -257,11 +256,11 @@
|
|
|
257
256
|
<div className="button-group">
|
|
258
257
|
<button
|
|
259
258
|
onClick={() => startEditing()}
|
|
260
|
-
disabled={
|
|
259
|
+
disabled={editing || isLoading}
|
|
261
260
|
>
|
|
262
261
|
{isLoading
|
|
263
262
|
? "Saving..."
|
|
264
|
-
:
|
|
263
|
+
: editing
|
|
265
264
|
? "Editing..."
|
|
266
265
|
: "Start Editing"}
|
|
267
266
|
</button>
|
|
@@ -279,7 +278,7 @@
|
|
|
279
278
|
|
|
280
279
|
// eslint-disable-next-line no-unused-vars
|
|
281
280
|
const ErrorDemo = () => {
|
|
282
|
-
const {
|
|
281
|
+
const { editing, startEditing, stopEditing } = useEditionController();
|
|
283
282
|
const [value] = useState("This edit will always fail");
|
|
284
283
|
const [result, setResult] = useState("");
|
|
285
284
|
|
|
@@ -289,26 +288,28 @@
|
|
|
289
288
|
<div className="control-group">
|
|
290
289
|
<label>Editable Content (always fails):</label>
|
|
291
290
|
<div className="editable-container">
|
|
292
|
-
<
|
|
293
|
-
|
|
291
|
+
<Editable
|
|
292
|
+
editing={editing}
|
|
294
293
|
onEditEnd={() => {
|
|
295
294
|
stopEditing();
|
|
296
295
|
setResult("");
|
|
297
296
|
}}
|
|
298
|
-
|
|
299
|
-
|
|
297
|
+
value={value}
|
|
298
|
+
name="errorText"
|
|
299
|
+
action={async (newText) => {
|
|
300
300
|
setResult("Processing...");
|
|
301
301
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
302
|
-
throw new Error(
|
|
302
|
+
throw new Error(
|
|
303
|
+
`Cannot save "${newText}" - simulated error`,
|
|
304
|
+
);
|
|
303
305
|
}}
|
|
304
|
-
value={value}
|
|
305
306
|
constraints={[SINGLE_SPACE_CONSTRAINT]}
|
|
306
307
|
/>
|
|
307
308
|
</div>
|
|
308
309
|
</div>
|
|
309
310
|
<div className="button-group">
|
|
310
|
-
<button onClick={() => startEditing()} disabled={
|
|
311
|
-
{
|
|
311
|
+
<button onClick={() => startEditing()} disabled={editing}>
|
|
312
|
+
{editing ? "Editing..." : "Start Editing (Will Fail)"}
|
|
312
313
|
</button>
|
|
313
314
|
</div>
|
|
314
315
|
<div
|
|
@@ -324,7 +325,7 @@
|
|
|
324
325
|
|
|
325
326
|
// eslint-disable-next-line no-unused-vars
|
|
326
327
|
const CustomStyleDemo = () => {
|
|
327
|
-
const {
|
|
328
|
+
const { editing, startEditing, stopEditing } = useEditionController();
|
|
328
329
|
const [value, setValue] = useState("Bold styled text");
|
|
329
330
|
const [result, setResult] = useState("");
|
|
330
331
|
|
|
@@ -334,29 +335,28 @@
|
|
|
334
335
|
<div className="control-group">
|
|
335
336
|
<label>Styled Editable Content:</label>
|
|
336
337
|
<div className="editable-container">
|
|
337
|
-
<
|
|
338
|
-
|
|
338
|
+
<Editable
|
|
339
|
+
editing={editing}
|
|
339
340
|
onEditEnd={() => {
|
|
340
341
|
stopEditing();
|
|
341
342
|
setResult("");
|
|
342
343
|
}}
|
|
343
|
-
name="test"
|
|
344
|
-
action={({ test }) => {
|
|
345
|
-
setValue(test);
|
|
346
|
-
return `Updated styled text: "${test}"`;
|
|
347
|
-
}}
|
|
348
344
|
value={value}
|
|
345
|
+
action={(newText) => {
|
|
346
|
+
setValue(newText);
|
|
347
|
+
return `Updated styled text: "${newText}"`;
|
|
348
|
+
}}
|
|
349
349
|
constraints={[SINGLE_SPACE_CONSTRAINT]}
|
|
350
350
|
>
|
|
351
351
|
<strong style={{ color: "#e74c3c", fontSize: "18px" }}>
|
|
352
352
|
{value}
|
|
353
353
|
</strong>
|
|
354
|
-
</
|
|
354
|
+
</Editable>
|
|
355
355
|
</div>
|
|
356
356
|
</div>
|
|
357
357
|
<div className="button-group">
|
|
358
|
-
<button onClick={() => startEditing()} disabled={
|
|
359
|
-
{
|
|
358
|
+
<button onClick={() => startEditing()} disabled={editing}>
|
|
359
|
+
{editing ? "Editing..." : "Start Editing"}
|
|
360
360
|
</button>
|
|
361
361
|
</div>
|
|
362
362
|
<div
|
|
@@ -371,38 +371,37 @@
|
|
|
371
371
|
};
|
|
372
372
|
|
|
373
373
|
// Create signal outside component to persist across renders
|
|
374
|
-
const
|
|
375
|
-
const
|
|
374
|
+
const editableTextSignal = signal("Click me to edit");
|
|
375
|
+
const updateTextAction = createAction(async (newText) => {
|
|
376
376
|
await new Promise((resolve) => setTimeout(resolve, 800));
|
|
377
|
-
|
|
377
|
+
editableTextSignal.value = newText;
|
|
378
|
+
return `Signal update: "${newText}"`;
|
|
378
379
|
});
|
|
379
|
-
const boundAction = action.bindParams({ test: valueSignal });
|
|
380
|
-
|
|
381
380
|
// eslint-disable-next-line no-unused-vars
|
|
382
|
-
const
|
|
381
|
+
const ActionObjectDemo = () => {
|
|
383
382
|
const [result, setResult] = useState("");
|
|
384
|
-
const {
|
|
383
|
+
const { editing, startEditing, stopEditing } = useEditionController();
|
|
385
384
|
|
|
386
385
|
return (
|
|
387
386
|
<div className="demo-card">
|
|
388
|
-
<h3 className="demo-title">
|
|
387
|
+
<h3 className="demo-title">Action object editable Text</h3>
|
|
389
388
|
<div className="control-group">
|
|
390
|
-
<label>
|
|
389
|
+
<label>Content:</label>
|
|
391
390
|
<div className="editable-container">
|
|
392
|
-
<
|
|
393
|
-
|
|
394
|
-
editable={editable}
|
|
391
|
+
<Editable
|
|
392
|
+
editing={editing}
|
|
395
393
|
onEditEnd={() => {
|
|
396
394
|
stopEditing();
|
|
397
395
|
setResult("");
|
|
398
396
|
}}
|
|
397
|
+
value={editableTextSignal.value}
|
|
398
|
+
name="signalText"
|
|
399
|
+
action={(newText) => {
|
|
400
|
+
updateTextAction(newText);
|
|
401
|
+
}}
|
|
399
402
|
onActionStart={() => {
|
|
400
|
-
setResult("Saving
|
|
403
|
+
setResult("Saving...");
|
|
401
404
|
}}
|
|
402
|
-
name="coucou"
|
|
403
|
-
action={boundAction}
|
|
404
|
-
cancelOnEscape
|
|
405
|
-
cancelOnBlurInvalid
|
|
406
405
|
constraints={[SINGLE_SPACE_CONSTRAINT]}
|
|
407
406
|
>
|
|
408
407
|
<span
|
|
@@ -410,27 +409,17 @@
|
|
|
410
409
|
startEditing();
|
|
411
410
|
}}
|
|
412
411
|
>
|
|
413
|
-
{
|
|
412
|
+
{editableTextSignal.value}
|
|
414
413
|
</span>
|
|
415
|
-
</
|
|
414
|
+
</Editable>
|
|
416
415
|
</div>
|
|
417
416
|
</div>
|
|
418
|
-
<div className="button-group">
|
|
419
|
-
<button
|
|
420
|
-
onClick={() => {
|
|
421
|
-
console.log("Setting editable signal to true");
|
|
422
|
-
valueSignal.value = "toto";
|
|
423
|
-
}}
|
|
424
|
-
>
|
|
425
|
-
Set signal value to "toto"
|
|
426
|
-
</button>
|
|
427
|
-
</div>
|
|
428
417
|
<div
|
|
429
|
-
className={`result-display ${result === "Saving
|
|
418
|
+
className={`result-display ${result === "Saving..." ? "result-loading" : result.startsWith("✅") ? "result-success" : result.startsWith("❌") ? "result-error" : ""}`}
|
|
430
419
|
>
|
|
431
|
-
|
|
420
|
+
Current value: {editableTextSignal.value}
|
|
432
421
|
<br />
|
|
433
|
-
{result || "Use buttons to control edit mode
|
|
422
|
+
{result || "Use buttons to control edit mode..."}
|
|
434
423
|
</div>
|
|
435
424
|
</div>
|
|
436
425
|
);
|
|
@@ -11,16 +11,21 @@
|
|
|
11
11
|
|
|
12
12
|
<script type="module" jsenv-type="module/jsx">
|
|
13
13
|
import { render } from "preact";
|
|
14
|
-
import { useState } from "preact/hooks";
|
|
15
|
-
|
|
16
|
-
import {
|
|
14
|
+
import { useState, useRef } from "preact/hooks";
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
// eslint-disable-next-line no-unused-vars
|
|
18
|
+
Link,
|
|
19
|
+
useSelectionController,
|
|
20
|
+
// eslint-disable-next-line no-unused-vars
|
|
21
|
+
SelectionContext,
|
|
22
|
+
defineRoutes,
|
|
23
|
+
} from "@jsenv/navi";
|
|
17
24
|
|
|
18
25
|
defineRoutes({});
|
|
19
26
|
|
|
20
27
|
// eslint-disable-next-line no-unused-vars
|
|
21
28
|
const App = () => {
|
|
22
|
-
const [contextSelectedItems, setContextSelectedItems] = useState([]);
|
|
23
|
-
|
|
24
29
|
return (
|
|
25
30
|
<div style="padding: 20px; font-family: system-ui, sans-serif; max-width: 800px;">
|
|
26
31
|
<h1>Link Component Demo</h1>
|
|
@@ -82,63 +87,7 @@
|
|
|
82
87
|
</div>
|
|
83
88
|
</section>
|
|
84
89
|
|
|
85
|
-
<
|
|
86
|
-
<h2>Selectable Links</h2>
|
|
87
|
-
<p style="color: #666; margin-bottom: 16px;">
|
|
88
|
-
Using Selection context for centralized state management.{" "}
|
|
89
|
-
<kbd style="background: #f0f0f0; padding: 2px 6px; border-radius: 3px; font-size: 0.9em;">
|
|
90
|
-
Cmd/Ctrl + Click
|
|
91
|
-
</kbd>{" "}
|
|
92
|
-
to multi-select,{" "}
|
|
93
|
-
<kbd style="background: #f0f0f0; padding: 2px 6px; border-radius: 3px; font-size: 0.9em;">
|
|
94
|
-
Shift + Click
|
|
95
|
-
</kbd>{" "}
|
|
96
|
-
for range selection, and{" "}
|
|
97
|
-
<kbd style="background: #f0f0f0; padding: 2px 6px; border-radius: 3px; font-size: 0.9em;">
|
|
98
|
-
Cmd/Ctrl + Delete
|
|
99
|
-
</kbd>{" "}
|
|
100
|
-
to show selected items.
|
|
101
|
-
</p>
|
|
102
|
-
|
|
103
|
-
<SelectionProvider
|
|
104
|
-
value={contextSelectedItems}
|
|
105
|
-
onChange={setContextSelectedItems}
|
|
106
|
-
>
|
|
107
|
-
<div style="border: 1px solid #e0e0e0; border-radius: 8px; padding: 16px; background: white;">
|
|
108
|
-
<div style="display: flex; flex-direction: column; gap: 8px;">
|
|
109
|
-
<Link href="#doc_report" name="doc" value="doc-1">
|
|
110
|
-
📋 Annual Report 2024
|
|
111
|
-
</Link>
|
|
112
|
-
|
|
113
|
-
<Link href="#doc_budget" name="doc" value="doc-2">
|
|
114
|
-
💰 Budget Proposal
|
|
115
|
-
</Link>
|
|
116
|
-
|
|
117
|
-
<Link href="#doc_presentation" name="doc" value="doc-3">
|
|
118
|
-
🎯 Marketing Presentation
|
|
119
|
-
</Link>
|
|
120
|
-
|
|
121
|
-
<Link href="#doc_manual" name="doc" value="doc-4">
|
|
122
|
-
📖 User Manual v2.0
|
|
123
|
-
</Link>
|
|
124
|
-
</div>
|
|
125
|
-
|
|
126
|
-
{contextSelectedItems.length > 0 && (
|
|
127
|
-
<div style="margin-top: 16px; padding: 12px; background: #e3f2fd; border-radius: 6px; border-left: 4px solid #1976d2;">
|
|
128
|
-
<strong>Selected items:</strong>{" "}
|
|
129
|
-
{contextSelectedItems.join(", ")}
|
|
130
|
-
<br />
|
|
131
|
-
<button
|
|
132
|
-
onClick={() => setContextSelectedItems([])}
|
|
133
|
-
style="margin-top: 8px; padding: 4px 12px; background: #1976d2; color: white; border: none; border-radius: 4px; font-size: 0.9em; cursor: pointer;"
|
|
134
|
-
>
|
|
135
|
-
Clear selection
|
|
136
|
-
</button>
|
|
137
|
-
</div>
|
|
138
|
-
)}
|
|
139
|
-
</div>
|
|
140
|
-
</SelectionProvider>
|
|
141
|
-
</section>
|
|
90
|
+
<SelectableLinkDemo />
|
|
142
91
|
|
|
143
92
|
<section>
|
|
144
93
|
<h2>Navigation Instructions</h2>
|
|
@@ -166,6 +115,79 @@
|
|
|
166
115
|
);
|
|
167
116
|
};
|
|
168
117
|
|
|
118
|
+
// eslint-disable-next-line no-unused-vars
|
|
119
|
+
const SelectableLinkDemo = () => {
|
|
120
|
+
const [selection, setSelection] = useState([]);
|
|
121
|
+
const linkContainerRef = useRef(null);
|
|
122
|
+
const selectionController = useSelectionController({
|
|
123
|
+
elementRef: linkContainerRef,
|
|
124
|
+
layout: "vertical",
|
|
125
|
+
value: selection,
|
|
126
|
+
onChange: setSelection,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
<section style="margin-bottom: 40px;">
|
|
131
|
+
<h2>Selectable Links</h2>
|
|
132
|
+
<p style="color: #666; margin-bottom: 16px;">
|
|
133
|
+
Using Selection context for centralized state management.{" "}
|
|
134
|
+
<kbd style="background: #f0f0f0; padding: 2px 6px; border-radius: 3px; font-size: 0.9em;">
|
|
135
|
+
Cmd/Ctrl + Click
|
|
136
|
+
</kbd>{" "}
|
|
137
|
+
to multi-select,{" "}
|
|
138
|
+
<kbd style="background: #f0f0f0; padding: 2px 6px; border-radius: 3px; font-size: 0.9em;">
|
|
139
|
+
Shift + Click
|
|
140
|
+
</kbd>{" "}
|
|
141
|
+
for range selection, and{" "}
|
|
142
|
+
<kbd style="background: #f0f0f0; padding: 2px 6px; border-radius: 3px; font-size: 0.9em;">
|
|
143
|
+
Cmd/Ctrl + Delete
|
|
144
|
+
</kbd>{" "}
|
|
145
|
+
to show selected items.
|
|
146
|
+
</p>
|
|
147
|
+
|
|
148
|
+
<SelectionContext.Provider
|
|
149
|
+
value={{ selection, selectionController }}
|
|
150
|
+
>
|
|
151
|
+
<div style="border: 1px solid #e0e0e0; border-radius: 8px; padding: 16px; background: white;">
|
|
152
|
+
<div
|
|
153
|
+
style="display: flex; flex-direction: column; gap: 8px;"
|
|
154
|
+
ref={linkContainerRef}
|
|
155
|
+
>
|
|
156
|
+
<Link href="#doc_report" name="doc" value="doc-1">
|
|
157
|
+
📋 Annual Report 2024
|
|
158
|
+
</Link>
|
|
159
|
+
|
|
160
|
+
<Link href="#doc_budget" name="doc" value="doc-2">
|
|
161
|
+
💰 Budget Proposal
|
|
162
|
+
</Link>
|
|
163
|
+
|
|
164
|
+
<Link href="#doc_presentation" name="doc" value="doc-3">
|
|
165
|
+
🎯 Marketing Presentation
|
|
166
|
+
</Link>
|
|
167
|
+
|
|
168
|
+
<Link href="#doc_manual" name="doc" value="doc-4">
|
|
169
|
+
📖 User Manual v2.0
|
|
170
|
+
</Link>
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
{selection.length > 0 && (
|
|
174
|
+
<div style="margin-top: 16px; padding: 12px; background: #e3f2fd; border-radius: 6px; border-left: 4px solid #1976d2;">
|
|
175
|
+
<strong>Selected items:</strong> {selection.join(", ")}
|
|
176
|
+
<br />
|
|
177
|
+
<button
|
|
178
|
+
onClick={() => setSelection([])}
|
|
179
|
+
style="margin-top: 8px; padding: 4px 12px; background: #1976d2; color: white; border: none; border-radius: 4px; font-size: 0.9em; cursor: pointer;"
|
|
180
|
+
>
|
|
181
|
+
Clear selection
|
|
182
|
+
</button>
|
|
183
|
+
</div>
|
|
184
|
+
)}
|
|
185
|
+
</div>
|
|
186
|
+
</SelectionContext.Provider>
|
|
187
|
+
</section>
|
|
188
|
+
);
|
|
189
|
+
};
|
|
190
|
+
|
|
169
191
|
render(<App />, document.querySelector("#root"));
|
|
170
192
|
</script>
|
|
171
193
|
</body>
|