@jsenv/navi 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/dist/jsenv_navi.js +22954 -0
  2. package/index.js +66 -16
  3. package/package.json +22 -11
  4. package/src/actions.js +50 -26
  5. package/src/browser_integration/browser_integration.js +31 -6
  6. package/src/browser_integration/via_history.js +42 -9
  7. package/src/components/action_execution/render_actionable_component.jsx +6 -4
  8. package/src/components/action_execution/use_action.js +51 -282
  9. package/src/components/action_execution/use_execute_action.js +106 -92
  10. package/src/components/action_execution/use_run_on_mount.js +9 -0
  11. package/src/components/action_renderer.jsx +21 -32
  12. package/src/components/demos/0_button_demo.html +574 -103
  13. package/src/components/demos/10_column_reordering_debug.html +277 -0
  14. package/src/components/demos/11_table_selection_debug.html +432 -0
  15. package/src/components/demos/1_checkbox_demo.html +579 -202
  16. package/src/components/demos/2_input_textual_demo.html +81 -138
  17. package/src/components/demos/3_radio_demo.html +0 -2
  18. package/src/components/demos/4_select_demo.html +19 -23
  19. package/src/components/demos/6_tablist_demo.html +77 -0
  20. package/src/components/demos/7_table_selection_demo.html +176 -0
  21. package/src/components/demos/8_table_fixed_headers_demo.html +584 -0
  22. package/src/components/demos/9_table_column_drag_demo.html +325 -0
  23. package/src/components/demos/action/0_button_demo.html +2 -4
  24. package/src/components/demos/action/1_input_text_demo.html +643 -222
  25. package/src/components/demos/action/3_details_demo.html +146 -115
  26. package/src/components/demos/action/4_input_checkbox_demo.html +442 -322
  27. package/src/components/demos/action/5_input_checkbox_state_demo.html +270 -0
  28. package/src/components/demos/action/6_checkbox_list_demo.html +304 -72
  29. package/src/components/demos/action/7_radio_list_demo.html +310 -170
  30. package/src/components/demos/action/{8_editable_text_demo.html → 8_editable_demo.html} +65 -76
  31. package/src/components/demos/action/9_link_demo.html +84 -62
  32. package/src/components/demos/ui_transition/0_action_renderer_ui_transition_demo.html +695 -0
  33. package/src/components/demos/ui_transition/1_nested_ui_transition_demo.html +429 -0
  34. package/src/components/demos/ui_transition/2_height_transition_test.html +295 -0
  35. package/src/components/details/details.jsx +62 -64
  36. package/src/components/edition/editable.jsx +186 -0
  37. package/src/components/field/README.md +247 -0
  38. package/src/components/{input → field}/button.jsx +151 -130
  39. package/src/components/field/checkbox_list.jsx +184 -0
  40. package/src/components/{collect_form_element_values.js → field/collect_form_element_values.js} +7 -4
  41. package/src/components/{input → field}/field_css.js +4 -1
  42. package/src/components/field/form.jsx +211 -0
  43. package/src/components/{input → field}/input.jsx +1 -0
  44. package/src/components/{input → field}/input_checkbox.jsx +132 -155
  45. package/src/components/{input → field}/input_radio.jsx +135 -46
  46. package/src/components/{input → field}/input_textual.jsx +247 -173
  47. package/src/components/field/label.jsx +32 -0
  48. package/src/components/field/radio_list.jsx +182 -0
  49. package/src/components/{input → field}/select.jsx +17 -32
  50. package/src/components/field/use_action_events.js +132 -0
  51. package/src/components/field/use_form_events.js +55 -0
  52. package/src/components/field/use_ui_state_controller.js +506 -0
  53. package/src/components/item_tracker/README.md +461 -0
  54. package/src/components/item_tracker/use_isolated_item_tracker.jsx +209 -0
  55. package/src/components/item_tracker/use_isolated_item_tracker_demo.html +148 -0
  56. package/src/components/item_tracker/use_isolated_item_tracker_demo.jsx +460 -0
  57. package/src/components/item_tracker/use_item_tracker.jsx +143 -0
  58. package/src/components/item_tracker/use_item_tracker_demo.html +207 -0
  59. package/src/components/item_tracker/use_item_tracker_demo.jsx +216 -0
  60. package/src/components/keyboard_shortcuts/active_keyboard_shortcuts.jsx +87 -0
  61. package/src/components/keyboard_shortcuts/aria_key_shortcuts.js +61 -0
  62. package/src/components/keyboard_shortcuts/keyboard_key_meta.js +17 -0
  63. package/src/components/keyboard_shortcuts/keyboard_shortcuts.js +371 -0
  64. package/src/components/link/link.jsx +65 -102
  65. package/src/components/link/link_with_icon.jsx +52 -0
  66. package/src/components/loader/loader_background.jsx +85 -64
  67. package/src/components/loader/rectangle_loading.jsx +38 -19
  68. package/src/components/route.jsx +8 -4
  69. package/src/components/selection/selection.jsx +1583 -0
  70. package/src/components/svg/font_sized_svg.jsx +45 -0
  71. package/src/components/svg/icon_and_text.jsx +21 -0
  72. package/src/components/svg/svg_mask_overlay.jsx +105 -0
  73. package/src/components/table/drag/table_drag.jsx +506 -0
  74. package/src/components/table/resize/table_resize.jsx +650 -0
  75. package/src/components/table/resize/table_size.js +43 -0
  76. package/src/components/table/selection/table_selection.js +106 -0
  77. package/src/components/table/selection/table_selection.jsx +203 -0
  78. package/src/components/table/sticky/sticky_group.js +354 -0
  79. package/src/components/table/sticky/table_sticky.js +25 -0
  80. package/src/components/table/sticky/table_sticky.jsx +501 -0
  81. package/src/components/table/table.jsx +721 -0
  82. package/src/components/table/table_css.js +211 -0
  83. package/src/components/table/table_ui.jsx +49 -0
  84. package/src/components/table/use_cells_and_columns.js +90 -0
  85. package/src/components/table/use_object_array_to_cells.js +46 -0
  86. package/src/components/table/z_indexes.js +23 -0
  87. package/src/components/tablist/tablist.jsx +99 -0
  88. package/src/components/text/overflow.jsx +15 -0
  89. package/src/components/text/text_and_count.jsx +28 -0
  90. package/src/components/ui_transition.jsx +128 -0
  91. package/src/components/use_auto_focus.js +58 -7
  92. package/src/components/use_batch_during_render.js +33 -0
  93. package/src/components/use_debounce_true.js +7 -7
  94. package/src/components/use_dependencies_diff.js +35 -0
  95. package/src/components/use_focus_group.js +4 -3
  96. package/src/components/use_initial_value.js +8 -34
  97. package/src/components/use_signal_sync.js +1 -1
  98. package/src/components/use_stable_callback.js +68 -0
  99. package/src/components/use_state_array.js +16 -9
  100. package/src/docs/actions.md +22 -0
  101. package/src/notes.md +33 -12
  102. package/src/route/route.js +97 -47
  103. package/src/store/resource_graph.js +2 -1
  104. package/src/store/tests/{resource_graph_dependencies.test.js → resource_graph_dependencies.test_manual.js} +13 -13
  105. package/src/utils/is_signal.js +20 -0
  106. package/src/utils/stringify_for_display.js +4 -23
  107. package/src/validation/constraints/confirm_constraint.js +14 -0
  108. package/src/validation/constraints/create_unique_value_constraint.js +27 -0
  109. package/src/validation/constraints/native_constraints.js +313 -0
  110. package/src/validation/constraints/readonly_constraint.js +36 -0
  111. package/src/validation/constraints/single_space_constraint.js +13 -0
  112. package/src/validation/custom_constraint_validation.js +599 -0
  113. package/src/validation/custom_message.js +18 -0
  114. package/src/validation/demos/browser_style.png +0 -0
  115. package/src/validation/demos/form_validation_demo.html +142 -0
  116. package/src/validation/demos/form_validation_demo_preact.html +87 -0
  117. package/src/validation/demos/form_validation_native_popover_demo.html +168 -0
  118. package/src/validation/demos/form_validation_vs_native_demo.html +172 -0
  119. package/src/validation/demos/validation_message_demo.html +203 -0
  120. package/src/validation/hooks/use_constraints.js +23 -0
  121. package/src/validation/hooks/use_custom_validation_ref.js +73 -0
  122. package/src/validation/hooks/use_validation_message.js +19 -0
  123. package/src/validation/validation_message.js +741 -0
  124. package/src/components/editable_text/editable_text.jsx +0 -96
  125. package/src/components/form.jsx +0 -144
  126. package/src/components/input/checkbox_list.jsx +0 -294
  127. package/src/components/input/field.jsx +0 -61
  128. package/src/components/input/radio_list.jsx +0 -283
  129. package/src/components/input/use_form_event.js +0 -20
  130. package/src/components/input/use_on_change.js +0 -12
  131. package/src/components/selection/selection.js +0 -5
  132. package/src/components/selection/selection_context.jsx +0 -262
  133. package/src/components/shortcut/shortcut_context.jsx +0 -390
  134. package/src/components/use_action_events.js +0 -37
  135. package/src/utils/iterable_weak_set.js +0 -62
  136. /package/src/components/demos/action/{11_nested_shortcuts_demo.html → 11_nested_shortcuts_demo.xhtml} +0 -0
  137. /package/src/components/{shortcut → keyboard_shortcuts}/os.js +0 -0
  138. /package/src/route/{route.test.html → route.xtest.html} +0 -0
@@ -4,10 +4,305 @@
4
4
  <meta charset="UTF-8" />
5
5
  <link rel="icon" href="data:," />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <title>Button loading demo</title>
7
+ <title>Button Demo</title>
8
+ <style>
9
+ body {
10
+ font-family:
11
+ -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
12
+ max-width: 1400px;
13
+ margin: 0 auto;
14
+ padding: 20px;
15
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
16
+ min-height: 100vh;
17
+ }
18
+
19
+ h1 {
20
+ text-align: center;
21
+ color: white;
22
+ margin-bottom: 30px;
23
+ font-size: 2.5rem;
24
+ text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
25
+ }
26
+
27
+ .demo-grid {
28
+ display: grid;
29
+ grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
30
+ gap: 20px;
31
+ margin-bottom: 30px;
32
+ }
33
+
34
+ .demo-card {
35
+ background: white;
36
+ border-radius: 12px;
37
+ padding: 25px;
38
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
39
+ border: 1px solid rgba(255, 255, 255, 0.2);
40
+ transition:
41
+ transform 0.2s ease,
42
+ box-shadow 0.2s ease;
43
+ }
44
+
45
+ .demo-card:hover {
46
+ transform: translateY(-2px);
47
+ box-shadow: 0 12px 35px rgba(0, 0, 0, 0.2);
48
+ }
49
+
50
+ .demo-title {
51
+ color: #2c3e50;
52
+ margin: 0 0 20px 0;
53
+ font-size: 1.3rem;
54
+ font-weight: 600;
55
+ border-bottom: 3px solid #667eea;
56
+ padding-bottom: 10px;
57
+ display: flex;
58
+ align-items: center;
59
+ gap: 10px;
60
+ }
61
+
62
+ .demo-title::before {
63
+ content: "🔘";
64
+ font-size: 1.2rem;
65
+ }
66
+
67
+ .button-row {
68
+ display: flex;
69
+ align-items: center;
70
+ gap: 15px;
71
+ margin: 15px 0;
72
+ padding: 12px;
73
+ background: #f8f9fa;
74
+ border-radius: 8px;
75
+ border: 1px solid #e9ecef;
76
+ transition: background 0.2s ease;
77
+ }
78
+
79
+ .button-row:hover {
80
+ background: #e8f4f8;
81
+ }
82
+
83
+ .button-container {
84
+ display: flex;
85
+ align-items: center;
86
+ gap: 10px;
87
+ flex: 1;
88
+ min-width: 0;
89
+ }
90
+
91
+ .toggle-button {
92
+ padding: 8px 16px;
93
+ background: linear-gradient(45deg, #667eea, #764ba2);
94
+ color: white;
95
+ border: none;
96
+ border-radius: 6px;
97
+ cursor: pointer;
98
+ font-size: 0.9rem;
99
+ font-weight: 500;
100
+ transition: all 0.2s ease;
101
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
102
+ flex-shrink: 0;
103
+ white-space: nowrap;
104
+ }
105
+
106
+ .toggle-button:hover {
107
+ background: linear-gradient(45deg, #5a6fd8, #6b3fa0);
108
+ transform: translateY(-1px);
109
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
110
+ }
111
+
112
+ .comparison {
113
+ display: flex;
114
+ flex-direction: column;
115
+ gap: 8px;
116
+ margin-top: 15px;
117
+ padding: 12px;
118
+ background: #fff3cd;
119
+ border-radius: 6px;
120
+ border-left: 4px solid #ffc107;
121
+ }
122
+
123
+ .comparison-label {
124
+ font-size: 0.85rem;
125
+ color: #856404;
126
+ font-weight: 500;
127
+ margin-bottom: 5px;
128
+ }
129
+
130
+ .native-examples {
131
+ display: flex;
132
+ flex-direction: column;
133
+ gap: 8px;
134
+ }
135
+
136
+ .native-examples button {
137
+ align-self: flex-start;
138
+ padding: 8px 16px;
139
+ font-size: 0.9rem;
140
+ }
141
+
142
+ .prop-control-demo {
143
+ grid-column: 1 / -1;
144
+ background: linear-gradient(45deg, #ff6b6b 0%, #feca57 100%);
145
+ color: white;
146
+ }
147
+
148
+ .prop-control-demo .demo-title {
149
+ color: white;
150
+ border-bottom-color: #ffffff;
151
+ }
152
+
153
+ .prop-control-demo .demo-title::before {
154
+ content: "🎛️";
155
+ }
156
+
157
+ .prop-controls {
158
+ margin-bottom: 25px;
159
+ display: grid;
160
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
161
+ gap: 15px;
162
+ }
163
+
164
+ .prop-select-group {
165
+ background: rgba(255, 255, 255, 0.1);
166
+ padding: 15px;
167
+ border-radius: 8px;
168
+ border: 1px solid rgba(255, 255, 255, 0.2);
169
+ }
170
+
171
+ .prop-select-label {
172
+ display: block;
173
+ margin-bottom: 8px;
174
+ font-weight: 500;
175
+ font-size: 0.95rem;
176
+ }
177
+
178
+ .prop-select {
179
+ width: 100%;
180
+ padding: 8px 12px;
181
+ border: none;
182
+ border-radius: 6px;
183
+ background: white;
184
+ color: #333;
185
+ font-size: 0.9rem;
186
+ cursor: pointer;
187
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
188
+ }
189
+
190
+ .state-controls {
191
+ background: rgba(255, 255, 255, 0.1);
192
+ padding: 15px;
193
+ border-radius: 8px;
194
+ border: 1px solid rgba(255, 255, 255, 0.2);
195
+ margin-bottom: 20px;
196
+ }
197
+
198
+ .state-controls h4 {
199
+ margin: 0 0 15px 0;
200
+ font-size: 1rem;
201
+ color: white;
202
+ }
203
+
204
+ .control-row {
205
+ display: flex;
206
+ gap: 15px;
207
+ flex-wrap: wrap;
208
+ margin-bottom: 10px;
209
+ }
210
+
211
+ .control-group {
212
+ display: flex;
213
+ flex-direction: column;
214
+ align-items: center;
215
+ gap: 8px;
216
+ background: rgba(255, 255, 255, 0.05);
217
+ padding: 12px;
218
+ border-radius: 8px;
219
+ border: 1px solid rgba(255, 255, 255, 0.1);
220
+ min-width: 100px;
221
+ }
222
+
223
+ .control-toggle {
224
+ padding: 8px 16px;
225
+ border: 2px solid rgba(255, 255, 255, 0.3);
226
+ background: rgba(255, 255, 255, 0.1);
227
+ color: white;
228
+ border-radius: 6px;
229
+ cursor: pointer;
230
+ font-size: 0.85rem;
231
+ font-weight: 500;
232
+ transition: all 0.2s ease;
233
+ min-width: 80px;
234
+ text-align: center;
235
+ }
236
+
237
+ .control-toggle.active {
238
+ background: rgba(255, 255, 255, 0.9);
239
+ color: #333;
240
+ border-color: white;
241
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
242
+ }
243
+
244
+ .control-toggle:hover {
245
+ background: rgba(255, 255, 255, 0.2);
246
+ border-color: rgba(255, 255, 255, 0.5);
247
+ }
248
+
249
+ .control-toggle.active:hover {
250
+ background: white;
251
+ }
252
+
253
+ .control-label {
254
+ font-size: 0.9rem;
255
+ color: white;
256
+ margin: 0;
257
+ }
258
+
259
+ .button-test-grid {
260
+ display: grid;
261
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
262
+ gap: 15px;
263
+ background: rgba(255, 255, 255, 0.1);
264
+ padding: 20px;
265
+ border-radius: 8px;
266
+ border: 1px solid rgba(255, 255, 255, 0.2);
267
+ }
268
+
269
+ .button-test-item {
270
+ display: flex;
271
+ flex-direction: column;
272
+ align-items: center;
273
+ gap: 10px;
274
+ padding: 15px;
275
+ background: rgba(255, 255, 255, 0.05);
276
+ border-radius: 6px;
277
+ text-align: center;
278
+ }
279
+
280
+ .button-label {
281
+ font-size: 0.9rem;
282
+ color: white;
283
+ margin-bottom: 8px;
284
+ }
285
+
286
+ @media (max-width: 768px) {
287
+ .demo-grid {
288
+ grid-template-columns: 1fr;
289
+ }
290
+
291
+ h1 {
292
+ font-size: 2rem;
293
+ }
294
+
295
+ .button-row {
296
+ flex-direction: column;
297
+ align-items: stretch;
298
+ gap: 10px;
299
+ }
300
+ }
301
+ </style>
8
302
  </head>
9
303
  <body>
10
- <div id="root" style="position: relative"></div>
304
+ <h1>🔘 Interactive Button Demo</h1>
305
+ <div id="root"></div>
11
306
 
12
307
  <script type="module" jsenv-type="module/jsx">
13
308
  import { render } from "preact";
@@ -15,136 +310,312 @@
15
310
  import {
16
311
  // eslint-disable-next-line no-unused-vars
17
312
  Button,
18
- // eslint-disable-next-line no-unused-vars
19
- Field,
20
313
  } from "@jsenv/navi";
21
314
 
22
315
  // eslint-disable-next-line no-unused-vars
23
316
  const App = () => {
317
+ return (
318
+ <div className="demo-grid">
319
+ <LoadingDemo />
320
+ <ReadOnlyDemo />
321
+ <DisabledDemo />
322
+ <SizeVariationsDemo />
323
+ <PropControlDemo />
324
+ </div>
325
+ );
326
+ };
327
+
328
+ // eslint-disable-next-line no-unused-vars
329
+ const LoadingDemo = () => {
24
330
  const [loading, setLoading] = useState(false);
25
- const [loadingB, setLoadingB] = useState(false);
26
- const [loadingC, setLoadingC] = useState(false);
27
- const [loadingD, setLoadingD] = useState(false);
28
331
 
29
332
  return (
30
- <div>
31
- <div style="display: flex; flex-direction: row; gap: 30px">
32
- <div>
33
- <p>
34
- <strong>Loading</strong>
35
- </p>
36
- <Button loading={loading}>Toggle</Button>
37
- <br />
38
- <button
39
- onClick={() => {
40
- setLoading(!loading);
41
- }}
42
- >
43
- Toggle
44
- </button>
333
+ <div className="demo-card">
334
+ <h3 className="demo-title">Loading States</h3>
335
+
336
+ <div className="button-row">
337
+ <div className="button-container">
338
+ <Button loading={loading}>Loading Button</Button>
45
339
  </div>
340
+ <button
341
+ className="toggle-button"
342
+ onClick={() => setLoading(!loading)}
343
+ >
344
+ {loading ? "Stop Loading" : "Start Loading"}
345
+ </button>
346
+ </div>
46
347
 
47
- <div>
48
- <p>
49
- <strong>Loading (action)</strong>
50
- </p>
51
- <Button loading={loadingB} action={() => {}}>
52
- B
53
- </Button>
54
- <br /> <br />
55
- <button
56
- onClick={() => {
57
- setLoadingB(!loadingB);
58
- }}
59
- >
60
- Toggle
348
+ <div className="comparison">
349
+ <span className="comparison-label">Native comparison:</span>
350
+ <div className="native-examples">
351
+ <button disabled={loading}>
352
+ {loading ? "Loading..." : "Standard Button"}
61
353
  </button>
62
354
  </div>
355
+ </div>
356
+ </div>
357
+ );
358
+ };
359
+
360
+ // eslint-disable-next-line no-unused-vars
361
+ const ReadOnlyDemo = () => {
362
+ const [loading, setLoading] = useState(false);
63
363
 
64
- <div>
65
- <p>
66
- <strong>Readonly</strong>
67
- </p>
68
- <Button loading={loadingC} readOnly>
69
- C
364
+ return (
365
+ <div className="demo-card">
366
+ <h3 className="demo-title">Read-Only State</h3>
367
+
368
+ <div className="button-row">
369
+ <div className="button-container">
370
+ <Button loading={loading} readOnly>
371
+ Read-Only Button
70
372
  </Button>
71
- <br />
72
- <button data-readonly>C</button>
73
- <br />
74
- <br />
75
- <button>
76
- <span
77
- onClick={() => {
78
- setLoadingC(!loadingC);
79
- }}
80
- >
81
- Toggle
82
- </span>
83
- </button>
84
373
  </div>
374
+ <button
375
+ className="toggle-button"
376
+ onClick={() => setLoading(!loading)}
377
+ >
378
+ Toggle Loading
379
+ </button>
380
+ </div>
85
381
 
86
- <div>
87
- <p>
88
- <strong>Disabled</strong>
89
- </p>
90
- <Button loading={loadingD} disabled>
91
- D
382
+ <div className="comparison">
383
+ <span className="comparison-label">Native comparison:</span>
384
+ <div className="native-examples">
385
+ <button data-readonly>Native Read-Only</button>
386
+ </div>
387
+ </div>
388
+ </div>
389
+ );
390
+ };
391
+
392
+ // eslint-disable-next-line no-unused-vars
393
+ const DisabledDemo = () => {
394
+ const [loading, setLoading] = useState(false);
395
+
396
+ return (
397
+ <div className="demo-card">
398
+ <h3 className="demo-title">Disabled State</h3>
399
+
400
+ <div className="button-row">
401
+ <div className="button-container">
402
+ <Button loading={loading} disabled>
403
+ Disabled Button
92
404
  </Button>
93
- <br />
94
- <button disabled>D</button>
95
- <br /> <br />
96
- <button>
97
- <span
98
- onClick={() => {
99
- setLoadingD(!loadingD);
100
- }}
101
- >
102
- Toggle
103
- </span>
104
- </button>
405
+ </div>
406
+ <button
407
+ className="toggle-button"
408
+ onClick={() => setLoading(!loading)}
409
+ >
410
+ Toggle Loading
411
+ </button>
412
+ </div>
413
+
414
+ <div className="comparison">
415
+ <span className="comparison-label">Native comparison:</span>
416
+ <div className="native-examples">
417
+ <button disabled>Native Disabled</button>
105
418
  </div>
106
419
  </div>
420
+ </div>
421
+ );
422
+ };
107
423
 
108
- <div style="display: flex; flex-direction: row; gap: 30px">
109
- <div>
110
- <p>
111
- <strong>Large button (anim speed shoud be consistent)</strong>
112
- </p>
424
+ // eslint-disable-next-line no-unused-vars
425
+ const SizeVariationsDemo = () => {
426
+ const [loading, setLoading] = useState(false);
427
+
428
+ return (
429
+ <div className="demo-card">
430
+ <h3 className="demo-title">Size Variations</h3>
431
+
432
+ <div className="button-row">
433
+ <div className="button-container">
434
+ <Button loading={loading}>Short</Button>
435
+ </div>
436
+ </div>
437
+
438
+ <div className="button-row">
439
+ <div className="button-container">
113
440
  <Button loading={loading}>
114
- This button is quite large but loader speed should be same as
115
- small buttons
441
+ This button is quite large but loader speed should be
442
+ consistent
116
443
  </Button>
117
- <br />
118
- <button
119
- onClick={() => {
120
- setLoading(!loading);
121
- }}
122
- >
123
- Toggle
124
- </button>
125
444
  </div>
126
445
  </div>
127
446
 
128
- <div>
129
- <div>
130
- <p>
131
- <strong>Big borders</strong>
132
- </p>
133
- <Button loading={loadingC} borderWidth={10}>
134
- C
447
+ <div className="button-row">
448
+ <div className="button-container">
449
+ <Button
450
+ loading={loading}
451
+ style={{ borderWidth: 8, padding: "12px 24px" }}
452
+ >
453
+ Thick Border Button
135
454
  </Button>
136
- <br /> <br />
137
- <button>
138
- <span
139
- onClick={() => {
140
- setLoadingC(!loadingC);
141
- }}
455
+ </div>
456
+ <button
457
+ className="toggle-button"
458
+ onClick={() => setLoading(!loading)}
459
+ >
460
+ {loading ? "Stop Loading" : "Start Loading"}
461
+ </button>
462
+ </div>
463
+
464
+ <div className="comparison">
465
+ <span className="comparison-label">Note:</span>
466
+ <div className="native-examples">
467
+ <small style={{ color: "#666" }}>
468
+ Loading animation speed should be consistent regardless of
469
+ button size
470
+ </small>
471
+ </div>
472
+ </div>
473
+ </div>
474
+ );
475
+ };
476
+
477
+ // eslint-disable-next-line no-unused-vars
478
+ const PropControlDemo = () => {
479
+ const [loading, setLoading] = useState(false);
480
+ const [disabled, setDisabled] = useState(false);
481
+ const [readOnly, setReadOnly] = useState(false);
482
+ const [buttonText, setButtonText] = useState("Custom Button");
483
+ const [buttonType, setButtonType] = useState("button");
484
+
485
+ const textOptions = [
486
+ "Custom Button",
487
+ "Click Me!",
488
+ "Submit Form",
489
+ "Save Changes",
490
+ "Long Button Text Example",
491
+ "🚀 With Emoji",
492
+ ];
493
+
494
+ const typeOptions = [
495
+ { name: "button", value: "button" },
496
+ { name: "submit", value: "submit" },
497
+ { name: "reset", value: "reset" },
498
+ ];
499
+
500
+ return (
501
+ <div className="demo-card prop-control-demo">
502
+ <h3 className="demo-title">Interactive Prop Controller</h3>
503
+
504
+ <div className="prop-controls">
505
+ <div className="prop-select-group">
506
+ <label className="prop-select-label">Button Text:</label>
507
+ <select
508
+ value={buttonText}
509
+ onChange={(e) => setButtonText(e.target.value)}
510
+ className="prop-select"
511
+ >
512
+ {textOptions.map((text) => (
513
+ <option key={text} value={text}>
514
+ {text}
515
+ </option>
516
+ ))}
517
+ </select>
518
+ </div>
519
+
520
+ <div className="prop-select-group">
521
+ <label className="prop-select-label">Button Type:</label>
522
+ <select
523
+ value={buttonType}
524
+ onChange={(e) => setButtonType(e.target.value)}
525
+ className="prop-select"
526
+ >
527
+ {typeOptions.map(({ name, value }) => (
528
+ <option key={value} value={value}>
529
+ {name}
530
+ </option>
531
+ ))}
532
+ </select>
533
+ </div>
534
+ </div>
535
+
536
+ <div className="state-controls">
537
+ <h4>Button Properties:</h4>
538
+ <div className="control-row">
539
+ <div className="control-group">
540
+ <button
541
+ className={`control-toggle ${loading ? "active" : ""}`}
542
+ onClick={() => setLoading(!loading)}
543
+ >
544
+ {loading ? "true" : "false"}
545
+ </button>
546
+ <span className="control-label">loading</span>
547
+ </div>
548
+
549
+ <div className="control-group">
550
+ <button
551
+ className={`control-toggle ${disabled ? "active" : ""}`}
552
+ onClick={() => setDisabled(!disabled)}
553
+ >
554
+ {disabled ? "true" : "false"}
555
+ </button>
556
+ <span className="control-label">disabled</span>
557
+ </div>
558
+
559
+ <div className="control-group">
560
+ <button
561
+ className={`control-toggle ${readOnly ? "active" : ""}`}
562
+ onClick={() => setReadOnly(!readOnly)}
142
563
  >
143
- Toggle
144
- </span>
564
+ {readOnly ? "true" : "false"}
565
+ </button>
566
+ <span className="control-label">readOnly</span>
567
+ </div>
568
+ </div>
569
+ </div>
570
+
571
+ <div className="button-test-grid">
572
+ <div className="button-test-item">
573
+ <span className="button-label">Custom Button Component</span>
574
+ <Button
575
+ type={buttonType}
576
+ loading={loading}
577
+ disabled={disabled}
578
+ readOnly={readOnly}
579
+ >
580
+ {buttonText}
581
+ </Button>
582
+ </div>
583
+
584
+ <div className="button-test-item">
585
+ <span className="button-label">Native Button</span>
586
+ <button
587
+ type={buttonType}
588
+ disabled={disabled || loading}
589
+ data-readonly={readOnly}
590
+ >
591
+ {loading ? "Loading..." : buttonText}
145
592
  </button>
146
593
  </div>
147
594
  </div>
595
+
596
+ <div
597
+ style={{
598
+ marginTop: "20px",
599
+ padding: "15px",
600
+ background: "rgba(255,255,255,0.1)",
601
+ borderRadius: "8px",
602
+ }}
603
+ >
604
+ <strong>Current Configuration:</strong>
605
+ <br />
606
+ <small>
607
+ type: "{buttonType}" | loading: {loading ? "true" : "false"} |
608
+ disabled: {disabled ? "true" : "false"} | readOnly:{" "}
609
+ {readOnly ? "true" : "false"}
610
+ </small>
611
+ <br />
612
+ <br />
613
+ <small>
614
+ Use the controls above to test different property combinations.
615
+ Compare how the custom Button component behaves versus a native
616
+ button.
617
+ </small>
618
+ </div>
148
619
  </div>
149
620
  );
150
621
  };