@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,876 +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 Text Demo</title>
|
|
8
|
-
<style>
|
|
9
|
-
body {
|
|
10
|
-
max-width: 1200px;
|
|
11
|
-
margin: 0 auto;
|
|
12
|
-
padding: 20px;
|
|
13
|
-
font-family:
|
|
14
|
-
system-ui,
|
|
15
|
-
-apple-system,
|
|
16
|
-
sans-serif;
|
|
17
|
-
background: #f5f5f5;
|
|
18
|
-
}
|
|
19
|
-
.demo-grid {
|
|
20
|
-
display: grid;
|
|
21
|
-
margin-bottom: 30px;
|
|
22
|
-
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
|
23
|
-
gap: 20px;
|
|
24
|
-
}
|
|
25
|
-
.demo-card {
|
|
26
|
-
padding: 20px;
|
|
27
|
-
background: white;
|
|
28
|
-
border-radius: 8px;
|
|
29
|
-
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
30
|
-
}
|
|
31
|
-
.demo-title {
|
|
32
|
-
margin: 0 0 15px 0;
|
|
33
|
-
padding-bottom: 8px;
|
|
34
|
-
color: #333;
|
|
35
|
-
font-weight: 600;
|
|
36
|
-
font-size: 16px;
|
|
37
|
-
border-bottom: 2px solid #e0e0e0;
|
|
38
|
-
}
|
|
39
|
-
.result-display {
|
|
40
|
-
min-height: 20px;
|
|
41
|
-
margin-top: 15px;
|
|
42
|
-
padding: 12px;
|
|
43
|
-
font-size: 14px;
|
|
44
|
-
font-family: monospace;
|
|
45
|
-
background: #f8f9fa;
|
|
46
|
-
border: 1px solid #dee2e6;
|
|
47
|
-
border-radius: 4px;
|
|
48
|
-
}
|
|
49
|
-
.result-success {
|
|
50
|
-
color: #155724;
|
|
51
|
-
background: #d4edda;
|
|
52
|
-
border-color: #c3e6cb;
|
|
53
|
-
}
|
|
54
|
-
.result-error {
|
|
55
|
-
color: #721c24;
|
|
56
|
-
background: #f8d7da;
|
|
57
|
-
border-color: #f5c6cb;
|
|
58
|
-
}
|
|
59
|
-
.result-loading {
|
|
60
|
-
color: #0c5460;
|
|
61
|
-
background: #d1ecf1;
|
|
62
|
-
border-color: #bee5eb;
|
|
63
|
-
}
|
|
64
|
-
nav a {
|
|
65
|
-
color: #007bff;
|
|
66
|
-
text-decoration: none;
|
|
67
|
-
}
|
|
68
|
-
nav a:hover {
|
|
69
|
-
text-decoration: underline;
|
|
70
|
-
}
|
|
71
|
-
h2 {
|
|
72
|
-
margin-top: 40px;
|
|
73
|
-
margin-bottom: 20px;
|
|
74
|
-
padding-top: 20px;
|
|
75
|
-
border-top: 2px solid #e0e0e0;
|
|
76
|
-
}
|
|
77
|
-
h2:first-of-type {
|
|
78
|
-
margin-top: 0;
|
|
79
|
-
padding-top: 0;
|
|
80
|
-
border-top: none;
|
|
81
|
-
}
|
|
82
|
-
.button-group {
|
|
83
|
-
display: flex;
|
|
84
|
-
margin-top: 10px;
|
|
85
|
-
gap: 10px;
|
|
86
|
-
}
|
|
87
|
-
button {
|
|
88
|
-
padding: 8px 16px;
|
|
89
|
-
font-size: 14px;
|
|
90
|
-
background: white;
|
|
91
|
-
border: 1px solid #ccc;
|
|
92
|
-
border-radius: 4px;
|
|
93
|
-
cursor: pointer;
|
|
94
|
-
}
|
|
95
|
-
button:hover {
|
|
96
|
-
background: #f8f9fa;
|
|
97
|
-
}
|
|
98
|
-
.control-group {
|
|
99
|
-
margin-bottom: 15px;
|
|
100
|
-
}
|
|
101
|
-
label {
|
|
102
|
-
margin-bottom: 15px;
|
|
103
|
-
color: #555;
|
|
104
|
-
font-weight: 500;
|
|
105
|
-
}
|
|
106
|
-
label input {
|
|
107
|
-
margin-left: 5px;
|
|
108
|
-
padding: 8px 12px;
|
|
109
|
-
}
|
|
110
|
-
</style>
|
|
111
|
-
</head>
|
|
112
|
-
<body>
|
|
113
|
-
<h1>Input Text Demo</h1>
|
|
114
|
-
<div id="root"></div>
|
|
115
|
-
|
|
116
|
-
<script type="module" jsenv-type="module/jsx">
|
|
117
|
-
import { render } from "preact";
|
|
118
|
-
import { useState } from "preact/hooks";
|
|
119
|
-
import {
|
|
120
|
-
// eslint-disable-next-line no-unused-vars
|
|
121
|
-
Input,
|
|
122
|
-
// eslint-disable-next-line no-unused-vars
|
|
123
|
-
Form,
|
|
124
|
-
// eslint-disable-next-line no-unused-vars
|
|
125
|
-
Button,
|
|
126
|
-
} from "@jsenv/navi";
|
|
127
|
-
|
|
128
|
-
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
129
|
-
|
|
130
|
-
// eslint-disable-next-line no-unused-vars
|
|
131
|
-
const App = () => {
|
|
132
|
-
return (
|
|
133
|
-
<>
|
|
134
|
-
<div className="demo-card" style={{ marginBottom: "30px" }}>
|
|
135
|
-
<h2>Input Text Demo - Overview</h2>
|
|
136
|
-
<p>
|
|
137
|
-
This demo showcases different ways to use the Input component
|
|
138
|
-
with text inputs.
|
|
139
|
-
</p>
|
|
140
|
-
<nav>
|
|
141
|
-
<h3>Sections:</h3>
|
|
142
|
-
<ul style={{ marginLeft: "20px" }}>
|
|
143
|
-
<li>
|
|
144
|
-
<a href="#basic-inputs">
|
|
145
|
-
1. Basic Input (no action/no form)
|
|
146
|
-
</a>{" "}
|
|
147
|
-
- Simple inputs with value handling
|
|
148
|
-
</li>
|
|
149
|
-
<li>
|
|
150
|
-
<a href="#input-actions">2. Input with Actions</a> - Inputs
|
|
151
|
-
that trigger actions on value change, including error
|
|
152
|
-
handling
|
|
153
|
-
</li>
|
|
154
|
-
<li>
|
|
155
|
-
<a href="#input-forms">3. Input inside Form</a> - Form
|
|
156
|
-
integration patterns
|
|
157
|
-
</li>
|
|
158
|
-
<li>
|
|
159
|
-
<a href="#datetime-local">4. Datetime-Local Inputs</a> -
|
|
160
|
-
Special handling for datetime inputs with timezone
|
|
161
|
-
conversion
|
|
162
|
-
</li>
|
|
163
|
-
</ul>
|
|
164
|
-
</nav>
|
|
165
|
-
</div>
|
|
166
|
-
|
|
167
|
-
<h2 id="basic-inputs">1. Basic Input (no action/no form)</h2>
|
|
168
|
-
<div className="demo-grid">
|
|
169
|
-
<BasicInputNoActionDemo />
|
|
170
|
-
<InputWithDefaultValueDemo />
|
|
171
|
-
</div>
|
|
172
|
-
|
|
173
|
-
<h2 id="input-actions">2. Input with Actions</h2>
|
|
174
|
-
<div className="demo-grid">
|
|
175
|
-
<BasicInputDemo />
|
|
176
|
-
<InputWithInitialValueDemo />
|
|
177
|
-
<InputWithExternalControlDemo />
|
|
178
|
-
<InputWithErrorDemo />
|
|
179
|
-
<InputWithValidationErrorDemo />
|
|
180
|
-
</div>
|
|
181
|
-
|
|
182
|
-
<h2 id="input-forms">3. Input inside Form</h2>
|
|
183
|
-
<div className="demo-grid">
|
|
184
|
-
<FormInputDemo />
|
|
185
|
-
<FormInputErrorDemo />
|
|
186
|
-
</div>
|
|
187
|
-
|
|
188
|
-
<h2 id="datetime-local">4. Datetime-Local Inputs</h2>
|
|
189
|
-
<div className="demo-grid">
|
|
190
|
-
<DateTimeLocalDemo />
|
|
191
|
-
<DateTimeLocalFormDemo />
|
|
192
|
-
</div>
|
|
193
|
-
</>
|
|
194
|
-
);
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
// eslint-disable-next-line no-unused-vars
|
|
198
|
-
const DateTimeLocalDemo = () => {
|
|
199
|
-
// External state (what the server/app knows) - stored as UTC ISO string
|
|
200
|
-
const [externalDateTime, setExternalDateTime] = useState(
|
|
201
|
-
"2024-01-15T14:30:00Z", // UTC time
|
|
202
|
-
);
|
|
203
|
-
|
|
204
|
-
// UI state tracking
|
|
205
|
-
const [uiDateTime, setUiDateTime] = useState(externalDateTime);
|
|
206
|
-
const [actionValue, setActionValue] = useState("");
|
|
207
|
-
const [result, setResult] = useState("");
|
|
208
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
209
|
-
|
|
210
|
-
const saveDateTime = async (dateTimeValue) => {
|
|
211
|
-
setIsLoading(true);
|
|
212
|
-
setActionValue(dateTimeValue || "(empty)");
|
|
213
|
-
setResult("Saving datetime...");
|
|
214
|
-
try {
|
|
215
|
-
await delay(1000);
|
|
216
|
-
|
|
217
|
-
// Simulate server response
|
|
218
|
-
const response = {
|
|
219
|
-
saved: dateTimeValue,
|
|
220
|
-
message: `Datetime saved successfully`,
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
// Update external state to match what was saved
|
|
224
|
-
setExternalDateTime(dateTimeValue);
|
|
225
|
-
setResult(`✅ ${response.message}\\nSaved: ${dateTimeValue}`);
|
|
226
|
-
return response;
|
|
227
|
-
} catch (error) {
|
|
228
|
-
setResult(`❌ Save failed: ${error.message}`);
|
|
229
|
-
throw error;
|
|
230
|
-
} finally {
|
|
231
|
-
setIsLoading(false);
|
|
232
|
-
}
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
return (
|
|
236
|
-
<div className="demo-card">
|
|
237
|
-
<h3 className="demo-title">Datetime-Local State Demo</h3>
|
|
238
|
-
|
|
239
|
-
{/* External State Control */}
|
|
240
|
-
<div
|
|
241
|
-
style={{
|
|
242
|
-
marginBottom: "20px",
|
|
243
|
-
padding: "12px",
|
|
244
|
-
background: "#e3f2fd",
|
|
245
|
-
borderRadius: "4px",
|
|
246
|
-
}}
|
|
247
|
-
>
|
|
248
|
-
<h4
|
|
249
|
-
style={{
|
|
250
|
-
margin: "0 0 10px 0",
|
|
251
|
-
fontSize: "14px",
|
|
252
|
-
color: "#1976d2",
|
|
253
|
-
}}
|
|
254
|
-
>
|
|
255
|
-
📊 External State (UTC)
|
|
256
|
-
</h4>
|
|
257
|
-
<div
|
|
258
|
-
style={{
|
|
259
|
-
fontFamily: "monospace",
|
|
260
|
-
fontSize: "12px",
|
|
261
|
-
marginBottom: "10px",
|
|
262
|
-
}}
|
|
263
|
-
>
|
|
264
|
-
Current: {externalDateTime || "(not set)"}
|
|
265
|
-
</div>
|
|
266
|
-
<div style={{ display: "flex", gap: "8px", flexWrap: "wrap" }}>
|
|
267
|
-
<button
|
|
268
|
-
onClick={() => setExternalDateTime("2024-01-15T14:30:00Z")}
|
|
269
|
-
style={{ padding: "4px 8px", fontSize: "12px" }}
|
|
270
|
-
>
|
|
271
|
-
Set Jan 15, 2:30 PM UTC
|
|
272
|
-
</button>
|
|
273
|
-
<button
|
|
274
|
-
onClick={() => setExternalDateTime("2024-12-25T09:00:00Z")}
|
|
275
|
-
style={{ padding: "4px 8px", fontSize: "12px" }}
|
|
276
|
-
>
|
|
277
|
-
Set Dec 25, 9:00 AM UTC
|
|
278
|
-
</button>
|
|
279
|
-
<button
|
|
280
|
-
onClick={() => setExternalDateTime(null)}
|
|
281
|
-
style={{ padding: "4px 8px", fontSize: "12px" }}
|
|
282
|
-
>
|
|
283
|
-
Clear
|
|
284
|
-
</button>
|
|
285
|
-
</div>
|
|
286
|
-
</div>
|
|
287
|
-
|
|
288
|
-
{/* UI Input */}
|
|
289
|
-
<div className="control-group">
|
|
290
|
-
<label>
|
|
291
|
-
Datetime Input (Local Time):
|
|
292
|
-
<Input
|
|
293
|
-
type="datetime-local"
|
|
294
|
-
name="datetime"
|
|
295
|
-
value={externalDateTime} // External state controls the input
|
|
296
|
-
onUIStateChange={(datetime) => {
|
|
297
|
-
setUiDateTime(datetime);
|
|
298
|
-
}}
|
|
299
|
-
action={saveDateTime}
|
|
300
|
-
/>
|
|
301
|
-
</label>
|
|
302
|
-
</div>
|
|
303
|
-
|
|
304
|
-
{/* State Display */}
|
|
305
|
-
<div style={{ marginBottom: "15px" }}>
|
|
306
|
-
<div
|
|
307
|
-
style={{ fontSize: "12px", color: "#666", marginBottom: "8px" }}
|
|
308
|
-
>
|
|
309
|
-
<strong>UI State:</strong> {uiDateTime || "(empty)"}
|
|
310
|
-
</div>
|
|
311
|
-
<div
|
|
312
|
-
style={{ fontSize: "12px", color: "#666", marginBottom: "8px" }}
|
|
313
|
-
>
|
|
314
|
-
<strong>Action receives:</strong>{" "}
|
|
315
|
-
{actionValue || "(waiting for action...)"}
|
|
316
|
-
</div>
|
|
317
|
-
</div>
|
|
318
|
-
|
|
319
|
-
<div
|
|
320
|
-
className={`result-display ${
|
|
321
|
-
isLoading
|
|
322
|
-
? "result-loading"
|
|
323
|
-
: result.startsWith("✅")
|
|
324
|
-
? "result-success"
|
|
325
|
-
: result.startsWith("❌")
|
|
326
|
-
? "result-error"
|
|
327
|
-
: ""
|
|
328
|
-
}`}
|
|
329
|
-
>
|
|
330
|
-
{result ||
|
|
331
|
-
"Change the datetime and blur/enter to trigger action..."}
|
|
332
|
-
</div>
|
|
333
|
-
</div>
|
|
334
|
-
);
|
|
335
|
-
};
|
|
336
|
-
|
|
337
|
-
// eslint-disable-next-line no-unused-vars
|
|
338
|
-
const DateTimeLocalFormDemo = () => {
|
|
339
|
-
const [externalDateTime, setExternalDateTime] = useState(
|
|
340
|
-
"2024-06-01T10:00:00Z",
|
|
341
|
-
);
|
|
342
|
-
const [uiDateTime, setUiDateTime] = useState(externalDateTime);
|
|
343
|
-
const [result, setResult] = useState("");
|
|
344
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
345
|
-
|
|
346
|
-
const submitForm = async (formData) => {
|
|
347
|
-
setIsLoading(true);
|
|
348
|
-
setResult("Submitting form...");
|
|
349
|
-
try {
|
|
350
|
-
await delay(1200);
|
|
351
|
-
|
|
352
|
-
const response = {
|
|
353
|
-
datetime: formData.appointmentTime,
|
|
354
|
-
message: "Appointment scheduled successfully",
|
|
355
|
-
};
|
|
356
|
-
|
|
357
|
-
// Update external state
|
|
358
|
-
setExternalDateTime(formData.appointmentTime);
|
|
359
|
-
setResult(
|
|
360
|
-
`✅ ${response.message}\\nScheduled for: ${formData.appointmentTime}`,
|
|
361
|
-
);
|
|
362
|
-
return response;
|
|
363
|
-
} catch (error) {
|
|
364
|
-
setResult(`❌ Form error: ${error.message}`);
|
|
365
|
-
throw error;
|
|
366
|
-
} finally {
|
|
367
|
-
setIsLoading(false);
|
|
368
|
-
}
|
|
369
|
-
};
|
|
370
|
-
|
|
371
|
-
const getResetValue = () => {
|
|
372
|
-
return externalDateTime || "(not set)";
|
|
373
|
-
};
|
|
374
|
-
|
|
375
|
-
return (
|
|
376
|
-
<div className="demo-card">
|
|
377
|
-
<h3 className="demo-title">Datetime-Local in Form</h3>
|
|
378
|
-
|
|
379
|
-
{/* External State Control */}
|
|
380
|
-
<div
|
|
381
|
-
style={{
|
|
382
|
-
marginBottom: "20px",
|
|
383
|
-
padding: "12px",
|
|
384
|
-
background: "#e3f2fd",
|
|
385
|
-
borderRadius: "4px",
|
|
386
|
-
}}
|
|
387
|
-
>
|
|
388
|
-
<h4
|
|
389
|
-
style={{
|
|
390
|
-
margin: "0 0 10px 0",
|
|
391
|
-
fontSize: "14px",
|
|
392
|
-
color: "#1976d2",
|
|
393
|
-
}}
|
|
394
|
-
>
|
|
395
|
-
📊 External State Control (UTC)
|
|
396
|
-
</h4>
|
|
397
|
-
<div
|
|
398
|
-
style={{
|
|
399
|
-
fontFamily: "monospace",
|
|
400
|
-
fontSize: "12px",
|
|
401
|
-
marginBottom: "10px",
|
|
402
|
-
}}
|
|
403
|
-
>
|
|
404
|
-
Current: {externalDateTime || "(not set)"}
|
|
405
|
-
</div>
|
|
406
|
-
<div style={{ display: "flex", gap: "8px", flexWrap: "wrap" }}>
|
|
407
|
-
<button
|
|
408
|
-
onClick={() => setExternalDateTime("2024-03-15T09:00:00Z")}
|
|
409
|
-
style={{ padding: "4px 8px", fontSize: "12px" }}
|
|
410
|
-
>
|
|
411
|
-
Set Mar 15, 9:00 AM UTC
|
|
412
|
-
</button>
|
|
413
|
-
<button
|
|
414
|
-
onClick={() => setExternalDateTime("2024-08-20T16:30:00Z")}
|
|
415
|
-
style={{ padding: "4px 8px", fontSize: "12px" }}
|
|
416
|
-
>
|
|
417
|
-
Set Aug 20, 4:30 PM UTC
|
|
418
|
-
</button>
|
|
419
|
-
<button
|
|
420
|
-
onClick={() => setExternalDateTime("2024-12-31T23:59:00Z")}
|
|
421
|
-
style={{ padding: "4px 8px", fontSize: "12px" }}
|
|
422
|
-
>
|
|
423
|
-
Set New Year's Eve
|
|
424
|
-
</button>
|
|
425
|
-
<button
|
|
426
|
-
onClick={() => setExternalDateTime(null)}
|
|
427
|
-
style={{ padding: "4px 8px", fontSize: "12px" }}
|
|
428
|
-
>
|
|
429
|
-
Clear
|
|
430
|
-
</button>
|
|
431
|
-
</div>
|
|
432
|
-
</div>
|
|
433
|
-
|
|
434
|
-
<Form action={submitForm}>
|
|
435
|
-
<div className="control-group">
|
|
436
|
-
<label>
|
|
437
|
-
Appointment Time:
|
|
438
|
-
<Input
|
|
439
|
-
type="datetime-local"
|
|
440
|
-
name="appointmentTime"
|
|
441
|
-
value={externalDateTime}
|
|
442
|
-
onUIStateChange={setUiDateTime}
|
|
443
|
-
/>
|
|
444
|
-
</label>
|
|
445
|
-
</div>
|
|
446
|
-
|
|
447
|
-
<div
|
|
448
|
-
style={{
|
|
449
|
-
fontSize: "12px",
|
|
450
|
-
color: "#666",
|
|
451
|
-
marginBottom: "10px",
|
|
452
|
-
}}
|
|
453
|
-
>
|
|
454
|
-
<strong>UI State:</strong> {uiDateTime || "(empty)"}
|
|
455
|
-
</div>
|
|
456
|
-
|
|
457
|
-
<div className="button-group">
|
|
458
|
-
<Button type="submit">
|
|
459
|
-
Schedule Appointment
|
|
460
|
-
{uiDateTime && (
|
|
461
|
-
<span style={{ fontSize: "11px", opacity: "0.7" }}>
|
|
462
|
-
{" "}
|
|
463
|
-
({uiDateTime})
|
|
464
|
-
</span>
|
|
465
|
-
)}
|
|
466
|
-
</Button>
|
|
467
|
-
<Button type="reset">
|
|
468
|
-
Reset to Saved
|
|
469
|
-
<span style={{ fontSize: "11px", opacity: "0.7" }}>
|
|
470
|
-
{" "}
|
|
471
|
-
({getResetValue()})
|
|
472
|
-
</span>
|
|
473
|
-
</Button>
|
|
474
|
-
</div>
|
|
475
|
-
</Form>
|
|
476
|
-
|
|
477
|
-
<div
|
|
478
|
-
className={`result-display ${
|
|
479
|
-
isLoading
|
|
480
|
-
? "result-loading"
|
|
481
|
-
: result.startsWith("✅")
|
|
482
|
-
? "result-success"
|
|
483
|
-
: result.startsWith("❌")
|
|
484
|
-
? "result-error"
|
|
485
|
-
: ""
|
|
486
|
-
}`}
|
|
487
|
-
>
|
|
488
|
-
{result || "Select a datetime and submit..."}
|
|
489
|
-
</div>
|
|
490
|
-
</div>
|
|
491
|
-
);
|
|
492
|
-
};
|
|
493
|
-
|
|
494
|
-
// eslint-disable-next-line no-unused-vars
|
|
495
|
-
const BasicInputNoActionDemo = () => {
|
|
496
|
-
const [result, setResult] = useState("");
|
|
497
|
-
|
|
498
|
-
return (
|
|
499
|
-
<div className="demo-card">
|
|
500
|
-
<h3 className="demo-title">Basic Input (No Action)</h3>
|
|
501
|
-
<div className="control-group">
|
|
502
|
-
<label>
|
|
503
|
-
Enter some text:
|
|
504
|
-
<Input
|
|
505
|
-
name="text"
|
|
506
|
-
placeholder="Type something..."
|
|
507
|
-
onUIStateChange={(text) => {
|
|
508
|
-
setResult(text ? `Current value: "${text}"` : "");
|
|
509
|
-
}}
|
|
510
|
-
/>
|
|
511
|
-
</label>
|
|
512
|
-
</div>
|
|
513
|
-
<div className="result-display">
|
|
514
|
-
{result || "Type in the input to see the value..."}
|
|
515
|
-
</div>
|
|
516
|
-
</div>
|
|
517
|
-
);
|
|
518
|
-
};
|
|
519
|
-
|
|
520
|
-
// eslint-disable-next-line no-unused-vars
|
|
521
|
-
const InputWithDefaultValueDemo = () => {
|
|
522
|
-
return (
|
|
523
|
-
<div className="demo-card">
|
|
524
|
-
<h3 className="demo-title">Input with Default Value</h3>
|
|
525
|
-
<div className="control-group">
|
|
526
|
-
<label>
|
|
527
|
-
Input with default value:
|
|
528
|
-
<Input
|
|
529
|
-
name="text"
|
|
530
|
-
defaultValue="Hello World!"
|
|
531
|
-
placeholder="Enter text..."
|
|
532
|
-
/>
|
|
533
|
-
</label>
|
|
534
|
-
</div>
|
|
535
|
-
<div className="result-display">
|
|
536
|
-
Input starts with default value.
|
|
537
|
-
</div>
|
|
538
|
-
</div>
|
|
539
|
-
);
|
|
540
|
-
};
|
|
541
|
-
|
|
542
|
-
// eslint-disable-next-line no-unused-vars
|
|
543
|
-
const InputWithErrorDemo = () => {
|
|
544
|
-
const [result, setResult] = useState("");
|
|
545
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
546
|
-
|
|
547
|
-
return (
|
|
548
|
-
<div className="demo-card">
|
|
549
|
-
<h3 className="demo-title">Input with Action (Always Fails)</h3>
|
|
550
|
-
<div className="control-group">
|
|
551
|
-
<label>
|
|
552
|
-
Enter text (will always fail):
|
|
553
|
-
<Input
|
|
554
|
-
name="text"
|
|
555
|
-
placeholder="Type something..."
|
|
556
|
-
action={async (text) => {
|
|
557
|
-
setIsLoading(true);
|
|
558
|
-
setResult("Processing...");
|
|
559
|
-
try {
|
|
560
|
-
await delay(1200);
|
|
561
|
-
throw new Error(`Processing failed for input: "${text}"`);
|
|
562
|
-
} catch (error) {
|
|
563
|
-
setResult(`❌ ${error.message}`);
|
|
564
|
-
throw error;
|
|
565
|
-
} finally {
|
|
566
|
-
setIsLoading(false);
|
|
567
|
-
}
|
|
568
|
-
}}
|
|
569
|
-
/>
|
|
570
|
-
</label>
|
|
571
|
-
</div>
|
|
572
|
-
<div
|
|
573
|
-
className={`result-display ${isLoading ? "result-loading" : result.startsWith("❌") ? "result-error" : ""}`}
|
|
574
|
-
>
|
|
575
|
-
{result || "This input action will always fail..."}
|
|
576
|
-
</div>
|
|
577
|
-
</div>
|
|
578
|
-
);
|
|
579
|
-
};
|
|
580
|
-
|
|
581
|
-
// eslint-disable-next-line no-unused-vars
|
|
582
|
-
const InputWithValidationErrorDemo = () => {
|
|
583
|
-
const [result, setResult] = useState("");
|
|
584
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
585
|
-
|
|
586
|
-
return (
|
|
587
|
-
<div className="demo-card">
|
|
588
|
-
<h3 className="demo-title">
|
|
589
|
-
Input with Validation (Fails on Short Text)
|
|
590
|
-
</h3>
|
|
591
|
-
<div className="control-group">
|
|
592
|
-
<label>
|
|
593
|
-
Enter text (min 5 characters):
|
|
594
|
-
<Input
|
|
595
|
-
name="text"
|
|
596
|
-
placeholder="Must be at least 5 characters..."
|
|
597
|
-
action={async (text) => {
|
|
598
|
-
setIsLoading(true);
|
|
599
|
-
setResult("Validating...");
|
|
600
|
-
try {
|
|
601
|
-
await delay(800);
|
|
602
|
-
if (!text || text.length < 5) {
|
|
603
|
-
throw new Error(
|
|
604
|
-
"Text must be at least 5 characters long",
|
|
605
|
-
);
|
|
606
|
-
}
|
|
607
|
-
const response = `✅ Valid input: "${text}" (${text.length} characters)`;
|
|
608
|
-
setResult(response);
|
|
609
|
-
return response;
|
|
610
|
-
} catch (error) {
|
|
611
|
-
setResult(`❌ Validation failed: ${error.message}`);
|
|
612
|
-
throw error;
|
|
613
|
-
} finally {
|
|
614
|
-
setIsLoading(false);
|
|
615
|
-
}
|
|
616
|
-
}}
|
|
617
|
-
/>
|
|
618
|
-
</label>
|
|
619
|
-
</div>
|
|
620
|
-
<div
|
|
621
|
-
className={`result-display ${isLoading ? "result-loading" : result.startsWith("✅") ? "result-success" : result.startsWith("❌") ? "result-error" : ""}`}
|
|
622
|
-
>
|
|
623
|
-
{result || "Enter at least 5 characters to pass validation..."}
|
|
624
|
-
</div>
|
|
625
|
-
</div>
|
|
626
|
-
);
|
|
627
|
-
};
|
|
628
|
-
|
|
629
|
-
// eslint-disable-next-line no-unused-vars
|
|
630
|
-
const BasicInputDemo = () => {
|
|
631
|
-
const [result, setResult] = useState("");
|
|
632
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
633
|
-
|
|
634
|
-
return (
|
|
635
|
-
<div className="demo-card">
|
|
636
|
-
<h3 className="demo-title">Basic Input with Action</h3>
|
|
637
|
-
<div className="control-group">
|
|
638
|
-
<label>
|
|
639
|
-
Enter some text:
|
|
640
|
-
<Input
|
|
641
|
-
name="text"
|
|
642
|
-
placeholder="Type something..."
|
|
643
|
-
action={async (text) => {
|
|
644
|
-
setIsLoading(true);
|
|
645
|
-
setResult("Processing...");
|
|
646
|
-
try {
|
|
647
|
-
await delay(1000);
|
|
648
|
-
const response = `✅ Processed: "${text}"`;
|
|
649
|
-
setResult(response);
|
|
650
|
-
return response;
|
|
651
|
-
} catch (error) {
|
|
652
|
-
setResult(`❌ Error: ${error.message}`);
|
|
653
|
-
throw error;
|
|
654
|
-
} finally {
|
|
655
|
-
setIsLoading(false);
|
|
656
|
-
}
|
|
657
|
-
}}
|
|
658
|
-
/>
|
|
659
|
-
</label>
|
|
660
|
-
</div>
|
|
661
|
-
<div
|
|
662
|
-
className={`result-display ${isLoading ? "result-loading" : result.startsWith("✅") ? "result-success" : result.startsWith("❌") ? "result-error" : ""}`}
|
|
663
|
-
>
|
|
664
|
-
{result || "Result will appear here..."}
|
|
665
|
-
</div>
|
|
666
|
-
</div>
|
|
667
|
-
);
|
|
668
|
-
};
|
|
669
|
-
|
|
670
|
-
// eslint-disable-next-line no-unused-vars
|
|
671
|
-
const InputWithInitialValueDemo = () => {
|
|
672
|
-
const [value, setValue] = useState("Hello World!");
|
|
673
|
-
const [result, setResult] = useState("");
|
|
674
|
-
|
|
675
|
-
return (
|
|
676
|
-
<div className="demo-card">
|
|
677
|
-
<h3 className="demo-title">Input with Initial Value</h3>
|
|
678
|
-
<div className="control-group">
|
|
679
|
-
<label>
|
|
680
|
-
Input with preset value:
|
|
681
|
-
<Input
|
|
682
|
-
name="text"
|
|
683
|
-
value={value}
|
|
684
|
-
onUIStateChange={setValue}
|
|
685
|
-
action={async (text) => {
|
|
686
|
-
setResult("Processing...");
|
|
687
|
-
await delay(800);
|
|
688
|
-
const response = `✅ Submitted: "${text}"`;
|
|
689
|
-
setResult(response);
|
|
690
|
-
return response;
|
|
691
|
-
}}
|
|
692
|
-
/>
|
|
693
|
-
</label>
|
|
694
|
-
</div>
|
|
695
|
-
<div
|
|
696
|
-
className={`result-display ${result.startsWith("✅") ? "result-success" : ""}`}
|
|
697
|
-
>
|
|
698
|
-
{result || "Submit to see result..."}
|
|
699
|
-
</div>
|
|
700
|
-
</div>
|
|
701
|
-
);
|
|
702
|
-
};
|
|
703
|
-
|
|
704
|
-
// eslint-disable-next-line no-unused-vars
|
|
705
|
-
const InputWithExternalControlDemo = () => {
|
|
706
|
-
const [value, setValue] = useState(undefined);
|
|
707
|
-
const [result, setResult] = useState("");
|
|
708
|
-
|
|
709
|
-
return (
|
|
710
|
-
<div className="demo-card">
|
|
711
|
-
<h3 className="demo-title">Input with External Control</h3>
|
|
712
|
-
<div className="control-group">
|
|
713
|
-
<label>
|
|
714
|
-
Externally controlled input:
|
|
715
|
-
<Input
|
|
716
|
-
name="text"
|
|
717
|
-
value={value}
|
|
718
|
-
placeholder="Use buttons below to set value"
|
|
719
|
-
action={async (text) => {
|
|
720
|
-
setResult("Processing...");
|
|
721
|
-
await delay(600);
|
|
722
|
-
const response = `✅ Action executed with: "${text}"`;
|
|
723
|
-
setResult(response);
|
|
724
|
-
setValue(text);
|
|
725
|
-
return response;
|
|
726
|
-
}}
|
|
727
|
-
/>
|
|
728
|
-
</label>
|
|
729
|
-
</div>
|
|
730
|
-
<div className="button-group">
|
|
731
|
-
<button
|
|
732
|
-
onClick={() => {
|
|
733
|
-
setValue("Preset Value 1");
|
|
734
|
-
}}
|
|
735
|
-
>
|
|
736
|
-
Set Value 1
|
|
737
|
-
</button>
|
|
738
|
-
<button
|
|
739
|
-
onClick={() => {
|
|
740
|
-
setValue("Preset Value 2");
|
|
741
|
-
}}
|
|
742
|
-
>
|
|
743
|
-
Set Value 2
|
|
744
|
-
</button>
|
|
745
|
-
<button
|
|
746
|
-
onClick={() => {
|
|
747
|
-
setValue("");
|
|
748
|
-
}}
|
|
749
|
-
>
|
|
750
|
-
Clear
|
|
751
|
-
</button>
|
|
752
|
-
</div>
|
|
753
|
-
<div
|
|
754
|
-
className={`result-display ${result.startsWith("✅") ? "result-success" : ""}`}
|
|
755
|
-
>
|
|
756
|
-
Current value: "{value}"<br />
|
|
757
|
-
{result || "Click buttons to change value, then submit..."}
|
|
758
|
-
</div>
|
|
759
|
-
</div>
|
|
760
|
-
);
|
|
761
|
-
};
|
|
762
|
-
|
|
763
|
-
// eslint-disable-next-line no-unused-vars
|
|
764
|
-
const FormInputDemo = () => {
|
|
765
|
-
const [result, setResult] = useState("");
|
|
766
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
767
|
-
|
|
768
|
-
const formDemoAction = async ({ username, email }) => {
|
|
769
|
-
setIsLoading(true);
|
|
770
|
-
setResult("Submitting form...");
|
|
771
|
-
try {
|
|
772
|
-
await delay(1200);
|
|
773
|
-
const response = `✅ Form submitted!\nUsername: ${username}\nEmail: ${email}`;
|
|
774
|
-
setResult(response);
|
|
775
|
-
return response;
|
|
776
|
-
} catch (error) {
|
|
777
|
-
setResult(`❌ Form error: ${error.message}`);
|
|
778
|
-
throw error;
|
|
779
|
-
} finally {
|
|
780
|
-
setIsLoading(false);
|
|
781
|
-
}
|
|
782
|
-
};
|
|
783
|
-
|
|
784
|
-
return (
|
|
785
|
-
<div className="demo-card">
|
|
786
|
-
<h3 className="demo-title">Input inside Form</h3>
|
|
787
|
-
<Form action={formDemoAction}>
|
|
788
|
-
<div className="control-group">
|
|
789
|
-
<label>
|
|
790
|
-
Username:
|
|
791
|
-
<Input name="username" placeholder="Enter username" />
|
|
792
|
-
</label>
|
|
793
|
-
</div>
|
|
794
|
-
<div className="control-group"></div>
|
|
795
|
-
<div className="button-group">
|
|
796
|
-
<Button type="submit">Submit Form</Button>
|
|
797
|
-
<Button type="reset">Reset</Button>
|
|
798
|
-
</div>
|
|
799
|
-
</Form>
|
|
800
|
-
<div
|
|
801
|
-
className={`result-display ${isLoading ? "result-loading" : result.startsWith("✅") ? "result-success" : result.startsWith("❌") ? "result-error" : ""}`}
|
|
802
|
-
>
|
|
803
|
-
{result || "Fill form and submit..."}
|
|
804
|
-
</div>
|
|
805
|
-
</div>
|
|
806
|
-
);
|
|
807
|
-
};
|
|
808
|
-
|
|
809
|
-
// eslint-disable-next-line no-unused-vars
|
|
810
|
-
const FormInputErrorDemo = () => {
|
|
811
|
-
const [result, setResult] = useState("");
|
|
812
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
813
|
-
|
|
814
|
-
const formErrorAction = async ({ username, email }) => {
|
|
815
|
-
setIsLoading(true);
|
|
816
|
-
setResult("Submitting form...");
|
|
817
|
-
try {
|
|
818
|
-
await delay(1000);
|
|
819
|
-
// Simulate server-side validation errors
|
|
820
|
-
if (!username || username.length < 3) {
|
|
821
|
-
throw new Error("Username must be at least 3 characters long");
|
|
822
|
-
}
|
|
823
|
-
if (username === "admin") {
|
|
824
|
-
throw new Error(
|
|
825
|
-
"Username 'admin' is reserved and cannot be used",
|
|
826
|
-
);
|
|
827
|
-
}
|
|
828
|
-
// This will never be reached due to the conditions above
|
|
829
|
-
const response = `✅ Form submitted!\nUsername: ${username}\nEmail: ${email}`;
|
|
830
|
-
setResult(response);
|
|
831
|
-
return response;
|
|
832
|
-
} catch (error) {
|
|
833
|
-
setResult(`❌ Form error: ${error.message}`);
|
|
834
|
-
throw error;
|
|
835
|
-
} finally {
|
|
836
|
-
setIsLoading(false);
|
|
837
|
-
}
|
|
838
|
-
};
|
|
839
|
-
|
|
840
|
-
return (
|
|
841
|
-
<div className="demo-card">
|
|
842
|
-
<h3 className="demo-title">Form with Validation Errors</h3>
|
|
843
|
-
<p
|
|
844
|
-
style={{ fontSize: "14px", color: "#666", marginBottom: "15px" }}
|
|
845
|
-
>
|
|
846
|
-
This form demonstrates server-side validation errors. Try: empty
|
|
847
|
-
fields, username < 3 chars, invalid email, or "admin" username.
|
|
848
|
-
</p>
|
|
849
|
-
<Form action={formErrorAction}>
|
|
850
|
-
<div className="control-group">
|
|
851
|
-
<label>
|
|
852
|
-
Username:
|
|
853
|
-
<Input
|
|
854
|
-
name="username"
|
|
855
|
-
placeholder="Enter username (try 'admin' or < 3 chars)"
|
|
856
|
-
/>
|
|
857
|
-
</label>
|
|
858
|
-
</div>
|
|
859
|
-
<div className="button-group">
|
|
860
|
-
<Button type="submit">Submit Form</Button>
|
|
861
|
-
<Button type="reset">Reset</Button>
|
|
862
|
-
</div>
|
|
863
|
-
</Form>
|
|
864
|
-
<div
|
|
865
|
-
className={`result-display ${isLoading ? "result-loading" : result.startsWith("✅") ? "result-success" : result.startsWith("❌") ? "result-error" : ""}`}
|
|
866
|
-
>
|
|
867
|
-
{result || "Fill form and submit to see validation errors..."}
|
|
868
|
-
</div>
|
|
869
|
-
</div>
|
|
870
|
-
);
|
|
871
|
-
};
|
|
872
|
-
|
|
873
|
-
render(<App />, document.querySelector("#root"));
|
|
874
|
-
</script>
|
|
875
|
-
</body>
|
|
876
|
-
</html>
|