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,655 +1,442 @@
1
- /**
2
- * Wizard Component
3
- *
4
- * A carousel-based wizard/stepper component with smooth animations, progress tracking, and completion state.
5
- * Features horizontal sliding transitions between steps, animated progress line, and step completion tracking.
6
- * Behaves like tabs - a "dumb" component that only renders and fires events. All navigation and validation
7
- * is controlled by the user.
8
- *
9
- * Features:
10
- * - Carousel animation with smooth sliding transitions between steps
11
- * - Fixed dimensions prevent layout shift between steps
12
- * - Animated progress line showing completion status
13
- * - Step indicators with completed/active/pending states
14
- * - Rich indicator support via slots (icons, custom content)
15
- * - Independent panel scrolling with scroll position reset
16
- * - ResizeObserver for responsive percentage widths
17
- * - Smart rendering - only updates DOM when necessary
18
- * - Accessibility with ARIA roles and attributes
19
- * - Fires events on step clicks - user controls navigation
20
- *
21
- * @example
22
- * <!-- Basic wizard with text labels -->
23
- * <ty-wizard width="900px" height="600px" active="welcome">
24
- * <ty-step id="welcome" label="Welcome">
25
- * <div class="p-6">
26
- * <h1>Welcome!</h1>
27
- * <button onclick="goToStep('account')">Next</button>
28
- * </div>
29
- * </ty-step>
30
- * <ty-step id="account" label="Account Setup">
31
- * <div class="p-6">
32
- * <h2>Account Setup</h2>
33
- * <button onclick="goToStep('welcome')">Previous</button>
34
- * <button onclick="goToStep('complete')">Next</button>
35
- * </div>
36
- * </ty-step>
37
- * </ty-wizard>
38
- *
39
- * @example
40
- * <!-- Wizard with custom indicators -->
41
- * <ty-wizard width="100%" height="700px" active="welcome" completed="welcome,account">
42
- * <!-- Custom indicator icons -->
43
- * <div slot="indicator-welcome">
44
- * <ty-icon name="hand" size="sm"></ty-icon>
45
- * </div>
46
- * <div slot="indicator-account">
47
- * <span class="text-lg font-bold">1</span>
48
- * </div>
49
- *
50
- * <ty-step id="welcome" label="Welcome">...</ty-step>
51
- * <ty-step id="account" label="Account">...</ty-step>
52
- * <ty-step id="profile" label="Profile">...</ty-step>
53
- * </ty-wizard>
54
- */
55
1
  import { ensureStyles } from '../utils/styles.js';
56
2
  import { wizardStyles } from '../styles/wizard.js';
57
- // ============================================================================
58
- // WeakMaps for State Management
59
- // ============================================================================
60
3
  const eventHandlers = new WeakMap();
61
4
  const resizeObservers = new WeakMap();
62
- // ============================================================================
63
- // Helper Functions - Attribute Parsing
64
- // ============================================================================
65
- /**
66
- * Extract wizard configuration from element attributes
67
- */
68
5
  function getWizardAttributes(el) {
69
- return {
70
- width: el.getAttribute('width') || '100%',
71
- height: el.getAttribute('height') || '700px',
72
- active: el.getAttribute('active'),
73
- completed: el.getAttribute('completed') || '',
74
- orientation: (el.getAttribute('orientation') || 'horizontal'),
75
- };
76
- }
77
- /**
78
- * Get all ty-step child elements
79
- */
6
+ return {
7
+ width: el.getAttribute('width') || '100%',
8
+ height: el.getAttribute('height') || '700px',
9
+ active: el.getAttribute('active'),
10
+ completed: el.getAttribute('completed') || '',
11
+ orientation: (el.getAttribute('orientation') || 'horizontal'),
12
+ };
13
+ }
80
14
  function getChildSteps(el) {
81
- return Array.from(el.querySelectorAll('ty-step'));
15
+ return Array.from(el.querySelectorAll('ty-step'));
82
16
  }
83
- /**
84
- * Get ID from a ty-step element
85
- */
86
17
  function getStepId(step) {
87
- return step.getAttribute('id');
18
+ return step.getAttribute('id');
88
19
  }
89
- /**
90
- * Get completed step IDs as a Set
91
- */
92
20
  function getCompletedStepIds(el) {
93
- const completedAttr = el.getAttribute('completed') || '';
94
- if (!completedAttr.trim())
95
- return new Set();
96
- return new Set(completedAttr.split(',').map(id => id.trim()).filter(Boolean));
97
- }
98
- /**
99
- * Check if wizard has a direct child indicator slot for this step-id.
100
- * Looks in ty-wizard's light DOM for slot='indicator-{step-id}' elements.
101
- */
21
+ const completedAttr = el.getAttribute('completed') || '';
22
+ if (!completedAttr.trim())
23
+ return new Set();
24
+ return new Set(completedAttr.split(',').map(id => id.trim()).filter(Boolean));
25
+ }
102
26
  function hasCustomIndicator(wizardEl, stepId) {
103
- return wizardEl.querySelector(`[slot='indicator-${stepId}']`) !== null;
27
+ return wizardEl.querySelector(`[slot='indicator-${stepId}']`) !== null;
104
28
  }
105
- /**
106
- * Check if step is disabled
107
- */
108
29
  function isStepDisabled(step) {
109
- return step.hasAttribute('disabled');
110
- }
111
- // ============================================================================
112
- // Helper Functions - Active Step Management
113
- // ============================================================================
114
- /**
115
- * Find index of step with given ID
116
- */
30
+ return step.hasAttribute('disabled');
31
+ }
117
32
  function findStepIndex(steps, stepId) {
118
- const index = steps.findIndex(step => getStepId(step) === stepId);
119
- return index >= 0 ? index : undefined;
33
+ const index = steps.findIndex(step => getStepId(step) === stepId);
34
+ return index >= 0 ? index : undefined;
120
35
  }
121
- /**
122
- * Get the active step ID, defaulting to first step if not specified
123
- */
124
36
  function getActiveStepId(el, steps) {
125
- const activeAttr = el.getAttribute('active');
126
- if (activeAttr && findStepIndex(steps, activeAttr) !== undefined) {
127
- return activeAttr;
128
- }
129
- // Default to first step
130
- if (steps.length > 0) {
131
- return getStepId(steps[0]);
132
- }
133
- return null;
134
- }
135
- /**
136
- * Set the active step by ID
137
- */
37
+ const activeAttr = el.getAttribute('active');
38
+ if (activeAttr && findStepIndex(steps, activeAttr) !== undefined) {
39
+ return activeAttr;
40
+ }
41
+ if (steps.length > 0) {
42
+ return getStepId(steps[0]);
43
+ }
44
+ return null;
45
+ }
138
46
  function setActiveStep(el, stepId) {
139
- el.setAttribute('active', stepId);
47
+ el.setAttribute('active', stepId);
140
48
  }
141
- /**
142
- * Calculate progress percentage for the progress line overlay
143
- */
144
49
  function calculateProgressPercent(steps, activeId, completedIds) {
145
- if (steps.length <= 1 || !activeId)
146
- return 0;
147
- const activeIndex = steps.findIndex(s => getStepId(s) === activeId);
148
- // Progress bar shows progress up to the currently active step
149
- return (activeIndex / (steps.length - 1)) * 100;
150
- }
151
- /**
152
- * Dispatch ty-wizard-step-change event
153
- */
50
+ if (steps.length <= 1 || !activeId)
51
+ return 0;
52
+ const activeIndex = steps.findIndex(s => getStepId(s) === activeId);
53
+ return (activeIndex / (steps.length - 1)) * 100;
54
+ }
154
55
  function dispatchStepChangeEvent(el, activeId, activeIndex, previousId, previousIndex) {
155
- const direction = previousIndex === null ? 'none'
156
- : activeIndex > previousIndex ? 'forward'
157
- : activeIndex < previousIndex ? 'backward'
158
- : 'none';
159
- const event = new CustomEvent('ty-wizard-step-change', {
160
- detail: {
161
- activeId,
162
- activeIndex,
163
- previousId,
164
- previousIndex,
165
- direction,
166
- },
167
- bubbles: true,
168
- cancelable: false,
169
- });
170
- el.dispatchEvent(event);
171
- }
172
- // ============================================================================
173
- // Event Handlers - Step Indicator Click
174
- // ============================================================================
175
- /**
176
- * Handle step indicator click - only dispatch event (like tabs)
177
- */
56
+ const direction = previousIndex === null ? 'none'
57
+ : activeIndex > previousIndex ? 'forward'
58
+ : activeIndex < previousIndex ? 'backward'
59
+ : 'none';
60
+ const event = new CustomEvent('ty-wizard-step-change', {
61
+ detail: {
62
+ activeId,
63
+ activeIndex,
64
+ previousId,
65
+ previousIndex,
66
+ direction,
67
+ },
68
+ bubbles: true,
69
+ cancelable: false,
70
+ });
71
+ el.dispatchEvent(event);
72
+ }
178
73
  function handleStepClick(el, stepId, event) {
179
- event.preventDefault();
180
- event.stopPropagation();
181
- const steps = getChildSteps(el);
182
- const targetIndex = steps.findIndex(s => getStepId(s) === stepId);
183
- if (targetIndex < 0)
184
- return;
185
- const currentActive = getActiveStepId(el, steps);
186
- const currentIndex = currentActive ? findStepIndex(steps, currentActive) ?? null : null;
187
- // Just dispatch event - user handles the actual navigation
188
- const direction = currentIndex === null ? 'none'
189
- : targetIndex > currentIndex ? 'forward'
190
- : targetIndex < currentIndex ? 'backward'
191
- : 'none';
192
- const event2 = new CustomEvent('ty-wizard-step-change', {
193
- detail: {
194
- activeId: stepId,
195
- activeIndex: targetIndex,
196
- previousId: currentActive,
197
- previousIndex: currentIndex,
198
- direction,
199
- },
200
- bubbles: true,
201
- cancelable: false,
202
- });
203
- el.dispatchEvent(event2);
204
- }
205
- /**
206
- * Cleanup existing event listeners
207
- */
74
+ event.preventDefault();
75
+ event.stopPropagation();
76
+ const steps = getChildSteps(el);
77
+ const targetIndex = steps.findIndex(s => getStepId(s) === stepId);
78
+ if (targetIndex < 0)
79
+ return;
80
+ const currentActive = getActiveStepId(el, steps);
81
+ const currentIndex = currentActive ? findStepIndex(steps, currentActive) ?? null : null;
82
+ const direction = currentIndex === null ? 'none'
83
+ : targetIndex > currentIndex ? 'forward'
84
+ : targetIndex < currentIndex ? 'backward'
85
+ : 'none';
86
+ const event2 = new CustomEvent('ty-wizard-step-change', {
87
+ detail: {
88
+ activeId: stepId,
89
+ activeIndex: targetIndex,
90
+ previousId: currentActive,
91
+ previousIndex: currentIndex,
92
+ direction,
93
+ },
94
+ bubbles: true,
95
+ cancelable: false,
96
+ });
97
+ el.dispatchEvent(event2);
98
+ }
208
99
  function cleanupEventListeners(el) {
209
- const handlers = eventHandlers.get(el);
210
- if (!handlers)
211
- return;
212
- const shadowRoot = el.shadowRoot;
213
- if (!shadowRoot)
214
- return;
215
- // Remove all step click handlers
216
- handlers.stepClickHandlers.forEach((handler, stepId) => {
217
- const button = shadowRoot.querySelector(`[data-step-id='${stepId}']`);
218
- if (button) {
219
- button.removeEventListener('pointerdown', handler);
220
- }
221
- });
222
- handlers.stepClickHandlers.clear();
223
- }
224
- /**
225
- * Setup event listeners for step indicator clicks
226
- */
100
+ const handlers = eventHandlers.get(el);
101
+ if (!handlers)
102
+ return;
103
+ const shadowRoot = el.shadowRoot;
104
+ if (!shadowRoot)
105
+ return;
106
+ handlers.stepClickHandlers.forEach((handler, stepId) => {
107
+ const button = shadowRoot.querySelector(`[data-step-id='${stepId}']`);
108
+ if (button) {
109
+ button.removeEventListener('pointerdown', handler);
110
+ }
111
+ });
112
+ handlers.stepClickHandlers.clear();
113
+ }
227
114
  function setupEventListeners(el, shadowRoot, steps) {
228
- // Clean up any existing listeners first
229
- cleanupEventListeners(el);
230
- // Initialize handlers storage
231
- const handlers = {
232
- stepClickHandlers: new Map(),
233
- };
234
- // Add click listener for each step indicator
235
- steps.forEach((step) => {
236
- const stepId = getStepId(step);
237
- if (!stepId)
238
- return;
239
- const button = shadowRoot.querySelector(`[data-step-id='${stepId}']`);
240
- if (button) {
241
- const handler = (e) => handleStepClick(el, stepId, e);
242
- button.addEventListener('pointerdown', handler);
243
- handlers.stepClickHandlers.set(stepId, handler);
244
- }
245
- });
246
- // Store handlers for cleanup
247
- eventHandlers.set(el, handlers);
248
- }
249
- // ============================================================================
250
- // Transform & Positioning Updates
251
- // ============================================================================
252
- /**
253
- * Update the transform on panels-wrapper based on active index and measured width
254
- */
115
+ cleanupEventListeners(el);
116
+ const handlers = {
117
+ stepClickHandlers: new Map(),
118
+ };
119
+ steps.forEach((step) => {
120
+ const stepId = getStepId(step);
121
+ if (!stepId)
122
+ return;
123
+ const button = shadowRoot.querySelector(`[data-step-id='${stepId}']`);
124
+ if (button) {
125
+ const handler = (e) => handleStepClick(el, stepId, e);
126
+ button.addEventListener('pointerdown', handler);
127
+ handlers.stepClickHandlers.set(stepId, handler);
128
+ }
129
+ });
130
+ eventHandlers.set(el, handlers);
131
+ }
255
132
  function updateTransform(el, activeIndex) {
256
- const shadowRoot = el.shadowRoot;
257
- if (!shadowRoot)
258
- return;
259
- const panelsWrapper = shadowRoot.querySelector('.panels-wrapper');
260
- if (!panelsWrapper)
261
- return;
262
- // Measure the actual width of the container
263
- const containerWidth = el.offsetWidth;
264
- const offsetPx = activeIndex * containerWidth;
265
- // Apply transform directly in pixels
266
- panelsWrapper.style.transform = `translateX(-${offsetPx}px)`;
267
- }
268
- /**
269
- * Update progress line overlay width
270
- */
133
+ const shadowRoot = el.shadowRoot;
134
+ if (!shadowRoot)
135
+ return;
136
+ const panelsWrapper = shadowRoot.querySelector('.panels-wrapper');
137
+ if (!panelsWrapper)
138
+ return;
139
+ const containerWidth = el.offsetWidth;
140
+ const offsetPx = activeIndex * containerWidth;
141
+ panelsWrapper.style.transform = `translateX(-${offsetPx}px)`;
142
+ }
271
143
  function updateProgressLine(el) {
272
- const shadowRoot = el.shadowRoot;
273
- if (!shadowRoot)
274
- return;
275
- const progressOverlay = shadowRoot.querySelector('.progress-overlay');
276
- if (!progressOverlay)
277
- return;
278
- const steps = getChildSteps(el);
279
- const activeId = getActiveStepId(el, steps);
280
- const completedIds = getCompletedStepIds(el);
281
- const progressPercent = calculateProgressPercent(steps, activeId, completedIds);
282
- progressOverlay.style.width = `${progressPercent}%`;
283
- }
284
- /**
285
- * Update ARIA attributes on step indicators without re-rendering
286
- */
144
+ const shadowRoot = el.shadowRoot;
145
+ if (!shadowRoot)
146
+ return;
147
+ const progressOverlay = shadowRoot.querySelector('.progress-overlay');
148
+ if (!progressOverlay)
149
+ return;
150
+ const steps = getChildSteps(el);
151
+ const activeId = getActiveStepId(el, steps);
152
+ const completedIds = getCompletedStepIds(el);
153
+ const progressPercent = calculateProgressPercent(steps, activeId, completedIds);
154
+ progressOverlay.style.width = `${progressPercent}%`;
155
+ }
287
156
  function updateStepIndicators(el, shadowRoot, activeId) {
288
- const steps = getChildSteps(el);
289
- const completedIds = getCompletedStepIds(el);
290
- const activeIndex = activeId ? steps.findIndex(s => getStepId(s) === activeId) : 0;
291
- steps.forEach((step, index) => {
292
- const stepId = getStepId(step);
293
- if (!stepId)
294
- return;
295
- const button = shadowRoot.querySelector(`[data-step-id='${stepId}']`);
296
- const circle = shadowRoot.querySelector(`[data-step-id='${stepId}'] .step-circle`);
297
- const isActive = stepId === activeId;
298
- if (button) {
299
- button.setAttribute('aria-selected', String(isActive));
300
- button.setAttribute('tabindex', isActive ? '0' : '-1');
301
- button.setAttribute('data-active', String(isActive));
302
- }
303
- if (circle) {
304
- // Use same status logic as rendering (respects user's status attribute)
305
- const state = getStepStatus(step, stepId, activeId, completedIds);
306
- circle.setAttribute('data-state', state);
307
- }
308
- // Also set data-active on the slotted indicator element in light DOM
309
- const slottedIndicator = el.querySelector(`[slot='indicator-${stepId}']`);
310
- if (slottedIndicator) {
311
- slottedIndicator.setAttribute('data-active', String(isActive));
312
- }
313
- });
314
- }
315
- /**
316
- * Update pointer-events, opacity, and data-active on step panels without re-rendering
317
- */
157
+ const steps = getChildSteps(el);
158
+ const completedIds = getCompletedStepIds(el);
159
+ const activeIndex = activeId ? steps.findIndex(s => getStepId(s) === activeId) : 0;
160
+ steps.forEach((step, index) => {
161
+ const stepId = getStepId(step);
162
+ if (!stepId)
163
+ return;
164
+ const button = shadowRoot.querySelector(`[data-step-id='${stepId}']`);
165
+ const circle = shadowRoot.querySelector(`[data-step-id='${stepId}'] .step-circle`);
166
+ const isActive = stepId === activeId;
167
+ if (button) {
168
+ button.setAttribute('aria-selected', String(isActive));
169
+ button.setAttribute('tabindex', isActive ? '0' : '-1');
170
+ button.setAttribute('data-active', String(isActive));
171
+ }
172
+ if (circle) {
173
+ const state = getStepStatus(step, stepId, activeId, completedIds);
174
+ circle.setAttribute('data-state', state);
175
+ }
176
+ const slottedIndicator = el.querySelector(`[slot='indicator-${stepId}']`);
177
+ if (slottedIndicator) {
178
+ slottedIndicator.setAttribute('data-active', String(isActive));
179
+ }
180
+ });
181
+ }
318
182
  function updatePanelInteraction(el, activeId) {
319
- const steps = getChildSteps(el);
320
- steps.forEach((step) => {
321
- const stepId = getStepId(step);
322
- if (!stepId)
323
- return;
324
- const isActive = stepId === activeId;
325
- // Set data-active attribute for framework conditional rendering
326
- step.setAttribute('data-active', String(isActive));
327
- if (isActive) {
328
- step.style.pointerEvents = 'auto';
329
- step.style.opacity = '1';
330
- }
331
- else {
332
- step.style.pointerEvents = 'none';
333
- step.style.opacity = '0';
334
- }
335
- });
336
- }
337
- /**
338
- * Update only the active step state without re-rendering DOM.
339
- * This is called when only the active or completed attribute changes.
340
- */
183
+ const steps = getChildSteps(el);
184
+ steps.forEach((step) => {
185
+ const stepId = getStepId(step);
186
+ if (!stepId)
187
+ return;
188
+ const isActive = stepId === activeId;
189
+ step.setAttribute('data-active', String(isActive));
190
+ if (isActive) {
191
+ step.style.pointerEvents = 'auto';
192
+ step.style.opacity = '1';
193
+ }
194
+ else {
195
+ step.style.pointerEvents = 'none';
196
+ step.style.opacity = '0';
197
+ }
198
+ });
199
+ }
341
200
  function updateActiveStepState(el, stepId) {
342
- const steps = getChildSteps(el);
343
- const shadowRoot = el.shadowRoot;
344
- if (!shadowRoot)
345
- return;
346
- const currentActive = getActiveStepId(el, steps);
347
- const currentIndex = currentActive ? findStepIndex(steps, currentActive) : undefined;
348
- const newIndex = findStepIndex(steps, stepId);
349
- // Only update if different step and valid
350
- if (currentActive === stepId || newIndex === undefined)
351
- return;
352
- // Update CSS variable for transform
353
- el.style.setProperty('--ty-wizard-active-index', String(newIndex));
354
- // Update transform directly
355
- updateTransform(el, newIndex);
356
- // Update step indicator states (visual + ARIA)
357
- updateStepIndicators(el, shadowRoot, stepId);
358
- // Update progress line overlay
359
- updateProgressLine(el);
360
- // Update pointer-events on panels
361
- updatePanelInteraction(el, stepId);
362
- // Reset scroll position of new active panel
363
- const newPanel = steps[newIndex];
364
- if (newPanel?.resetScroll) {
365
- newPanel.resetScroll();
366
- }
367
- // Dispatch change event
368
- dispatchStepChangeEvent(el, stepId, newIndex, currentActive, currentIndex ?? null);
369
- }
370
- // ============================================================================
371
- // ResizeObserver for Responsive Width
372
- // ============================================================================
373
- /**
374
- * Setup ResizeObserver for percentage widths
375
- */
201
+ const steps = getChildSteps(el);
202
+ const shadowRoot = el.shadowRoot;
203
+ if (!shadowRoot)
204
+ return;
205
+ const currentActive = getActiveStepId(el, steps);
206
+ const currentIndex = currentActive ? findStepIndex(steps, currentActive) : undefined;
207
+ const newIndex = findStepIndex(steps, stepId);
208
+ if (currentActive === stepId || newIndex === undefined)
209
+ return;
210
+ el.style.setProperty('--ty-wizard-active-index', String(newIndex));
211
+ updateTransform(el, newIndex);
212
+ updateStepIndicators(el, shadowRoot, stepId);
213
+ updateProgressLine(el);
214
+ updatePanelInteraction(el, stepId);
215
+ const newPanel = steps[newIndex];
216
+ if (newPanel?.resetScroll) {
217
+ newPanel.resetScroll();
218
+ }
219
+ dispatchStepChangeEvent(el, stepId, newIndex, currentActive, currentIndex ?? null);
220
+ }
376
221
  function setupResizeObserver(el) {
377
- // Clean up old observer
378
- const oldObserver = resizeObservers.get(el);
379
- if (oldObserver) {
380
- oldObserver.disconnect();
381
- }
382
- const { width } = getWizardAttributes(el);
383
- // Only setup observer for percentage widths
384
- if (width.includes('%')) {
385
- const observer = new ResizeObserver((entries) => {
386
- const entry = entries[0];
387
- const measuredWidth = entry.contentRect.width;
388
- const steps = getChildSteps(el);
389
- const activeId = getActiveStepId(el, steps);
390
- const activeIndex = activeId ? findStepIndex(steps, activeId) : 0;
391
- // Update CSS variable with measured width
392
- el.style.setProperty('--ty-wizard-width', `${measuredWidth}px`);
393
- // Update transform with new width
394
- if (activeIndex !== undefined) {
395
- updateTransform(el, activeIndex);
396
- }
397
- });
398
- observer.observe(el);
399
- resizeObservers.set(el, observer);
400
- }
401
- }
402
- /**
403
- * Cleanup ResizeObserver
404
- */
222
+ const oldObserver = resizeObservers.get(el);
223
+ if (oldObserver) {
224
+ oldObserver.disconnect();
225
+ }
226
+ const { width } = getWizardAttributes(el);
227
+ if (width.includes('%')) {
228
+ const observer = new ResizeObserver((entries) => {
229
+ const entry = entries[0];
230
+ const measuredWidth = entry.contentRect.width;
231
+ const steps = getChildSteps(el);
232
+ const activeId = getActiveStepId(el, steps);
233
+ const activeIndex = activeId ? findStepIndex(steps, activeId) : 0;
234
+ el.style.setProperty('--ty-wizard-width', `${measuredWidth}px`);
235
+ if (activeIndex !== undefined) {
236
+ updateTransform(el, activeIndex);
237
+ }
238
+ });
239
+ observer.observe(el);
240
+ resizeObservers.set(el, observer);
241
+ }
242
+ }
405
243
  function cleanupResizeObserver(el) {
406
- const observer = resizeObservers.get(el);
407
- if (observer) {
408
- observer.disconnect();
409
- resizeObservers.delete(el);
410
- }
411
- }
412
- // ============================================================================
413
- // Rendering Functions
414
- // ============================================================================
415
- /**
416
- * Get step status - use user's explicit status attribute or fall back to automatic detection
417
- */
244
+ const observer = resizeObservers.get(el);
245
+ if (observer) {
246
+ observer.disconnect();
247
+ resizeObservers.delete(el);
248
+ }
249
+ }
418
250
  function getStepStatus(step, stepId, activeId, completedIds) {
419
- // Check if user provided explicit status
420
- const explicitStatus = step.getAttribute('status');
421
- if (explicitStatus && ['completed', 'active', 'pending', 'error'].includes(explicitStatus)) {
422
- return explicitStatus;
423
- }
424
- // Fall back to automatic detection
425
- const isActive = stepId === activeId;
426
- const isCompleted = completedIds.has(stepId);
427
- return isCompleted ? 'completed' : isActive ? 'active' : 'pending';
428
- }
429
- /**
430
- * Generate HTML for step indicators with progress line
431
- */
251
+ const explicitStatus = step.getAttribute('status');
252
+ if (explicitStatus && ['completed', 'active', 'pending', 'error'].includes(explicitStatus)) {
253
+ return explicitStatus;
254
+ }
255
+ const isActive = stepId === activeId;
256
+ const isCompleted = completedIds.has(stepId);
257
+ return isCompleted ? 'completed' : isActive ? 'active' : 'pending';
258
+ }
432
259
  function renderStepIndicators(wizardEl, steps, activeId, completedIds) {
433
- const activeIndex = activeId ? steps.findIndex(s => getStepId(s) === activeId) : 0;
434
- const indicators = steps.map((step, index) => {
435
- const stepId = getStepId(step);
436
- if (!stepId)
437
- return '';
438
- const label = step.getAttribute('label') || `Step ${index + 1}`;
439
- const description = step.getAttribute('description') || '';
440
- const disabled = isStepDisabled(step);
441
- const isActive = stepId === activeId;
442
- const hasCustom = hasCustomIndicator(wizardEl, stepId);
443
- // User can override status via attribute
444
- const state = getStepStatus(step, stepId, activeId, completedIds);
445
- // Icon content varies by state
446
- let circleContent = '';
447
- if (hasCustom) {
448
- circleContent = `<slot name="indicator-${stepId}"></slot>`;
449
- }
450
- else if (state === 'completed') {
451
- // Checkmark for completed
452
- circleContent = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>`;
453
- }
454
- else if (state === 'error') {
455
- // X icon for error
456
- circleContent = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>`;
457
- }
458
- else {
459
- // Number for active/pending
460
- circleContent = `<span style="font-size: 12px; font-weight: 700;">${index + 1}</span>`;
461
- }
462
- // No aria-controls: the button lives in shadow DOM, the panel (<ty-step>,
463
- // see the render() step below) is light DOM — ARIA id-references don't
464
- // resolve across a shadow-tree boundary, so a cross-boundary reference
465
- // here is spec-invalid even though getElementById can technically find
466
- // the target (axe's aria-valid-attr-value correctly flags it). The
467
- // load-bearing relationship is role=tab + aria-selected here, and
468
- // aria-labelledby (pointing back at this button) on the <ty-step> side.
469
- return `<button
470
- class="step-indicator"
471
- role="tab"
472
- data-step-id="${stepId}"
473
- id="step-${stepId}"
474
- aria-selected="${isActive}"
475
- tabindex="${isActive ? '0' : '-1'}"
476
- data-active="${isActive}"
477
- ${disabled ? 'disabled aria-disabled="true"' : ''}
478
- >
479
- <div class="step-circle" data-state="${state}" part="step-circle">
480
- ${circleContent}
481
- </div>
482
- <div class="step-text">
483
- <span class="step-label">${label}</span>
484
- ${description ? `<span class="step-description">${description}</span>` : ''}
485
- </div>
486
- </button>`;
487
- }).join('');
488
- const progressPercent = calculateProgressPercent(steps, activeId, completedIds);
489
- const stepCount = steps.length;
490
- return `
491
- <div class="step-indicators-wrapper" part="indicators-wrapper">
492
- <div class="step-indicators" style="--ty-wizard-step-count: ${stepCount}">
493
- <div class="progress-line" part="progress-line" role="progressbar" aria-label="Wizard progress" aria-valuenow="${Math.round(progressPercent)}" aria-valuemin="0" aria-valuemax="100">
494
- <div class="progress-overlay" style="width: ${progressPercent}%"></div>
495
- </div>
496
- <div class="step-tablist" role="tablist" style="display: contents;">
497
- ${indicators}
498
- </div>
499
- </div>
500
- </div>
501
- `;
502
- }
503
- /**
504
- * Render the wizard container with step indicators and panel viewport.
505
- * Smart rendering: checks if structure exists and only updates when needed.
506
- */
260
+ const activeIndex = activeId ? steps.findIndex(s => getStepId(s) === activeId) : 0;
261
+ const indicators = steps.map((step, index) => {
262
+ const stepId = getStepId(step);
263
+ if (!stepId)
264
+ return '';
265
+ const label = step.getAttribute('label') || `Step ${index + 1}`;
266
+ const description = step.getAttribute('description') || '';
267
+ const disabled = isStepDisabled(step);
268
+ const isActive = stepId === activeId;
269
+ const hasCustom = hasCustomIndicator(wizardEl, stepId);
270
+ const state = getStepStatus(step, stepId, activeId, completedIds);
271
+ let circleContent = '';
272
+ if (hasCustom) {
273
+ circleContent = `<slot name="indicator-${stepId}"></slot>`;
274
+ }
275
+ else if (state === 'completed') {
276
+ circleContent = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>`;
277
+ }
278
+ else if (state === 'error') {
279
+ circleContent = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>`;
280
+ }
281
+ else {
282
+ circleContent = `<span style="font-size: 12px; font-weight: 700;">${index + 1}</span>`;
283
+ }
284
+ return `<button
285
+ class="step-indicator"
286
+ role="tab"
287
+ data-step-id="${stepId}"
288
+ id="step-${stepId}"
289
+ aria-selected="${isActive}"
290
+ tabindex="${isActive ? '0' : '-1'}"
291
+ data-active="${isActive}"
292
+ ${disabled ? 'disabled aria-disabled="true"' : ''}
293
+ >
294
+ <div class="step-circle" data-state="${state}" part="step-circle">
295
+ ${circleContent}
296
+ </div>
297
+ <div class="step-text">
298
+ <span class="step-label">${label}</span>
299
+ ${description ? `<span class="step-description">${description}</span>` : ''}
300
+ </div>
301
+ </button>`;
302
+ }).join('');
303
+ const progressPercent = calculateProgressPercent(steps, activeId, completedIds);
304
+ const stepCount = steps.length;
305
+ return `
306
+ <div class="step-indicators-wrapper" part="indicators-wrapper">
307
+ <div class="step-indicators" style="--ty-wizard-step-count: ${stepCount}">
308
+ <div class="progress-line" part="progress-line" role="progressbar" aria-label="Wizard progress" aria-valuenow="${Math.round(progressPercent)}" aria-valuemin="0" aria-valuemax="100">
309
+ <div class="progress-overlay" style="width: ${progressPercent}%"></div>
310
+ </div>
311
+ <div class="step-tablist" role="tablist" style="display: contents;">
312
+ ${indicators}
313
+ </div>
314
+ </div>
315
+ </div>
316
+ `;
317
+ }
507
318
  function render(el) {
508
- const shadowRoot = el.shadowRoot;
509
- if (!shadowRoot)
510
- return;
511
- const { width, height } = getWizardAttributes(el);
512
- const steps = getChildSteps(el);
513
- const activeId = getActiveStepId(el, steps);
514
- const activeIndex = activeId ? (findStepIndex(steps, activeId) ?? 0) : 0;
515
- const completedIds = getCompletedStepIds(el);
516
- // Each <ty-step> IS its tabpanel (single catch-all <slot> in the carousel
517
- // viewport below) — same pattern and same rationale as ty-tabs. `id` is
518
- // also the stepId (getStepId reads it directly), so it's never reassigned.
519
- steps.forEach((step) => {
520
- const stepId = getStepId(step);
521
- if (!stepId)
522
- return;
523
- step.setAttribute('role', 'tabpanel');
524
- step.setAttribute('aria-labelledby', `step-${stepId}`);
525
- step.setAttribute('tabindex', '0');
526
- });
527
- // Check if structure already exists
528
- const existingContainer = shadowRoot.querySelector('.wizard-container');
529
- const existingIndicators = shadowRoot.querySelector('.step-indicators-wrapper');
530
- const existingViewport = shadowRoot.querySelector('.panels-viewport');
531
- // Ensure styles are loaded
532
- ensureStyles(shadowRoot, { css: wizardStyles, id: 'ty-wizard' });
533
- // Set CSS variables for dimensions and step count
534
- el.style.setProperty('--ty-wizard-width', width.includes('%') ? '100%' : width);
535
- el.style.setProperty('--ty-wizard-height', height);
536
- el.style.setProperty('--ty-wizard-active-index', String(activeIndex));
537
- el.style.setProperty('--ty-wizard-step-count', String(steps.length));
538
- if (existingContainer && existingIndicators && existingViewport) {
539
- // === SMART UPDATE: Structure exists, only update what changed ===
540
- // Remove old indicators and replace
541
- existingIndicators.remove();
542
- const newIndicatorsHtml = renderStepIndicators(el, steps, activeId, completedIds);
543
- const tempDiv = document.createElement('div');
544
- tempDiv.innerHTML = newIndicatorsHtml;
545
- existingContainer.insertBefore(tempDiv.firstElementChild, existingViewport);
546
- // Re-setup event listeners (indicators were recreated)
547
- setupEventListeners(el, shadowRoot, steps);
548
- // Update ARIA and data-active attributes
549
- updateStepIndicators(el, shadowRoot, activeId || '');
550
- // Measure indicators height
551
- requestAnimationFrame(() => {
552
- const indicators = shadowRoot.querySelector('.step-indicators-wrapper');
553
- if (indicators) {
554
- const indicatorsHeight = indicators.offsetHeight;
555
- el.style.setProperty('--ty-wizard-indicators-height', `${indicatorsHeight}px`);
556
- }
557
- // Update transform with current active index
558
- updateTransform(el, activeIndex);
559
- // Update progress line
560
- updateProgressLine(el);
561
- });
562
- // Update panel interaction states
563
- if (activeId) {
564
- updatePanelInteraction(el, activeId);
565
- }
566
- }
567
- else {
568
- // === FULL RENDER: First time or structure missing ===
569
- shadowRoot.innerHTML = `
570
- <div class="wizard-container">
571
- ${renderStepIndicators(el, steps, activeId, completedIds)}
572
- <div class="panels-viewport" part="panels-container">
573
- <div class="panels-wrapper">
574
- <slot></slot>
575
- </div>
576
- </div>
577
- </div>
578
- `;
579
- // Measure indicators height, update transform and progress after render
580
- requestAnimationFrame(() => {
581
- const indicators = shadowRoot.querySelector('.step-indicators-wrapper');
582
- if (indicators) {
583
- const indicatorsHeight = indicators.offsetHeight;
584
- el.style.setProperty('--ty-wizard-indicators-height', `${indicatorsHeight}px`);
585
- }
586
- // Update transform with measured width
587
- updateTransform(el, activeIndex);
588
- // Update progress line
589
- updateProgressLine(el);
590
- });
591
- // Setup event listeners
592
- setupEventListeners(el, shadowRoot, steps);
593
- // Update ARIA and data-active attributes on initial render
594
- updateStepIndicators(el, shadowRoot, activeId || '');
595
- // Setup ResizeObserver for responsive width
596
- setupResizeObserver(el);
597
- // Update step panel states
598
- if (activeId) {
599
- updatePanelInteraction(el, activeId);
600
- }
601
- }
602
- }
603
- /**
604
- * Cleanup when wizard component is disconnected
605
- */
319
+ const shadowRoot = el.shadowRoot;
320
+ if (!shadowRoot)
321
+ return;
322
+ const { width, height } = getWizardAttributes(el);
323
+ const steps = getChildSteps(el);
324
+ const activeId = getActiveStepId(el, steps);
325
+ const activeIndex = activeId ? (findStepIndex(steps, activeId) ?? 0) : 0;
326
+ const completedIds = getCompletedStepIds(el);
327
+ steps.forEach((step) => {
328
+ const stepId = getStepId(step);
329
+ if (!stepId)
330
+ return;
331
+ step.setAttribute('role', 'tabpanel');
332
+ step.setAttribute('aria-labelledby', `step-${stepId}`);
333
+ step.setAttribute('tabindex', '0');
334
+ });
335
+ const existingContainer = shadowRoot.querySelector('.wizard-container');
336
+ const existingIndicators = shadowRoot.querySelector('.step-indicators-wrapper');
337
+ const existingViewport = shadowRoot.querySelector('.panels-viewport');
338
+ ensureStyles(shadowRoot, { css: wizardStyles, id: 'ty-wizard' });
339
+ el.style.setProperty('--ty-wizard-width', width.includes('%') ? '100%' : width);
340
+ el.style.setProperty('--ty-wizard-height', height);
341
+ el.style.setProperty('--ty-wizard-active-index', String(activeIndex));
342
+ el.style.setProperty('--ty-wizard-step-count', String(steps.length));
343
+ if (existingContainer && existingIndicators && existingViewport) {
344
+ existingIndicators.remove();
345
+ const newIndicatorsHtml = renderStepIndicators(el, steps, activeId, completedIds);
346
+ const tempDiv = document.createElement('div');
347
+ tempDiv.innerHTML = newIndicatorsHtml;
348
+ existingContainer.insertBefore(tempDiv.firstElementChild, existingViewport);
349
+ setupEventListeners(el, shadowRoot, steps);
350
+ updateStepIndicators(el, shadowRoot, activeId || '');
351
+ requestAnimationFrame(() => {
352
+ const indicators = shadowRoot.querySelector('.step-indicators-wrapper');
353
+ if (indicators) {
354
+ const indicatorsHeight = indicators.offsetHeight;
355
+ el.style.setProperty('--ty-wizard-indicators-height', `${indicatorsHeight}px`);
356
+ }
357
+ updateTransform(el, activeIndex);
358
+ updateProgressLine(el);
359
+ });
360
+ if (activeId) {
361
+ updatePanelInteraction(el, activeId);
362
+ }
363
+ }
364
+ else {
365
+ shadowRoot.innerHTML = `
366
+ <div class="wizard-container">
367
+ ${renderStepIndicators(el, steps, activeId, completedIds)}
368
+ <div class="panels-viewport" part="panels-container">
369
+ <div class="panels-wrapper">
370
+ <slot></slot>
371
+ </div>
372
+ </div>
373
+ </div>
374
+ `;
375
+ requestAnimationFrame(() => {
376
+ const indicators = shadowRoot.querySelector('.step-indicators-wrapper');
377
+ if (indicators) {
378
+ const indicatorsHeight = indicators.offsetHeight;
379
+ el.style.setProperty('--ty-wizard-indicators-height', `${indicatorsHeight}px`);
380
+ }
381
+ updateTransform(el, activeIndex);
382
+ updateProgressLine(el);
383
+ });
384
+ setupEventListeners(el, shadowRoot, steps);
385
+ updateStepIndicators(el, shadowRoot, activeId || '');
386
+ setupResizeObserver(el);
387
+ if (activeId) {
388
+ updatePanelInteraction(el, activeId);
389
+ }
390
+ }
391
+ }
606
392
  function cleanup(el) {
607
- cleanupEventListeners(el);
608
- cleanupResizeObserver(el);
609
- }
610
- // ============================================================================
611
- // Component Definition
612
- // ============================================================================
613
- /**
614
- * TyWizard Web Component
615
- */
393
+ cleanupEventListeners(el);
394
+ cleanupResizeObserver(el);
395
+ }
616
396
  export class TyWizard extends HTMLElement {
617
- /** Observed attributes */
618
- static get observedAttributes() {
619
- return ['width', 'height', 'active', 'completed', 'orientation'];
620
- }
621
- constructor() {
622
- super();
623
- this.attachShadow({ mode: 'open' });
624
- }
625
- connectedCallback() {
626
- render(this);
627
- }
628
- disconnectedCallback() {
629
- cleanup(this);
630
- }
631
- attributeChangedCallback(name, oldValue, newValue) {
632
- // Smart rendering: only full render when structural attributes change
633
- if (name === 'active') {
634
- // Active step changed - update state, then do smart render
635
- if (newValue) {
636
- updateActiveStepState(this, newValue);
637
- }
638
- // Always call render after active change to update indicator states
639
- render(this);
640
- }
641
- else if (name === 'completed') {
642
- // Completed steps changed - update progress line and indicators
643
- render(this);
644
- }
645
- else {
646
- // Other attributes changed (width, height, linear, orientation) - full render
647
- render(this);
648
- }
649
- }
650
- }
651
- // Register the custom element
397
+ static get observedAttributes() {
398
+ return ['width', 'height', 'active', 'completed', 'orientation'];
399
+ }
400
+ constructor() {
401
+ super();
402
+ this._childObserver = null;
403
+ this._childRenderQueued = false;
404
+ this.attachShadow({ mode: 'open' });
405
+ }
406
+ connectedCallback() {
407
+ render(this);
408
+ this._childObserver = new MutationObserver(() => {
409
+ if (this._childRenderQueued)
410
+ return;
411
+ this._childRenderQueued = true;
412
+ queueMicrotask(() => {
413
+ this._childRenderQueued = false;
414
+ if (this.isConnected)
415
+ render(this);
416
+ });
417
+ });
418
+ this._childObserver.observe(this, { childList: true });
419
+ }
420
+ disconnectedCallback() {
421
+ this._childObserver?.disconnect();
422
+ this._childObserver = null;
423
+ cleanup(this);
424
+ }
425
+ attributeChangedCallback(name, oldValue, newValue) {
426
+ if (name === 'active') {
427
+ if (newValue) {
428
+ updateActiveStepState(this, newValue);
429
+ }
430
+ render(this);
431
+ }
432
+ else if (name === 'completed') {
433
+ render(this);
434
+ }
435
+ else {
436
+ render(this);
437
+ }
438
+ }
439
+ }
652
440
  if (!customElements.get('ty-wizard')) {
653
- customElements.define('ty-wizard', TyWizard);
441
+ customElements.define('ty-wizard', TyWizard);
654
442
  }
655
- //# sourceMappingURL=wizard.js.map