@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.
- package/dist/jsenv_navi.js +22959 -0
- package/index.js +66 -16
- package/package.json +23 -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/field/input_textual.jsx +418 -0
- 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/input_textual.jsx +0 -338
- 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
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
<script type="module" jsenv-type="module/jsx">
|
|
13
13
|
import { render } from "preact";
|
|
14
|
+
import { useState } from "preact/hooks";
|
|
14
15
|
import {
|
|
15
16
|
// eslint-disable-next-line no-unused-vars
|
|
16
17
|
Button,
|
|
@@ -18,194 +19,333 @@
|
|
|
18
19
|
Form,
|
|
19
20
|
// eslint-disable-next-line no-unused-vars
|
|
20
21
|
RadioList,
|
|
22
|
+
// eslint-disable-next-line no-unused-vars
|
|
23
|
+
Radio,
|
|
24
|
+
// eslint-disable-next-line no-unused-vars
|
|
25
|
+
Label,
|
|
21
26
|
} from "@jsenv/navi";
|
|
22
27
|
|
|
23
28
|
// eslint-disable-next-line no-unused-vars
|
|
24
29
|
const App = () => {
|
|
25
30
|
return (
|
|
26
|
-
<div>
|
|
27
|
-
<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
</p>
|
|
31
|
+
<div style="display: flex; flex-direction: column; gap: 40px">
|
|
32
|
+
<DynamicRadioListDemo />
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
32
36
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
{ label: "Green", value: "green" },
|
|
42
|
-
]}
|
|
43
|
-
</RadioList>
|
|
44
|
-
</div>
|
|
37
|
+
// eslint-disable-next-line no-unused-vars
|
|
38
|
+
const DynamicRadioListDemo = () => {
|
|
39
|
+
// Available color choices with their selection state
|
|
40
|
+
const [colorChoices, setColorChoices] = useState([
|
|
41
|
+
{ id: 1, color: "red", selected: true },
|
|
42
|
+
{ id: 2, color: "blue", selected: false },
|
|
43
|
+
{ id: 3, color: "green", selected: false },
|
|
44
|
+
]);
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
</p>
|
|
46
|
+
// Derived state: currently selected color in data state
|
|
47
|
+
const selectedColor =
|
|
48
|
+
colorChoices.find((choice) => choice.selected)?.color || null;
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
50
|
+
// UI FORM STATE - This represents the current form state (may differ from data state)
|
|
51
|
+
const [uiSelectedColor, setUiSelectedColor] = useState(selectedColor);
|
|
52
|
+
|
|
53
|
+
// Next ID for new colors
|
|
54
|
+
const [nextId, setNextId] = useState(4);
|
|
55
|
+
|
|
56
|
+
// Form submission feedback
|
|
57
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
58
|
+
const [submitMessage, setSubmitMessage] = useState("");
|
|
59
|
+
const [submitError, setSubmitError] = useState("");
|
|
60
|
+
const [forceError, setForceError] = useState(false);
|
|
61
|
+
|
|
62
|
+
// Random colors pool
|
|
63
|
+
const randomColors = [
|
|
64
|
+
"purple",
|
|
65
|
+
"orange",
|
|
66
|
+
"yellow",
|
|
67
|
+
"pink",
|
|
68
|
+
"cyan",
|
|
69
|
+
"magenta",
|
|
70
|
+
"lime",
|
|
71
|
+
"indigo",
|
|
72
|
+
"coral",
|
|
73
|
+
"teal",
|
|
74
|
+
"gold",
|
|
75
|
+
"silver",
|
|
76
|
+
"navy",
|
|
77
|
+
"maroon",
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
// Action that simulates submitting form data (takes 1s)
|
|
81
|
+
const submitForm = async ({ color }) => {
|
|
82
|
+
setIsSubmitting(true);
|
|
83
|
+
setSubmitError("");
|
|
84
|
+
setSubmitMessage(`Syncing color: ${color || "none"}...`);
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
console.log("Submitting form data:", color);
|
|
88
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
89
|
+
|
|
90
|
+
// Simulate error if checkbox is checked
|
|
91
|
+
if (forceError) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
"Simulated server error: Failed to update color preference",
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Update the data state to match the form submission
|
|
98
|
+
setColorChoices((prev) =>
|
|
99
|
+
prev.map((choice) => ({
|
|
100
|
+
...choice,
|
|
101
|
+
selected: choice.color === color,
|
|
102
|
+
})),
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
setSubmitMessage(`✅ Successfully synced: ${color || "none"}`);
|
|
106
|
+
console.log("Data state updated from form:", color);
|
|
107
|
+
} catch (error) {
|
|
108
|
+
setSubmitError(error.message);
|
|
109
|
+
setSubmitMessage("");
|
|
110
|
+
} finally {
|
|
111
|
+
setIsSubmitting(false);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// Add a new color to the data state
|
|
116
|
+
const addColorToState = (color) => {
|
|
117
|
+
setColorChoices((prev) => [
|
|
118
|
+
...prev,
|
|
119
|
+
{ id: nextId, color, selected: false },
|
|
120
|
+
]);
|
|
121
|
+
setNextId((prev) => prev + 1);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// Remove a color from the data state
|
|
125
|
+
const removeColorFromState = (colorId) => {
|
|
126
|
+
setColorChoices((prev) =>
|
|
127
|
+
prev.filter((choice) => choice.id !== colorId),
|
|
128
|
+
);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// Select a color in the data state (radio behavior - only one selected)
|
|
132
|
+
const selectColorInState = (colorId) => {
|
|
133
|
+
setColorChoices((prev) =>
|
|
134
|
+
prev.map((choice) => ({
|
|
135
|
+
...choice,
|
|
136
|
+
selected: choice.id === colorId,
|
|
137
|
+
})),
|
|
138
|
+
);
|
|
139
|
+
};
|
|
69
140
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
141
|
+
// Clear selection in the data state
|
|
142
|
+
const clearSelectionInState = () => {
|
|
143
|
+
setColorChoices((prev) =>
|
|
144
|
+
prev.map((choice) => ({
|
|
145
|
+
...choice,
|
|
146
|
+
selected: false,
|
|
147
|
+
})),
|
|
148
|
+
);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
// Get the color that reset would sync to
|
|
152
|
+
const getResetColor = () => {
|
|
153
|
+
return colorChoices.find((choice) => choice.selected)?.color || null;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// Add a random color choice to the data state
|
|
157
|
+
const addRandomColor = () => {
|
|
158
|
+
const availableColors = randomColors.filter(
|
|
159
|
+
(color) => !colorChoices.some((choice) => choice.color === color),
|
|
160
|
+
);
|
|
161
|
+
if (availableColors.length > 0) {
|
|
162
|
+
const randomColor =
|
|
163
|
+
availableColors[
|
|
164
|
+
Math.floor(Math.random() * availableColors.length)
|
|
165
|
+
];
|
|
166
|
+
addColorToState(randomColor);
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
return (
|
|
171
|
+
<div style="border: 2px solid #ddd; padding: 20px; border-radius: 8px; background: #f9f9f9;">
|
|
172
|
+
<h2>State ↔ UI Form Demo (Radio Selection)</h2>
|
|
173
|
+
|
|
174
|
+
<div style="display: flex; gap: 20px; margin: 20px 0;">
|
|
175
|
+
{/* DATA STATE SECTION */}
|
|
176
|
+
<div style="flex: 1; padding: 15px; background: #fff; border-radius: 6px; border: 1px solid #ccc;">
|
|
177
|
+
<h3 style="margin: 0 0 15px 0; color: #2196F3;">
|
|
178
|
+
🗄️ Data State
|
|
179
|
+
</h3>
|
|
180
|
+
<p style="margin: 0 0 15px 0; font-size: 14px; color: #666;">
|
|
181
|
+
Direct manipulation of the data source (single selection)
|
|
73
182
|
</p>
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
>
|
|
80
|
-
<RadioList
|
|
81
|
-
id="color_form_log"
|
|
82
|
-
label="Select your favorite color:"
|
|
83
|
-
name="color"
|
|
183
|
+
|
|
184
|
+
<div style="margin-bottom: 15px;">
|
|
185
|
+
<button
|
|
186
|
+
onClick={addRandomColor}
|
|
187
|
+
style="padding: 6px 12px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 12px; margin-right: 8px;"
|
|
84
188
|
>
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
</Form>
|
|
95
|
-
</div>
|
|
96
|
-
</div>
|
|
189
|
+
+ Add Random Color
|
|
190
|
+
</button>
|
|
191
|
+
<button
|
|
192
|
+
onClick={clearSelectionInState}
|
|
193
|
+
style="padding: 6px 12px; background: #ff9800; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 12px;"
|
|
194
|
+
>
|
|
195
|
+
Clear Selection
|
|
196
|
+
</button>
|
|
197
|
+
</div>
|
|
97
198
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
{ label: "Blue", id: "blue", value: "blue" },
|
|
117
|
-
{ label: "Green", id: "green", value: "green" },
|
|
118
|
-
]}
|
|
119
|
-
</RadioList>
|
|
120
|
-
</div>
|
|
199
|
+
<div style="display: flex; flex-direction: column; gap: 8px;">
|
|
200
|
+
{colorChoices.map(({ id, color, selected }) => (
|
|
201
|
+
<div
|
|
202
|
+
key={id}
|
|
203
|
+
style={{
|
|
204
|
+
display: "flex",
|
|
205
|
+
alignItems: "center",
|
|
206
|
+
gap: "8px",
|
|
207
|
+
padding: "6px",
|
|
208
|
+
border: "1px solid #eee",
|
|
209
|
+
borderRadius: "3px",
|
|
210
|
+
background: selected ? "#e8f5e8" : "#fafafa",
|
|
211
|
+
borderColor: selected ? "#4CAF50" : "#eee",
|
|
212
|
+
}}
|
|
213
|
+
>
|
|
214
|
+
<span style="flex: 1; font-size: 14px;">
|
|
215
|
+
{color.charAt(0).toUpperCase() + color.slice(1)}
|
|
216
|
+
</span>
|
|
121
217
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
218
|
+
<button
|
|
219
|
+
onClick={() => selectColorInState(id)}
|
|
220
|
+
style={{
|
|
221
|
+
padding: "4px 8px",
|
|
222
|
+
fontSize: "11px",
|
|
223
|
+
background: selected ? "#4CAF50" : "#2196F3",
|
|
224
|
+
color: "white",
|
|
225
|
+
border: "none",
|
|
226
|
+
borderRadius: "3px",
|
|
227
|
+
cursor: "pointer",
|
|
228
|
+
minWidth: "60px",
|
|
229
|
+
}}
|
|
230
|
+
>
|
|
231
|
+
{selected ? "Selected" : "Select"}
|
|
232
|
+
</button>
|
|
233
|
+
|
|
234
|
+
<button
|
|
235
|
+
onClick={() => removeColorFromState(id)}
|
|
236
|
+
style="padding: 4px 6px; background: #f44336; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 11px;"
|
|
237
|
+
title="Remove this color"
|
|
238
|
+
>
|
|
239
|
+
🗑
|
|
240
|
+
</button>
|
|
241
|
+
</div>
|
|
242
|
+
))}
|
|
243
|
+
</div>
|
|
244
|
+
</div>
|
|
245
|
+
|
|
246
|
+
{/* UI FORM SECTION */}
|
|
247
|
+
<div style="flex: 1; padding: 15px; background: #fff; border-radius: 6px; border: 1px solid #ccc;">
|
|
248
|
+
<h3 style="margin: 0 0 15px 0; color: #FF9800;">📝 UI Form</h3>
|
|
249
|
+
<p style="margin: 0 0 15px 0; font-size: 14px; color: #666;">
|
|
250
|
+
Radio button interface for user interaction
|
|
126
251
|
</p>
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
252
|
+
|
|
253
|
+
<Form action={submitForm}>
|
|
254
|
+
<fieldset style="border: 1px solid #ddd; padding: 10px; border-radius: 4px;">
|
|
255
|
+
<legend>Select Favorite Color:</legend>
|
|
256
|
+
<RadioList
|
|
257
|
+
name="color"
|
|
258
|
+
onUIStateChange={(color) => {
|
|
259
|
+
setUiSelectedColor(color);
|
|
260
|
+
}}
|
|
261
|
+
>
|
|
262
|
+
{colorChoices.map(({ id, color }) => (
|
|
263
|
+
<Label key={id} style="display: block; margin: 5px 0;">
|
|
264
|
+
{color.charAt(0).toUpperCase() + color.slice(1)}
|
|
265
|
+
<Radio
|
|
266
|
+
value={color}
|
|
267
|
+
checked={selectedColor === color}
|
|
268
|
+
/>
|
|
269
|
+
</Label>
|
|
270
|
+
))}
|
|
271
|
+
</RadioList>
|
|
272
|
+
</fieldset>
|
|
273
|
+
|
|
274
|
+
{/* Error simulation checkbox */}
|
|
275
|
+
<div style="margin: 10px 0; padding: 8px; background: #ffebee; border-radius: 4px; border: 1px solid #ffcdd2;">
|
|
276
|
+
<label style="display: flex; align-items: center; gap: 8px; font-size: 12px; color: #d32f2f;">
|
|
277
|
+
<input
|
|
278
|
+
type="checkbox"
|
|
279
|
+
checked={forceError}
|
|
280
|
+
onChange={(e) => setForceError(e.target.checked)}
|
|
281
|
+
style="margin: 0;"
|
|
282
|
+
/>
|
|
283
|
+
🔧 Force submission error (for testing)
|
|
284
|
+
</label>
|
|
285
|
+
</div>
|
|
286
|
+
|
|
287
|
+
{/* Submission feedback messages */}
|
|
288
|
+
{(isSubmitting || submitMessage || submitError) && (
|
|
289
|
+
<div style="margin: 10px 0; padding: 10px; border-radius: 4px; font-size: 14px;">
|
|
290
|
+
{isSubmitting && (
|
|
291
|
+
<div style="color: #1976d2; background: #e3f2fd; padding: 8px; border-radius: 3px; border-left: 3px solid #1976d2;">
|
|
292
|
+
🔄 {submitMessage}
|
|
293
|
+
</div>
|
|
294
|
+
)}
|
|
295
|
+
{!isSubmitting && submitMessage && (
|
|
296
|
+
<div style="color: #388e3c; background: #e8f5e8; padding: 8px; border-radius: 3px; border-left: 3px solid #388e3c;">
|
|
297
|
+
{submitMessage}
|
|
298
|
+
</div>
|
|
299
|
+
)}
|
|
300
|
+
{submitError && (
|
|
301
|
+
<div style="color: #d32f2f; background: #ffebee; padding: 8px; border-radius: 3px; border-left: 3px solid #d32f2f;">
|
|
302
|
+
❌ {submitError}
|
|
303
|
+
</div>
|
|
304
|
+
)}
|
|
305
|
+
</div>
|
|
306
|
+
)}
|
|
307
|
+
|
|
308
|
+
<div style="margin-top: 15px; display: flex; align-items: flex-start; flex-direction: column; gap: 8px;">
|
|
309
|
+
<Button type="submit" disabled={isSubmitting}>
|
|
310
|
+
{isSubmitting ? "⏳ Submitting..." : "📤 Submit Form"}{" "}
|
|
311
|
+
{!isSubmitting && uiSelectedColor && (
|
|
312
|
+
<span style="font-size: 11px; opacity: 0.7;">
|
|
313
|
+
({uiSelectedColor})
|
|
314
|
+
</span>
|
|
315
|
+
)}
|
|
316
|
+
</Button>
|
|
317
|
+
<Button type="reset">
|
|
318
|
+
🔄 Reset to State{" "}
|
|
319
|
+
{getResetColor() && (
|
|
320
|
+
<span style="font-size: 11px; opacity: 0.7;">
|
|
321
|
+
({getResetColor()})
|
|
322
|
+
</span>
|
|
323
|
+
)}
|
|
324
|
+
</Button>
|
|
325
|
+
</div>
|
|
149
326
|
</Form>
|
|
150
327
|
</div>
|
|
151
328
|
</div>
|
|
152
329
|
|
|
153
|
-
<div>
|
|
154
|
-
<
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
>
|
|
171
|
-
|
|
172
|
-
{ label: "Red", id: "red", value: "red" },
|
|
173
|
-
{ label: "Blue", id: "blue", value: "blue" },
|
|
174
|
-
{ label: "Green", id: "green", value: "green" },
|
|
175
|
-
]}
|
|
176
|
-
</RadioList>
|
|
177
|
-
|
|
178
|
-
<Button type="submit">Submit</Button>
|
|
179
|
-
<Button type="reset">Reset</Button>
|
|
180
|
-
</Form>
|
|
181
|
-
</div>
|
|
182
|
-
|
|
183
|
-
<div>
|
|
184
|
-
<p>
|
|
185
|
-
<strong>Form + required</strong>
|
|
186
|
-
</p>
|
|
187
|
-
<Form
|
|
188
|
-
action={async () => {
|
|
189
|
-
await new Promise((resolve) => setTimeout(resolve, 1_000));
|
|
190
|
-
}}
|
|
191
|
-
>
|
|
192
|
-
<RadioList
|
|
193
|
-
id="color_form_required"
|
|
194
|
-
label="Select your favorite color:"
|
|
195
|
-
name="color"
|
|
196
|
-
required
|
|
197
|
-
data-required-message="Veuillez sélectionner une couleur"
|
|
198
|
-
>
|
|
199
|
-
{[
|
|
200
|
-
{ label: "Red", id: "red", value: "red" },
|
|
201
|
-
{ label: "Blue", id: "blue", value: "blue" },
|
|
202
|
-
{ label: "Green", id: "green", value: "green" },
|
|
203
|
-
]}
|
|
204
|
-
</RadioList>
|
|
205
|
-
|
|
206
|
-
<Button type="submit">Submit</Button>
|
|
207
|
-
<Button type="reset">Reset</Button>
|
|
208
|
-
</Form>
|
|
330
|
+
<div style="background: #f0f8ff; padding: 15px; border-radius: 6px; margin: 20px 0; border-left: 4px solid #2196F3;">
|
|
331
|
+
<h4 style="margin: 0 0 10px 0;">How it works:</h4>
|
|
332
|
+
<ul style="margin: 0; padding-left: 20px; font-size: 14px;">
|
|
333
|
+
<li>
|
|
334
|
+
<strong>Data State</strong>: The actual data with direct
|
|
335
|
+
manipulation buttons (single selection only)
|
|
336
|
+
</li>
|
|
337
|
+
<li>
|
|
338
|
+
<strong>UI Form</strong>: Radio button interface that can
|
|
339
|
+
differ from the data state
|
|
340
|
+
</li>
|
|
341
|
+
<li>
|
|
342
|
+
<strong>Submit</strong>: Updates data state to match the form
|
|
343
|
+
selection
|
|
344
|
+
</li>
|
|
345
|
+
<li>
|
|
346
|
+
<strong>Reset</strong>: Syncs form back to current data state
|
|
347
|
+
</li>
|
|
348
|
+
</ul>
|
|
209
349
|
</div>
|
|
210
350
|
</div>
|
|
211
351
|
);
|