@jsenv/navi 0.10.1 → 0.11.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 +13858 -23295
- package/dist/jsenv_navi.js.map +1281 -0
- package/package.json +5 -7
- package/index.js +0 -122
- package/src/action_private_properties.js +0 -11
- package/src/action_proxy_test.html +0 -353
- package/src/action_run_states.js +0 -5
- package/src/actions.js +0 -1401
- package/src/browser_integration/browser_integration.js +0 -216
- package/src/browser_integration/document_back_and_forward.js +0 -17
- package/src/browser_integration/document_loading_signal.js +0 -100
- package/src/browser_integration/document_state_signal.js +0 -9
- package/src/browser_integration/document_url_signal.js +0 -9
- package/src/browser_integration/use_is_visited.js +0 -19
- package/src/browser_integration/via_history.js +0 -232
- package/src/browser_integration/via_navigation.js +0 -168
- package/src/components/action_execution/form_context.js +0 -5
- package/src/components/action_execution/render_actionable_component.jsx +0 -29
- package/src/components/action_execution/use_action.js +0 -99
- package/src/components/action_execution/use_execute_action.js +0 -177
- package/src/components/action_execution/use_run_on_mount.js +0 -9
- package/src/components/action_renderer.jsx +0 -125
- package/src/components/callout/callout.js +0 -990
- package/src/components/callout/callout_demo.html +0 -201
- package/src/components/callout/test_dynamic_positioning.html +0 -161
- package/src/components/callout/test_html_document_iframe.html +0 -182
- package/src/components/demos/0_button_demo.html +0 -707
- package/src/components/demos/10_column_reordering_debug.html +0 -277
- package/src/components/demos/11_table_selection_debug.html +0 -432
- package/src/components/demos/1_checkbox_demo.html +0 -754
- package/src/components/demos/2_input_textual_demo.html +0 -286
- package/src/components/demos/3_radio_demo.html +0 -874
- package/src/components/demos/4_select_demo.html +0 -100
- package/src/components/demos/5_list_scrollable_demo.html +0 -153
- package/src/components/demos/6_tablist_demo.html +0 -77
- package/src/components/demos/7_table_selection_demo.html +0 -176
- package/src/components/demos/8_table_fixed_headers_demo.html +0 -584
- package/src/components/demos/9_table_column_drag_demo.html +0 -325
- package/src/components/demos/action/0_button_demo.html +0 -204
- package/src/components/demos/action/10_shortcuts_demo.html +0 -189
- package/src/components/demos/action/11_nested_shortcuts_demo.xhtml +0 -401
- package/src/components/demos/action/1_input_text_demo.html +0 -876
- package/src/components/demos/action/2_form_multiple.html +0 -303
- package/src/components/demos/action/3_details_demo.html +0 -203
- package/src/components/demos/action/4_input_checkbox_demo.html +0 -731
- package/src/components/demos/action/5_input_checkbox_state_demo.html +0 -270
- package/src/components/demos/action/6_checkbox_list_demo.html +0 -341
- package/src/components/demos/action/7_radio_list_demo.html +0 -357
- package/src/components/demos/action/8_editable_demo.html +0 -431
- package/src/components/demos/action/9_link_demo.html +0 -194
- package/src/components/demos/demo.md +0 -0
- package/src/components/demos/route/basic/basic.html +0 -14
- package/src/components/demos/route/basic/basic_route_demo.jsx +0 -224
- package/src/components/demos/route/multi/multi.html +0 -14
- package/src/components/demos/route/multi/multi_route_demo.jsx +0 -277
- package/src/components/demos/ui_transition/0_action_renderer_ui_transition_demo.html +0 -695
- package/src/components/demos/ui_transition/1_nested_ui_transition_demo.html +0 -429
- package/src/components/demos/ui_transition/2_height_transition_test.html +0 -295
- package/src/components/details/details.jsx +0 -245
- package/src/components/details/summary_marker.jsx +0 -141
- package/src/components/edition/editable.jsx +0 -186
- package/src/components/error_boundary_context.js +0 -9
- package/src/components/field/README.md +0 -247
- package/src/components/field/button.jsx +0 -429
- package/src/components/field/checkbox_list.jsx +0 -185
- package/src/components/field/collect_form_element_values.js +0 -82
- package/src/components/field/custom_field.js +0 -106
- package/src/components/field/form.jsx +0 -209
- package/src/components/field/input.jsx +0 -16
- package/src/components/field/input_checkbox.jsx +0 -434
- package/src/components/field/input_radio.jsx +0 -432
- package/src/components/field/input_textual.jsx +0 -389
- package/src/components/field/label.jsx +0 -46
- package/src/components/field/radio_list.jsx +0 -183
- package/src/components/field/select.jsx +0 -256
- package/src/components/field/use_action_events.js +0 -132
- package/src/components/field/use_form_events.js +0 -59
- package/src/components/field/use_ui_state_controller.js +0 -506
- package/src/components/item_tracker/README.md +0 -461
- package/src/components/item_tracker/use_isolated_item_tracker.jsx +0 -209
- package/src/components/item_tracker/use_isolated_item_tracker_demo.html +0 -148
- package/src/components/item_tracker/use_isolated_item_tracker_demo.jsx +0 -460
- package/src/components/item_tracker/use_item_tracker.jsx +0 -143
- package/src/components/item_tracker/use_item_tracker_demo.html +0 -207
- package/src/components/item_tracker/use_item_tracker_demo.jsx +0 -216
- package/src/components/keyboard_shortcuts/active_keyboard_shortcuts.jsx +0 -87
- package/src/components/keyboard_shortcuts/aria_key_shortcuts.js +0 -61
- package/src/components/keyboard_shortcuts/keyboard_key_meta.js +0 -17
- package/src/components/keyboard_shortcuts/keyboard_shortcuts.js +0 -371
- package/src/components/keyboard_shortcuts/os.js +0 -9
- package/src/components/layout/demos/demo_flex.html +0 -638
- package/src/components/layout/demos/demo_layout_style_buttons.html +0 -351
- package/src/components/layout/demos/demo_layout_style_input.html +0 -226
- package/src/components/layout/demos/demo_layout_style_text.html +0 -514
- package/src/components/layout/flex.jsx +0 -109
- package/src/components/layout/layout_context.jsx +0 -3
- package/src/components/layout/spacing.jsx +0 -20
- package/src/components/layout/use_layout_style.js +0 -249
- package/src/components/link/link.jsx +0 -267
- package/src/components/link/link_with_icon.jsx +0 -52
- package/src/components/loader/loader_background.jsx +0 -372
- package/src/components/loader/loading_spinner.jsx +0 -68
- package/src/components/loader/network_speed.js +0 -83
- package/src/components/loader/rectangle_loading.jsx +0 -244
- package/src/components/props_composition/demos/demo_with_props_style.html +0 -81
- package/src/components/props_composition/with_props_class_name.js +0 -37
- package/src/components/props_composition/with_props_style.js +0 -26
- package/src/components/route.jsx +0 -19
- package/src/components/selection/selection.jsx +0 -1583
- package/src/components/svg/font_sized_svg.jsx +0 -59
- package/src/components/svg/icon_and_text.jsx +0 -21
- package/src/components/svg/svg_mask_overlay.jsx +0 -105
- package/src/components/table/drag/table_drag.jsx +0 -506
- package/src/components/table/resize/table_resize.jsx +0 -650
- package/src/components/table/resize/table_size.js +0 -43
- package/src/components/table/selection/table_selection.js +0 -106
- package/src/components/table/selection/table_selection.jsx +0 -203
- package/src/components/table/sticky/sticky_group.js +0 -354
- package/src/components/table/sticky/table_sticky.js +0 -25
- package/src/components/table/sticky/table_sticky.jsx +0 -501
- package/src/components/table/table.jsx +0 -721
- package/src/components/table/table_css.js +0 -211
- package/src/components/table/table_ui.jsx +0 -49
- package/src/components/table/use_cells_and_columns.js +0 -90
- package/src/components/table/use_object_array_to_cells.js +0 -46
- package/src/components/table/z_indexes.js +0 -23
- package/src/components/tablist/tablist.jsx +0 -99
- package/src/components/text/demos/demo_text_and_icon.html +0 -421
- package/src/components/text/overflow.jsx +0 -15
- package/src/components/text/text.jsx +0 -83
- package/src/components/text/text_and_count.jsx +0 -28
- package/src/components/ui_transition.jsx +0 -128
- package/src/components/use_auto_focus.js +0 -94
- package/src/components/use_batch_during_render.js +0 -33
- package/src/components/use_debounce_true.js +0 -31
- package/src/components/use_dependencies_diff.js +0 -35
- package/src/components/use_focus_group.js +0 -20
- package/src/components/use_initial_value.js +0 -78
- package/src/components/use_is_visited.js +0 -19
- package/src/components/use_ref_array.js +0 -38
- package/src/components/use_signal_sync.js +0 -50
- package/src/components/use_stable_callback.js +0 -68
- package/src/components/use_state_array.js +0 -47
- package/src/docs/actions.md +0 -250
- package/src/docs/demos/resource/action_status.jsx +0 -42
- package/src/docs/demos/resource/demo.md +0 -1
- package/src/docs/demos/resource/resource_demo_0.html +0 -84
- package/src/docs/demos/resource/resource_demo_10_post_gc.html +0 -364
- package/src/docs/demos/resource/resource_demo_11_describe_many.html +0 -362
- package/src/docs/demos/resource/resource_demo_2.html +0 -173
- package/src/docs/demos/resource/resource_demo_3_filtered_users.html +0 -415
- package/src/docs/demos/resource/resource_demo_4_details.html +0 -284
- package/src/docs/demos/resource/resource_demo_5_renderer_lazy.html +0 -115
- package/src/docs/demos/resource/resource_demo_6_gc.html +0 -217
- package/src/docs/demos/resource/resource_demo_7_child_gc.html +0 -240
- package/src/docs/demos/resource/resource_demo_8_proxy_gc.html +0 -319
- package/src/docs/demos/resource/resource_demo_9_describe_one.html +0 -472
- package/src/docs/demos/resource/tata.jsx +0 -3
- package/src/docs/demos/resource/toto.jsx +0 -3
- package/src/docs/demos/user_nav/user_nav.html +0 -12
- package/src/docs/demos/user_nav/user_nav.jsx +0 -330
- package/src/docs/resource_dependencies.md +0 -103
- package/src/docs/resource_with_params.md +0 -80
- package/src/navi_css_vars.js +0 -14
- package/src/notes.md +0 -34
- package/src/route/route.js +0 -596
- package/src/route/route.xtest.html +0 -228
- package/src/store/array_signal_store.js +0 -537
- package/src/store/local_storage_signal.js +0 -17
- package/src/store/resource_graph.js +0 -1304
- package/src/store/tests/resource_graph_autoreload_demo.html +0 -12
- package/src/store/tests/resource_graph_autoreload_demo.jsx +0 -964
- package/src/store/tests/resource_graph_dependencies.test_manual.js +0 -95
- package/src/store/value_in_local_storage.js +0 -187
- package/src/symbol_object_signal.js +0 -1
- package/src/use_action_data.js +0 -10
- package/src/use_action_status.js +0 -47
- package/src/utils/add_many_event_listeners.js +0 -15
- package/src/utils/array_add_remove.js +0 -61
- package/src/utils/array_signal.js +0 -15
- package/src/utils/compare_two_js_values.js +0 -172
- package/src/utils/execute_with_cleanup.js +0 -21
- package/src/utils/get_caller_info.js +0 -85
- package/src/utils/is_signal.js +0 -20
- package/src/utils/js_value_weak_map.js +0 -162
- package/src/utils/js_value_weak_map_demo.html +0 -690
- package/src/utils/merge_two_js_values.js +0 -53
- package/src/utils/stringify_for_display.js +0 -131
- package/src/utils/weak_effect.js +0 -48
- package/src/validation/constraints/confirm_constraint.js +0 -14
- package/src/validation/constraints/create_unique_value_constraint.js +0 -27
- package/src/validation/constraints/native_constraints.js +0 -338
- package/src/validation/constraints/readonly_constraint.js +0 -41
- package/src/validation/constraints/same_as_constraint.js +0 -42
- package/src/validation/constraints/single_space_constraint.js +0 -13
- package/src/validation/custom_constraint_validation.js +0 -793
- package/src/validation/custom_message.js +0 -18
- package/src/validation/demos/browser_style.png +0 -0
- package/src/validation/demos/demo_same_as_constraint.html +0 -259
- package/src/validation/demos/form_validation_demo.html +0 -142
- package/src/validation/demos/form_validation_demo_preact.html +0 -87
- package/src/validation/demos/form_validation_native_popover_demo.html +0 -168
- package/src/validation/demos/form_validation_vs_native_demo.html +0 -172
- package/src/validation/hooks/use_constraints.js +0 -23
- package/src/validation/hooks/use_custom_validation_ref.js +0 -73
- package/src/validation/hooks/use_validation_message.js +0 -19
- package/src/validation/input_change_effect.js +0 -106
|
@@ -1,270 +0,0 @@
|
|
|
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>Input Checkbox State Demo</title>
|
|
8
|
-
</head>
|
|
9
|
-
<body>
|
|
10
|
-
<div id="root" style="position: relative"></div>
|
|
11
|
-
|
|
12
|
-
<script type="module" jsenv-type="module/jsx">
|
|
13
|
-
import { render } from "preact";
|
|
14
|
-
import { useState } from "preact/hooks";
|
|
15
|
-
import {
|
|
16
|
-
// eslint-disable-next-line no-unused-vars
|
|
17
|
-
Button,
|
|
18
|
-
// eslint-disable-next-line no-unused-vars
|
|
19
|
-
Form,
|
|
20
|
-
// eslint-disable-next-line no-unused-vars
|
|
21
|
-
Label,
|
|
22
|
-
// eslint-disable-next-line no-unused-vars
|
|
23
|
-
Checkbox,
|
|
24
|
-
} from "@jsenv/navi";
|
|
25
|
-
|
|
26
|
-
// eslint-disable-next-line no-unused-vars
|
|
27
|
-
const App = () => {
|
|
28
|
-
return (
|
|
29
|
-
<div style="display: flex; flex-direction: column; gap: 40px">
|
|
30
|
-
<SingleCheckboxStateDemo />
|
|
31
|
-
</div>
|
|
32
|
-
);
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
// eslint-disable-next-line no-unused-vars
|
|
36
|
-
const SingleCheckboxStateDemo = () => {
|
|
37
|
-
// DATA STATE - This represents the actual data/server state
|
|
38
|
-
const [isEnabledInState, setIsEnabledInState] = useState(true);
|
|
39
|
-
const [checkboxBgColor, setCheckboxBgColor] = useState("#2196F3");
|
|
40
|
-
|
|
41
|
-
// UI FORM STATE - This represents the current form state (may differ from data state)
|
|
42
|
-
const [uiIsEnabled, setUiIsEnabled] = useState(isEnabledInState);
|
|
43
|
-
|
|
44
|
-
// Form submission feedback
|
|
45
|
-
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
46
|
-
const [submitMessage, setSubmitMessage] = useState("");
|
|
47
|
-
const [submitError, setSubmitError] = useState("");
|
|
48
|
-
const [forceError, setForceError] = useState(false);
|
|
49
|
-
|
|
50
|
-
// Action that simulates submitting form data (takes 1s)
|
|
51
|
-
const submitForm = async ({ notifications }) => {
|
|
52
|
-
const isChecked = notifications === "on"; // Form checkbox value
|
|
53
|
-
|
|
54
|
-
setIsSubmitting(true);
|
|
55
|
-
setSubmitError("");
|
|
56
|
-
setSubmitMessage(
|
|
57
|
-
`Syncing notification setting: ${isChecked ? "enabled" : "disabled"}...`,
|
|
58
|
-
);
|
|
59
|
-
|
|
60
|
-
try {
|
|
61
|
-
console.log("Submitting form data:", { notifications: isChecked });
|
|
62
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
63
|
-
|
|
64
|
-
// Simulate error if checkbox is checked
|
|
65
|
-
if (forceError) {
|
|
66
|
-
throw new Error(
|
|
67
|
-
"Simulated server error: Failed to update notification preferences",
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Update the data state to match the form submission
|
|
72
|
-
setIsEnabledInState(isChecked);
|
|
73
|
-
|
|
74
|
-
setSubmitMessage(
|
|
75
|
-
`✅ Successfully synced: notifications ${isChecked ? "enabled" : "disabled"}`,
|
|
76
|
-
);
|
|
77
|
-
console.log("Data state updated from form:", isChecked);
|
|
78
|
-
} catch (error) {
|
|
79
|
-
setSubmitError(error.message);
|
|
80
|
-
setSubmitMessage("");
|
|
81
|
-
} finally {
|
|
82
|
-
setIsSubmitting(false);
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
// Get what the reset would sync to
|
|
87
|
-
const getResetValue = () => {
|
|
88
|
-
return isEnabledInState ? "checked" : "unchecked";
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
// Get what the submit would send
|
|
92
|
-
const getSubmitValue = () => {
|
|
93
|
-
return uiIsEnabled ? "checked" : "unchecked";
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
return (
|
|
97
|
-
<div style="border: 2px solid #ddd; padding: 20px; border-radius: 8px; background: #f9f9f9;">
|
|
98
|
-
<h2>Single Checkbox State ↔ UI Form Demo</h2>
|
|
99
|
-
|
|
100
|
-
<div style="display: flex; gap: 20px; margin: 20px 0;">
|
|
101
|
-
{/* DATA STATE SECTION */}
|
|
102
|
-
<div style="flex: 1; padding: 15px; background: #fff; border-radius: 6px; border: 1px solid #ccc;">
|
|
103
|
-
<h3 style="margin: 0 0 15px 0; color: #2196F3;">
|
|
104
|
-
🗄️ Data State
|
|
105
|
-
</h3>
|
|
106
|
-
<p style="margin: 0 0 15px 0; font-size: 14px; color: #666;">
|
|
107
|
-
Direct manipulation of the data source
|
|
108
|
-
</p>
|
|
109
|
-
|
|
110
|
-
<div style="padding: 15px; border: 1px solid #eee; border-radius: 4px; background: #fafafa;">
|
|
111
|
-
<div style="display: flex; align-items: center; gap: 12px; margin-bottom: 15px;">
|
|
112
|
-
<label style="display: flex; align-items: center; gap: 8px; font-size: 16px; font-weight: 500; cursor: pointer;">
|
|
113
|
-
<input
|
|
114
|
-
type="checkbox"
|
|
115
|
-
checked={isEnabledInState}
|
|
116
|
-
onChange={(e) => setIsEnabledInState(e.target.checked)}
|
|
117
|
-
style="transform: scale(1.2);"
|
|
118
|
-
/>
|
|
119
|
-
🔔 Notifications{" "}
|
|
120
|
-
{isEnabledInState ? "ENABLED" : "DISABLED"}
|
|
121
|
-
</label>
|
|
122
|
-
</div>
|
|
123
|
-
|
|
124
|
-
<div style="margin-bottom: 15px;">
|
|
125
|
-
<label style="display: flex; align-items: center; gap: 8px; font-size: 14px;">
|
|
126
|
-
<span style="font-weight: 500;">UI Checkbox Color:</span>
|
|
127
|
-
<select
|
|
128
|
-
value={checkboxBgColor}
|
|
129
|
-
onChange={(e) => setCheckboxBgColor(e.target.value)}
|
|
130
|
-
style="padding: 4px 8px; border: 1px solid #ccc; border-radius: 3px; font-size: 12px;"
|
|
131
|
-
>
|
|
132
|
-
<option value="#2196F3">Blue</option>
|
|
133
|
-
<option value="#4CAF50">Green</option>
|
|
134
|
-
<option value="#FF9800">Orange</option>
|
|
135
|
-
<option value="#9C27B0">Purple</option>
|
|
136
|
-
<option value="#F44336">Red</option>
|
|
137
|
-
<option value="#607D8B">Blue Grey</option>
|
|
138
|
-
<option value="#795548">Brown</option>
|
|
139
|
-
<option value="#E91E63">Pink</option>
|
|
140
|
-
</select>
|
|
141
|
-
<div
|
|
142
|
-
style={{
|
|
143
|
-
width: "16px",
|
|
144
|
-
height: "16px",
|
|
145
|
-
backgroundColor: checkboxBgColor,
|
|
146
|
-
borderRadius: "2px",
|
|
147
|
-
border: "1px solid #ccc",
|
|
148
|
-
}}
|
|
149
|
-
title="Current color preview"
|
|
150
|
-
></div>
|
|
151
|
-
</label>
|
|
152
|
-
</div>
|
|
153
|
-
|
|
154
|
-
<div style="font-size: 12px; color: #666; margin-top: 8px;">
|
|
155
|
-
Click the checkbox above to toggle the data state directly
|
|
156
|
-
</div>
|
|
157
|
-
</div>
|
|
158
|
-
</div>
|
|
159
|
-
|
|
160
|
-
{/* UI FORM SECTION */}
|
|
161
|
-
<div style="flex: 1; padding: 15px; background: #fff; border-radius: 6px; border: 1px solid #ccc;">
|
|
162
|
-
<h3 style="margin: 0 0 15px 0; color: #FF9800;">📝 UI Form</h3>
|
|
163
|
-
<p style="margin: 0 0 15px 0; font-size: 14px; color: #666;">
|
|
164
|
-
Checkbox interface for user interaction
|
|
165
|
-
</p>
|
|
166
|
-
|
|
167
|
-
<Form action={submitForm}>
|
|
168
|
-
<fieldset style="border: 1px solid #ddd; padding: 15px; border-radius: 4px;">
|
|
169
|
-
<legend>Notification Settings:</legend>
|
|
170
|
-
|
|
171
|
-
<Label style="display: flex; align-items: center; gap: 10px; font-size: 16px; cursor: pointer;">
|
|
172
|
-
<Checkbox
|
|
173
|
-
name="notifications"
|
|
174
|
-
checked={isEnabledInState}
|
|
175
|
-
onUIStateChange={(uiState) =>
|
|
176
|
-
setUiIsEnabled(Boolean(uiState))
|
|
177
|
-
}
|
|
178
|
-
accentColor={checkboxBgColor}
|
|
179
|
-
/>
|
|
180
|
-
🔔 Enable push notifications
|
|
181
|
-
</Label>
|
|
182
|
-
</fieldset>
|
|
183
|
-
|
|
184
|
-
{/* Error simulation checkbox */}
|
|
185
|
-
<div style="margin: 10px 0; padding: 8px; background: #ffebee; border-radius: 4px; border: 1px solid #ffcdd2;">
|
|
186
|
-
<label style="display: flex; align-items: center; gap: 8px; font-size: 12px; color: #d32f2f;">
|
|
187
|
-
<input
|
|
188
|
-
type="checkbox"
|
|
189
|
-
checked={forceError}
|
|
190
|
-
onChange={(e) => setForceError(e.target.checked)}
|
|
191
|
-
style="margin: 0;"
|
|
192
|
-
/>
|
|
193
|
-
🔧 Force submission error (for testing)
|
|
194
|
-
</label>
|
|
195
|
-
</div>
|
|
196
|
-
|
|
197
|
-
{/* Submission feedback messages */}
|
|
198
|
-
{(isSubmitting || submitMessage || submitError) && (
|
|
199
|
-
<div style="margin: 10px 0; padding: 10px; border-radius: 4px; font-size: 14px;">
|
|
200
|
-
{isSubmitting && (
|
|
201
|
-
<div style="color: #1976d2; background: #e3f2fd; padding: 8px; border-radius: 3px; border-left: 3px solid #1976d2;">
|
|
202
|
-
🔄 {submitMessage}
|
|
203
|
-
</div>
|
|
204
|
-
)}
|
|
205
|
-
{!isSubmitting && submitMessage && (
|
|
206
|
-
<div style="color: #388e3c; background: #e8f5e8; padding: 8px; border-radius: 3px; border-left: 3px solid #388e3c;">
|
|
207
|
-
{submitMessage}
|
|
208
|
-
</div>
|
|
209
|
-
)}
|
|
210
|
-
{submitError && (
|
|
211
|
-
<div style="color: #d32f2f; background: #ffebee; padding: 8px; border-radius: 3px; border-left: 3px solid #d32f2f;">
|
|
212
|
-
❌ {submitError}
|
|
213
|
-
</div>
|
|
214
|
-
)}
|
|
215
|
-
</div>
|
|
216
|
-
)}
|
|
217
|
-
|
|
218
|
-
<div style="margin-top: 15px; display: flex; flex-direction: column; align-items: flex-start; gap: 8px;">
|
|
219
|
-
<Button type="submit" disabled={isSubmitting}>
|
|
220
|
-
{isSubmitting ? "⏳ Submitting..." : "📤 Submit Form"}{" "}
|
|
221
|
-
{!isSubmitting && (
|
|
222
|
-
<span style="font-size: 11px; opacity: 0.7;">
|
|
223
|
-
({getSubmitValue()})
|
|
224
|
-
</span>
|
|
225
|
-
)}
|
|
226
|
-
</Button>
|
|
227
|
-
<Button type="reset">
|
|
228
|
-
🔄 Reset to State
|
|
229
|
-
<span style="font-size: 11px; opacity: 0.7;">
|
|
230
|
-
({getResetValue()})
|
|
231
|
-
</span>
|
|
232
|
-
</Button>
|
|
233
|
-
</div>
|
|
234
|
-
</Form>
|
|
235
|
-
</div>
|
|
236
|
-
</div>
|
|
237
|
-
|
|
238
|
-
<div style="background: #f0f8ff; padding: 15px; border-radius: 6px; margin: 20px 0; border-left: 4px solid #2196F3;">
|
|
239
|
-
<h4 style="margin: 0 0 10px 0;">How it works:</h4>
|
|
240
|
-
<ul style="margin: 0; padding-left: 20px; font-size: 14px;">
|
|
241
|
-
<li>
|
|
242
|
-
<strong>Data State</strong>: The actual notification setting
|
|
243
|
-
with direct toggle button
|
|
244
|
-
</li>
|
|
245
|
-
<li>
|
|
246
|
-
<strong>UI Form</strong>: Checkbox interface that can differ
|
|
247
|
-
from the data state
|
|
248
|
-
</li>
|
|
249
|
-
<li>
|
|
250
|
-
<strong>Submit</strong>: Updates data state to match the form
|
|
251
|
-
checkbox selection
|
|
252
|
-
</li>
|
|
253
|
-
<li>
|
|
254
|
-
<strong>Reset</strong>: Syncs form checkbox back to current
|
|
255
|
-
data state
|
|
256
|
-
</li>
|
|
257
|
-
<li>
|
|
258
|
-
<strong>Button Labels</strong>: Show exactly what will be
|
|
259
|
-
submitted or reset to (checked/unchecked)
|
|
260
|
-
</li>
|
|
261
|
-
</ul>
|
|
262
|
-
</div>
|
|
263
|
-
</div>
|
|
264
|
-
);
|
|
265
|
-
};
|
|
266
|
-
|
|
267
|
-
render(<App />, document.querySelector("#root"));
|
|
268
|
-
</script>
|
|
269
|
-
</body>
|
|
270
|
-
</html>
|
|
@@ -1,341 +0,0 @@
|
|
|
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>Checkbox list demo</title>
|
|
8
|
-
</head>
|
|
9
|
-
<body>
|
|
10
|
-
<div id="root" style="position: relative"></div>
|
|
11
|
-
|
|
12
|
-
<script type="module" jsenv-type="module/jsx">
|
|
13
|
-
import { render } from "preact";
|
|
14
|
-
import { useState } from "preact/hooks";
|
|
15
|
-
import {
|
|
16
|
-
// eslint-disable-next-line no-unused-vars
|
|
17
|
-
Button,
|
|
18
|
-
// eslint-disable-next-line no-unused-vars
|
|
19
|
-
Form,
|
|
20
|
-
// eslint-disable-next-line no-unused-vars
|
|
21
|
-
CheckboxList,
|
|
22
|
-
// eslint-disable-next-line no-unused-vars
|
|
23
|
-
Label,
|
|
24
|
-
// eslint-disable-next-line no-unused-vars
|
|
25
|
-
Checkbox,
|
|
26
|
-
} from "@jsenv/navi";
|
|
27
|
-
|
|
28
|
-
// eslint-disable-next-line no-unused-vars
|
|
29
|
-
const App = () => {
|
|
30
|
-
return (
|
|
31
|
-
<div style="display: flex; flex-direction: column; gap: 40px">
|
|
32
|
-
<DynamicCheckboxListDemo />
|
|
33
|
-
<div style="display: flex; flex-direction: row; gap: 30px"></div>
|
|
34
|
-
</div>
|
|
35
|
-
);
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
// eslint-disable-next-line no-unused-vars
|
|
39
|
-
const DynamicCheckboxListDemo = () => {
|
|
40
|
-
// Available color choices with their selection state
|
|
41
|
-
const [colorChoices, setColorChoices] = useState([
|
|
42
|
-
{ id: 1, color: "red", selected: true },
|
|
43
|
-
{ id: 2, color: "blue", selected: true },
|
|
44
|
-
{ id: 3, color: "green", selected: false },
|
|
45
|
-
]);
|
|
46
|
-
|
|
47
|
-
// Derived state: currently selected colors in data state
|
|
48
|
-
|
|
49
|
-
const selectedColors = colorChoices
|
|
50
|
-
.filter((choice) => choice.selected)
|
|
51
|
-
.map((choice) => choice.color);
|
|
52
|
-
|
|
53
|
-
// UI FORM STATE - This represents the current form state (may differ from data state)
|
|
54
|
-
const [uiSelectedColors, setUiSelectedColors] =
|
|
55
|
-
useState(selectedColors);
|
|
56
|
-
|
|
57
|
-
// Next ID for new colors
|
|
58
|
-
const [nextId, setNextId] = useState(4);
|
|
59
|
-
|
|
60
|
-
// Form submission feedback
|
|
61
|
-
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
62
|
-
const [submitMessage, setSubmitMessage] = useState("");
|
|
63
|
-
const [submitError, setSubmitError] = useState("");
|
|
64
|
-
const [forceError, setForceError] = useState(false);
|
|
65
|
-
|
|
66
|
-
// Random colors pool
|
|
67
|
-
const randomColors = [
|
|
68
|
-
"purple",
|
|
69
|
-
"orange",
|
|
70
|
-
"yellow",
|
|
71
|
-
"pink",
|
|
72
|
-
"cyan",
|
|
73
|
-
"magenta",
|
|
74
|
-
"lime",
|
|
75
|
-
"indigo",
|
|
76
|
-
"coral",
|
|
77
|
-
"teal",
|
|
78
|
-
"gold",
|
|
79
|
-
"silver",
|
|
80
|
-
"navy",
|
|
81
|
-
"maroon",
|
|
82
|
-
];
|
|
83
|
-
|
|
84
|
-
// Action that simulates submitting form data (takes 1s)
|
|
85
|
-
const submitForm = async ({ colors }) => {
|
|
86
|
-
setIsSubmitting(true);
|
|
87
|
-
setSubmitError("");
|
|
88
|
-
setSubmitMessage(
|
|
89
|
-
`Syncing colors: ${colors?.length ? colors.join(", ") : "none"}...`,
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
try {
|
|
93
|
-
console.log("Submitting form data:", colors);
|
|
94
|
-
await new Promise((resolve) => setTimeout(resolve, 1000000));
|
|
95
|
-
|
|
96
|
-
// Simulate error if checkbox is checked
|
|
97
|
-
if (forceError) {
|
|
98
|
-
throw new Error(
|
|
99
|
-
"Simulated server error: Failed to update color preferences",
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Update the data state to match the form submission
|
|
104
|
-
setColorChoices((prev) =>
|
|
105
|
-
prev.map((choice) => ({
|
|
106
|
-
...choice,
|
|
107
|
-
selected: colors?.includes(choice.color) || false,
|
|
108
|
-
})),
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
setSubmitMessage(
|
|
112
|
-
`✅ Successfully synced: ${colors?.length ? colors.join(", ") : "none"}`,
|
|
113
|
-
);
|
|
114
|
-
console.log("Data state updated from form:", colors);
|
|
115
|
-
} catch (error) {
|
|
116
|
-
setSubmitError(error.message);
|
|
117
|
-
setSubmitMessage("");
|
|
118
|
-
} finally {
|
|
119
|
-
setIsSubmitting(false);
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
// Add a new color to the data state
|
|
124
|
-
const addColorToState = (color) => {
|
|
125
|
-
setColorChoices((prev) => [
|
|
126
|
-
...prev,
|
|
127
|
-
{ id: nextId, color, selected: false },
|
|
128
|
-
]);
|
|
129
|
-
setNextId((prev) => prev + 1);
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
// Remove a color from the data state
|
|
133
|
-
const removeColorFromState = (colorId) => {
|
|
134
|
-
setColorChoices((prev) =>
|
|
135
|
-
prev.filter((choice) => choice.id !== colorId),
|
|
136
|
-
);
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
// Toggle selection state in the data state
|
|
140
|
-
const toggleColorInState = (colorId) => {
|
|
141
|
-
setColorChoices((prev) =>
|
|
142
|
-
prev.map((choice) =>
|
|
143
|
-
choice.id === colorId
|
|
144
|
-
? { ...choice, selected: !choice.selected }
|
|
145
|
-
: choice,
|
|
146
|
-
),
|
|
147
|
-
);
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
// Get the colors that reset would sync to
|
|
151
|
-
const getResetColors = () => {
|
|
152
|
-
return colorChoices
|
|
153
|
-
.filter((choice) => choice.selected)
|
|
154
|
-
.map((choice) => choice.color);
|
|
155
|
-
}; // Add a random color choice to the data state
|
|
156
|
-
const addRandomColor = () => {
|
|
157
|
-
const availableColors = randomColors.filter(
|
|
158
|
-
(color) => !colorChoices.some((choice) => choice.color === color),
|
|
159
|
-
);
|
|
160
|
-
if (availableColors.length > 0) {
|
|
161
|
-
const randomColor =
|
|
162
|
-
availableColors[
|
|
163
|
-
Math.floor(Math.random() * availableColors.length)
|
|
164
|
-
];
|
|
165
|
-
addColorToState(randomColor);
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
return (
|
|
170
|
-
<div style="border: 2px solid #ddd; padding: 20px; border-radius: 8px; background: #f9f9f9;">
|
|
171
|
-
<h2>State ↔ UI Form Demo</h2>
|
|
172
|
-
|
|
173
|
-
<div style="display: flex; gap: 20px; margin: 20px 0;">
|
|
174
|
-
{/* DATA STATE SECTION */}
|
|
175
|
-
<div style="flex: 1; padding: 15px; background: #fff; border-radius: 6px; border: 1px solid #ccc;">
|
|
176
|
-
<h3 style="margin: 0 0 15px 0; color: #2196F3;">
|
|
177
|
-
🗄️ Data State
|
|
178
|
-
</h3>
|
|
179
|
-
<p style="margin: 0 0 15px 0; font-size: 14px; color: #666;">
|
|
180
|
-
Direct manipulation of the data source
|
|
181
|
-
</p>
|
|
182
|
-
|
|
183
|
-
<div style="margin-bottom: 15px;">
|
|
184
|
-
<button
|
|
185
|
-
onClick={addRandomColor}
|
|
186
|
-
style="padding: 6px 12px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 12px; margin-right: 8px;"
|
|
187
|
-
>
|
|
188
|
-
+ Add Random Color
|
|
189
|
-
</button>
|
|
190
|
-
</div>
|
|
191
|
-
|
|
192
|
-
<div style="display: flex; flex-direction: column; gap: 8px;">
|
|
193
|
-
{colorChoices.map(({ id, color, selected }) => (
|
|
194
|
-
<div
|
|
195
|
-
key={id}
|
|
196
|
-
style="display: flex; align-items: center; gap: 8px; padding: 6px; border: 1px solid #eee; border-radius: 3px; background: #fafafa;"
|
|
197
|
-
>
|
|
198
|
-
<span style="flex: 1; font-size: 14px;">
|
|
199
|
-
{color.charAt(0).toUpperCase() + color.slice(1)}
|
|
200
|
-
</span>
|
|
201
|
-
|
|
202
|
-
<button
|
|
203
|
-
onClick={() => toggleColorInState(id)}
|
|
204
|
-
style={{
|
|
205
|
-
padding: "4px 8px",
|
|
206
|
-
fontSize: "11px",
|
|
207
|
-
background: selected ? "#4CAF50" : "#ccc",
|
|
208
|
-
color: "white",
|
|
209
|
-
border: "none",
|
|
210
|
-
borderRadius: "3px",
|
|
211
|
-
cursor: "pointer",
|
|
212
|
-
minWidth: "60px",
|
|
213
|
-
}}
|
|
214
|
-
>
|
|
215
|
-
{selected ? "Selected" : "Not Selected"}
|
|
216
|
-
</button>
|
|
217
|
-
|
|
218
|
-
<button
|
|
219
|
-
onClick={() => removeColorFromState(id)}
|
|
220
|
-
style="padding: 4px 6px; background: #f44336; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 11px;"
|
|
221
|
-
title="Remove this color"
|
|
222
|
-
>
|
|
223
|
-
🗑
|
|
224
|
-
</button>
|
|
225
|
-
</div>
|
|
226
|
-
))}
|
|
227
|
-
</div>
|
|
228
|
-
</div>
|
|
229
|
-
|
|
230
|
-
{/* UI FORM SECTION */}
|
|
231
|
-
<div style="flex: 1; padding: 15px; background: #fff; border-radius: 6px; border: 1px solid #ccc;">
|
|
232
|
-
<h3 style="margin: 0 0 15px 0; color: #FF9800;">📝 UI Form</h3>
|
|
233
|
-
<p style="margin: 0 0 15px 0; font-size: 14px; color: #666;">
|
|
234
|
-
Checkbox interface for user interaction
|
|
235
|
-
</p>
|
|
236
|
-
|
|
237
|
-
<Form action={submitForm}>
|
|
238
|
-
<fieldset style="border: 1px solid #ddd; padding: 10px; border-radius: 4px;">
|
|
239
|
-
<legend>Select Colors:</legend>
|
|
240
|
-
<CheckboxList
|
|
241
|
-
name="colors"
|
|
242
|
-
onUIStateChange={(colors) => {
|
|
243
|
-
setUiSelectedColors(colors);
|
|
244
|
-
}}
|
|
245
|
-
>
|
|
246
|
-
{colorChoices.map(({ id, color }) => (
|
|
247
|
-
<Label key={id} style="display: block; margin: 5px 0;">
|
|
248
|
-
{color.charAt(0).toUpperCase() + color.slice(1)}
|
|
249
|
-
<Checkbox
|
|
250
|
-
value={color}
|
|
251
|
-
checked={selectedColors.includes(color)}
|
|
252
|
-
/>
|
|
253
|
-
</Label>
|
|
254
|
-
))}
|
|
255
|
-
</CheckboxList>
|
|
256
|
-
</fieldset>
|
|
257
|
-
|
|
258
|
-
{/* Error simulation checkbox */}
|
|
259
|
-
<div style="margin: 10px 0; padding: 8px; background: #ffebee; border-radius: 4px; border: 1px solid #ffcdd2;">
|
|
260
|
-
<label style="display: flex; align-items: center; gap: 8px; font-size: 12px; color: #d32f2f;">
|
|
261
|
-
<input
|
|
262
|
-
type="checkbox"
|
|
263
|
-
checked={forceError}
|
|
264
|
-
onChange={(e) => setForceError(e.target.checked)}
|
|
265
|
-
style="margin: 0;"
|
|
266
|
-
/>
|
|
267
|
-
🔧 Force submission error (for testing)
|
|
268
|
-
</label>
|
|
269
|
-
</div>
|
|
270
|
-
|
|
271
|
-
{/* Submission feedback messages */}
|
|
272
|
-
{(isSubmitting || submitMessage || submitError) && (
|
|
273
|
-
<div style="margin: 10px 0; padding: 10px; border-radius: 4px; font-size: 14px;">
|
|
274
|
-
{isSubmitting && (
|
|
275
|
-
<div style="color: #1976d2; background: #e3f2fd; padding: 8px; border-radius: 3px; border-left: 3px solid #1976d2;">
|
|
276
|
-
🔄 {submitMessage}
|
|
277
|
-
</div>
|
|
278
|
-
)}
|
|
279
|
-
{!isSubmitting && submitMessage && (
|
|
280
|
-
<div style="color: #388e3c; background: #e8f5e8; padding: 8px; border-radius: 3px; border-left: 3px solid #388e3c;">
|
|
281
|
-
{submitMessage}
|
|
282
|
-
</div>
|
|
283
|
-
)}
|
|
284
|
-
{submitError && (
|
|
285
|
-
<div style="color: #d32f2f; background: #ffebee; padding: 8px; border-radius: 3px; border-left: 3px solid #d32f2f;">
|
|
286
|
-
❌ {submitError}
|
|
287
|
-
</div>
|
|
288
|
-
)}
|
|
289
|
-
</div>
|
|
290
|
-
)}
|
|
291
|
-
|
|
292
|
-
<div style="margin-top: 15px; display: flex; align-items: flex-start; flex-direction: column; gap: 8px;">
|
|
293
|
-
<Button type="submit" disabled={isSubmitting}>
|
|
294
|
-
{isSubmitting ? "⏳ Submitting..." : "📤 Submit Form"}{" "}
|
|
295
|
-
{!isSubmitting && uiSelectedColors?.length > 0 && (
|
|
296
|
-
<span style="font-size: 11px; opacity: 0.7;">
|
|
297
|
-
({uiSelectedColors.join(", ")})
|
|
298
|
-
</span>
|
|
299
|
-
)}
|
|
300
|
-
</Button>
|
|
301
|
-
<Button type="reset">
|
|
302
|
-
🔄 Reset to State{" "}
|
|
303
|
-
{getResetColors().length > 0 && (
|
|
304
|
-
<span style="font-size: 11px; opacity: 0.7;">
|
|
305
|
-
({getResetColors().join(", ")})
|
|
306
|
-
</span>
|
|
307
|
-
)}
|
|
308
|
-
</Button>
|
|
309
|
-
</div>
|
|
310
|
-
</Form>
|
|
311
|
-
</div>
|
|
312
|
-
</div>
|
|
313
|
-
|
|
314
|
-
<div style="background: #f0f8ff; padding: 15px; border-radius: 6px; margin: 20px 0; border-left: 4px solid #2196F3;">
|
|
315
|
-
<h4 style="margin: 0 0 10px 0;">How it works:</h4>
|
|
316
|
-
<ul style="margin: 0; padding-left: 20px; font-size: 14px;">
|
|
317
|
-
<li>
|
|
318
|
-
<strong>Data State</strong>: The actual data with direct
|
|
319
|
-
manipulation buttons
|
|
320
|
-
</li>
|
|
321
|
-
<li>
|
|
322
|
-
<strong>UI Form</strong>: Checkbox interface that can differ
|
|
323
|
-
from the data state
|
|
324
|
-
</li>
|
|
325
|
-
<li>
|
|
326
|
-
<strong>Submit</strong>: Updates data state to match the form
|
|
327
|
-
selections
|
|
328
|
-
</li>
|
|
329
|
-
<li>
|
|
330
|
-
<strong>Reset</strong>: Syncs form back to current data state
|
|
331
|
-
</li>
|
|
332
|
-
</ul>
|
|
333
|
-
</div>
|
|
334
|
-
</div>
|
|
335
|
-
);
|
|
336
|
-
};
|
|
337
|
-
|
|
338
|
-
render(<App />, document.querySelector("#root"));
|
|
339
|
-
</script>
|
|
340
|
-
</body>
|
|
341
|
-
</html>
|