@tachui/forms 0.7.1-alpha → 0.8.0-alpha

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 (90) hide show
  1. package/README.md +87 -272
  2. package/dist/DatePicker-D5nRFTUm.js +475 -0
  3. package/dist/DatePicker-D5nRFTUm.js.map +1 -0
  4. package/dist/Select-yZyKooXk.js +945 -0
  5. package/dist/Select-yZyKooXk.js.map +1 -0
  6. package/dist/Slider-0-oal5YR.js +644 -0
  7. package/dist/Slider-0-oal5YR.js.map +1 -0
  8. package/dist/TextField-hX15dY3U.js +509 -0
  9. package/dist/TextField-hX15dY3U.js.map +1 -0
  10. package/dist/components/advanced/Slider.d.ts +190 -0
  11. package/dist/components/advanced/Slider.d.ts.map +1 -0
  12. package/dist/components/advanced/Stepper.d.ts +161 -0
  13. package/dist/components/advanced/Stepper.d.ts.map +1 -0
  14. package/dist/components/advanced/index.d.ts +15 -0
  15. package/dist/components/advanced/index.d.ts.map +1 -0
  16. package/dist/components/advanced/index.js +6 -0
  17. package/dist/components/advanced/index.js.map +1 -0
  18. package/dist/components/date-picker/DatePicker.d.ts +126 -0
  19. package/dist/components/date-picker/DatePicker.d.ts.map +1 -0
  20. package/dist/components/date-picker/index.d.ts +14 -0
  21. package/dist/components/date-picker/index.d.ts.map +1 -0
  22. package/dist/components/date-picker/index.js +5 -0
  23. package/dist/components/date-picker/index.js.map +1 -0
  24. package/dist/components/form-container/index.d.ts +58 -0
  25. package/dist/components/form-container/index.d.ts.map +1 -0
  26. package/dist/components/selection/Checkbox.d.ts.map +1 -0
  27. package/dist/components/selection/Radio.d.ts.map +1 -0
  28. package/dist/components/selection/Select.d.ts.map +1 -0
  29. package/dist/components/selection/index.d.ts +68 -0
  30. package/dist/components/selection/index.d.ts.map +1 -0
  31. package/dist/components/selection/index.js +12 -0
  32. package/dist/components/selection/index.js.map +1 -0
  33. package/dist/components/text-input/TextField.d.ts.map +1 -0
  34. package/dist/components/text-input/index.d.ts +8 -0
  35. package/dist/components/text-input/index.d.ts.map +1 -0
  36. package/dist/components/text-input/index.js +18 -0
  37. package/dist/components/text-input/index.js.map +1 -0
  38. package/dist/{state/index.js → index-D3WfkqVv.js} +15 -8
  39. package/dist/index-D3WfkqVv.js.map +1 -0
  40. package/dist/index.d.ts +10 -15
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +198 -376
  43. package/dist/index.js.map +1 -0
  44. package/dist/state/index.d.ts.map +1 -1
  45. package/dist/types/index.d.ts.map +1 -1
  46. package/dist/utils/index.d.ts +19 -0
  47. package/dist/utils/index.d.ts.map +1 -0
  48. package/dist/validation/component-validation.d.ts +11 -2
  49. package/dist/validation/component-validation.d.ts.map +1 -1
  50. package/dist/validation/index.d.ts.map +1 -1
  51. package/dist/validation/index.js +282 -191
  52. package/dist/validation/index.js.map +1 -0
  53. package/package.json +53 -39
  54. package/src/components/advanced/Slider.ts +722 -0
  55. package/src/components/advanced/Stepper.ts +715 -0
  56. package/src/components/advanced/index.ts +20 -0
  57. package/src/components/date-picker/DatePicker.ts +925 -0
  58. package/src/components/date-picker/index.ts +20 -0
  59. package/src/components/form-container/index.ts +266 -0
  60. package/src/components/selection/Checkbox.ts +478 -0
  61. package/src/components/selection/Radio.ts +470 -0
  62. package/src/components/selection/Select.ts +620 -0
  63. package/src/components/selection/index.ts +81 -0
  64. package/src/components/text-input/TextField.ts +728 -0
  65. package/src/components/text-input/index.ts +35 -0
  66. package/src/index.ts +48 -0
  67. package/src/state/index.ts +544 -0
  68. package/src/types/index.ts +579 -0
  69. package/src/utils/formatters.ts +184 -0
  70. package/src/utils/index.ts +57 -0
  71. package/src/validation/component-validation.ts +429 -0
  72. package/src/validation/index.ts +641 -0
  73. package/dist/TextField-CGBM3x7K.js +0 -1799
  74. package/dist/components/Form.d.ts +0 -76
  75. package/dist/components/Form.d.ts.map +0 -1
  76. package/dist/components/index.d.ts +0 -9
  77. package/dist/components/index.d.ts.map +0 -1
  78. package/dist/components/index.js +0 -28
  79. package/dist/components/input/Checkbox.d.ts.map +0 -1
  80. package/dist/components/input/Radio.d.ts.map +0 -1
  81. package/dist/components/input/Select.d.ts.map +0 -1
  82. package/dist/components/input/TextField.d.ts.map +0 -1
  83. package/dist/components/input/index.d.ts +0 -11
  84. package/dist/components/input/index.d.ts.map +0 -1
  85. package/dist/utils/validators.d.ts +0 -101
  86. package/dist/utils/validators.d.ts.map +0 -1
  87. /package/dist/components/{input → selection}/Checkbox.d.ts +0 -0
  88. /package/dist/components/{input → selection}/Radio.d.ts +0 -0
  89. /package/dist/components/{input → selection}/Select.d.ts +0 -0
  90. /package/dist/components/{input → text-input}/TextField.d.ts +0 -0
@@ -1,1799 +0,0 @@
1
- import { createSignal as E, createEffect as j, h as s, text as b, defaultChildrenRenderer as Le, useLifecycle as Ve, setupOutsideClickDetection as Te, isSignal as Ae } from "@tachui/core";
2
- import { createFormState as Oe, createField as ee } from "./state/index.js";
3
- function he(e, t) {
4
- return Array.isArray(e) ? e.flatMap((n) => pe(n, t)) : pe(e, t);
5
- }
6
- function pe(e, t) {
7
- if (e == null)
8
- return [];
9
- if (typeof e == "object" && "type" in e && e.type === "component") {
10
- const n = {
11
- ...e,
12
- props: {
13
- ...e.props,
14
- _formContext: t
15
- }
16
- }, l = e.render.bind(n)();
17
- return Array.isArray(l) ? l : [l];
18
- }
19
- return Le(e);
20
- }
21
- const Pe = (e) => {
22
- const {
23
- onSubmit: t,
24
- onChange: n,
25
- validation: r = {
26
- validateOn: "blur",
27
- stopOnFirstError: !1,
28
- debounceMs: 300
29
- },
30
- initialValues: l = {},
31
- resetOnSubmit: d = !1,
32
- preserveValues: v = !1,
33
- children: w,
34
- ...F
35
- } = e, g = Oe(l), [T, S] = E("idle"), A = async (h) => {
36
- if (h && h.preventDefault(), !!t)
37
- try {
38
- if (S("idle"), await g.validateForm()) {
39
- const u = g.watch();
40
- await t(u, g.state), S("success"), d && g.resetForm();
41
- } else
42
- S("error");
43
- } catch (o) {
44
- S("error"), console.error("Form submission error:", o);
45
- }
46
- };
47
- j(() => {
48
- if (n) {
49
- const h = g.state, o = Object.keys(h.fields).filter((u) => h.fields[u].dirty);
50
- if (o.length > 0) {
51
- const u = o[o.length - 1], k = h.fields[u];
52
- n(u, k.value, k);
53
- }
54
- }
55
- });
56
- const C = {
57
- register: g.register,
58
- unregister: g.unregister,
59
- setValue: g.setValue,
60
- getValue: g.getValue,
61
- getError: g.getError,
62
- validateField: g.validateField,
63
- validation: r,
64
- onChange: n
65
- };
66
- return {
67
- type: "component",
68
- id: F.id || "form",
69
- render: () => s(
70
- "form",
71
- {
72
- ...F,
73
- onsubmit: A,
74
- novalidate: !0,
75
- // We handle validation ourselves
76
- "data-tachui-form": !0,
77
- "data-form-state": g.state.valid ? "valid" : "invalid",
78
- "data-form-submitting": g.state.submitting,
79
- "data-submission-result": T()
80
- },
81
- ...he(w, C)
82
- ),
83
- props: e,
84
- cleanup: [
85
- () => {
86
- v || g.resetForm();
87
- }
88
- ]
89
- };
90
- }, Re = (e) => {
91
- const {
92
- title: t,
93
- description: n,
94
- header: r,
95
- footer: l,
96
- style: d = "automatic",
97
- spacing: v = 12,
98
- collapsible: w = !1,
99
- collapsed: F = !1,
100
- onToggle: g,
101
- accessibilityLabel: T,
102
- accessibilityRole: S = "group",
103
- children: A,
104
- ...C
105
- } = e, [m, h] = E(F), o = () => {
106
- const a = !m();
107
- h(a), g && g(a);
108
- }, u = (a) => a ? typeof a == "string" ? a : typeof a == "function" ? a() : a : null, k = () => {
109
- const a = {
110
- marginBottom: "20px",
111
- border: "none",
112
- // Remove default fieldset border
113
- padding: "0",
114
- // Remove default fieldset padding
115
- margin: "0 0 20px 0"
116
- // Override default fieldset margin
117
- };
118
- switch (d) {
119
- case "grouped":
120
- return {
121
- ...a,
122
- backgroundColor: "#ffffff",
123
- border: "1px solid #e0e0e0",
124
- borderRadius: "12px",
125
- overflow: "hidden"
126
- };
127
- case "inset":
128
- return {
129
- ...a,
130
- backgroundColor: "#f8f9fa",
131
- border: "1px solid #e9ecef",
132
- borderRadius: "8px",
133
- margin: "0 16px 20px 16px"
134
- };
135
- case "sidebar":
136
- return {
137
- ...a,
138
- borderLeft: "3px solid #007AFF",
139
- paddingLeft: "16px",
140
- backgroundColor: "#f8f9fa",
141
- borderRadius: "0 8px 8px 0"
142
- };
143
- case "plain":
144
- return a;
145
- default:
146
- return {
147
- ...a,
148
- backgroundColor: "#ffffff",
149
- border: "1px solid #f0f0f0",
150
- borderRadius: "8px"
151
- };
152
- }
153
- }, O = () => {
154
- const a = {
155
- fontSize: "16px",
156
- fontWeight: "600",
157
- color: "#1a1a1a",
158
- margin: "0 0 12px 0"
159
- };
160
- switch (d) {
161
- case "grouped":
162
- case "inset":
163
- return {
164
- ...a,
165
- padding: "12px 16px 0 16px",
166
- backgroundColor: "#f8f9fa",
167
- borderBottom: "1px solid #e9ecef"
168
- };
169
- case "sidebar":
170
- return {
171
- ...a,
172
- fontSize: "14px",
173
- textTransform: "uppercase",
174
- letterSpacing: "0.5px",
175
- color: "#666",
176
- marginBottom: "8px"
177
- };
178
- default:
179
- return {
180
- ...a,
181
- padding: "0 0 8px 0"
182
- };
183
- }
184
- }, f = () => {
185
- const a = {
186
- display: "flex",
187
- flexDirection: "column",
188
- gap: `${v}px`
189
- };
190
- switch (d) {
191
- case "grouped":
192
- case "inset":
193
- return {
194
- ...a,
195
- padding: "16px"
196
- };
197
- case "sidebar":
198
- return {
199
- ...a,
200
- padding: "8px 0"
201
- };
202
- default:
203
- return {
204
- ...a,
205
- padding: "12px"
206
- };
207
- }
208
- }, x = () => {
209
- const a = {
210
- fontSize: "14px",
211
- color: "#666",
212
- margin: "8px 0 0 0"
213
- };
214
- switch (d) {
215
- case "grouped":
216
- case "inset":
217
- return {
218
- ...a,
219
- padding: "0 16px 12px 16px",
220
- backgroundColor: "#f8f9fa",
221
- borderTop: "1px solid #e9ecef"
222
- };
223
- default:
224
- return {
225
- ...a,
226
- padding: "0 0 4px 0"
227
- };
228
- }
229
- }, c = () => {
230
- const a = t || u(r);
231
- if (!a) return [];
232
- const z = O();
233
- if (w) {
234
- const R = m() ? "▶" : "▼";
235
- return [
236
- s(
237
- "legend",
238
- {
239
- style: {
240
- ...z,
241
- cursor: "pointer",
242
- display: "flex",
243
- alignItems: "center",
244
- gap: "8px"
245
- },
246
- onclick: o
247
- },
248
- s(
249
- "span",
250
- {
251
- style: {
252
- fontSize: "12px",
253
- color: "#666"
254
- }
255
- },
256
- b(R)
257
- ),
258
- ...typeof a == "string" ? [b(a)] : [a.render()].flat()
259
- )
260
- ];
261
- }
262
- return [
263
- s(
264
- "legend",
265
- { style: z },
266
- ...typeof a == "string" ? [b(a)] : [a.render()].flat()
267
- )
268
- ];
269
- }, p = () => !n || m() ? [] : [
270
- s(
271
- "div",
272
- {
273
- style: {
274
- fontSize: "14px",
275
- color: "#666",
276
- marginBottom: "12px",
277
- padding: d === "grouped" || d === "inset" ? "0 16px" : "0"
278
- }
279
- },
280
- b(n)
281
- )
282
- ], $ = () => w && m() ? [] : [
283
- s(
284
- "div",
285
- {
286
- style: f()
287
- },
288
- ...he(A, e._formContext)
289
- )
290
- ], L = () => {
291
- const a = u(l);
292
- return !a || m() ? [] : [
293
- s(
294
- "div",
295
- {
296
- style: x()
297
- },
298
- ...typeof a == "string" ? [b(a)] : [a.render()].flat()
299
- )
300
- ];
301
- };
302
- return {
303
- type: "component",
304
- id: C.id || "form-section",
305
- render: () => s(
306
- "fieldset",
307
- {
308
- ...C,
309
- style: k(),
310
- "aria-label": T,
311
- role: S,
312
- "data-tachui-form-section": !0,
313
- "data-collapsible": w,
314
- "data-collapsed": m(),
315
- "data-style": d
316
- },
317
- ...c(),
318
- ...p(),
319
- ...$(),
320
- ...L()
321
- ),
322
- props: e
323
- };
324
- }, Ne = {
325
- /**
326
- * Create a form with automatic field registration
327
- */
328
- createAutoForm: (e) => (console.log("Auto form creation:", e), Pe),
329
- /**
330
- * Form validation utilities
331
- */
332
- validation: {
333
- /**
334
- * Create a validation schema from field definitions
335
- */
336
- createSchema: (e) => ({
337
- fields: e,
338
- validate: async (t) => ({ valid: !0, errors: {} })
339
- })
340
- },
341
- /**
342
- * Form serialization utilities
343
- */
344
- serialize: {
345
- /**
346
- * Convert form values to FormData
347
- */
348
- toFormData: (e) => {
349
- const t = new FormData();
350
- return Object.entries(e).forEach(([n, r]) => {
351
- r != null && (r instanceof File || r instanceof Blob ? t.append(n, r) : Array.isArray(r) ? r.forEach((l, d) => {
352
- t.append(`${n}[${d}]`, String(l));
353
- }) : t.append(n, String(r)));
354
- }), t;
355
- },
356
- /**
357
- * Convert form values to URL search params
358
- */
359
- toURLSearchParams: (e) => {
360
- const t = new URLSearchParams();
361
- return Object.entries(e).forEach(([n, r]) => {
362
- r != null && (Array.isArray(r) ? r.forEach((l) => t.append(n, String(l))) : t.append(n, String(r)));
363
- }), t;
364
- },
365
- /**
366
- * Convert form values to JSON
367
- */
368
- toJSON: (e) => JSON.stringify(e, null, 2)
369
- }
370
- }, be = (e) => {
371
- const {
372
- name: t,
373
- label: n,
374
- disabled: r = !1,
375
- required: l = !1,
376
- checked: d,
377
- defaultChecked: v = !1,
378
- indeterminate: w = !1,
379
- validation: F,
380
- onChange: g,
381
- onBlur: T,
382
- onFocus: S,
383
- error: A,
384
- helperText: C,
385
- ...m
386
- } = e, h = e._formContext, o = ee(t, d ?? v, F);
387
- h && h.register(t, F);
388
- const [u, k] = E(!1);
389
- d !== void 0 && j(() => {
390
- o.value() !== d && o.setValue(d);
391
- });
392
- const O = (L) => {
393
- const a = L.target.checked;
394
- o.setValue(a), h && h.setValue(t, a), g && g(t, a, o);
395
- }, f = (L) => {
396
- k(!0), o.onFocus(), S && S(t, o.value());
397
- }, x = (L) => {
398
- k(!1), o.onBlur(), T && T(t, o.value());
399
- }, c = A || o.error() || h?.getError(t), p = (L) => {
400
- if (L.key === " " || L.key === "Enter") {
401
- L.preventDefault();
402
- const _ = L.target;
403
- _.checked = !_.checked, O(L);
404
- }
405
- };
406
- return {
407
- type: "component",
408
- id: m.id || `checkbox-${t}`,
409
- render: () => s(
410
- "div",
411
- {
412
- ...m,
413
- class: `tachui-checkbox ${m.class || ""}`.trim(),
414
- "data-tachui-checkbox-container": !0,
415
- "data-field-state": c ? "error" : o.validating() ? "validating" : "valid",
416
- "data-checked": o.value(),
417
- "data-indeterminate": w,
418
- "data-disabled": r
419
- },
420
- // Checkbox input and label wrapper
421
- s(
422
- "label",
423
- {
424
- "data-tachui-checkbox-label": !0,
425
- "data-focused": u(),
426
- "data-disabled": r
427
- },
428
- // Hidden native checkbox for accessibility
429
- s("input", {
430
- type: "checkbox",
431
- id: m.id || t,
432
- name: t,
433
- checked: o.value(),
434
- disabled: r,
435
- required: l,
436
- onchange: O,
437
- onfocus: f,
438
- onblur: x,
439
- onkeydown: p,
440
- "aria-invalid": !!c,
441
- "aria-describedby": [c ? `${t}-error` : null, C ? `${t}-helper` : null].filter(Boolean).join(" ") || void 0,
442
- "data-tachui-checkbox-input": !0,
443
- style: {
444
- position: "absolute",
445
- opacity: "0",
446
- width: "1px",
447
- height: "1px",
448
- margin: "-1px",
449
- padding: "0",
450
- border: "0",
451
- clip: "rect(0,0,0,0)"
452
- }
453
- }),
454
- // Custom checkbox visual
455
- s(
456
- "div",
457
- {
458
- "data-tachui-checkbox-visual": !0,
459
- "data-checked": o.value(),
460
- "data-indeterminate": w,
461
- "data-focused": u(),
462
- "data-disabled": r,
463
- "data-error": !!c,
464
- "aria-hidden": "true",
465
- role: "presentation"
466
- },
467
- ...o.value() || w ? [
468
- s(
469
- "div",
470
- {
471
- "data-tachui-checkbox-indicator": !0,
472
- "data-type": w ? "indeterminate" : "checked"
473
- },
474
- b(w ? "−" : "✓")
475
- )
476
- ] : []
477
- ),
478
- ...n ? [
479
- s(
480
- "span",
481
- {
482
- "data-tachui-checkbox-text": !0,
483
- "data-disabled": r
484
- },
485
- b(n),
486
- ...l ? [
487
- s(
488
- "span",
489
- {
490
- "aria-label": "required",
491
- "data-required-indicator": !0
492
- },
493
- b(" *")
494
- )
495
- ] : []
496
- )
497
- ] : []
498
- ),
499
- ...c ? [
500
- s(
501
- "div",
502
- {
503
- id: `${t}-error`,
504
- role: "alert",
505
- "aria-live": "polite",
506
- "data-tachui-error": !0
507
- },
508
- b(c)
509
- )
510
- ] : [],
511
- ...C && !c ? [
512
- s(
513
- "div",
514
- {
515
- id: `${t}-helper`,
516
- "data-tachui-helper": !0
517
- },
518
- b(C)
519
- )
520
- ] : [],
521
- ...o.validating() ? [
522
- s(
523
- "div",
524
- {
525
- "data-tachui-validation-spinner": !0,
526
- "aria-label": "Validating..."
527
- },
528
- b("⏳")
529
- )
530
- ] : []
531
- ),
532
- props: e,
533
- cleanup: [
534
- () => {
535
- h && h.unregister(t);
536
- }
537
- ]
538
- };
539
- }, Me = (e) => {
540
- const { size: t = "medium", ...n } = e, r = {
541
- ...n,
542
- class: `tachui-switch tachui-switch-${t} ${n.class || ""}`.trim()
543
- }, l = be(r);
544
- return {
545
- ...l,
546
- render: () => {
547
- const d = l.render();
548
- return Array.isArray(d) ? d.map((v) => ({
549
- ...v,
550
- props: {
551
- ...v.props,
552
- "data-tachui-switch": !0,
553
- "data-switch-size": t
554
- }
555
- })) : {
556
- ...d,
557
- props: {
558
- ...d.props,
559
- "data-tachui-switch": !0,
560
- "data-switch-size": t
561
- }
562
- };
563
- }
564
- };
565
- }, _e = (e) => {
566
- const {
567
- name: t,
568
- label: n,
569
- options: r,
570
- value: l,
571
- defaultValue: d = [],
572
- onChange: v,
573
- validation: w,
574
- error: F,
575
- helperText: g,
576
- disabled: T = !1,
577
- required: S = !1,
578
- direction: A = "vertical",
579
- ...C
580
- } = e, m = ee(t, l ?? d, w), h = (u, k) => {
581
- const O = m.value() || [];
582
- let f;
583
- k ? f = [...O, u] : f = O.filter((x) => x !== u), m.setValue(f), v && v(t, f, u);
584
- };
585
- return {
586
- type: "component",
587
- id: C.id || `checkbox-group-${t}`,
588
- render: () => s(
589
- "fieldset",
590
- {
591
- ...C,
592
- "data-tachui-checkbox-group": !0,
593
- "data-direction": A,
594
- "data-disabled": T
595
- },
596
- ...n ? [
597
- s(
598
- "legend",
599
- {
600
- "data-tachui-group-label": !0
601
- },
602
- b(n),
603
- ...S ? [
604
- s(
605
- "span",
606
- {
607
- "aria-label": "required",
608
- "data-required-indicator": !0
609
- },
610
- b(" *")
611
- )
612
- ] : []
613
- )
614
- ] : [],
615
- // Checkbox options
616
- s(
617
- "div",
618
- {
619
- "data-tachui-checkbox-options": !0,
620
- "data-direction": A
621
- },
622
- ...r.flatMap((u, k) => {
623
- const f = be({
624
- name: `${t}-${k}`,
625
- label: u.label,
626
- checked: (m.value() || []).includes(u.value),
627
- disabled: T || u.disabled,
628
- onChange: (x, c) => h(u.value, c)
629
- }).render();
630
- return Array.isArray(f) ? f : [f];
631
- })
632
- ),
633
- ...F ? [
634
- s(
635
- "div",
636
- {
637
- id: `${t}-error`,
638
- role: "alert",
639
- "aria-live": "polite",
640
- "data-tachui-error": !0
641
- },
642
- b(F)
643
- )
644
- ] : [],
645
- ...g && !F ? [
646
- s(
647
- "div",
648
- {
649
- id: `${t}-helper`,
650
- "data-tachui-helper": !0
651
- },
652
- b(g)
653
- )
654
- ] : []
655
- ),
656
- props: e
657
- };
658
- }, Ie = (e) => {
659
- const {
660
- name: t,
661
- value: n,
662
- label: r,
663
- checked: l,
664
- groupName: d,
665
- disabled: v = !1,
666
- required: w = !1,
667
- validation: F,
668
- onChange: g,
669
- onBlur: T,
670
- onFocus: S,
671
- error: A,
672
- helperText: C,
673
- ...m
674
- } = e, h = e._formContext, o = d || t, u = ee(o, l ? n : void 0, F);
675
- h && h.register(o, F);
676
- const [k, O] = E(!1), f = () => u.value() === n, x = (a) => {
677
- a.target.checked && (u.setValue(n), h && h.setValue(o, n), g && g(o, n, u));
678
- }, c = (a) => {
679
- O(!0), u.onFocus(), S && S(o, u.value());
680
- }, p = (a) => {
681
- O(!1), u.onBlur(), T && T(o, u.value());
682
- }, $ = A || u.error() || h?.getError(o), L = (a) => {
683
- (a.key === " " || a.key === "Enter") && (a.preventDefault(), x(a));
684
- };
685
- return {
686
- type: "component",
687
- id: m.id || `radio-${o}-${n}`,
688
- render: () => s(
689
- "div",
690
- {
691
- ...m,
692
- class: `tachui-radio ${m.class || ""}`.trim(),
693
- "data-tachui-radio-container": !0,
694
- "data-field-state": $ ? "error" : u.validating() ? "validating" : "valid",
695
- "data-checked": f(),
696
- "data-disabled": v
697
- },
698
- // Radio input and label wrapper
699
- s(
700
- "label",
701
- {
702
- "data-tachui-radio-label": !0,
703
- "data-focused": k(),
704
- "data-disabled": v
705
- },
706
- // Hidden native radio for accessibility
707
- s("input", {
708
- type: "radio",
709
- id: m.id || `${o}-${n}`,
710
- name: o,
711
- value: n,
712
- checked: f(),
713
- disabled: v,
714
- required: w,
715
- onchange: x,
716
- onfocus: c,
717
- onblur: p,
718
- onkeydown: L,
719
- "aria-invalid": !!$,
720
- "aria-describedby": [
721
- $ ? `${o}-error` : null,
722
- C ? `${o}-helper` : null
723
- ].filter(Boolean).join(" ") || void 0,
724
- "data-tachui-radio-input": !0,
725
- style: {
726
- position: "absolute",
727
- opacity: "0",
728
- width: "1px",
729
- height: "1px",
730
- margin: "-1px",
731
- padding: "0",
732
- border: "0",
733
- clip: "rect(0,0,0,0)"
734
- }
735
- }),
736
- // Custom radio visual
737
- s(
738
- "div",
739
- {
740
- "data-tachui-radio-visual": !0,
741
- "data-checked": f(),
742
- "data-focused": k(),
743
- "data-disabled": v,
744
- "data-error": !!$,
745
- "aria-hidden": "true",
746
- role: "presentation"
747
- },
748
- ...f() ? [
749
- s("div", {
750
- "data-tachui-radio-dot": !0
751
- })
752
- ] : []
753
- ),
754
- ...r ? [
755
- s(
756
- "span",
757
- {
758
- "data-tachui-radio-text": !0,
759
- "data-disabled": v
760
- },
761
- b(r),
762
- ...w ? [
763
- s(
764
- "span",
765
- {
766
- "aria-label": "required",
767
- "data-required-indicator": !0
768
- },
769
- b(" *")
770
- )
771
- ] : []
772
- )
773
- ] : []
774
- )
775
- ),
776
- props: e,
777
- cleanup: [
778
- () => {
779
- }
780
- ]
781
- };
782
- }, ze = (e) => {
783
- const {
784
- name: t,
785
- label: n,
786
- options: r,
787
- value: l,
788
- defaultValue: d,
789
- onChange: v,
790
- validation: w,
791
- error: F,
792
- helperText: g,
793
- disabled: T = !1,
794
- required: S = !1,
795
- direction: A = "vertical",
796
- ...C
797
- } = e, m = e._formContext, h = ee(t, l ?? d, w);
798
- m && m.register(t, w), l !== void 0 && j(() => {
799
- h.value() !== l && h.setValue(l);
800
- });
801
- const o = (f) => {
802
- h.setValue(f), m && m.setValue(t, f), v && v(t, f);
803
- }, u = (f) => {
804
- const x = r.findIndex((p) => p.value === h.value());
805
- let c = x;
806
- switch (f.key) {
807
- case "ArrowDown":
808
- case "ArrowRight":
809
- f.preventDefault(), c = (x + 1) % r.length;
810
- break;
811
- case "ArrowUp":
812
- case "ArrowLeft":
813
- f.preventDefault(), c = x === 0 ? r.length - 1 : x - 1;
814
- break;
815
- case "Home":
816
- f.preventDefault(), c = 0;
817
- break;
818
- case "End":
819
- f.preventDefault(), c = r.length - 1;
820
- break;
821
- default:
822
- return;
823
- }
824
- for (; r[c]?.disabled && (f.key === "ArrowDown" || f.key === "ArrowRight" ? c = (c + 1) % r.length : c = c === 0 ? r.length - 1 : c - 1, c !== x); )
825
- ;
826
- r[c]?.disabled || (o(r[c].value), setTimeout(() => {
827
- const p = document.querySelector(
828
- `input[name="${t}"][value="${r[c].value}"]`
829
- );
830
- p && p.focus();
831
- }, 0));
832
- }, k = F || h.error();
833
- return {
834
- type: "component",
835
- id: C.id || `radio-group-${t}`,
836
- render: () => s(
837
- "fieldset",
838
- {
839
- ...C,
840
- "data-tachui-radio-group": !0,
841
- "data-direction": A,
842
- "data-disabled": T,
843
- role: "radiogroup",
844
- "aria-invalid": !!k,
845
- "aria-describedby": [k ? `${t}-error` : null, g ? `${t}-helper` : null].filter(Boolean).join(" ") || void 0,
846
- onkeydown: u
847
- },
848
- ...n ? [
849
- s(
850
- "legend",
851
- {
852
- "data-tachui-group-label": !0
853
- },
854
- b(n),
855
- ...S ? [
856
- s(
857
- "span",
858
- {
859
- "aria-label": "required",
860
- "data-required-indicator": !0
861
- },
862
- b(" *")
863
- )
864
- ] : []
865
- )
866
- ] : [],
867
- // Radio options
868
- s(
869
- "div",
870
- {
871
- "data-tachui-radio-options": !0,
872
- "data-direction": A
873
- },
874
- ...r.flatMap((f, x) => {
875
- const p = Ie({
876
- name: `${t}-${x}`,
877
- groupName: t,
878
- value: f.value,
879
- label: f.label,
880
- checked: h.value() === f.value,
881
- disabled: T || f.disabled,
882
- required: S,
883
- onChange: () => o(f.value),
884
- _formContext: m
885
- }).render();
886
- return Array.isArray(p) ? p : [p];
887
- })
888
- ),
889
- ...k ? [
890
- s(
891
- "div",
892
- {
893
- id: `${t}-error`,
894
- role: "alert",
895
- "aria-live": "polite",
896
- "data-tachui-error": !0
897
- },
898
- b(k)
899
- )
900
- ] : [],
901
- ...g && !k ? [
902
- s(
903
- "div",
904
- {
905
- id: `${t}-helper`,
906
- "data-tachui-helper": !0
907
- },
908
- b(g)
909
- )
910
- ] : []
911
- ),
912
- props: e,
913
- cleanup: [
914
- () => {
915
- m && m.unregister(t);
916
- }
917
- ]
918
- };
919
- }, ge = (e) => {
920
- const {
921
- name: t,
922
- label: n,
923
- options: r,
924
- multiple: l = !1,
925
- searchable: d = !1,
926
- clearable: v = !1,
927
- placeholder: w = l ? "Select options..." : "Select an option...",
928
- noOptionsMessage: F = "No options available",
929
- loadingMessage: g = "Loading...",
930
- maxMenuHeight: T = 200,
931
- disabled: S = !1,
932
- required: A = !1,
933
- value: C,
934
- defaultValue: m,
935
- validation: h,
936
- onChange: o,
937
- onBlur: u,
938
- onFocus: k,
939
- error: O,
940
- helperText: f,
941
- ...x
942
- } = e, c = e._formContext, p = ee(t, C ?? m, h);
943
- c && c.register(t, h);
944
- const [$, L] = E(!1), [_, a] = E(!1), [z, R] = E(""), [N, H] = E(-1), [ne] = E(!1);
945
- C !== void 0 && j(() => {
946
- p.value() !== C && p.setValue(C);
947
- });
948
- const I = () => {
949
- if (!d || !z())
950
- return r;
951
- const i = z().toLowerCase();
952
- return r.filter(
953
- (y) => y.label.toLowerCase().includes(i) || String(y.value).toLowerCase().includes(i)
954
- );
955
- }, ae = () => {
956
- const i = p.value();
957
- if (l)
958
- return !i || !Array.isArray(i) || i.length === 0 ? w : r.filter((P) => i.includes(P.value)).map((P) => P.label).join(", ");
959
- {
960
- if (i == null || i === "")
961
- return w;
962
- const y = r.find((P) => P.value === i);
963
- return y ? y.label : String(i);
964
- }
965
- }, Q = (i) => {
966
- if (i.disabled) return;
967
- let y;
968
- if (l) {
969
- const P = p.value() || [];
970
- P.includes(i.value) ? y = P.filter((le) => le !== i.value) : y = [...P, i.value];
971
- } else
972
- y = i.value, L(!1), R("");
973
- p.setValue(y), c && c.setValue(t, y), o && o(t, y, p);
974
- }, K = () => {
975
- if (S) return;
976
- const i = !$();
977
- L(i), i && (H(-1), d && R(""));
978
- }, te = (i) => {
979
- const y = i.target;
980
- R(y.value), H(-1);
981
- }, X = (i) => {
982
- const y = I();
983
- switch (i.key) {
984
- case "Enter":
985
- i.preventDefault(), $() ? N() >= 0 && y[N()] && Q(y[N()]) : K();
986
- break;
987
- case " ":
988
- (!d || !$()) && (i.preventDefault(), K());
989
- break;
990
- case "Escape":
991
- i.preventDefault(), L(!1), R("");
992
- break;
993
- case "ArrowDown":
994
- if (i.preventDefault(), !$())
995
- K();
996
- else {
997
- const P = Math.min(N() + 1, y.length - 1);
998
- H(P);
999
- }
1000
- break;
1001
- case "ArrowUp":
1002
- if (i.preventDefault(), $()) {
1003
- const P = Math.max(N() - 1, -1);
1004
- H(P);
1005
- }
1006
- break;
1007
- case "Home":
1008
- $() && (i.preventDefault(), H(0));
1009
- break;
1010
- case "End":
1011
- $() && (i.preventDefault(), H(y.length - 1));
1012
- break;
1013
- case "Backspace":
1014
- v && !d && p.value() !== null && (i.preventDefault(), re());
1015
- break;
1016
- }
1017
- }, B = () => {
1018
- a(!0), p.onFocus(), k && k(t, p.value());
1019
- }, D = (i) => {
1020
- requestAnimationFrame(() => {
1021
- const y = i.relatedTarget;
1022
- i.target.closest("[data-tachui-select-container]")?.contains(y) || (a(!1), L(!1), p.onBlur(), u && u(t, p.value()));
1023
- });
1024
- }, re = () => {
1025
- const i = l ? [] : null;
1026
- p.setValue(i), c && c.setValue(t, i), o && o(t, i, p);
1027
- }, G = (i) => {
1028
- const y = p.value();
1029
- return l ? Array.isArray(y) && y.includes(i.value) : y === i.value;
1030
- }, M = O || p.error() || c?.getError(t), W = {
1031
- type: "component",
1032
- id: x.id || `select-${t}`,
1033
- cleanup: [],
1034
- render: () => ({
1035
- type: "element",
1036
- tag: "div",
1037
- props: {
1038
- ...x,
1039
- class: `tachui-select ${x.class || ""}`.trim(),
1040
- "data-tachui-select-container": !0,
1041
- "data-field-state": M ? "error" : p.validating() ? "validating" : "valid",
1042
- "data-open": $(),
1043
- "data-disabled": S,
1044
- "data-multiple": l,
1045
- "data-searchable": d
1046
- },
1047
- children: [
1048
- // Label
1049
- ...n ? [
1050
- s(
1051
- "label",
1052
- {
1053
- for: x.id || t,
1054
- "data-tachui-label": !0,
1055
- "data-required": A
1056
- },
1057
- b(n),
1058
- ...A ? [
1059
- s(
1060
- "span",
1061
- {
1062
- "aria-label": "required",
1063
- "data-required-indicator": !0
1064
- },
1065
- b(" *")
1066
- )
1067
- ] : []
1068
- )
1069
- ] : [],
1070
- // Select trigger
1071
- {
1072
- type: "element",
1073
- tag: "div",
1074
- props: {
1075
- id: x.id || t,
1076
- tabindex: S ? -1 : 0,
1077
- role: "combobox",
1078
- "aria-expanded": $(),
1079
- "aria-haspopup": "listbox",
1080
- "aria-invalid": !!M,
1081
- "aria-describedby": [M ? `${t}-error` : null, f ? `${t}-helper` : null].filter(Boolean).join(" ") || void 0,
1082
- onclick: K,
1083
- onkeydown: X,
1084
- onfocus: B,
1085
- onblur: D,
1086
- "data-tachui-select-trigger": !0,
1087
- "data-focused": _(),
1088
- "data-disabled": S,
1089
- "data-error": !!M
1090
- },
1091
- children: [
1092
- // Display value
1093
- {
1094
- type: "element",
1095
- tag: "div",
1096
- props: {
1097
- "data-tachui-select-value": !0,
1098
- "data-placeholder": !p.value() || l && (!p.value() || p.value().length === 0)
1099
- },
1100
- children: [b(ae())]
1101
- },
1102
- // Actions (clear, dropdown arrow)
1103
- {
1104
- type: "element",
1105
- tag: "div",
1106
- props: {
1107
- "data-tachui-select-actions": !0
1108
- },
1109
- children: [
1110
- // Clear button
1111
- ...v && p.value() && (!l || Array.isArray(p.value()) && p.value().length > 0) ? [
1112
- {
1113
- type: "element",
1114
- tag: "button",
1115
- props: {
1116
- type: "button",
1117
- onclick: (i) => {
1118
- i.stopPropagation(), re();
1119
- },
1120
- "aria-label": "Clear selection",
1121
- "data-tachui-select-clear": !0
1122
- },
1123
- children: [b("×")]
1124
- }
1125
- ] : [],
1126
- // Dropdown arrow
1127
- {
1128
- type: "element",
1129
- tag: "div",
1130
- props: {
1131
- "data-tachui-select-arrow": !0,
1132
- "data-open": $()
1133
- },
1134
- children: [b("▼")]
1135
- }
1136
- ]
1137
- }
1138
- ]
1139
- },
1140
- // Dropdown menu
1141
- ...$() ? [
1142
- {
1143
- type: "element",
1144
- tag: "div",
1145
- props: {
1146
- "data-tachui-select-dropdown": !0,
1147
- style: {
1148
- maxHeight: `${T}px`
1149
- }
1150
- },
1151
- children: [
1152
- // Search input
1153
- ...d ? [
1154
- {
1155
- type: "element",
1156
- tag: "div",
1157
- props: {
1158
- "data-tachui-select-search": !0
1159
- },
1160
- children: [
1161
- {
1162
- type: "element",
1163
- tag: "input",
1164
- props: {
1165
- type: "text",
1166
- placeholder: "Search...",
1167
- value: z(),
1168
- oninput: te,
1169
- "data-tachui-select-search-input": !0
1170
- }
1171
- }
1172
- ]
1173
- }
1174
- ] : [],
1175
- // Options list
1176
- {
1177
- type: "element",
1178
- tag: "div",
1179
- props: {
1180
- role: "listbox",
1181
- "aria-multiselectable": l,
1182
- "data-tachui-select-options": !0
1183
- },
1184
- children: I().length > 0 ? I().map((i, y) => ({
1185
- type: "element",
1186
- tag: "div",
1187
- props: {
1188
- role: "option",
1189
- "aria-selected": G(i),
1190
- "aria-disabled": i.disabled,
1191
- onclick: () => Q(i),
1192
- "data-tachui-select-option": !0,
1193
- "data-selected": G(i),
1194
- "data-highlighted": N() === y,
1195
- "data-disabled": i.disabled,
1196
- "data-group": i.group
1197
- },
1198
- children: [
1199
- // Selection indicator for multiple
1200
- ...l ? [
1201
- {
1202
- type: "element",
1203
- tag: "div",
1204
- props: {
1205
- "data-tachui-select-checkbox": !0,
1206
- "data-checked": G(i)
1207
- },
1208
- children: [b(G(i) ? "✓" : "")]
1209
- }
1210
- ] : [],
1211
- // Option label
1212
- b(i.label)
1213
- ]
1214
- })) : [
1215
- {
1216
- type: "element",
1217
- tag: "div",
1218
- props: {
1219
- "data-tachui-select-no-options": !0
1220
- },
1221
- children: [b(ne() ? g : F)]
1222
- }
1223
- ]
1224
- }
1225
- ]
1226
- }
1227
- ] : [],
1228
- // Error message
1229
- ...M ? [
1230
- {
1231
- type: "element",
1232
- tag: "div",
1233
- props: {
1234
- id: `${t}-error`,
1235
- role: "alert",
1236
- "aria-live": "polite",
1237
- "data-tachui-error": !0
1238
- },
1239
- children: [b(M)]
1240
- }
1241
- ] : [],
1242
- // Helper text
1243
- ...f && !M ? [
1244
- {
1245
- type: "element",
1246
- tag: "div",
1247
- props: {
1248
- id: `${t}-helper`,
1249
- "data-tachui-helper": !0
1250
- },
1251
- children: [b(f)]
1252
- }
1253
- ] : []
1254
- ]
1255
- }),
1256
- props: e
1257
- };
1258
- return W.cleanup || (W.cleanup = []), W.cleanup.push(() => {
1259
- c && c.unregister(t);
1260
- }), Ve(W, {
1261
- onDOMReady: (i, y) => {
1262
- y && Te(
1263
- W,
1264
- () => {
1265
- $() && (L(!1), a(!1), p.onBlur(), u && u(t, p.value()));
1266
- },
1267
- "[data-tachui-select-container]"
1268
- );
1269
- }
1270
- }), W;
1271
- }, Ue = (e) => ge({ ...e, multiple: !0 }), je = (e) => ge({ ...e, searchable: !0 }), Y = {
1272
- /**
1273
- * Phone number formatter (US format)
1274
- */
1275
- phone: (e) => {
1276
- const t = e.replace(/\D/g, "");
1277
- return t.length <= 3 ? t : t.length <= 6 ? `(${t.slice(0, 3)}) ${t.slice(3)}` : `(${t.slice(0, 3)}) ${t.slice(3, 6)}-${t.slice(6, 10)}`;
1278
- },
1279
- /**
1280
- * Credit card formatter
1281
- */
1282
- creditCard: (e) => e.replace(/\D/g, "").replace(/(\d{4})(?=\d)/g, "$1 "),
1283
- /**
1284
- * Currency formatter
1285
- */
1286
- currency: (e) => {
1287
- const t = parseFloat(e.replace(/[^\d.]/g, ""));
1288
- return Number.isNaN(t) ? "" : new Intl.NumberFormat("en-US", {
1289
- style: "currency",
1290
- currency: "USD"
1291
- }).format(t);
1292
- },
1293
- /**
1294
- * Uppercase formatter
1295
- */
1296
- uppercase: (e) => e.toUpperCase(),
1297
- /**
1298
- * Lowercase formatter
1299
- */
1300
- lowercase: (e) => e.toLowerCase(),
1301
- /**
1302
- * Title case formatter
1303
- */
1304
- titleCase: (e) => e.replace(
1305
- /\w\S*/g,
1306
- (t) => t.charAt(0).toUpperCase() + t.substr(1).toLowerCase()
1307
- ),
1308
- /**
1309
- * Social Security Number formatter
1310
- */
1311
- ssn: (e) => {
1312
- const t = e.replace(/\D/g, "");
1313
- return t.length <= 3 ? t : t.length <= 5 ? `${t.slice(0, 3)}-${t.slice(3)}` : `${t.slice(0, 3)}-${t.slice(3, 5)}-${t.slice(5, 9)}`;
1314
- },
1315
- /**
1316
- * Postal code formatter (US ZIP)
1317
- */
1318
- postalCode: (e) => {
1319
- const t = e.replace(/\D/g, "");
1320
- return t.length <= 5 ? t : `${t.slice(0, 5)}-${t.slice(5, 9)}`;
1321
- },
1322
- /**
1323
- * Decimal number formatter
1324
- */
1325
- decimal: (e = 2) => (t) => {
1326
- const n = parseFloat(t.replace(/[^\d.-]/g, ""));
1327
- return Number.isNaN(n) ? "" : n.toFixed(e);
1328
- },
1329
- /**
1330
- * Percentage formatter
1331
- */
1332
- percentage: (e) => {
1333
- const t = parseFloat(e.replace(/[^\d.-]/g, ""));
1334
- return Number.isNaN(t) ? "" : `${t}%`;
1335
- },
1336
- /**
1337
- * Custom formatter factory
1338
- */
1339
- custom: (e) => e
1340
- }, Z = {
1341
- /**
1342
- * Phone number parser - extracts digits only
1343
- */
1344
- phone: (e) => e.replace(/\D/g, ""),
1345
- /**
1346
- * Credit card parser - extracts digits only
1347
- */
1348
- creditCard: (e) => e.replace(/\D/g, ""),
1349
- /**
1350
- * Currency parser - extracts numeric value
1351
- */
1352
- currency: (e) => {
1353
- const t = e.match(/[\d.-]+/);
1354
- return t ? t[0] : "";
1355
- },
1356
- /**
1357
- * SSN parser - extracts digits only
1358
- */
1359
- ssn: (e) => e.replace(/\D/g, ""),
1360
- /**
1361
- * Postal code parser - extracts digits only
1362
- */
1363
- postalCode: (e) => e.replace(/\D/g, ""),
1364
- /**
1365
- * Decimal parser - extracts number
1366
- */
1367
- decimal: (e) => {
1368
- const t = e.match(/^-?\d*\.?\d*/);
1369
- return t ? t[0] : "";
1370
- },
1371
- /**
1372
- * Percentage parser - extracts number without %
1373
- */
1374
- percentage: (e) => e.replace(/[^\d.-]/g, ""),
1375
- /**
1376
- * No-op parser (returns value unchanged)
1377
- */
1378
- none: (e) => e,
1379
- /**
1380
- * Custom parser factory
1381
- */
1382
- custom: (e) => e
1383
- }, se = (e, t) => e === void 0 ? t : typeof e == "function" || Ae(e) ? e() : e, q = (e) => {
1384
- const {
1385
- name: t,
1386
- label: n,
1387
- placeholder: r,
1388
- type: l = "text",
1389
- multiline: d = !1,
1390
- rows: v = 3,
1391
- minLength: w,
1392
- maxLength: F,
1393
- pattern: g,
1394
- autocomplete: T,
1395
- spellcheck: S = !0,
1396
- disabled: A = !1,
1397
- required: C = !1,
1398
- validation: m,
1399
- value: h,
1400
- defaultValue: o = "",
1401
- onChange: u,
1402
- onBlur: k,
1403
- onFocus: O,
1404
- error: f,
1405
- helperText: x,
1406
- // New enhanced features
1407
- keyboardType: c = "default",
1408
- returnKeyType: p,
1409
- autoCapitalize: $,
1410
- autoFocus: L = !1,
1411
- accessibilityLabel: _,
1412
- accessibilityHint: a,
1413
- accessibilityRole: z = "textbox",
1414
- formatter: R,
1415
- parser: N,
1416
- validateOnChange: H = !1,
1417
- validateOnBlur: ne = !0,
1418
- font: I,
1419
- textAlign: ae,
1420
- text: Q,
1421
- placeholderSignal: K,
1422
- disabledSignal: te,
1423
- ...X
1424
- } = e, B = e._formContext, D = ee(t, h ?? o, m);
1425
- B && B.register(t, m);
1426
- const [re, G] = E(!1), [M, W] = E(0), [i, y] = E(""), [P, le] = E(""), [me, ye] = E(!1);
1427
- j(() => {
1428
- if (Q) {
1429
- const V = se(Q, "");
1430
- y(V), V !== D.value() && D.setValue(V);
1431
- }
1432
- }), j(() => {
1433
- K && le(se(K, ""));
1434
- }), j(() => {
1435
- te && ye(se(te, !1));
1436
- }), h !== void 0 && j(() => {
1437
- D.value() !== h && D.setValue(h);
1438
- }), j(() => {
1439
- const V = D.value() || "";
1440
- W(String(V).length);
1441
- });
1442
- const de = (V) => {
1443
- if (R)
1444
- try {
1445
- return R(V);
1446
- } catch (U) {
1447
- return console.warn("TextField formatter error:", U), V;
1448
- }
1449
- return V;
1450
- }, ve = (V) => {
1451
- if (N)
1452
- try {
1453
- return N(V);
1454
- } catch (U) {
1455
- return console.warn("TextField parser error:", U), V;
1456
- }
1457
- return V;
1458
- }, xe = (V) => {
1459
- const U = V.target, ce = U.value, ie = ve(ce), ue = de(ie);
1460
- if (D.setValue(ie), ue !== ce && U) {
1461
- const fe = U.selectionStart || 0;
1462
- U.value = ue, U.setSelectionRange(fe, fe);
1463
- }
1464
- B && B.setValue(t, ie), H && D.validate(), u && u(t, ie, D);
1465
- }, ke = (V) => {
1466
- G(!0), D.onFocus(), O && O(t, D.value());
1467
- }, Ce = (V) => {
1468
- G(!1), D.onBlur(), ne && D.validate(), k && k(t, D.value());
1469
- }, we = (V) => {
1470
- V.key === "Enter" && !d && (V.preventDefault(), B?.submitForm && B.submitForm());
1471
- }, J = f || D.error() || B?.getError(t), Fe = K ? P() : r, Se = te ? me() : A, $e = Q ? i() : D.value() || "", De = de($e), oe = {
1472
- id: X.id || t,
1473
- name: t,
1474
- value: De,
1475
- placeholder: Fe,
1476
- disabled: Se,
1477
- required: C,
1478
- minlength: w,
1479
- maxlength: F,
1480
- pattern: g,
1481
- autocomplete: T,
1482
- spellcheck: S,
1483
- oninput: xe,
1484
- onfocus: ke,
1485
- onblur: Ce,
1486
- onkeydown: we,
1487
- // Enhanced accessibility
1488
- "aria-invalid": !!J,
1489
- "aria-describedby": [
1490
- J ? `${t}-error` : null,
1491
- x ? `${t}-helper` : null,
1492
- F ? `${t}-counter` : null,
1493
- a ? `${t}-hint` : null
1494
- ].filter(Boolean).join(" ") || void 0,
1495
- "aria-label": _,
1496
- role: z,
1497
- // Mobile features
1498
- inputMode: c !== "default" ? c : void 0,
1499
- enterKeyHint: p,
1500
- autoCapitalize: $,
1501
- autoFocus: L,
1502
- // Data attributes for styling and debugging
1503
- "data-tachui-textfield": !0,
1504
- "data-field-name": t,
1505
- "data-field-type": l,
1506
- "data-field-valid": !J,
1507
- "data-field-touched": D.touched(),
1508
- "data-field-dirty": D.dirty(),
1509
- "data-field-focused": re(),
1510
- "data-field-validating": D.validating(),
1511
- "data-field-has-formatter": !!R,
1512
- "data-field-has-parser": !!N,
1513
- // Typography styling
1514
- style: {
1515
- ...I?.family && { fontFamily: I.family },
1516
- ...I?.size && { fontSize: typeof I.size == "number" ? `${I.size}px` : I.size },
1517
- ...I?.weight && { fontWeight: I.weight },
1518
- ...I?.style && { fontStyle: I.style },
1519
- ...ae && { textAlign: ae }
1520
- // Additional styling can be applied via modifiers
1521
- }
1522
- };
1523
- return l && !d && (oe.type = l), {
1524
- type: "component",
1525
- id: X.id || `textfield-${t}`,
1526
- render: () => s(
1527
- "div",
1528
- {
1529
- ...X,
1530
- class: `tachui-textfield ${X.class || ""}`.trim(),
1531
- "data-tachui-textfield-container": !0,
1532
- "data-field-state": J ? "error" : D.validating() ? "validating" : "valid"
1533
- },
1534
- ...n ? [
1535
- s(
1536
- "label",
1537
- {
1538
- for: oe.id,
1539
- "data-tachui-label": !0,
1540
- "data-required": C
1541
- },
1542
- b(n),
1543
- ...C ? [
1544
- s(
1545
- "span",
1546
- {
1547
- "aria-label": "required",
1548
- "data-required-indicator": !0
1549
- },
1550
- b(" *")
1551
- )
1552
- ] : []
1553
- )
1554
- ] : [],
1555
- // Input field
1556
- s(d ? "textarea" : "input", {
1557
- ...oe,
1558
- ...d ? { rows: v } : {}
1559
- }),
1560
- ...F ? [
1561
- s(
1562
- "div",
1563
- {
1564
- id: `${t}-counter`,
1565
- "data-tachui-character-counter": !0,
1566
- "data-over-limit": M() > F
1567
- },
1568
- b(`${M()}/${F}`)
1569
- )
1570
- ] : [],
1571
- ...J ? [
1572
- s(
1573
- "div",
1574
- {
1575
- id: `${t}-error`,
1576
- role: "alert",
1577
- "aria-live": "polite",
1578
- "data-tachui-error": !0
1579
- },
1580
- b(J)
1581
- )
1582
- ] : [],
1583
- ...x && !J ? [
1584
- s(
1585
- "div",
1586
- {
1587
- id: `${t}-helper`,
1588
- "data-tachui-helper": !0
1589
- },
1590
- b(x)
1591
- )
1592
- ] : [],
1593
- ...a ? [
1594
- s(
1595
- "div",
1596
- {
1597
- id: `${t}-hint`,
1598
- "data-tachui-accessibility-hint": !0,
1599
- "aria-hidden": "true"
1600
- },
1601
- b(a)
1602
- )
1603
- ] : [],
1604
- ...D.validating() ? [
1605
- s(
1606
- "div",
1607
- {
1608
- "data-tachui-validation-spinner": !0,
1609
- "aria-label": "Validating...",
1610
- "aria-live": "polite"
1611
- },
1612
- b("⏳")
1613
- )
1614
- ] : []
1615
- ),
1616
- props: e,
1617
- cleanup: [
1618
- () => {
1619
- B && B.unregister(t);
1620
- }
1621
- ]
1622
- };
1623
- }, He = (e) => q({
1624
- ...e,
1625
- type: "email",
1626
- keyboardType: "email",
1627
- validation: {
1628
- rules: ["required", "email"],
1629
- validateOn: "blur",
1630
- ...e.validation
1631
- },
1632
- accessibilityRole: "textbox",
1633
- accessibilityLabel: e.accessibilityLabel || "Email address"
1634
- }), Ke = (e) => {
1635
- const {
1636
- showStrengthIndicator: t = !1,
1637
- strongValidation: n = !1,
1638
- minLength: r,
1639
- ...l
1640
- } = e, d = ["required"];
1641
- return n ? d.push("strongPassword") : (d.push("minLength"), d.push({ name: "minLength", options: { minLength: r || 6 } })), q({
1642
- ...l,
1643
- type: "password",
1644
- validation: {
1645
- rules: d,
1646
- validateOn: "change",
1647
- ...e.validation
1648
- },
1649
- accessibilityLabel: e.accessibilityLabel || "Password"
1650
- });
1651
- }, We = (e) => q({
1652
- ...e,
1653
- type: "search",
1654
- keyboardType: "search",
1655
- placeholder: e.placeholder || "Search...",
1656
- accessibilityRole: "searchbox",
1657
- accessibilityLabel: e.accessibilityLabel || "Search"
1658
- }), Ge = (e) => q({
1659
- ...e,
1660
- type: "url",
1661
- keyboardType: "url",
1662
- validation: {
1663
- rules: ["url"],
1664
- validateOn: "blur",
1665
- ...e.validation
1666
- },
1667
- accessibilityLabel: e.accessibilityLabel || "Website URL"
1668
- }), Je = (e) => {
1669
- const { format: t = "us", ...n } = e;
1670
- return q({
1671
- ...n,
1672
- type: "tel",
1673
- keyboardType: "phone",
1674
- formatter: Y.phone,
1675
- parser: Z.phone,
1676
- validation: {
1677
- rules: ["phone"],
1678
- validateOn: "blur",
1679
- ...e.validation
1680
- },
1681
- accessibilityLabel: e.accessibilityLabel || "Phone number"
1682
- });
1683
- }, Qe = (e) => {
1684
- const { min: t, max: n, precision: r = 0, currency: l = !1, ...d } = e, v = ["numeric"];
1685
- return t !== void 0 && (v.push("min"), v.push({ name: "min", options: { min: t } })), n !== void 0 && (v.push("max"), v.push({ name: "max", options: { max: n } })), q({
1686
- ...d,
1687
- type: "number",
1688
- keyboardType: "numeric",
1689
- formatter: l ? Y.currency : r > 0 ? Y.decimal(r) : void 0,
1690
- parser: l ? Z.currency : Z.decimal,
1691
- validation: {
1692
- rules: v,
1693
- validateOn: "blur",
1694
- ...e.validation
1695
- },
1696
- accessibilityLabel: e.accessibilityLabel || "Number"
1697
- });
1698
- }, Xe = (e) => q({
1699
- ...e,
1700
- type: "text",
1701
- keyboardType: "numeric",
1702
- formatter: Y.creditCard,
1703
- parser: Z.creditCard,
1704
- maxLength: 19,
1705
- // 16 digits + 3 spaces
1706
- validation: {
1707
- rules: ["creditCard"],
1708
- validateOn: "blur",
1709
- ...e.validation
1710
- },
1711
- accessibilityLabel: e.accessibilityLabel || "Credit card number"
1712
- }), Ye = (e) => q({
1713
- ...e,
1714
- type: "text",
1715
- keyboardType: "numeric",
1716
- formatter: Y.ssn,
1717
- parser: Z.ssn,
1718
- maxLength: 11,
1719
- // 9 digits + 2 hyphens
1720
- validation: {
1721
- rules: ["ssn"],
1722
- validateOn: "blur",
1723
- ...e.validation
1724
- },
1725
- accessibilityLabel: e.accessibilityLabel || "Social Security Number"
1726
- }), Ze = (e) => q({
1727
- ...e,
1728
- type: "text",
1729
- keyboardType: "numeric",
1730
- formatter: Y.postalCode,
1731
- parser: Z.postalCode,
1732
- maxLength: 10,
1733
- // 5 or 9 digits + hyphen
1734
- validation: {
1735
- rules: ["zipCode"],
1736
- // Use zipCode to match test expectation
1737
- validateOn: "blur",
1738
- ...e.validation
1739
- },
1740
- accessibilityLabel: e.accessibilityLabel || "Postal code"
1741
- }), et = (e) => q({
1742
- ...e,
1743
- multiline: !0,
1744
- accessibilityLabel: e.accessibilityLabel || "Text area"
1745
- }), tt = (e) => {
1746
- const { min: t, max: n, ...r } = e, l = ["date"];
1747
- return t && l.push({ name: "min", options: { min: new Date(t) } }), n && l.push({ name: "max", options: { max: new Date(n) } }), q({
1748
- ...r,
1749
- type: "date",
1750
- validation: {
1751
- rules: l,
1752
- validateOn: "blur",
1753
- ...e.validation
1754
- },
1755
- accessibilityLabel: e.accessibilityLabel || "Date"
1756
- });
1757
- }, at = (e) => q({
1758
- ...e,
1759
- type: "time",
1760
- validation: {
1761
- rules: ["time"],
1762
- validateOn: "blur",
1763
- ...e.validation
1764
- },
1765
- accessibilityLabel: e.accessibilityLabel || "Time"
1766
- }), rt = (e) => q({
1767
- ...e,
1768
- type: "color",
1769
- accessibilityLabel: e.accessibilityLabel || "Color picker"
1770
- });
1771
- export {
1772
- be as C,
1773
- tt as D,
1774
- He as E,
1775
- Pe as F,
1776
- Ue as M,
1777
- Qe as N,
1778
- Ke as P,
1779
- Ie as R,
1780
- Me as S,
1781
- et as T,
1782
- Ge as U,
1783
- Re as a,
1784
- Ne as b,
1785
- _e as c,
1786
- ze as d,
1787
- je as e,
1788
- ge as f,
1789
- rt as g,
1790
- Xe as h,
1791
- Je as i,
1792
- Ze as j,
1793
- We as k,
1794
- Ye as l,
1795
- q as m,
1796
- at as n,
1797
- Y as o,
1798
- Z as p
1799
- };