@jsenv/navi 0.0.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/index.js +51 -0
- package/package.json +38 -0
- package/src/action_private_properties.js +11 -0
- package/src/action_proxy_test.html +353 -0
- package/src/action_run_states.js +5 -0
- package/src/actions.js +1377 -0
- package/src/browser_integration/browser_integration.js +191 -0
- package/src/browser_integration/document_back_and_forward.js +17 -0
- package/src/browser_integration/document_loading_signal.js +100 -0
- package/src/browser_integration/document_state_signal.js +9 -0
- package/src/browser_integration/document_url_signal.js +9 -0
- package/src/browser_integration/use_is_visited.js +19 -0
- package/src/browser_integration/via_history.js +199 -0
- package/src/browser_integration/via_navigation.js +168 -0
- package/src/components/action_execution/form_context.js +8 -0
- package/src/components/action_execution/render_actionable_component.jsx +27 -0
- package/src/components/action_execution/use_action.js +330 -0
- package/src/components/action_execution/use_execute_action.js +161 -0
- package/src/components/action_renderer.jsx +136 -0
- package/src/components/collect_form_element_values.js +79 -0
- package/src/components/demos/0_button_demo.html +155 -0
- package/src/components/demos/1_checkbox_demo.html +257 -0
- package/src/components/demos/2_input_textual_demo.html +354 -0
- package/src/components/demos/3_radio_demo.html +222 -0
- package/src/components/demos/4_select_demo.html +104 -0
- package/src/components/demos/5_list_scrollable_demo.html +153 -0
- package/src/components/demos/action/0_button_demo.html +204 -0
- package/src/components/demos/action/10_shortcuts_demo.html +189 -0
- package/src/components/demos/action/11_nested_shortcuts_demo.html +401 -0
- package/src/components/demos/action/1_input_text_demo.html +461 -0
- package/src/components/demos/action/2_form_multiple.html +303 -0
- package/src/components/demos/action/3_details_demo.html +172 -0
- package/src/components/demos/action/4_input_checkbox_demo.html +611 -0
- package/src/components/demos/action/6_checkbox_list_demo.html +109 -0
- package/src/components/demos/action/7_radio_list_demo.html +217 -0
- package/src/components/demos/action/8_editable_text_demo.html +442 -0
- package/src/components/demos/action/9_link_demo.html +172 -0
- package/src/components/demos/demo.md +0 -0
- package/src/components/demos/route/basic/basic.html +14 -0
- package/src/components/demos/route/basic/basic_route_demo.jsx +224 -0
- package/src/components/demos/route/multi/multi.html +14 -0
- package/src/components/demos/route/multi/multi_route_demo.jsx +277 -0
- package/src/components/details/details.jsx +248 -0
- package/src/components/details/summary_marker.jsx +141 -0
- package/src/components/editable_text/editable_text.jsx +96 -0
- package/src/components/error_boundary_context.js +9 -0
- package/src/components/form.jsx +144 -0
- package/src/components/input/button.jsx +333 -0
- package/src/components/input/checkbox_list.jsx +294 -0
- package/src/components/input/field.jsx +61 -0
- package/src/components/input/field_css.js +118 -0
- package/src/components/input/input.jsx +15 -0
- package/src/components/input/input_checkbox.jsx +370 -0
- package/src/components/input/input_radio.jsx +299 -0
- package/src/components/input/input_textual.jsx +338 -0
- package/src/components/input/radio_list.jsx +283 -0
- package/src/components/input/select.jsx +273 -0
- package/src/components/input/use_form_event.js +20 -0
- package/src/components/input/use_on_change.js +12 -0
- package/src/components/link/link.jsx +291 -0
- package/src/components/loader/loader_background.jsx +324 -0
- package/src/components/loader/loading_spinner.jsx +68 -0
- package/src/components/loader/network_speed.js +83 -0
- package/src/components/loader/rectangle_loading.jsx +225 -0
- package/src/components/route.jsx +15 -0
- package/src/components/selection/selection.js +5 -0
- package/src/components/selection/selection_context.jsx +262 -0
- package/src/components/shortcut/os.js +9 -0
- package/src/components/shortcut/shortcut_context.jsx +390 -0
- package/src/components/use_action_events.js +37 -0
- package/src/components/use_auto_focus.js +43 -0
- package/src/components/use_debounce_true.js +31 -0
- package/src/components/use_focus_group.js +19 -0
- package/src/components/use_initial_value.js +104 -0
- package/src/components/use_is_visited.js +19 -0
- package/src/components/use_ref_array.js +38 -0
- package/src/components/use_signal_sync.js +50 -0
- package/src/components/use_state_array.js +40 -0
- package/src/docs/actions.md +228 -0
- package/src/docs/demos/resource/action_status.jsx +42 -0
- package/src/docs/demos/resource/demo.md +1 -0
- package/src/docs/demos/resource/resource_demo_0.html +84 -0
- package/src/docs/demos/resource/resource_demo_10_post_gc.html +364 -0
- package/src/docs/demos/resource/resource_demo_11_describe_many.html +362 -0
- package/src/docs/demos/resource/resource_demo_2.html +173 -0
- package/src/docs/demos/resource/resource_demo_3_filtered_users.html +415 -0
- package/src/docs/demos/resource/resource_demo_4_details.html +284 -0
- package/src/docs/demos/resource/resource_demo_5_renderer_lazy.html +115 -0
- package/src/docs/demos/resource/resource_demo_6_gc.html +217 -0
- package/src/docs/demos/resource/resource_demo_7_child_gc.html +240 -0
- package/src/docs/demos/resource/resource_demo_8_proxy_gc.html +319 -0
- package/src/docs/demos/resource/resource_demo_9_describe_one.html +472 -0
- package/src/docs/demos/resource/tata.jsx +3 -0
- package/src/docs/demos/resource/toto.jsx +3 -0
- package/src/docs/demos/user_nav/user_nav.html +12 -0
- package/src/docs/demos/user_nav/user_nav.jsx +330 -0
- package/src/docs/resource_dependencies.md +103 -0
- package/src/docs/resource_with_params.md +80 -0
- package/src/notes.md +13 -0
- package/src/route/route.js +518 -0
- package/src/route/route.test.html +228 -0
- package/src/store/array_signal_store.js +537 -0
- package/src/store/local_storage_signal.js +17 -0
- package/src/store/resource_graph.js +1303 -0
- package/src/store/tests/resource_graph_autoreload_demo.html +12 -0
- package/src/store/tests/resource_graph_autoreload_demo.jsx +964 -0
- package/src/store/tests/resource_graph_dependencies.test.js +95 -0
- package/src/store/value_in_local_storage.js +187 -0
- package/src/symbol_object_signal.js +1 -0
- package/src/use_action_data.js +10 -0
- package/src/use_action_status.js +47 -0
- package/src/utils/add_many_event_listeners.js +15 -0
- package/src/utils/array_add_remove.js +61 -0
- package/src/utils/array_signal.js +15 -0
- package/src/utils/compare_two_js_values.js +172 -0
- package/src/utils/execute_with_cleanup.js +21 -0
- package/src/utils/get_caller_info.js +85 -0
- package/src/utils/iterable_weak_set.js +62 -0
- package/src/utils/js_value_weak_map.js +162 -0
- package/src/utils/js_value_weak_map_demo.html +690 -0
- package/src/utils/merge_two_js_values.js +53 -0
- package/src/utils/stringify_for_display.js +150 -0
- package/src/utils/weak_effect.js +48 -0
|
@@ -0,0 +1,461 @@
|
|
|
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
|
+
font-family:
|
|
11
|
+
system-ui,
|
|
12
|
+
-apple-system,
|
|
13
|
+
sans-serif;
|
|
14
|
+
max-width: 1200px;
|
|
15
|
+
margin: 0 auto;
|
|
16
|
+
padding: 20px;
|
|
17
|
+
background: #f5f5f5;
|
|
18
|
+
}
|
|
19
|
+
.demo-grid {
|
|
20
|
+
display: grid;
|
|
21
|
+
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
|
22
|
+
gap: 20px;
|
|
23
|
+
margin-bottom: 30px;
|
|
24
|
+
}
|
|
25
|
+
.demo-card {
|
|
26
|
+
background: white;
|
|
27
|
+
border-radius: 8px;
|
|
28
|
+
padding: 20px;
|
|
29
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
30
|
+
}
|
|
31
|
+
.demo-title {
|
|
32
|
+
margin: 0 0 15px 0;
|
|
33
|
+
color: #333;
|
|
34
|
+
font-size: 16px;
|
|
35
|
+
font-weight: 600;
|
|
36
|
+
border-bottom: 2px solid #e0e0e0;
|
|
37
|
+
padding-bottom: 8px;
|
|
38
|
+
}
|
|
39
|
+
.result-display {
|
|
40
|
+
margin-top: 15px;
|
|
41
|
+
padding: 12px;
|
|
42
|
+
background: #f8f9fa;
|
|
43
|
+
border: 1px solid #dee2e6;
|
|
44
|
+
border-radius: 4px;
|
|
45
|
+
min-height: 20px;
|
|
46
|
+
font-family: monospace;
|
|
47
|
+
font-size: 14px;
|
|
48
|
+
}
|
|
49
|
+
.result-success {
|
|
50
|
+
background: #d4edda;
|
|
51
|
+
border-color: #c3e6cb;
|
|
52
|
+
color: #155724;
|
|
53
|
+
}
|
|
54
|
+
.result-error {
|
|
55
|
+
background: #f8d7da;
|
|
56
|
+
border-color: #f5c6cb;
|
|
57
|
+
color: #721c24;
|
|
58
|
+
}
|
|
59
|
+
.result-loading {
|
|
60
|
+
background: #d1ecf1;
|
|
61
|
+
border-color: #bee5eb;
|
|
62
|
+
color: #0c5460;
|
|
63
|
+
}
|
|
64
|
+
.button-group {
|
|
65
|
+
display: flex;
|
|
66
|
+
gap: 10px;
|
|
67
|
+
margin-top: 10px;
|
|
68
|
+
}
|
|
69
|
+
button {
|
|
70
|
+
padding: 8px 16px;
|
|
71
|
+
border: 1px solid #ccc;
|
|
72
|
+
border-radius: 4px;
|
|
73
|
+
background: white;
|
|
74
|
+
cursor: pointer;
|
|
75
|
+
font-size: 14px;
|
|
76
|
+
}
|
|
77
|
+
button:hover {
|
|
78
|
+
background: #f8f9fa;
|
|
79
|
+
}
|
|
80
|
+
button[type="submit"] {
|
|
81
|
+
background: #007bff;
|
|
82
|
+
color: white;
|
|
83
|
+
border-color: #007bff;
|
|
84
|
+
}
|
|
85
|
+
button[type="submit"]:hover {
|
|
86
|
+
background: #0056b3;
|
|
87
|
+
}
|
|
88
|
+
button[type="reset"] {
|
|
89
|
+
background: #6c757d;
|
|
90
|
+
color: white;
|
|
91
|
+
border-color: #6c757d;
|
|
92
|
+
}
|
|
93
|
+
button[type="reset"]:hover {
|
|
94
|
+
background: #545b62;
|
|
95
|
+
}
|
|
96
|
+
input {
|
|
97
|
+
width: 100%;
|
|
98
|
+
padding: 8px 12px;
|
|
99
|
+
border: 1px solid #ccc;
|
|
100
|
+
border-radius: 4px;
|
|
101
|
+
font-size: 14px;
|
|
102
|
+
box-sizing: border-box;
|
|
103
|
+
}
|
|
104
|
+
.control-group {
|
|
105
|
+
margin-bottom: 15px;
|
|
106
|
+
}
|
|
107
|
+
label {
|
|
108
|
+
display: block;
|
|
109
|
+
margin-bottom: 5px;
|
|
110
|
+
font-weight: 500;
|
|
111
|
+
color: #555;
|
|
112
|
+
}
|
|
113
|
+
</style>
|
|
114
|
+
</head>
|
|
115
|
+
<body>
|
|
116
|
+
<h1>Input Text Demo</h1>
|
|
117
|
+
<div id="root"></div>
|
|
118
|
+
|
|
119
|
+
<script type="module" jsenv-type="module/jsx">
|
|
120
|
+
import { render } from "preact";
|
|
121
|
+
import { useState } from "preact/hooks";
|
|
122
|
+
// eslint-disable-next-line no-unused-vars
|
|
123
|
+
import { Field, Input, Form, Button, createAction } from "@jsenv/navi";
|
|
124
|
+
|
|
125
|
+
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
126
|
+
|
|
127
|
+
// eslint-disable-next-line no-unused-vars
|
|
128
|
+
const App = () => {
|
|
129
|
+
return (
|
|
130
|
+
<>
|
|
131
|
+
<div className="demo-grid">
|
|
132
|
+
<BasicInputDemo />
|
|
133
|
+
<InputWithInitialValueDemo />
|
|
134
|
+
<InputWithExternalControlDemo />
|
|
135
|
+
<FormInputDemo />
|
|
136
|
+
<FormWithExternalControlDemo />
|
|
137
|
+
<SharedActionDemo />
|
|
138
|
+
</div>
|
|
139
|
+
</>
|
|
140
|
+
);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// eslint-disable-next-line no-unused-vars
|
|
144
|
+
const BasicInputDemo = () => {
|
|
145
|
+
const [result, setResult] = useState("");
|
|
146
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
<div className="demo-card">
|
|
150
|
+
<h3 className="demo-title">Basic Input with Action</h3>
|
|
151
|
+
<div className="control-group">
|
|
152
|
+
<label htmlFor="basic-input">Enter some text:</label>
|
|
153
|
+
<Input
|
|
154
|
+
id="basic-input"
|
|
155
|
+
name="text"
|
|
156
|
+
placeholder="Type something..."
|
|
157
|
+
action={async ({ text }) => {
|
|
158
|
+
setIsLoading(true);
|
|
159
|
+
setResult("Processing...");
|
|
160
|
+
try {
|
|
161
|
+
await delay(1000);
|
|
162
|
+
const response = `✅ Processed: "${text}"`;
|
|
163
|
+
setResult(response);
|
|
164
|
+
return response;
|
|
165
|
+
} catch (error) {
|
|
166
|
+
setResult(`❌ Error: ${error.message}`);
|
|
167
|
+
throw error;
|
|
168
|
+
} finally {
|
|
169
|
+
setIsLoading(false);
|
|
170
|
+
}
|
|
171
|
+
}}
|
|
172
|
+
/>
|
|
173
|
+
</div>
|
|
174
|
+
<div
|
|
175
|
+
className={`result-display ${isLoading ? "result-loading" : result.startsWith("✅") ? "result-success" : result.startsWith("❌") ? "result-error" : ""}`}
|
|
176
|
+
>
|
|
177
|
+
{result || "Result will appear here..."}
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
);
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// eslint-disable-next-line no-unused-vars
|
|
184
|
+
const InputWithInitialValueDemo = () => {
|
|
185
|
+
const [result, setResult] = useState("");
|
|
186
|
+
|
|
187
|
+
return (
|
|
188
|
+
<div className="demo-card">
|
|
189
|
+
<h3 className="demo-title">Input with Initial Value</h3>
|
|
190
|
+
<div className="control-group">
|
|
191
|
+
<label htmlFor="initial-input">Input with preset value:</label>
|
|
192
|
+
<Input
|
|
193
|
+
id="initial-input"
|
|
194
|
+
name="text"
|
|
195
|
+
value="Hello World!"
|
|
196
|
+
action={async ({ text }) => {
|
|
197
|
+
setResult("Processing...");
|
|
198
|
+
await delay(800);
|
|
199
|
+
const response = `✅ Submitted: "${text}"`;
|
|
200
|
+
setResult(response);
|
|
201
|
+
return response;
|
|
202
|
+
}}
|
|
203
|
+
/>
|
|
204
|
+
</div>
|
|
205
|
+
<div
|
|
206
|
+
className={`result-display ${result.startsWith("✅") ? "result-success" : ""}`}
|
|
207
|
+
>
|
|
208
|
+
{result || "Submit to see result..."}
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
);
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// eslint-disable-next-line no-unused-vars
|
|
215
|
+
const InputWithExternalControlDemo = () => {
|
|
216
|
+
const [inputValue, setInputValue] = useState(undefined);
|
|
217
|
+
const [result, setResult] = useState("");
|
|
218
|
+
|
|
219
|
+
return (
|
|
220
|
+
<div className="demo-card">
|
|
221
|
+
<h3 className="demo-title">Input with External Control</h3>
|
|
222
|
+
<div className="control-group">
|
|
223
|
+
<label htmlFor="controlled-input">
|
|
224
|
+
Externally controlled input:
|
|
225
|
+
</label>
|
|
226
|
+
<Input
|
|
227
|
+
id="controlled-input"
|
|
228
|
+
name="text"
|
|
229
|
+
value={inputValue}
|
|
230
|
+
placeholder="Use buttons below to set value"
|
|
231
|
+
action={async ({ text }) => {
|
|
232
|
+
setResult("Processing...");
|
|
233
|
+
await delay(600);
|
|
234
|
+
const response = `✅ Action executed with: "${text}"`;
|
|
235
|
+
setResult(response);
|
|
236
|
+
return response;
|
|
237
|
+
}}
|
|
238
|
+
/>
|
|
239
|
+
</div>
|
|
240
|
+
<div className="button-group">
|
|
241
|
+
<button
|
|
242
|
+
onClick={() => {
|
|
243
|
+
setInputValue("Preset Value 1");
|
|
244
|
+
}}
|
|
245
|
+
>
|
|
246
|
+
Set Value 1
|
|
247
|
+
</button>
|
|
248
|
+
<button
|
|
249
|
+
onClick={() => {
|
|
250
|
+
setInputValue("Preset Value 2");
|
|
251
|
+
}}
|
|
252
|
+
>
|
|
253
|
+
Set Value 2
|
|
254
|
+
</button>
|
|
255
|
+
<button
|
|
256
|
+
onClick={() => {
|
|
257
|
+
setInputValue("");
|
|
258
|
+
}}
|
|
259
|
+
>
|
|
260
|
+
Clear
|
|
261
|
+
</button>
|
|
262
|
+
</div>
|
|
263
|
+
<div
|
|
264
|
+
className={`result-display ${result.startsWith("✅") ? "result-success" : ""}`}
|
|
265
|
+
>
|
|
266
|
+
Current value: "{inputValue}"<br />
|
|
267
|
+
{result || "Click buttons to change value, then submit..."}
|
|
268
|
+
</div>
|
|
269
|
+
</div>
|
|
270
|
+
);
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
// eslint-disable-next-line no-unused-vars
|
|
274
|
+
const FormInputDemo = () => {
|
|
275
|
+
const [result, setResult] = useState("");
|
|
276
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
277
|
+
|
|
278
|
+
return (
|
|
279
|
+
<div className="demo-card">
|
|
280
|
+
<h3 className="demo-title">Input inside Form</h3>
|
|
281
|
+
<Form
|
|
282
|
+
action={async ({ username, email }) => {
|
|
283
|
+
setIsLoading(true);
|
|
284
|
+
setResult("Submitting form...");
|
|
285
|
+
try {
|
|
286
|
+
await delay(1200);
|
|
287
|
+
const response = `✅ Form submitted!\nUsername: ${username}\nEmail: ${email}`;
|
|
288
|
+
setResult(response);
|
|
289
|
+
return response;
|
|
290
|
+
} catch (error) {
|
|
291
|
+
setResult(`❌ Form error: ${error.message}`);
|
|
292
|
+
throw error;
|
|
293
|
+
} finally {
|
|
294
|
+
setIsLoading(false);
|
|
295
|
+
}
|
|
296
|
+
}}
|
|
297
|
+
>
|
|
298
|
+
<div className="control-group">
|
|
299
|
+
<label htmlFor="form-username">Username:</label>
|
|
300
|
+
<Input
|
|
301
|
+
id="form-username"
|
|
302
|
+
name="username"
|
|
303
|
+
placeholder="Enter username"
|
|
304
|
+
/>
|
|
305
|
+
</div>
|
|
306
|
+
<div className="control-group">
|
|
307
|
+
<label htmlFor="form-email">Email:</label>
|
|
308
|
+
<Input
|
|
309
|
+
id="form-email"
|
|
310
|
+
name="email"
|
|
311
|
+
type="email"
|
|
312
|
+
placeholder="Enter email"
|
|
313
|
+
/>
|
|
314
|
+
</div>
|
|
315
|
+
<div className="button-group">
|
|
316
|
+
<Button type="submit">Submit Form</Button>
|
|
317
|
+
<Button type="reset">Reset</Button>
|
|
318
|
+
</div>
|
|
319
|
+
</Form>
|
|
320
|
+
<div
|
|
321
|
+
className={`result-display ${isLoading ? "result-loading" : result.startsWith("✅") ? "result-success" : result.startsWith("❌") ? "result-error" : ""}`}
|
|
322
|
+
>
|
|
323
|
+
{result || "Fill form and submit..."}
|
|
324
|
+
</div>
|
|
325
|
+
</div>
|
|
326
|
+
);
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
// eslint-disable-next-line no-unused-vars
|
|
330
|
+
const FormWithExternalControlDemo = () => {
|
|
331
|
+
const [username, setUsername] = useState(undefined);
|
|
332
|
+
const [email, setEmail] = useState(undefined);
|
|
333
|
+
const [result, setResult] = useState("");
|
|
334
|
+
|
|
335
|
+
return (
|
|
336
|
+
<div className="demo-card">
|
|
337
|
+
<h3 className="demo-title">Form with External Control</h3>
|
|
338
|
+
<Form
|
|
339
|
+
action={async (params) => {
|
|
340
|
+
setResult("Submitting form...");
|
|
341
|
+
await delay(1000);
|
|
342
|
+
const response = `✅ Form submitted!\nUsername: ${params.username}\nEmail: ${params.email}`;
|
|
343
|
+
setResult(response);
|
|
344
|
+
return response;
|
|
345
|
+
}}
|
|
346
|
+
>
|
|
347
|
+
<div className="control-group">
|
|
348
|
+
<label htmlFor="controlled-form-username">Username:</label>
|
|
349
|
+
<Input
|
|
350
|
+
id="controlled-form-username"
|
|
351
|
+
name="username"
|
|
352
|
+
value={username}
|
|
353
|
+
placeholder="Controlled by buttons"
|
|
354
|
+
/>
|
|
355
|
+
</div>
|
|
356
|
+
<div className="control-group">
|
|
357
|
+
<label htmlFor="controlled-form-email">Email:</label>
|
|
358
|
+
<Input
|
|
359
|
+
id="controlled-form-email"
|
|
360
|
+
name="email"
|
|
361
|
+
value={email}
|
|
362
|
+
placeholder="Controlled by buttons"
|
|
363
|
+
/>
|
|
364
|
+
</div>
|
|
365
|
+
<div className="button-group">
|
|
366
|
+
<button
|
|
367
|
+
type="button"
|
|
368
|
+
onClick={() => {
|
|
369
|
+
setUsername("john_doe");
|
|
370
|
+
setEmail("john@example.com");
|
|
371
|
+
}}
|
|
372
|
+
>
|
|
373
|
+
Set User 1
|
|
374
|
+
</button>
|
|
375
|
+
<button
|
|
376
|
+
type="button"
|
|
377
|
+
onClick={() => {
|
|
378
|
+
setUsername("jane_smith");
|
|
379
|
+
setEmail("jane@example.com");
|
|
380
|
+
}}
|
|
381
|
+
>
|
|
382
|
+
Set User 2
|
|
383
|
+
</button>
|
|
384
|
+
<button
|
|
385
|
+
type="button"
|
|
386
|
+
onClick={() => {
|
|
387
|
+
setUsername("");
|
|
388
|
+
setEmail("");
|
|
389
|
+
}}
|
|
390
|
+
>
|
|
391
|
+
Clear All
|
|
392
|
+
</button>
|
|
393
|
+
</div>
|
|
394
|
+
<div className="button-group">
|
|
395
|
+
<Button type="submit">Submit Form</Button>
|
|
396
|
+
<Button type="reset">Reset</Button>
|
|
397
|
+
</div>
|
|
398
|
+
</Form>
|
|
399
|
+
<div
|
|
400
|
+
className={`result-display ${result.startsWith("✅") ? "result-success" : ""}`}
|
|
401
|
+
>
|
|
402
|
+
Current: {username} / {email}
|
|
403
|
+
<br />
|
|
404
|
+
{result || "Use buttons to set values, then submit..."}
|
|
405
|
+
</div>
|
|
406
|
+
</div>
|
|
407
|
+
);
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
// eslint-disable-next-line no-unused-vars
|
|
411
|
+
const SharedActionDemo = () => {
|
|
412
|
+
const [result, setResult] = useState("");
|
|
413
|
+
|
|
414
|
+
return (
|
|
415
|
+
<div className="demo-card">
|
|
416
|
+
<h3 className="demo-title">Shared Action Example</h3>
|
|
417
|
+
<div className="control-group">
|
|
418
|
+
<label htmlFor="shared-input-1">Input 1 (shared action):</label>
|
|
419
|
+
<Input
|
|
420
|
+
id="shared-input-1"
|
|
421
|
+
name="name"
|
|
422
|
+
placeholder="Enter name for input 1"
|
|
423
|
+
action={sharedAction}
|
|
424
|
+
onActionStart={() => {
|
|
425
|
+
setResult("Processing...");
|
|
426
|
+
}}
|
|
427
|
+
onActionEnd={(e) => {
|
|
428
|
+
setResult(e.detail.data);
|
|
429
|
+
}}
|
|
430
|
+
/>
|
|
431
|
+
</div>
|
|
432
|
+
<div className="control-group">
|
|
433
|
+
<label htmlFor="shared-input-2">
|
|
434
|
+
Input 2 (same shared action):
|
|
435
|
+
</label>
|
|
436
|
+
<Input
|
|
437
|
+
id="shared-input-2"
|
|
438
|
+
name="name"
|
|
439
|
+
placeholder="Enter name for input 2"
|
|
440
|
+
action={sharedAction}
|
|
441
|
+
/>
|
|
442
|
+
</div>
|
|
443
|
+
<div
|
|
444
|
+
className={`result-display ${result.startsWith("✅") ? "result-success" : ""}`}
|
|
445
|
+
>
|
|
446
|
+
{result || "Type in either input and submit..."}
|
|
447
|
+
</div>
|
|
448
|
+
</div>
|
|
449
|
+
);
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
const sharedAction = createAction(async ({ name }) => {
|
|
453
|
+
await delay(800);
|
|
454
|
+
const response = `✅ Hello ${name}! (from shared action)`;
|
|
455
|
+
return response;
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
render(<App />, document.querySelector("#root"));
|
|
459
|
+
</script>
|
|
460
|
+
</body>
|
|
461
|
+
</html>
|