tyrell-components 1.0.0-RC13 → 1.0.0-RC15

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 (358) hide show
  1. package/README.md +10 -2
  2. package/css/tyrell-theme.css +192 -180
  3. package/css/tyrell.css +23 -0
  4. package/dist/tyrell-colors-static.css +1 -316
  5. package/dist/tyrell-theme.css +1 -1454
  6. package/dist/tyrell.css +1 -1371
  7. package/dist/tyrell.js +1 -1
  8. package/lib/base/ty-component.d.ts +20 -10
  9. package/lib/base/ty-component.js +196 -319
  10. package/lib/components/button.d.ts +3 -17
  11. package/lib/components/button.js +261 -295
  12. package/lib/components/calendar-month.d.ts +4 -50
  13. package/lib/components/calendar-month.js +443 -582
  14. package/lib/components/calendar-navigation.d.ts +3 -48
  15. package/lib/components/calendar-navigation.js +239 -362
  16. package/lib/components/calendar.d.ts +8 -83
  17. package/lib/components/calendar.js +616 -829
  18. package/lib/components/checkbox.d.ts +5 -72
  19. package/lib/components/checkbox.js +273 -416
  20. package/lib/components/copy.d.ts +4 -57
  21. package/lib/components/copy.js +280 -397
  22. package/lib/components/date-picker.d.ts +21 -163
  23. package/lib/components/date-picker.js +1143 -1674
  24. package/lib/components/file-upload.d.ts +9 -15
  25. package/lib/components/file-upload.js +404 -426
  26. package/lib/components/icon.d.ts +3 -24
  27. package/lib/components/icon.js +141 -230
  28. package/lib/components/input.d.ts +8 -108
  29. package/lib/components/input.js +593 -784
  30. package/lib/components/modal.d.ts +17 -26
  31. package/lib/components/modal.js +340 -484
  32. package/lib/components/option.d.ts +2 -9
  33. package/lib/components/option.js +168 -216
  34. package/lib/components/popup.d.ts +2 -17
  35. package/lib/components/popup.js +249 -380
  36. package/lib/components/radio.d.ts +5 -25
  37. package/lib/components/radio.js +407 -461
  38. package/lib/components/resize-observer.d.ts +3 -30
  39. package/lib/components/resize-observer.js +69 -103
  40. package/lib/components/scroll-container.d.ts +2 -12
  41. package/lib/components/scroll-container.js +293 -338
  42. package/lib/components/select.d.ts +87 -159
  43. package/lib/components/select.js +1609 -2090
  44. package/lib/components/selected-tags.d.ts +5 -48
  45. package/lib/components/selected-tags.js +188 -276
  46. package/lib/components/step.d.ts +1 -6
  47. package/lib/components/step.js +62 -68
  48. package/lib/components/switch.d.ts +1 -12
  49. package/lib/components/switch.js +214 -247
  50. package/lib/components/tab.d.ts +1 -6
  51. package/lib/components/tab.js +62 -69
  52. package/lib/components/tabs.d.ts +6 -63
  53. package/lib/components/tabs.js +545 -777
  54. package/lib/components/tag.d.ts +3 -37
  55. package/lib/components/tag.js +236 -335
  56. package/lib/components/textarea.d.ts +7 -87
  57. package/lib/components/textarea.js +457 -627
  58. package/lib/components/tooltip.d.ts +3 -11
  59. package/lib/components/tooltip.js +307 -443
  60. package/lib/components/wizard.d.ts +6 -54
  61. package/lib/components/wizard.js +405 -618
  62. package/lib/icons/fontawesome/brands.d.ts +0 -1
  63. package/lib/icons/fontawesome/brands.js +0 -8
  64. package/lib/icons/fontawesome/regular.d.ts +0 -1
  65. package/lib/icons/fontawesome/regular.js +0 -8
  66. package/lib/icons/fontawesome/solid.d.ts +0 -1
  67. package/lib/icons/fontawesome/solid.js +0 -8
  68. package/lib/icons/heroicons/micro.d.ts +0 -1
  69. package/lib/icons/heroicons/micro.js +392 -400
  70. package/lib/icons/heroicons/mini.d.ts +0 -1
  71. package/lib/icons/heroicons/mini.js +382 -390
  72. package/lib/icons/heroicons/outline.d.ts +0 -1
  73. package/lib/icons/heroicons/outline.js +337 -345
  74. package/lib/icons/heroicons/solid.d.ts +0 -1
  75. package/lib/icons/heroicons/solid.js +407 -415
  76. package/lib/icons/lucide.d.ts +0 -1
  77. package/lib/icons/lucide.js +24479 -26351
  78. package/lib/icons/material/filled.d.ts +0 -1
  79. package/lib/icons/material/filled.js +9653 -9663
  80. package/lib/icons/material/outlined.d.ts +0 -1
  81. package/lib/icons/material/outlined.js +10271 -10281
  82. package/lib/icons/material/round.d.ts +0 -1
  83. package/lib/icons/material/round.js +10495 -10505
  84. package/lib/icons/material/sharp.d.ts +0 -1
  85. package/lib/icons/material/sharp.js +9905 -9915
  86. package/lib/icons/material/two-tone.d.ts +0 -1
  87. package/lib/icons/material/two-tone.js +12792 -12802
  88. package/lib/index.d.ts +0 -1
  89. package/lib/index.js +21 -41
  90. package/lib/styles/button.d.ts +2 -20
  91. package/lib/styles/button.js +202 -404
  92. package/lib/styles/calendar-month.d.ts +1 -5
  93. package/lib/styles/calendar-month.js +127 -244
  94. package/lib/styles/calendar-navigation.d.ts +2 -6
  95. package/lib/styles/calendar-navigation.js +64 -104
  96. package/lib/styles/calendar.d.ts +1 -2
  97. package/lib/styles/calendar.js +9 -22
  98. package/lib/styles/checkbox.d.ts +0 -1
  99. package/lib/styles/checkbox.js +45 -94
  100. package/lib/styles/copy.d.ts +0 -1
  101. package/lib/styles/copy.js +67 -106
  102. package/lib/styles/custom-scrollbar.d.ts +1 -2
  103. package/lib/styles/custom-scrollbar.js +80 -115
  104. package/lib/styles/date-picker.d.ts +1 -5
  105. package/lib/styles/date-picker.js +261 -335
  106. package/lib/styles/field-size.d.ts +6 -0
  107. package/lib/styles/field-size.js +91 -0
  108. package/lib/styles/file-upload.d.ts +1 -2
  109. package/lib/styles/file-upload.js +143 -188
  110. package/lib/styles/icon.d.ts +2 -6
  111. package/lib/styles/icon.js +92 -170
  112. package/lib/styles/input.d.ts +1 -6
  113. package/lib/styles/input.js +125 -290
  114. package/lib/styles/modal.d.ts +1 -2
  115. package/lib/styles/modal.js +71 -108
  116. package/lib/styles/option.d.ts +2 -6
  117. package/lib/styles/option.js +49 -64
  118. package/lib/styles/popup.d.ts +1 -2
  119. package/lib/styles/popup.js +42 -77
  120. package/lib/styles/radio.d.ts +0 -1
  121. package/lib/styles/radio.js +69 -110
  122. package/lib/styles/resize-observer.d.ts +1 -2
  123. package/lib/styles/resize-observer.js +2 -12
  124. package/lib/styles/scroll-container.d.ts +1 -2
  125. package/lib/styles/scroll-container.js +100 -149
  126. package/lib/styles/select-base.d.ts +0 -1
  127. package/lib/styles/select-base.js +597 -785
  128. package/lib/styles/select.d.ts +1 -2
  129. package/lib/styles/select.js +140 -236
  130. package/lib/styles/step.d.ts +1 -2
  131. package/lib/styles/step.js +22 -33
  132. package/lib/styles/switch.d.ts +0 -1
  133. package/lib/styles/switch.js +34 -73
  134. package/lib/styles/tab.d.ts +1 -2
  135. package/lib/styles/tab.js +22 -34
  136. package/lib/styles/tabs.d.ts +3 -11
  137. package/lib/styles/tabs.js +190 -222
  138. package/lib/styles/tag.d.ts +1 -5
  139. package/lib/styles/tag.js +136 -218
  140. package/lib/styles/textarea.d.ts +1 -2
  141. package/lib/styles/textarea.js +210 -319
  142. package/lib/styles/tooltip.d.ts +0 -1
  143. package/lib/styles/tooltip.js +3 -13
  144. package/lib/styles/wizard.d.ts +5 -21
  145. package/lib/styles/wizard.js +170 -306
  146. package/lib/types/common.d.ts +2 -2
  147. package/lib/types/common.js +6 -17
  148. package/lib/utils/calendar-utils.d.ts +4 -21
  149. package/lib/utils/calendar-utils.js +100 -273
  150. package/lib/utils/custom-scrollbar.d.ts +6 -20
  151. package/lib/utils/custom-scrollbar.js +273 -317
  152. package/lib/utils/flavor-sheet.d.ts +0 -1
  153. package/lib/utils/flavor-sheet.js +14 -37
  154. package/lib/utils/icon-registry.d.ts +0 -2
  155. package/lib/utils/icon-registry.js +137 -243
  156. package/lib/utils/icons.d.ts +0 -1
  157. package/lib/utils/icons.js +0 -9
  158. package/lib/utils/loader-registry.d.ts +0 -1
  159. package/lib/utils/loader-registry.js +3 -35
  160. package/lib/utils/locale.d.ts +2 -23
  161. package/lib/utils/locale.js +34 -120
  162. package/lib/utils/mobile.d.ts +0 -1
  163. package/lib/utils/mobile.js +1 -18
  164. package/lib/utils/number-format.d.ts +6 -3
  165. package/lib/utils/number-format.js +54 -110
  166. package/lib/utils/positioning.d.ts +50 -7
  167. package/lib/utils/positioning.js +224 -234
  168. package/lib/utils/property-capture.d.ts +5 -27
  169. package/lib/utils/property-capture.js +36 -144
  170. package/lib/utils/property-manager.d.ts +0 -1
  171. package/lib/utils/property-manager.js +126 -196
  172. package/lib/utils/resize-observer.d.ts +0 -1
  173. package/lib/utils/resize-observer.js +23 -59
  174. package/lib/utils/scroll-lock.d.ts +4 -11
  175. package/lib/utils/scroll-lock.js +65 -172
  176. package/lib/utils/styles.d.ts +0 -1
  177. package/lib/utils/styles.js +17 -41
  178. package/lib/version.d.ts +1 -2
  179. package/lib/version.js +1 -11
  180. package/package.json +4 -4
  181. package/lib/base/ty-component.d.ts.map +0 -1
  182. package/lib/base/ty-component.js.map +0 -1
  183. package/lib/components/button.d.ts.map +0 -1
  184. package/lib/components/button.js.map +0 -1
  185. package/lib/components/calendar-month.d.ts.map +0 -1
  186. package/lib/components/calendar-month.js.map +0 -1
  187. package/lib/components/calendar-navigation.d.ts.map +0 -1
  188. package/lib/components/calendar-navigation.js.map +0 -1
  189. package/lib/components/calendar.d.ts.map +0 -1
  190. package/lib/components/calendar.js.map +0 -1
  191. package/lib/components/checkbox.d.ts.map +0 -1
  192. package/lib/components/checkbox.js.map +0 -1
  193. package/lib/components/copy.d.ts.map +0 -1
  194. package/lib/components/copy.js.map +0 -1
  195. package/lib/components/date-picker.d.ts.map +0 -1
  196. package/lib/components/date-picker.js.map +0 -1
  197. package/lib/components/file-upload.d.ts.map +0 -1
  198. package/lib/components/file-upload.js.map +0 -1
  199. package/lib/components/icon.d.ts.map +0 -1
  200. package/lib/components/icon.js.map +0 -1
  201. package/lib/components/input.d.ts.map +0 -1
  202. package/lib/components/input.js.map +0 -1
  203. package/lib/components/modal.d.ts.map +0 -1
  204. package/lib/components/modal.js.map +0 -1
  205. package/lib/components/option.d.ts.map +0 -1
  206. package/lib/components/option.js.map +0 -1
  207. package/lib/components/popup.d.ts.map +0 -1
  208. package/lib/components/popup.js.map +0 -1
  209. package/lib/components/radio.d.ts.map +0 -1
  210. package/lib/components/radio.js.map +0 -1
  211. package/lib/components/resize-observer.d.ts.map +0 -1
  212. package/lib/components/resize-observer.js.map +0 -1
  213. package/lib/components/scroll-container.d.ts.map +0 -1
  214. package/lib/components/scroll-container.js.map +0 -1
  215. package/lib/components/select.d.ts.map +0 -1
  216. package/lib/components/select.js.map +0 -1
  217. package/lib/components/selected-tags.d.ts.map +0 -1
  218. package/lib/components/selected-tags.js.map +0 -1
  219. package/lib/components/step.d.ts.map +0 -1
  220. package/lib/components/step.js.map +0 -1
  221. package/lib/components/switch.d.ts.map +0 -1
  222. package/lib/components/switch.js.map +0 -1
  223. package/lib/components/tab.d.ts.map +0 -1
  224. package/lib/components/tab.js.map +0 -1
  225. package/lib/components/tabs.d.ts.map +0 -1
  226. package/lib/components/tabs.js.map +0 -1
  227. package/lib/components/tag.d.ts.map +0 -1
  228. package/lib/components/tag.js.map +0 -1
  229. package/lib/components/textarea.d.ts.map +0 -1
  230. package/lib/components/textarea.js.map +0 -1
  231. package/lib/components/tooltip.d.ts.map +0 -1
  232. package/lib/components/tooltip.js.map +0 -1
  233. package/lib/components/wizard.d.ts.map +0 -1
  234. package/lib/components/wizard.js.map +0 -1
  235. package/lib/icons/fontawesome/brands.d.ts.map +0 -1
  236. package/lib/icons/fontawesome/brands.js.map +0 -1
  237. package/lib/icons/fontawesome/regular.d.ts.map +0 -1
  238. package/lib/icons/fontawesome/regular.js.map +0 -1
  239. package/lib/icons/fontawesome/solid.d.ts.map +0 -1
  240. package/lib/icons/fontawesome/solid.js.map +0 -1
  241. package/lib/icons/heroicons/micro.d.ts.map +0 -1
  242. package/lib/icons/heroicons/micro.js.map +0 -1
  243. package/lib/icons/heroicons/mini.d.ts.map +0 -1
  244. package/lib/icons/heroicons/mini.js.map +0 -1
  245. package/lib/icons/heroicons/outline.d.ts.map +0 -1
  246. package/lib/icons/heroicons/outline.js.map +0 -1
  247. package/lib/icons/heroicons/solid.d.ts.map +0 -1
  248. package/lib/icons/heroicons/solid.js.map +0 -1
  249. package/lib/icons/lucide.d.ts.map +0 -1
  250. package/lib/icons/lucide.js.map +0 -1
  251. package/lib/icons/material/filled.d.ts.map +0 -1
  252. package/lib/icons/material/filled.js.map +0 -1
  253. package/lib/icons/material/outlined.d.ts.map +0 -1
  254. package/lib/icons/material/outlined.js.map +0 -1
  255. package/lib/icons/material/round.d.ts.map +0 -1
  256. package/lib/icons/material/round.js.map +0 -1
  257. package/lib/icons/material/sharp.d.ts.map +0 -1
  258. package/lib/icons/material/sharp.js.map +0 -1
  259. package/lib/icons/material/two-tone.d.ts.map +0 -1
  260. package/lib/icons/material/two-tone.js.map +0 -1
  261. package/lib/index.d.ts.map +0 -1
  262. package/lib/index.js.map +0 -1
  263. package/lib/styles/button.d.ts.map +0 -1
  264. package/lib/styles/button.js.map +0 -1
  265. package/lib/styles/calendar-month.d.ts.map +0 -1
  266. package/lib/styles/calendar-month.js.map +0 -1
  267. package/lib/styles/calendar-navigation.d.ts.map +0 -1
  268. package/lib/styles/calendar-navigation.js.map +0 -1
  269. package/lib/styles/calendar.d.ts.map +0 -1
  270. package/lib/styles/calendar.js.map +0 -1
  271. package/lib/styles/checkbox.d.ts.map +0 -1
  272. package/lib/styles/checkbox.js.map +0 -1
  273. package/lib/styles/copy.d.ts.map +0 -1
  274. package/lib/styles/copy.js.map +0 -1
  275. package/lib/styles/custom-scrollbar.d.ts.map +0 -1
  276. package/lib/styles/custom-scrollbar.js.map +0 -1
  277. package/lib/styles/date-picker.d.ts.map +0 -1
  278. package/lib/styles/date-picker.js.map +0 -1
  279. package/lib/styles/file-upload.d.ts.map +0 -1
  280. package/lib/styles/file-upload.js.map +0 -1
  281. package/lib/styles/icon.d.ts.map +0 -1
  282. package/lib/styles/icon.js.map +0 -1
  283. package/lib/styles/input.d.ts.map +0 -1
  284. package/lib/styles/input.js.map +0 -1
  285. package/lib/styles/modal.d.ts.map +0 -1
  286. package/lib/styles/modal.js.map +0 -1
  287. package/lib/styles/multiselect.d.ts +0 -8
  288. package/lib/styles/multiselect.d.ts.map +0 -1
  289. package/lib/styles/multiselect.js +0 -849
  290. package/lib/styles/multiselect.js.map +0 -1
  291. package/lib/styles/option.d.ts.map +0 -1
  292. package/lib/styles/option.js.map +0 -1
  293. package/lib/styles/popup.d.ts.map +0 -1
  294. package/lib/styles/popup.js.map +0 -1
  295. package/lib/styles/radio.d.ts.map +0 -1
  296. package/lib/styles/radio.js.map +0 -1
  297. package/lib/styles/resize-observer.d.ts.map +0 -1
  298. package/lib/styles/resize-observer.js.map +0 -1
  299. package/lib/styles/scroll-container.d.ts.map +0 -1
  300. package/lib/styles/scroll-container.js.map +0 -1
  301. package/lib/styles/select-base.d.ts.map +0 -1
  302. package/lib/styles/select-base.js.map +0 -1
  303. package/lib/styles/select.d.ts.map +0 -1
  304. package/lib/styles/select.js.map +0 -1
  305. package/lib/styles/step.d.ts.map +0 -1
  306. package/lib/styles/step.js.map +0 -1
  307. package/lib/styles/switch.d.ts.map +0 -1
  308. package/lib/styles/switch.js.map +0 -1
  309. package/lib/styles/tab.d.ts.map +0 -1
  310. package/lib/styles/tab.js.map +0 -1
  311. package/lib/styles/tabs.d.ts.map +0 -1
  312. package/lib/styles/tabs.js.map +0 -1
  313. package/lib/styles/tag.d.ts.map +0 -1
  314. package/lib/styles/tag.js.map +0 -1
  315. package/lib/styles/textarea.d.ts.map +0 -1
  316. package/lib/styles/textarea.js.map +0 -1
  317. package/lib/styles/tooltip.d.ts.map +0 -1
  318. package/lib/styles/tooltip.js.map +0 -1
  319. package/lib/styles/wizard.d.ts.map +0 -1
  320. package/lib/styles/wizard.js.map +0 -1
  321. package/lib/types/common.d.ts.map +0 -1
  322. package/lib/types/common.js.map +0 -1
  323. package/lib/utils/calendar-utils.d.ts.map +0 -1
  324. package/lib/utils/calendar-utils.js.map +0 -1
  325. package/lib/utils/custom-scrollbar.d.ts.map +0 -1
  326. package/lib/utils/custom-scrollbar.js.map +0 -1
  327. package/lib/utils/flavor-sheet.d.ts.map +0 -1
  328. package/lib/utils/flavor-sheet.js.map +0 -1
  329. package/lib/utils/icon-registry.d.ts.map +0 -1
  330. package/lib/utils/icon-registry.js.map +0 -1
  331. package/lib/utils/icons.d.ts.map +0 -1
  332. package/lib/utils/icons.js.map +0 -1
  333. package/lib/utils/loader-registry.d.ts.map +0 -1
  334. package/lib/utils/loader-registry.js.map +0 -1
  335. package/lib/utils/locale.d.ts.map +0 -1
  336. package/lib/utils/locale.js.map +0 -1
  337. package/lib/utils/mobile.d.ts.map +0 -1
  338. package/lib/utils/mobile.js.map +0 -1
  339. package/lib/utils/number-format.d.ts.map +0 -1
  340. package/lib/utils/number-format.js.map +0 -1
  341. package/lib/utils/parse-boolean.d.ts +0 -39
  342. package/lib/utils/parse-boolean.d.ts.map +0 -1
  343. package/lib/utils/parse-boolean.js +0 -58
  344. package/lib/utils/parse-boolean.js.map +0 -1
  345. package/lib/utils/positioning.d.ts.map +0 -1
  346. package/lib/utils/positioning.js.map +0 -1
  347. package/lib/utils/property-capture.d.ts.map +0 -1
  348. package/lib/utils/property-capture.js.map +0 -1
  349. package/lib/utils/property-manager.d.ts.map +0 -1
  350. package/lib/utils/property-manager.js.map +0 -1
  351. package/lib/utils/resize-observer.d.ts.map +0 -1
  352. package/lib/utils/resize-observer.js.map +0 -1
  353. package/lib/utils/scroll-lock.d.ts.map +0 -1
  354. package/lib/utils/scroll-lock.js.map +0 -1
  355. package/lib/utils/styles.d.ts.map +0 -1
  356. package/lib/utils/styles.js.map +0 -1
  357. package/lib/version.d.ts.map +0 -1
  358. package/lib/version.js.map +0 -1
@@ -1,19 +1,4 @@
1
- /**
2
- * TyInput Web Component
3
- * PORTED FROM: clj/ty/components/input.cljs
4
- * Phase D: Complete with debounce feature for input and change events
5
- *
6
- * Enhanced input component with:
7
- * - Label, error messages, semantic styling
8
- * - Icon slots (start/end)
9
- * - Numeric formatting with shadow values
10
- * - Currency, percent, compact notation
11
- * - Format-on-blur / raw-on-focus behavior
12
- * - Debounce (0-5000ms) for input/change events
13
- * - Immediate event firing on blur (cancels pending debounce)
14
- *
15
- * NOTE: Checkbox functionality is in separate ty-checkbox component
16
- */
1
+ import { DEFAULT_SIZE } from '../types/common.js';
17
2
  import { TyComponent } from '../base/ty-component.js';
18
3
  import { ensureStyles } from '../utils/styles.js';
19
4
  import { syncCustomFlavorSheet } from '../utils/flavor-sheet.js';
@@ -21,780 +6,604 @@ import { inputStyles, inputCustomFlavorCss } from '../styles/input.js';
21
6
  import { formatNumber, parseNumericValue, shouldFormat as shouldFormatType } from '../utils/number-format.js';
22
7
  import { getEffectiveLocale, observeLocaleChanges } from '../utils/locale.js';
23
8
  import { REQUIRED_ICON_SVG } from '../utils/icons.js';
24
- /** Password reveal toggle icons (Lucide eye / eye-off) */
25
9
  const EYE_ICON_SVG = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0"/><circle cx="12" cy="12" r="3"/></svg>`;
26
10
  const EYE_OFF_ICON_SVG = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49"/><path d="M14.084 14.158a3 3 0 0 1-4.242-4.242"/><path d="M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143"/><path d="m2 2 20 20"/></svg>`;
27
11
  export class TyInput extends TyComponent {
28
- constructor() {
29
- super(); // TyComponent handles attachInternals() and attachShadow()
30
- // ============================================================================
31
- // INTERNAL STATE - Not managed by PropertyManager
32
- // NOTE: _internals provided by TyComponent base class
33
- // ============================================================================
34
- // Shadow value - parsed numeric value for formatting
35
- this._shadowValue = null;
36
- // Focus state - toggles format/raw display
37
- this._isFocused = false;
38
- // Listener setup tracking
39
- this._listenersSetup = false;
40
- // True when WE auto-set flavor to 'danger' because `error` was present, so we
41
- // know to restore 'neutral' when the error clears (and not clobber a flavor
42
- // the consumer set themselves).
43
- this._errorAutoDanger = false;
44
- this._customFlavorSheet = null;
45
- // Store references to handlers for cleanup
46
- this._inputHandler = null;
47
- this._changeHandler = null;
48
- this._focusHandler = null;
49
- this._blurHandler = null;
50
- // Debounce timers
51
- this._inputDebounceTimer = null;
52
- this._changeDebounceTimer = null;
53
- // Apply styles to shadow root
54
- const shadow = this.shadowRoot;
55
- ensureStyles(shadow, { css: inputStyles, id: 'ty-input' });
56
- }
57
- /**
58
- * Called when component connects to DOM
59
- * TyComponent already handled pre-connection property capture
60
- */
61
- onConnect() {
62
- // Initialize shadow value from current value property
63
- this.initializeShadowValue();
64
- // NOTE: Event listeners are set up in render() after input element is created
65
- // setupEventListeners() will be called after initial render
66
- // Setup locale observer to watch for ancestor lang changes
67
- this._localeObserver = observeLocaleChanges(this, () => {
68
- this.render();
69
- });
70
- this._syncCustomFlavor();
71
- }
72
- /** Custom (non-built-in) flavors — see utils/flavor-sheet.ts. */
73
- _syncCustomFlavor() {
74
- this._customFlavorSheet = syncCustomFlavorSheet(this.shadowRoot, this._customFlavorSheet, this.flavor, ({ base }) => inputCustomFlavorCss(base));
75
- }
76
- /**
77
- * Called when component disconnects from DOM
78
- */
79
- onDisconnect() {
80
- // Clean up event listeners
81
- this.removeEventListeners();
82
- // Cleanup locale observer
83
- if (this._localeObserver) {
84
- this._localeObserver();
85
- this._localeObserver = undefined;
86
- }
87
- // Clear any pending debounce timers
88
- if (this._inputDebounceTimer !== null) {
89
- clearTimeout(this._inputDebounceTimer);
90
- this._inputDebounceTimer = null;
91
- }
92
- if (this._changeDebounceTimer !== null) {
93
- clearTimeout(this._changeDebounceTimer);
94
- this._changeDebounceTimer = null;
95
- }
96
- }
97
- // ============================================================================
98
- // TYCOMPONENT LIFECYCLE HOOKS
99
- // ============================================================================
100
- /**
101
- * Handle property changes - called BEFORE render
102
- * This replaces the old attributeChangedCallback logic
103
- */
104
- onPropertiesChanged(changes) {
105
- for (const { name, newValue } of changes) {
106
- switch (name) {
107
- case 'flavor':
108
- this._syncCustomFlavor();
109
- break;
110
- case 'value':
111
- // Parse to shadow value for numeric types
112
- this._shadowValue = this.parseShadowValue(newValue || '');
113
- break;
114
- case 'type':
115
- // Re-parse shadow value when type changes
116
- const currentValue = this.getProperty('value') || '';
117
- this._shadowValue = this.parseShadowValue(currentValue);
118
- break;
119
- case 'error':
120
- if (newValue) {
121
- // Auto-set flavor to danger when an error appears on a neutral field
122
- if (this.getProperty('flavor') === 'neutral') {
123
- this.setProperty('flavor', 'danger');
124
- this._errorAutoDanger = true;
125
- }
126
- }
127
- else if (this._errorAutoDanger) {
128
- // Error cleared and WE were the ones who set danger → restore neutral
129
- // (live validation passing should drop the danger styling).
130
- if (this.getProperty('flavor') === 'danger') {
131
- this.setProperty('flavor', 'neutral');
132
- }
133
- this._errorAutoDanger = false;
134
- }
135
- break;
136
- }
137
- }
138
- }
139
- /**
140
- * Hook: Called when form is reset
141
- * Clear shadow value and focus state
142
- */
143
- onFormReset() {
144
- // Reset shadow value to default (empty)
145
- this._shadowValue = null;
146
- // Reset focus state
147
- this._isFocused = false;
148
- // Clear any pending debounce timers
149
- if (this._inputDebounceTimer !== null) {
150
- clearTimeout(this._inputDebounceTimer);
151
- this._inputDebounceTimer = null;
152
- }
153
- if (this._changeDebounceTimer !== null) {
154
- clearTimeout(this._changeDebounceTimer);
155
- this._changeDebounceTimer = null;
156
- }
157
- this.render();
158
- }
159
- /**
160
- * Override form value to return shadow value
161
- * This ensures numeric types submit their parsed values
162
- */
163
- getFormValue() {
164
- const formValue = this._shadowValue !== null ? String(this._shadowValue) : null;
165
- return formValue;
166
- }
167
- /**
168
- * Initialize shadow value from the initial value attribute
169
- */
170
- initializeShadowValue() {
171
- const currentValue = this.getProperty('value');
172
- if (currentValue) {
173
- this._shadowValue = this.parseShadowValue(currentValue);
174
- // Update form value
175
- this._internals.setFormValue(this._shadowValue !== null ? String(this._shadowValue) : null);
176
- }
177
- }
178
- /**
179
- * Parse a string value to the appropriate shadow value type
180
- */
181
- parseShadowValue(value) {
182
- // Defensive check: ensure value is actually a string before calling .trim()
183
- if (!value || typeof value !== 'string' || value.trim() === '')
184
- return null;
185
- // For numeric types, parse to number
186
- if (shouldFormatType(this.type)) {
187
- const parsed = parseNumericValue(value);
188
- if (parsed !== null && this.precision === 0) {
189
- return Math.round(parsed);
190
- }
191
- return parsed;
192
- }
193
- // For other types, keep as string
194
- return value;
195
- }
196
- /**
197
- * Check if current input should format numbers
198
- */
199
- shouldFormat() {
200
- return shouldFormatType(this.type) &&
201
- !this._isFocused &&
202
- this._shadowValue !== null &&
203
- typeof this._shadowValue === 'number';
204
- }
205
- /**
206
- * Get the format configuration for current input
207
- */
208
- getFormatConfig() {
209
- return {
210
- type: this.type,
211
- locale: this.locale, // Use getter which calls getEffectiveLocale correctly
212
- currency: this.currency,
213
- precision: this.precision
214
- };
215
- }
216
- /**
217
- * Get the display value (formatted or raw)
218
- */
219
- getDisplayValue() {
220
- if (this.shouldFormat()) {
221
- let valueToFormat = this._shadowValue;
222
- // For percent: divide by 100 (user enters 15, displays as 15%)
223
- // This matches ClojureScript behavior
224
- if (this.type === 'percent') {
225
- valueToFormat = valueToFormat / 100;
226
- }
227
- return formatNumber(valueToFormat, this.getFormatConfig());
228
- }
229
- return this._shadowValue !== null ? String(this._shadowValue) : '';
230
- }
231
- // ============================================================================
232
- // PROPERTY ACCESSORS - Simplified with TyComponent
233
- // NOTE: validateFlavor removed - now handled by property configuration
234
- // ============================================================================
235
- // Core properties - simple getProperty/setProperty pattern
236
- get type() { return this.getProperty('type'); }
237
- set type(v) { this.setProperty('type', v); }
238
- // Value - special getter returns shadow value
239
- get value() {
240
- return this._shadowValue !== null ? String(this._shadowValue) : '';
241
- }
242
- set value(v) { this.setProperty('value', v); }
243
- get name() { return this.getProperty('name'); }
244
- set name(v) { this.setProperty('name', v); }
245
- get placeholder() { return this.getProperty('placeholder'); }
246
- set placeholder(v) { this.setProperty('placeholder', v); }
247
- get label() { return this.getProperty('label'); }
248
- set label(v) { this.setProperty('label', v); }
249
- get disabled() { return this.getProperty('disabled'); }
250
- set disabled(v) { this.setProperty('disabled', v); }
251
- get required() { return this.getProperty('required'); }
252
- set required(v) { this.setProperty('required', v); }
253
- get error() { return this.getProperty('error'); }
254
- set error(v) { this.setProperty('error', v); }
255
- get size() { return this.getProperty('size'); }
256
- set size(v) { this.setProperty('size', v); }
257
- get flavor() { return this.getProperty('flavor'); }
258
- set flavor(v) { this.setProperty('flavor', v); }
259
- // Numeric formatting properties
260
- get currency() { return this.getProperty('currency'); }
261
- set currency(v) { this.setProperty('currency', v); }
262
- // Locale - special getter uses getEffectiveLocale()
263
- get locale() {
264
- return getEffectiveLocale(this, this.getProperty('locale'));
265
- }
266
- set locale(v) { this.setProperty('locale', v); }
267
- get precision() { return this.getProperty('precision'); }
268
- set precision(v) { this.setProperty('precision', v); }
269
- // Debounce property
270
- get debounce() { return this.getProperty('debounce'); }
271
- set debounce(v) { this.setProperty('debounce', v); }
272
- // Form property
273
- get form() {
274
- return this._internals.form;
275
- }
276
- /**
277
- * Build CSS class list for input wrapper
278
- */
279
- buildClassList() {
280
- const classes = [this.size];
281
- if (this.disabled)
282
- classes.push('disabled');
283
- if (this.required)
284
- classes.push('required');
285
- if (this.error)
286
- classes.push('error');
287
- return classes.join(' ');
288
- }
289
- /**
290
- * Dispatch input event (helper method for debouncing)
291
- */
292
- dispatchInputEvent(rawValue, originalEvent) {
293
- this.dispatchEvent(new CustomEvent('input', {
294
- detail: {
295
- value: this._shadowValue,
296
- formattedValue: this.shouldFormat() ? this.getDisplayValue() : null,
297
- rawValue: rawValue,
298
- originalEvent: originalEvent
299
- },
300
- bubbles: true,
301
- composed: true
302
- }));
303
- }
304
- /**
305
- * Dispatch change event (helper method for debouncing)
306
- */
307
- dispatchChangeEvent(rawValue, originalEvent) {
308
- this.dispatchEvent(new CustomEvent('change', {
309
- detail: {
310
- value: this._shadowValue,
311
- formattedValue: this.shouldFormat() ? this.getDisplayValue() : null,
312
- rawValue: rawValue,
313
- originalEvent: originalEvent
314
- },
315
- bubbles: true,
316
- composed: true
317
- }));
318
- }
319
- /**
320
- * Remove event listeners for cleanup
321
- */
322
- removeEventListeners() {
323
- if (!this._listenersSetup)
324
- return;
325
- const shadow = this.shadowRoot;
326
- if (!shadow)
327
- return;
328
- const inputEl = shadow.querySelector('input');
329
- if (!inputEl)
330
- return;
331
- // Remove all event listeners using stored handler references
332
- if (this._inputHandler) {
333
- inputEl.removeEventListener('input', this._inputHandler);
334
- this._inputHandler = null;
335
- }
336
- if (this._changeHandler) {
337
- inputEl.removeEventListener('change', this._changeHandler);
338
- this._changeHandler = null;
339
- }
340
- if (this._focusHandler) {
341
- inputEl.removeEventListener('focus', this._focusHandler);
342
- this._focusHandler = null;
343
- }
344
- if (this._blurHandler) {
345
- inputEl.removeEventListener('blur', this._blurHandler);
346
- this._blurHandler = null;
347
- }
348
- this._listenersSetup = false;
349
- }
350
- /**
351
- * Setup event listeners for input element
352
- * IMPORTANT: Only called ONCE, not on every render (like ClojureScript)
353
- */
354
- setupEventListeners() {
355
- if (this._listenersSetup) {
356
- return; // Already setup
357
- }
358
- const shadow = this.shadowRoot;
359
- const inputEl = shadow.querySelector('input');
360
- const wrapperEl = shadow.querySelector('.input-wrapper');
361
- if (!inputEl || !wrapperEl) {
362
- console.warn('[TyInput.setupEventListeners] ⚠️ Input element not found!');
363
- return;
364
- }
365
- // Create and store handler references for cleanup
366
- // Input event - update shadow value and form (with debounce support)
367
- this._inputHandler = (e) => {
368
- // Stop native event from propagating - only our custom event should bubble
369
- e.stopPropagation();
370
- e.stopImmediatePropagation();
371
- const target = e.target;
372
- const rawValue = target.value;
373
- // Parse to shadow value
374
- this._shadowValue = this.parseShadowValue(rawValue);
375
- // Update form value with shadow value
376
- this._internals.setFormValue(this._shadowValue !== null ? String(this._shadowValue) : null);
377
- this.updateValidity();
378
- // Clear existing timer
379
- if (this._inputDebounceTimer !== null) {
380
- clearTimeout(this._inputDebounceTimer);
381
- }
382
- // If debounce is set, debounce the event
383
- if (this.debounce > 0) {
384
- this._inputDebounceTimer = window.setTimeout(() => {
385
- // Use current value, not the captured rawValue
386
- this.dispatchInputEvent(inputEl.value, e);
387
- this._inputDebounceTimer = null;
388
- }, this.debounce);
389
- }
390
- else {
391
- // Fire immediately if no debounce
392
- this.dispatchInputEvent(rawValue, e);
393
- }
394
- };
395
- // Change event (with debounce support)
396
- this._changeHandler = (e) => {
397
- // Stop native event from propagating - only our custom event should bubble
398
- e.stopPropagation();
399
- e.stopImmediatePropagation();
400
- const target = e.target;
401
- const rawValue = target.value;
402
- // Parse to shadow value
403
- this._shadowValue = this.parseShadowValue(rawValue);
404
- // Update form value
405
- this._internals.setFormValue(this._shadowValue !== null ? String(this._shadowValue) : null);
406
- this.updateValidity();
407
- // Clear existing timer
408
- if (this._changeDebounceTimer !== null) {
409
- clearTimeout(this._changeDebounceTimer);
410
- }
411
- // If debounce is set, debounce the event
412
- if (this.debounce > 0) {
413
- this._changeDebounceTimer = window.setTimeout(() => {
414
- // Use current value, not the captured rawValue
415
- this.dispatchChangeEvent(inputEl.value, e);
416
- this._changeDebounceTimer = null;
417
- }, this.debounce);
418
- }
419
- else {
420
- // Fire immediately if no debounce
421
- this.dispatchChangeEvent(rawValue, e);
422
- }
423
- };
424
- // Focus event - show raw value for numeric types
425
- this._focusHandler = (e) => {
426
- this._isFocused = true;
427
- wrapperEl.classList.add('focused');
428
- // For numeric types, show raw shadow value
429
- if (shouldFormatType(this.type) && this._shadowValue !== null) {
430
- inputEl.value = String(this._shadowValue);
431
- }
432
- this.dispatchEvent(new CustomEvent('focus', {
433
- detail: { originalEvent: e },
434
- bubbles: true,
435
- composed: true
436
- }));
437
- };
438
- // Blur event - fire pending debounced events immediately, then show formatted value
439
- this._blurHandler = (e) => {
440
- const target = e.target;
441
- const rawValue = target.value;
442
- // Fire any pending debounced input event immediately on blur
443
- if (this._inputDebounceTimer !== null) {
444
- clearTimeout(this._inputDebounceTimer);
445
- this._inputDebounceTimer = null;
446
- this.dispatchInputEvent(rawValue, e);
447
- }
448
- // Fire any pending debounced change event immediately on blur
449
- if (this._changeDebounceTimer !== null) {
450
- clearTimeout(this._changeDebounceTimer);
451
- this._changeDebounceTimer = null;
452
- this.dispatchChangeEvent(rawValue, e);
453
- }
454
- this._isFocused = false;
455
- wrapperEl.classList.remove('focused');
456
- // For numeric types, show formatted value
457
- if (this.shouldFormat()) {
458
- inputEl.value = this.getDisplayValue();
459
- }
460
- this.dispatchEvent(new CustomEvent('blur', {
461
- detail: { originalEvent: e },
462
- bubbles: true,
463
- composed: true
464
- }));
465
- };
466
- // Add event listeners
467
- inputEl.addEventListener('input', this._inputHandler);
468
- inputEl.addEventListener('change', this._changeHandler);
469
- inputEl.addEventListener('focus', this._focusHandler);
470
- inputEl.addEventListener('blur', this._blurHandler);
471
- this._listenersSetup = true;
472
- }
473
- /**
474
- * Render the input component with wrapper and slots
475
- * Phase C: Uses getDisplayValue() for formatted output
476
- *
477
- */
478
- render() {
479
- const shadow = this.shadowRoot;
480
- const existingInput = shadow.querySelector('input');
481
- const existingWrapper = shadow.querySelector('.input-wrapper');
482
- const existingLabel = shadow.querySelector('.ty-field-label');
483
- const existingError = shadow.querySelector('.error-message');
484
- const classes = this.buildClassList();
485
- // Map input type (all numeric types use 'text' in DOM, others pass through)
486
- const inputType = ['password', 'email', 'tel', 'url'].includes(this.type)
487
- ? this.type
488
- : 'text';
489
- // Set inputmode for mobile keyboard hint
490
- const inputMode = shouldFormatType(this.type) ? 'decimal'
491
- : this.type === 'email' ? 'email'
492
- : this.type === 'tel' ? 'tel'
493
- : this.type === 'url' ? 'url'
494
- : undefined;
495
- // Get display value (formatted or raw based on focus)
496
- const displayValue = this.getDisplayValue();
497
- // If input exists, just update properties (like ClojureScript does)
498
- if (existingInput && existingWrapper) {
499
- existingInput.type = inputType;
500
- existingInput.value = displayValue;
501
- existingInput.placeholder = this.placeholder;
502
- existingInput.name = this.name;
503
- if (inputMode)
504
- existingInput.inputMode = inputMode;
505
- else
506
- existingInput.removeAttribute('inputmode');
507
- // Update wrapper classes
508
- existingWrapper.className = `input-wrapper ${classes}`;
509
- // Set disabled property AND manage attribute
510
- existingInput.disabled = this.disabled;
511
- if (this.disabled) {
512
- existingInput.setAttribute('disabled', '');
513
- }
514
- else {
515
- existingInput.removeAttribute('disabled');
516
- }
517
- // Set required property AND manage attribute
518
- existingInput.required = this.required;
519
- if (this.required) {
520
- existingInput.setAttribute('required', '');
521
- }
522
- else {
523
- existingInput.removeAttribute('required');
524
- }
525
- // a11y: the shadow <label> isn't programmatically linked to this input,
526
- // so give the control an accessible name directly.
527
- if (this.label)
528
- existingInput.setAttribute('aria-label', this.label);
529
- else
530
- existingInput.removeAttribute('aria-label');
531
- // ===== BUG FIX: Dynamic label creation =====
532
- // Update or CREATE label if needed
533
- if (this.label) {
534
- if (existingLabel) {
535
- // Label exists, just update it
536
- existingLabel.innerHTML = `${this.label}${this.required ? `<span class="required-icon">${REQUIRED_ICON_SVG}</span>` : ''}`;
537
- existingLabel.style.display = 'flex';
538
- }
539
- else {
540
- // Label doesn't exist but we need one - CREATE IT!
541
- const labelEl = document.createElement('label');
542
- labelEl.className = 'ty-field-label';
543
- labelEl.innerHTML = `${this.label}${this.required ? `<span class="required-icon">${REQUIRED_ICON_SVG}</span>` : ''}`;
544
- // Insert label BEFORE the input-wrapper
545
- const container = shadow.querySelector('.input-container');
546
- if (container) {
547
- container.insertBefore(labelEl, existingWrapper);
548
- }
549
- }
550
- }
551
- else if (existingLabel) {
552
- // No label text, hide existing label
553
- ;
554
- existingLabel.style.display = 'none';
555
- }
556
- // Update error message
557
- if (this.error) {
558
- if (existingError) {
559
- existingError.textContent = this.error;
560
- }
561
- else {
562
- const errorEl = document.createElement('div');
563
- errorEl.className = 'error-message';
564
- errorEl.textContent = this.error;
565
- shadow.querySelector('.input-container')?.appendChild(errorEl);
566
- }
567
- }
568
- else if (existingError) {
569
- existingError.remove();
570
- }
571
- }
572
- else {
573
- // Create initial structure with wrapper and slots
574
- const labelHtml = this.label ? `
575
- <label class="ty-field-label">
576
- ${this.label}
577
- ${this.required ? `<span class="required-icon">${REQUIRED_ICON_SVG}</span>` : ''}
578
- </label>
579
- ` : '';
580
- const errorHtml = this.error ? `
581
- <div class="error-message">${this.error}</div>
582
- ` : '';
583
- shadow.innerHTML = `
584
- <div class="input-container">
585
- ${labelHtml}
586
- <div class="input-wrapper ${classes}">
587
- <slot name="start"></slot>
588
- <input
589
- type="${inputType}"
590
- value="${displayValue}"
591
- placeholder="${this.placeholder}"
592
- name="${this.name}"
593
- ${inputMode ? `inputmode="${inputMode}"` : ''}
594
- />
595
- <slot name="end"></slot>
596
- ${inputType === 'password' ? `
597
- <button class="password-toggle" type="button"
598
- aria-label="Show password" aria-pressed="false">
599
- ${EYE_ICON_SVG}
600
- </button>` : ''}
601
- </div>
602
- ${errorHtml}
603
- </div>
604
- `;
605
- // Set boolean properties after creating element
606
- const inputEl = shadow.querySelector('input');
607
- inputEl.disabled = this.disabled;
608
- inputEl.required = this.required;
609
- if (this.label)
610
- inputEl.setAttribute('aria-label', this.label); // a11y: name the control
611
- this.setupPasswordToggle();
612
- // Setup event listeners ONCE
613
- this.setupEventListeners();
614
- }
615
- // Reflect constraint validation (required, etc.) to the owning <form>.
616
- this.updateValidity();
617
- }
618
- /**
619
- * Wire the password reveal toggle (rendered only for type="password").
620
- * Toggles the NATIVE input's type between password/text — the component's
621
- * `type` property stays "password" (form value and API unaffected).
622
- */
623
- setupPasswordToggle() {
624
- const shadow = this.shadowRoot;
625
- const toggle = shadow.querySelector('.password-toggle');
626
- const inputEl = shadow.querySelector('input');
627
- if (!toggle || !inputEl)
628
- return;
629
- // Keep focus in the input while clicking the toggle
630
- toggle.addEventListener('mousedown', (e) => e.preventDefault());
631
- toggle.addEventListener('click', () => {
632
- const reveal = inputEl.type === 'password';
633
- inputEl.type = reveal ? 'text' : 'password';
634
- toggle.innerHTML = reveal ? EYE_OFF_ICON_SVG : EYE_ICON_SVG;
635
- toggle.setAttribute('aria-label', reveal ? 'Hide password' : 'Show password');
636
- toggle.setAttribute('aria-pressed', String(reveal));
637
- });
638
- }
639
- /**
640
- * Reflect constraint validation to the owning <form> via ElementInternals.
641
- * Without this `required` is only cosmetic — the inner shadow <input> can't
642
- * block the outer form. Empty + required => valueMissing; otherwise valid.
643
- * Disabled fields are barred from constraint validation.
644
- */
645
- updateValidity() {
646
- const inputEl = this.shadowRoot?.querySelector('input');
647
- if (this.disabled) {
648
- this._internals.setValidity({});
649
- return;
650
- }
651
- const current = inputEl ? inputEl.value : String(this.getProperty('value') ?? '');
652
- if (this.required && current.trim() === '') {
653
- this._internals.setValidity({ valueMissing: true }, 'Please fill out this field', inputEl ?? undefined);
654
- }
655
- else {
656
- this._internals.setValidity({});
657
- }
658
- }
12
+ constructor() {
13
+ super();
14
+ this._shadowValue = null;
15
+ this._isFocused = false;
16
+ this._listenersSetup = false;
17
+ this._errorAutoDanger = false;
18
+ this._customFlavorSheet = null;
19
+ this._inputHandler = null;
20
+ this._changeHandler = null;
21
+ this._focusHandler = null;
22
+ this._blurHandler = null;
23
+ this._inputDebounceTimer = null;
24
+ this._changeDebounceTimer = null;
25
+ const shadow = this.shadowRoot;
26
+ ensureStyles(shadow, { css: inputStyles, id: 'ty-input' });
27
+ }
28
+ onConnect() {
29
+ this.initializeShadowValue();
30
+ this._localeObserver = observeLocaleChanges(this, () => {
31
+ this.render();
32
+ });
33
+ this._syncCustomFlavor();
34
+ }
35
+ _syncCustomFlavor() {
36
+ this._customFlavorSheet = syncCustomFlavorSheet(this.shadowRoot, this._customFlavorSheet, this.flavor, ({ base }) => inputCustomFlavorCss(base));
37
+ }
38
+ onDisconnect() {
39
+ this.removeEventListeners();
40
+ if (this._localeObserver) {
41
+ this._localeObserver();
42
+ this._localeObserver = undefined;
43
+ }
44
+ if (this._inputDebounceTimer !== null) {
45
+ clearTimeout(this._inputDebounceTimer);
46
+ this._inputDebounceTimer = null;
47
+ }
48
+ if (this._changeDebounceTimer !== null) {
49
+ clearTimeout(this._changeDebounceTimer);
50
+ this._changeDebounceTimer = null;
51
+ }
52
+ }
53
+ onPropertiesChanged(changes) {
54
+ for (const { name, newValue } of changes) {
55
+ switch (name) {
56
+ case 'flavor':
57
+ this._syncCustomFlavor();
58
+ break;
59
+ case 'value':
60
+ this._shadowValue = this.parseShadowValue(newValue || '');
61
+ break;
62
+ case 'type':
63
+ const currentValue = this.getProperty('value') || '';
64
+ this._shadowValue = this.parseShadowValue(currentValue);
65
+ break;
66
+ case 'error':
67
+ if (newValue) {
68
+ if (this.getProperty('flavor') === 'neutral') {
69
+ this.setProperty('flavor', 'danger');
70
+ this._errorAutoDanger = true;
71
+ }
72
+ }
73
+ else if (this._errorAutoDanger) {
74
+ if (this.getProperty('flavor') === 'danger') {
75
+ this.setProperty('flavor', 'neutral');
76
+ }
77
+ this._errorAutoDanger = false;
78
+ }
79
+ break;
80
+ }
81
+ }
82
+ }
83
+ onFormReset() {
84
+ this._shadowValue = null;
85
+ this.initializeShadowValue();
86
+ this._isFocused = false;
87
+ if (this._inputDebounceTimer !== null) {
88
+ clearTimeout(this._inputDebounceTimer);
89
+ this._inputDebounceTimer = null;
90
+ }
91
+ if (this._changeDebounceTimer !== null) {
92
+ clearTimeout(this._changeDebounceTimer);
93
+ this._changeDebounceTimer = null;
94
+ }
95
+ this.render();
96
+ }
97
+ getFormValue() {
98
+ const formValue = this._shadowValue !== null ? String(this._shadowValue) : null;
99
+ return formValue;
100
+ }
101
+ initializeShadowValue() {
102
+ const currentValue = this.getProperty('value');
103
+ if (currentValue) {
104
+ this._shadowValue = this.parseShadowValue(currentValue);
105
+ this._internals.setFormValue(this._shadowValue !== null ? String(this._shadowValue) : null);
106
+ }
107
+ }
108
+ parseShadowValue(value) {
109
+ if (!value || typeof value !== 'string' || value.trim() === '')
110
+ return null;
111
+ if (shouldFormatType(this.type)) {
112
+ const parsed = parseNumericValue(value);
113
+ if (parsed !== null && this.precision === 0) {
114
+ return Math.round(parsed);
115
+ }
116
+ return parsed;
117
+ }
118
+ return value;
119
+ }
120
+ shouldFormat() {
121
+ return shouldFormatType(this.type) &&
122
+ !this._isFocused &&
123
+ this._shadowValue !== null &&
124
+ typeof this._shadowValue === 'number';
125
+ }
126
+ getFormatConfig() {
127
+ return {
128
+ type: this.type,
129
+ locale: this.locale,
130
+ currency: this.currency,
131
+ precision: this.precision
132
+ };
133
+ }
134
+ getDisplayValue() {
135
+ if (this.shouldFormat()) {
136
+ let valueToFormat = this._shadowValue;
137
+ if (this.type === 'percent') {
138
+ valueToFormat = valueToFormat / 100;
139
+ }
140
+ return formatNumber(valueToFormat, this.getFormatConfig());
141
+ }
142
+ return this._shadowValue !== null ? String(this._shadowValue) : '';
143
+ }
144
+ get type() { return this.getProperty('type'); }
145
+ set type(v) { this.setProperty('type', v); }
146
+ get value() {
147
+ return this._shadowValue !== null ? String(this._shadowValue) : '';
148
+ }
149
+ set value(v) { this.setProperty('value', v); }
150
+ get name() { return this.getProperty('name'); }
151
+ set name(v) { this.setProperty('name', v); }
152
+ get placeholder() { return this.getProperty('placeholder'); }
153
+ set placeholder(v) { this.setProperty('placeholder', v); }
154
+ get label() { return this.getProperty('label'); }
155
+ set label(v) { this.setProperty('label', v); }
156
+ get disabled() { return this.getProperty('disabled'); }
157
+ set disabled(v) { this.setProperty('disabled', v); }
158
+ get required() { return this.getProperty('required'); }
159
+ set required(v) { this.setProperty('required', v); }
160
+ get error() { return this.getProperty('error'); }
161
+ set error(v) { this.setProperty('error', v); }
162
+ get size() { return this.getProperty('size'); }
163
+ set size(v) { this.setProperty('size', v); }
164
+ get flavor() { return this.getProperty('flavor'); }
165
+ set flavor(v) { this.setProperty('flavor', v); }
166
+ get currency() { return this.getProperty('currency'); }
167
+ set currency(v) { this.setProperty('currency', v); }
168
+ get locale() {
169
+ return getEffectiveLocale(this, this.getProperty('locale'));
170
+ }
171
+ set locale(v) { this.setProperty('locale', v); }
172
+ get precision() { return this.getProperty('precision'); }
173
+ set precision(v) { this.setProperty('precision', v); }
174
+ get debounce() { return this.getProperty('debounce'); }
175
+ set debounce(v) { this.setProperty('debounce', v); }
176
+ get form() {
177
+ return this._internals.form;
178
+ }
179
+ buildClassList() {
180
+ const classes = [];
181
+ if (this.disabled)
182
+ classes.push('disabled');
183
+ if (this.required)
184
+ classes.push('required');
185
+ if (this.error)
186
+ classes.push('error');
187
+ return classes.join(' ');
188
+ }
189
+ dispatchInputEvent(rawValue, originalEvent) {
190
+ this.dispatchEvent(new CustomEvent('input', {
191
+ detail: {
192
+ value: this._shadowValue,
193
+ formattedValue: this.shouldFormat() ? this.getDisplayValue() : null,
194
+ rawValue: rawValue,
195
+ originalEvent: originalEvent
196
+ },
197
+ bubbles: true,
198
+ composed: true
199
+ }));
200
+ }
201
+ dispatchChangeEvent(rawValue, originalEvent) {
202
+ this.dispatchEvent(new CustomEvent('change', {
203
+ detail: {
204
+ value: this._shadowValue,
205
+ formattedValue: this.shouldFormat() ? this.getDisplayValue() : null,
206
+ rawValue: rawValue,
207
+ originalEvent: originalEvent
208
+ },
209
+ bubbles: true,
210
+ composed: true
211
+ }));
212
+ }
213
+ removeEventListeners() {
214
+ if (!this._listenersSetup)
215
+ return;
216
+ const shadow = this.shadowRoot;
217
+ if (!shadow)
218
+ return;
219
+ const inputEl = shadow.querySelector('input');
220
+ if (!inputEl)
221
+ return;
222
+ if (this._inputHandler) {
223
+ inputEl.removeEventListener('input', this._inputHandler);
224
+ this._inputHandler = null;
225
+ }
226
+ if (this._changeHandler) {
227
+ inputEl.removeEventListener('change', this._changeHandler);
228
+ this._changeHandler = null;
229
+ }
230
+ if (this._focusHandler) {
231
+ inputEl.removeEventListener('focus', this._focusHandler);
232
+ this._focusHandler = null;
233
+ }
234
+ if (this._blurHandler) {
235
+ inputEl.removeEventListener('blur', this._blurHandler);
236
+ this._blurHandler = null;
237
+ }
238
+ this._listenersSetup = false;
239
+ }
240
+ setupEventListeners() {
241
+ if (this._listenersSetup) {
242
+ return;
243
+ }
244
+ const shadow = this.shadowRoot;
245
+ const inputEl = shadow.querySelector('input');
246
+ const wrapperEl = shadow.querySelector('.input-wrapper');
247
+ if (!inputEl || !wrapperEl) {
248
+ console.warn('[TyInput.setupEventListeners] ⚠️ Input element not found!');
249
+ return;
250
+ }
251
+ this._inputHandler = (e) => {
252
+ e.stopPropagation();
253
+ e.stopImmediatePropagation();
254
+ const target = e.target;
255
+ const rawValue = target.value;
256
+ this._shadowValue = this.parseShadowValue(rawValue);
257
+ this._internals.setFormValue(this._shadowValue !== null ? String(this._shadowValue) : null);
258
+ this.updateValidity();
259
+ if (this._inputDebounceTimer !== null) {
260
+ clearTimeout(this._inputDebounceTimer);
261
+ }
262
+ if (this.debounce > 0) {
263
+ this._inputDebounceTimer = window.setTimeout(() => {
264
+ this.dispatchInputEvent(inputEl.value, e);
265
+ this._inputDebounceTimer = null;
266
+ }, this.debounce);
267
+ }
268
+ else {
269
+ this.dispatchInputEvent(rawValue, e);
270
+ }
271
+ };
272
+ this._changeHandler = (e) => {
273
+ e.stopPropagation();
274
+ e.stopImmediatePropagation();
275
+ const target = e.target;
276
+ const rawValue = target.value;
277
+ this._shadowValue = this.parseShadowValue(rawValue);
278
+ this._internals.setFormValue(this._shadowValue !== null ? String(this._shadowValue) : null);
279
+ this.updateValidity();
280
+ if (this._changeDebounceTimer !== null) {
281
+ clearTimeout(this._changeDebounceTimer);
282
+ }
283
+ if (this.debounce > 0) {
284
+ this._changeDebounceTimer = window.setTimeout(() => {
285
+ this.dispatchChangeEvent(inputEl.value, e);
286
+ this._changeDebounceTimer = null;
287
+ }, this.debounce);
288
+ }
289
+ else {
290
+ this.dispatchChangeEvent(rawValue, e);
291
+ }
292
+ };
293
+ this._focusHandler = (e) => {
294
+ this._isFocused = true;
295
+ wrapperEl.classList.add('focused');
296
+ if (shouldFormatType(this.type) && this._shadowValue !== null) {
297
+ inputEl.value = String(this._shadowValue);
298
+ }
299
+ this.dispatchEvent(new CustomEvent('focus', {
300
+ detail: { originalEvent: e },
301
+ bubbles: true,
302
+ composed: true
303
+ }));
304
+ };
305
+ this._blurHandler = (e) => {
306
+ const target = e.target;
307
+ const rawValue = target.value;
308
+ if (this._inputDebounceTimer !== null) {
309
+ clearTimeout(this._inputDebounceTimer);
310
+ this._inputDebounceTimer = null;
311
+ this.dispatchInputEvent(rawValue, e);
312
+ }
313
+ if (this._changeDebounceTimer !== null) {
314
+ clearTimeout(this._changeDebounceTimer);
315
+ this._changeDebounceTimer = null;
316
+ this.dispatchChangeEvent(rawValue, e);
317
+ }
318
+ this._isFocused = false;
319
+ wrapperEl.classList.remove('focused');
320
+ if (this.shouldFormat()) {
321
+ inputEl.value = this.getDisplayValue();
322
+ }
323
+ this.dispatchEvent(new CustomEvent('blur', {
324
+ detail: { originalEvent: e },
325
+ bubbles: true,
326
+ composed: true
327
+ }));
328
+ };
329
+ inputEl.addEventListener('input', this._inputHandler);
330
+ inputEl.addEventListener('change', this._changeHandler);
331
+ inputEl.addEventListener('focus', this._focusHandler);
332
+ inputEl.addEventListener('blur', this._blurHandler);
333
+ this._listenersSetup = true;
334
+ }
335
+ render() {
336
+ const shadow = this.shadowRoot;
337
+ const existingInput = shadow.querySelector('input');
338
+ const existingWrapper = shadow.querySelector('.input-wrapper');
339
+ const existingLabel = shadow.querySelector('.ty-field-label');
340
+ const existingError = shadow.querySelector('.error-message');
341
+ const classes = this.buildClassList();
342
+ const inputType = ['password', 'email', 'tel', 'url'].includes(this.type)
343
+ ? this.type
344
+ : 'text';
345
+ const inputMode = shouldFormatType(this.type) ? 'decimal'
346
+ : this.type === 'email' ? 'email'
347
+ : this.type === 'tel' ? 'tel'
348
+ : this.type === 'url' ? 'url'
349
+ : undefined;
350
+ const displayValue = this.getDisplayValue();
351
+ if (existingInput && existingWrapper) {
352
+ existingInput.type = inputType;
353
+ existingInput.value = displayValue;
354
+ existingInput.placeholder = this.placeholder;
355
+ existingInput.name = this.name;
356
+ if (inputMode)
357
+ existingInput.inputMode = inputMode;
358
+ else
359
+ existingInput.removeAttribute('inputmode');
360
+ existingWrapper.className = `input-wrapper ${classes}`;
361
+ existingInput.disabled = this.disabled;
362
+ if (this.disabled) {
363
+ existingInput.setAttribute('disabled', '');
364
+ }
365
+ else {
366
+ existingInput.removeAttribute('disabled');
367
+ }
368
+ existingInput.required = this.required;
369
+ if (this.required) {
370
+ existingInput.setAttribute('required', '');
371
+ }
372
+ else {
373
+ existingInput.removeAttribute('required');
374
+ }
375
+ if (this.label)
376
+ existingInput.setAttribute('aria-label', this.label);
377
+ else
378
+ existingInput.removeAttribute('aria-label');
379
+ if (this.label) {
380
+ if (existingLabel) {
381
+ existingLabel.innerHTML = `${this.label}${this.required ? `<span class="required-icon">${REQUIRED_ICON_SVG}</span>` : ''}`;
382
+ existingLabel.style.display = 'flex';
383
+ }
384
+ else {
385
+ const labelEl = document.createElement('label');
386
+ labelEl.className = 'ty-field-label';
387
+ labelEl.innerHTML = `${this.label}${this.required ? `<span class="required-icon">${REQUIRED_ICON_SVG}</span>` : ''}`;
388
+ const container = shadow.querySelector('.input-container');
389
+ if (container) {
390
+ container.insertBefore(labelEl, existingWrapper);
391
+ }
392
+ }
393
+ }
394
+ else if (existingLabel) {
395
+ ;
396
+ existingLabel.style.display = 'none';
397
+ }
398
+ if (this.error) {
399
+ if (existingError) {
400
+ existingError.textContent = this.error;
401
+ }
402
+ else {
403
+ const errorEl = document.createElement('div');
404
+ errorEl.className = 'error-message';
405
+ errorEl.textContent = this.error;
406
+ shadow.querySelector('.input-container')?.appendChild(errorEl);
407
+ }
408
+ }
409
+ else if (existingError) {
410
+ existingError.remove();
411
+ }
412
+ }
413
+ else {
414
+ const labelHtml = this.label ? `
415
+ <label class="ty-field-label">
416
+ ${this.label}
417
+ ${this.required ? `<span class="required-icon">${REQUIRED_ICON_SVG}</span>` : ''}
418
+ </label>
419
+ ` : '';
420
+ const errorHtml = this.error ? `
421
+ <div class="error-message">${this.error}</div>
422
+ ` : '';
423
+ shadow.innerHTML = `
424
+ <div class="input-container">
425
+ ${labelHtml}
426
+ <div class="input-wrapper ${classes}">
427
+ <slot name="start"></slot>
428
+ <input
429
+ type="${inputType}"
430
+ value="${displayValue}"
431
+ placeholder="${this.placeholder}"
432
+ name="${this.name}"
433
+ ${inputMode ? `inputmode="${inputMode}"` : ''}
434
+ />
435
+ <slot name="end"></slot>
436
+ ${inputType === 'password' ? `
437
+ <button class="password-toggle" type="button"
438
+ aria-label="Show password" aria-pressed="false">
439
+ ${EYE_ICON_SVG}
440
+ </button>` : ''}
441
+ </div>
442
+ ${errorHtml}
443
+ </div>
444
+ `;
445
+ const inputEl = shadow.querySelector('input');
446
+ inputEl.disabled = this.disabled;
447
+ inputEl.required = this.required;
448
+ if (this.label)
449
+ inputEl.setAttribute('aria-label', this.label);
450
+ this.setupPasswordToggle();
451
+ this.setupEventListeners();
452
+ }
453
+ this.updateValidity();
454
+ }
455
+ setupPasswordToggle() {
456
+ const shadow = this.shadowRoot;
457
+ const toggle = shadow.querySelector('.password-toggle');
458
+ const inputEl = shadow.querySelector('input');
459
+ if (!toggle || !inputEl)
460
+ return;
461
+ toggle.addEventListener('mousedown', (e) => e.preventDefault());
462
+ toggle.addEventListener('click', () => {
463
+ const reveal = inputEl.type === 'password';
464
+ inputEl.type = reveal ? 'text' : 'password';
465
+ toggle.innerHTML = reveal ? EYE_OFF_ICON_SVG : EYE_ICON_SVG;
466
+ toggle.setAttribute('aria-label', reveal ? 'Hide password' : 'Show password');
467
+ toggle.setAttribute('aria-pressed', String(reveal));
468
+ });
469
+ }
470
+ updateValidity() {
471
+ const inputEl = this.shadowRoot?.querySelector('input');
472
+ if (this.disabled) {
473
+ this._internals.setValidity({});
474
+ return;
475
+ }
476
+ const current = inputEl ? inputEl.value : String(this.getProperty('value') ?? '');
477
+ if (this.required && current.trim() === '') {
478
+ this._internals.setValidity({ valueMissing: true }, 'Please fill out this field', inputEl ?? undefined);
479
+ }
480
+ else {
481
+ this._internals.setValidity({});
482
+ }
483
+ }
659
484
  }
660
485
  TyInput.formAssociated = true;
661
- // ============================================================================
662
- // PROPERTY CONFIGURATION - Declarative property lifecycle
663
- // ============================================================================
664
486
  TyInput.properties = {
665
- // Core properties
666
- type: {
667
- type: 'string',
668
- visual: true,
669
- default: 'text',
670
- validate: (v) => {
671
- const validTypes = [
672
- 'text', 'email', 'password', 'number', 'tel', 'url',
673
- 'currency', 'percent', 'compact'
674
- ];
675
- return validTypes.includes(v);
676
- },
677
- coerce: (v) => {
678
- const validTypes = [
679
- 'text', 'email', 'password', 'number', 'tel', 'url',
680
- 'currency', 'percent', 'compact'
681
- ];
682
- if (!validTypes.includes(v)) {
683
- console.warn(`[ty-input] Invalid type '${v}'. Using 'text'.`);
684
- return 'text';
685
- }
686
- return v;
687
- }
688
- },
689
- value: {
690
- type: 'string',
691
- visual: true,
692
- formValue: true,
693
- emitChange: true,
694
- default: ''
695
- },
696
- name: {
697
- type: 'string',
698
- default: ''
699
- },
700
- placeholder: {
701
- type: 'string',
702
- visual: true,
703
- default: ''
704
- },
705
- label: {
706
- type: 'string',
707
- visual: true,
708
- default: ''
709
- },
710
- disabled: {
711
- type: 'boolean',
712
- visual: true,
713
- default: false
714
- },
715
- required: {
716
- type: 'boolean',
717
- visual: true,
718
- default: false
719
- },
720
- error: {
721
- type: 'string',
722
- visual: true,
723
- default: ''
724
- },
725
- size: {
726
- type: 'string',
727
- visual: true,
728
- default: 'md',
729
- // Fields come in exactly three sizes; legacy xs/xl map to sm/lg.
730
- validate: (v) => ['sm', 'md', 'lg'].includes(v),
731
- coerce: (v) => {
732
- if (v === 'xs')
733
- return 'sm';
734
- if (v === 'xl')
735
- return 'lg';
736
- if (!['sm', 'md', 'lg'].includes(v)) {
737
- console.warn(`[ty-input] Invalid size '${v}'. Using 'md'.`);
738
- return 'md';
739
- }
740
- return v;
741
- }
742
- },
743
- // Any string is accepted: built-ins get static CSS, other identifiers
744
- // become custom flavors (see _syncCustomFlavor), garbage degrades to
745
- // the default look.
746
- flavor: {
747
- type: 'string',
748
- visual: true,
749
- default: 'neutral'
750
- },
751
- // Numeric formatting properties
752
- currency: {
753
- type: 'string',
754
- visual: true,
755
- default: 'USD'
756
- },
757
- locale: {
758
- type: 'string',
759
- visual: true,
760
- default: 'en-US'
761
- },
762
- precision: {
763
- type: 'number',
764
- visual: true,
765
- default: undefined,
766
- validate: (v) => {
767
- if (v === undefined || v === null)
768
- return true;
769
- return typeof v === 'number' && v >= 0 && v <= 10;
770
- },
771
- coerce: (v) => {
772
- if (v === undefined || v === null)
773
- return undefined;
774
- const num = Number(v);
775
- if (isNaN(num))
776
- return undefined;
777
- return Math.max(0, Math.min(10, Math.floor(num)));
778
- }
779
- },
780
- // Debounce property
781
- debounce: {
782
- type: 'number',
783
- default: 0,
784
- validate: (v) => {
785
- const num = Number(v);
786
- return !isNaN(num) && num >= 0 && num <= 5000;
787
- },
788
- coerce: (v) => {
789
- const num = Number(v);
790
- if (isNaN(num))
791
- return 0;
792
- return Math.max(0, Math.min(5000, Math.floor(num)));
793
- }
794
- }
487
+ type: {
488
+ type: 'string',
489
+ visual: true,
490
+ default: 'text',
491
+ validate: (v) => {
492
+ const validTypes = [
493
+ 'text', 'email', 'password', 'number', 'tel', 'url',
494
+ 'currency', 'percent', 'compact'
495
+ ];
496
+ return validTypes.includes(v);
497
+ },
498
+ coerce: (v) => {
499
+ const validTypes = [
500
+ 'text', 'email', 'password', 'number', 'tel', 'url',
501
+ 'currency', 'percent', 'compact'
502
+ ];
503
+ if (!validTypes.includes(v)) {
504
+ console.warn(`[ty-input] Invalid type '${v}'. Using 'text'.`);
505
+ return 'text';
506
+ }
507
+ return v;
508
+ }
509
+ },
510
+ value: {
511
+ type: 'string',
512
+ visual: true,
513
+ formValue: true,
514
+ emitChange: true,
515
+ default: ''
516
+ },
517
+ name: {
518
+ type: 'string',
519
+ default: ''
520
+ },
521
+ placeholder: {
522
+ type: 'string',
523
+ visual: true,
524
+ default: ''
525
+ },
526
+ label: {
527
+ type: 'string',
528
+ visual: true,
529
+ default: ''
530
+ },
531
+ disabled: {
532
+ type: 'boolean',
533
+ visual: true,
534
+ default: false
535
+ },
536
+ required: {
537
+ type: 'boolean',
538
+ visual: true,
539
+ default: false
540
+ },
541
+ error: {
542
+ type: 'string',
543
+ visual: true,
544
+ default: ''
545
+ },
546
+ size: {
547
+ type: 'string',
548
+ visual: true,
549
+ default: DEFAULT_SIZE,
550
+ validate: (v) => ["xs", "sm", "md", "lg", "xl"].includes(v),
551
+ coerce: (v) => {
552
+ if (!["xs", "sm", "md", "lg", "xl"].includes(v)) {
553
+ console.warn(`[ty-input] Invalid size '${v}'. Using '${DEFAULT_SIZE}'.`);
554
+ return DEFAULT_SIZE;
555
+ }
556
+ return v;
557
+ }
558
+ },
559
+ flavor: {
560
+ type: 'string',
561
+ visual: true,
562
+ default: 'neutral'
563
+ },
564
+ currency: {
565
+ type: 'string',
566
+ visual: true,
567
+ default: 'USD'
568
+ },
569
+ locale: {
570
+ type: 'string',
571
+ visual: true,
572
+ default: 'en-US'
573
+ },
574
+ precision: {
575
+ type: 'number',
576
+ visual: true,
577
+ default: undefined,
578
+ validate: (v) => {
579
+ if (v === undefined || v === null)
580
+ return true;
581
+ return typeof v === 'number' && v >= 0 && v <= 10;
582
+ },
583
+ coerce: (v) => {
584
+ if (v === undefined || v === null)
585
+ return undefined;
586
+ const num = Number(v);
587
+ if (isNaN(num))
588
+ return undefined;
589
+ return Math.max(0, Math.min(10, Math.floor(num)));
590
+ }
591
+ },
592
+ debounce: {
593
+ type: 'number',
594
+ default: 0,
595
+ validate: (v) => {
596
+ const num = Number(v);
597
+ return !isNaN(num) && num >= 0 && num <= 5000;
598
+ },
599
+ coerce: (v) => {
600
+ const num = Number(v);
601
+ if (isNaN(num))
602
+ return 0;
603
+ return Math.max(0, Math.min(5000, Math.floor(num)));
604
+ }
605
+ }
795
606
  };
796
- // Register the custom element
797
607
  if (!customElements.get('ty-input')) {
798
- customElements.define('ty-input', TyInput);
608
+ customElements.define('ty-input', TyInput);
799
609
  }
800
- //# sourceMappingURL=input.js.map