tyrell-components 1.0.0-TC44 → 1.0.0-TC46

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 (350) hide show
  1. package/dist/tyrell.js +1 -1
  2. package/lib/base/ty-component.d.ts +12 -7
  3. package/lib/base/ty-component.js +8 -139
  4. package/lib/components/button.d.ts +2 -16
  5. package/lib/components/button.js +0 -36
  6. package/lib/components/calendar-month.d.ts +2 -48
  7. package/lib/components/calendar-month.js +4 -166
  8. package/lib/components/calendar-navigation.d.ts +3 -48
  9. package/lib/components/calendar-navigation.js +3 -126
  10. package/lib/components/calendar.d.ts +3 -80
  11. package/lib/components/calendar.js +8 -230
  12. package/lib/components/checkbox.d.ts +4 -71
  13. package/lib/components/checkbox.js +7 -151
  14. package/lib/components/copy.d.ts +3 -56
  15. package/lib/components/copy.js +1 -120
  16. package/lib/components/date-picker.d.ts +10 -160
  17. package/lib/components/date-picker.js +31 -577
  18. package/lib/components/file-upload.d.ts +3 -15
  19. package/lib/components/file-upload.js +0 -37
  20. package/lib/components/icon.d.ts +3 -24
  21. package/lib/components/icon.js +1 -90
  22. package/lib/components/input.d.ts +7 -107
  23. package/lib/components/input.js +4 -193
  24. package/lib/components/modal.d.ts +3 -26
  25. package/lib/components/modal.js +1 -188
  26. package/lib/components/option.d.ts +2 -9
  27. package/lib/components/option.js +0 -48
  28. package/lib/components/popup.d.ts +2 -17
  29. package/lib/components/popup.js +3 -130
  30. package/lib/components/radio.d.ts +3 -23
  31. package/lib/components/radio.js +0 -63
  32. package/lib/components/resize-observer.d.ts +2 -30
  33. package/lib/components/resize-observer.js +0 -45
  34. package/lib/components/scroll-container.d.ts +2 -12
  35. package/lib/components/scroll-container.js +3 -46
  36. package/lib/components/select.d.ts +43 -150
  37. package/lib/components/select.js +68 -558
  38. package/lib/components/selected-tags.d.ts +4 -47
  39. package/lib/components/selected-tags.js +0 -94
  40. package/lib/components/step.d.ts +1 -6
  41. package/lib/components/step.js +0 -6
  42. package/lib/components/switch.d.ts +0 -11
  43. package/lib/components/switch.js +2 -36
  44. package/lib/components/tab.d.ts +1 -6
  45. package/lib/components/tab.js +0 -7
  46. package/lib/components/tabs.d.ts +3 -63
  47. package/lib/components/tabs.js +2 -282
  48. package/lib/components/tag.d.ts +2 -36
  49. package/lib/components/tag.js +5 -105
  50. package/lib/components/textarea.d.ts +6 -86
  51. package/lib/components/textarea.js +2 -173
  52. package/lib/components/tooltip.d.ts +3 -11
  53. package/lib/components/tooltip.js +0 -154
  54. package/lib/components/wizard.d.ts +4 -54
  55. package/lib/components/wizard.js +0 -228
  56. package/lib/icons/fontawesome/brands.d.ts +0 -1
  57. package/lib/icons/fontawesome/brands.js +0 -8
  58. package/lib/icons/fontawesome/regular.d.ts +0 -1
  59. package/lib/icons/fontawesome/regular.js +0 -8
  60. package/lib/icons/fontawesome/solid.d.ts +0 -1
  61. package/lib/icons/fontawesome/solid.js +0 -8
  62. package/lib/icons/heroicons/micro.d.ts +0 -1
  63. package/lib/icons/heroicons/micro.js +0 -8
  64. package/lib/icons/heroicons/mini.d.ts +0 -1
  65. package/lib/icons/heroicons/mini.js +0 -8
  66. package/lib/icons/heroicons/outline.d.ts +0 -1
  67. package/lib/icons/heroicons/outline.js +0 -8
  68. package/lib/icons/heroicons/solid.d.ts +0 -1
  69. package/lib/icons/heroicons/solid.js +0 -8
  70. package/lib/icons/lucide.d.ts +0 -1
  71. package/lib/icons/lucide.js +0 -1872
  72. package/lib/icons/material/filled.d.ts +0 -1
  73. package/lib/icons/material/filled.js +0 -10
  74. package/lib/icons/material/outlined.d.ts +0 -1
  75. package/lib/icons/material/outlined.js +0 -10
  76. package/lib/icons/material/round.d.ts +0 -1
  77. package/lib/icons/material/round.js +0 -10
  78. package/lib/icons/material/sharp.d.ts +0 -1
  79. package/lib/icons/material/sharp.js +0 -10
  80. package/lib/icons/material/two-tone.d.ts +0 -1
  81. package/lib/icons/material/two-tone.js +0 -10
  82. package/lib/index.d.ts +0 -1
  83. package/lib/index.js +0 -20
  84. package/lib/styles/button.d.ts +2 -20
  85. package/lib/styles/button.js +13 -120
  86. package/lib/styles/calendar-month.d.ts +1 -5
  87. package/lib/styles/calendar-month.js +3 -60
  88. package/lib/styles/calendar-navigation.d.ts +2 -6
  89. package/lib/styles/calendar-navigation.js +1 -26
  90. package/lib/styles/calendar.d.ts +1 -2
  91. package/lib/styles/calendar.js +0 -13
  92. package/lib/styles/checkbox.d.ts +0 -1
  93. package/lib/styles/checkbox.js +1 -23
  94. package/lib/styles/copy.d.ts +0 -1
  95. package/lib/styles/copy.js +2 -17
  96. package/lib/styles/custom-scrollbar.d.ts +1 -2
  97. package/lib/styles/custom-scrollbar.js +3 -18
  98. package/lib/styles/date-picker.d.ts +1 -5
  99. package/lib/styles/date-picker.js +5 -35
  100. package/lib/styles/file-upload.d.ts +1 -2
  101. package/lib/styles/file-upload.js +0 -15
  102. package/lib/styles/icon.d.ts +2 -6
  103. package/lib/styles/icon.js +1 -26
  104. package/lib/styles/input.d.ts +1 -6
  105. package/lib/styles/input.js +3 -53
  106. package/lib/styles/modal.d.ts +1 -2
  107. package/lib/styles/modal.js +3 -23
  108. package/lib/styles/option.d.ts +2 -6
  109. package/lib/styles/option.js +1 -11
  110. package/lib/styles/popup.d.ts +1 -2
  111. package/lib/styles/popup.js +4 -25
  112. package/lib/styles/radio.d.ts +0 -1
  113. package/lib/styles/radio.js +1 -15
  114. package/lib/styles/resize-observer.d.ts +1 -2
  115. package/lib/styles/resize-observer.js +0 -7
  116. package/lib/styles/scroll-container.d.ts +1 -2
  117. package/lib/styles/scroll-container.js +0 -19
  118. package/lib/styles/select-base.d.ts +0 -1
  119. package/lib/styles/select-base.js +9 -70
  120. package/lib/styles/select.d.ts +1 -2
  121. package/lib/styles/select.js +50 -18
  122. package/lib/styles/step.d.ts +1 -2
  123. package/lib/styles/step.js +0 -4
  124. package/lib/styles/switch.d.ts +0 -1
  125. package/lib/styles/switch.js +2 -20
  126. package/lib/styles/tab.d.ts +1 -2
  127. package/lib/styles/tab.js +0 -5
  128. package/lib/styles/tabs.d.ts +3 -11
  129. package/lib/styles/tabs.js +4 -47
  130. package/lib/styles/tag.d.ts +1 -5
  131. package/lib/styles/tag.js +2 -31
  132. package/lib/styles/textarea.d.ts +1 -2
  133. package/lib/styles/textarea.js +0 -44
  134. package/lib/styles/tooltip.d.ts +0 -1
  135. package/lib/styles/tooltip.js +0 -10
  136. package/lib/styles/wizard.d.ts +5 -21
  137. package/lib/styles/wizard.js +5 -85
  138. package/lib/types/common.d.ts +0 -2
  139. package/lib/types/common.js +0 -12
  140. package/lib/utils/calendar-utils.d.ts +4 -21
  141. package/lib/utils/calendar-utils.js +4 -177
  142. package/lib/utils/custom-scrollbar.d.ts +6 -20
  143. package/lib/utils/custom-scrollbar.js +1 -45
  144. package/lib/utils/flavor-sheet.d.ts +0 -1
  145. package/lib/utils/flavor-sheet.js +0 -23
  146. package/lib/utils/icon-registry.d.ts +0 -2
  147. package/lib/utils/icon-registry.js +1 -107
  148. package/lib/utils/icons.d.ts +0 -1
  149. package/lib/utils/icons.js +0 -9
  150. package/lib/utils/loader-registry.d.ts +0 -1
  151. package/lib/utils/loader-registry.js +0 -32
  152. package/lib/utils/locale.d.ts +2 -23
  153. package/lib/utils/locale.js +0 -86
  154. package/lib/utils/mobile.d.ts +0 -1
  155. package/lib/utils/mobile.js +0 -17
  156. package/lib/utils/number-format.d.ts +0 -2
  157. package/lib/utils/number-format.js +2 -65
  158. package/lib/utils/positioning.d.ts +4 -2
  159. package/lib/utils/positioning.js +2 -54
  160. package/lib/utils/property-capture.d.ts +5 -27
  161. package/lib/utils/property-capture.js +0 -108
  162. package/lib/utils/property-manager.d.ts +0 -1
  163. package/lib/utils/property-manager.js +0 -70
  164. package/lib/utils/resize-observer.d.ts +0 -1
  165. package/lib/utils/resize-observer.js +0 -36
  166. package/lib/utils/scroll-lock.d.ts +4 -11
  167. package/lib/utils/scroll-lock.js +0 -107
  168. package/lib/utils/styles.d.ts +0 -1
  169. package/lib/utils/styles.js +0 -24
  170. package/lib/version.d.ts +1 -2
  171. package/lib/version.js +1 -11
  172. package/package.json +2 -2
  173. package/lib/base/ty-component.d.ts.map +0 -1
  174. package/lib/base/ty-component.js.map +0 -1
  175. package/lib/components/button.d.ts.map +0 -1
  176. package/lib/components/button.js.map +0 -1
  177. package/lib/components/calendar-month.d.ts.map +0 -1
  178. package/lib/components/calendar-month.js.map +0 -1
  179. package/lib/components/calendar-navigation.d.ts.map +0 -1
  180. package/lib/components/calendar-navigation.js.map +0 -1
  181. package/lib/components/calendar.d.ts.map +0 -1
  182. package/lib/components/calendar.js.map +0 -1
  183. package/lib/components/checkbox.d.ts.map +0 -1
  184. package/lib/components/checkbox.js.map +0 -1
  185. package/lib/components/copy.d.ts.map +0 -1
  186. package/lib/components/copy.js.map +0 -1
  187. package/lib/components/date-picker.d.ts.map +0 -1
  188. package/lib/components/date-picker.js.map +0 -1
  189. package/lib/components/file-upload.d.ts.map +0 -1
  190. package/lib/components/file-upload.js.map +0 -1
  191. package/lib/components/icon.d.ts.map +0 -1
  192. package/lib/components/icon.js.map +0 -1
  193. package/lib/components/input.d.ts.map +0 -1
  194. package/lib/components/input.js.map +0 -1
  195. package/lib/components/modal.d.ts.map +0 -1
  196. package/lib/components/modal.js.map +0 -1
  197. package/lib/components/option.d.ts.map +0 -1
  198. package/lib/components/option.js.map +0 -1
  199. package/lib/components/popup.d.ts.map +0 -1
  200. package/lib/components/popup.js.map +0 -1
  201. package/lib/components/radio.d.ts.map +0 -1
  202. package/lib/components/radio.js.map +0 -1
  203. package/lib/components/resize-observer.d.ts.map +0 -1
  204. package/lib/components/resize-observer.js.map +0 -1
  205. package/lib/components/scroll-container.d.ts.map +0 -1
  206. package/lib/components/scroll-container.js.map +0 -1
  207. package/lib/components/select.d.ts.map +0 -1
  208. package/lib/components/select.js.map +0 -1
  209. package/lib/components/selected-tags.d.ts.map +0 -1
  210. package/lib/components/selected-tags.js.map +0 -1
  211. package/lib/components/step.d.ts.map +0 -1
  212. package/lib/components/step.js.map +0 -1
  213. package/lib/components/switch.d.ts.map +0 -1
  214. package/lib/components/switch.js.map +0 -1
  215. package/lib/components/tab.d.ts.map +0 -1
  216. package/lib/components/tab.js.map +0 -1
  217. package/lib/components/tabs.d.ts.map +0 -1
  218. package/lib/components/tabs.js.map +0 -1
  219. package/lib/components/tag.d.ts.map +0 -1
  220. package/lib/components/tag.js.map +0 -1
  221. package/lib/components/textarea.d.ts.map +0 -1
  222. package/lib/components/textarea.js.map +0 -1
  223. package/lib/components/tooltip.d.ts.map +0 -1
  224. package/lib/components/tooltip.js.map +0 -1
  225. package/lib/components/wizard.d.ts.map +0 -1
  226. package/lib/components/wizard.js.map +0 -1
  227. package/lib/icons/fontawesome/brands.d.ts.map +0 -1
  228. package/lib/icons/fontawesome/brands.js.map +0 -1
  229. package/lib/icons/fontawesome/regular.d.ts.map +0 -1
  230. package/lib/icons/fontawesome/regular.js.map +0 -1
  231. package/lib/icons/fontawesome/solid.d.ts.map +0 -1
  232. package/lib/icons/fontawesome/solid.js.map +0 -1
  233. package/lib/icons/heroicons/micro.d.ts.map +0 -1
  234. package/lib/icons/heroicons/micro.js.map +0 -1
  235. package/lib/icons/heroicons/mini.d.ts.map +0 -1
  236. package/lib/icons/heroicons/mini.js.map +0 -1
  237. package/lib/icons/heroicons/outline.d.ts.map +0 -1
  238. package/lib/icons/heroicons/outline.js.map +0 -1
  239. package/lib/icons/heroicons/solid.d.ts.map +0 -1
  240. package/lib/icons/heroicons/solid.js.map +0 -1
  241. package/lib/icons/lucide.d.ts.map +0 -1
  242. package/lib/icons/lucide.js.map +0 -1
  243. package/lib/icons/material/filled.d.ts.map +0 -1
  244. package/lib/icons/material/filled.js.map +0 -1
  245. package/lib/icons/material/outlined.d.ts.map +0 -1
  246. package/lib/icons/material/outlined.js.map +0 -1
  247. package/lib/icons/material/round.d.ts.map +0 -1
  248. package/lib/icons/material/round.js.map +0 -1
  249. package/lib/icons/material/sharp.d.ts.map +0 -1
  250. package/lib/icons/material/sharp.js.map +0 -1
  251. package/lib/icons/material/two-tone.d.ts.map +0 -1
  252. package/lib/icons/material/two-tone.js.map +0 -1
  253. package/lib/index.d.ts.map +0 -1
  254. package/lib/index.js.map +0 -1
  255. package/lib/styles/button.d.ts.map +0 -1
  256. package/lib/styles/button.js.map +0 -1
  257. package/lib/styles/calendar-month.d.ts.map +0 -1
  258. package/lib/styles/calendar-month.js.map +0 -1
  259. package/lib/styles/calendar-navigation.d.ts.map +0 -1
  260. package/lib/styles/calendar-navigation.js.map +0 -1
  261. package/lib/styles/calendar.d.ts.map +0 -1
  262. package/lib/styles/calendar.js.map +0 -1
  263. package/lib/styles/checkbox.d.ts.map +0 -1
  264. package/lib/styles/checkbox.js.map +0 -1
  265. package/lib/styles/copy.d.ts.map +0 -1
  266. package/lib/styles/copy.js.map +0 -1
  267. package/lib/styles/custom-scrollbar.d.ts.map +0 -1
  268. package/lib/styles/custom-scrollbar.js.map +0 -1
  269. package/lib/styles/date-picker.d.ts.map +0 -1
  270. package/lib/styles/date-picker.js.map +0 -1
  271. package/lib/styles/file-upload.d.ts.map +0 -1
  272. package/lib/styles/file-upload.js.map +0 -1
  273. package/lib/styles/icon.d.ts.map +0 -1
  274. package/lib/styles/icon.js.map +0 -1
  275. package/lib/styles/input.d.ts.map +0 -1
  276. package/lib/styles/input.js.map +0 -1
  277. package/lib/styles/modal.d.ts.map +0 -1
  278. package/lib/styles/modal.js.map +0 -1
  279. package/lib/styles/multiselect.d.ts +0 -8
  280. package/lib/styles/multiselect.d.ts.map +0 -1
  281. package/lib/styles/multiselect.js +0 -849
  282. package/lib/styles/multiselect.js.map +0 -1
  283. package/lib/styles/option.d.ts.map +0 -1
  284. package/lib/styles/option.js.map +0 -1
  285. package/lib/styles/popup.d.ts.map +0 -1
  286. package/lib/styles/popup.js.map +0 -1
  287. package/lib/styles/radio.d.ts.map +0 -1
  288. package/lib/styles/radio.js.map +0 -1
  289. package/lib/styles/resize-observer.d.ts.map +0 -1
  290. package/lib/styles/resize-observer.js.map +0 -1
  291. package/lib/styles/scroll-container.d.ts.map +0 -1
  292. package/lib/styles/scroll-container.js.map +0 -1
  293. package/lib/styles/select-base.d.ts.map +0 -1
  294. package/lib/styles/select-base.js.map +0 -1
  295. package/lib/styles/select.d.ts.map +0 -1
  296. package/lib/styles/select.js.map +0 -1
  297. package/lib/styles/step.d.ts.map +0 -1
  298. package/lib/styles/step.js.map +0 -1
  299. package/lib/styles/switch.d.ts.map +0 -1
  300. package/lib/styles/switch.js.map +0 -1
  301. package/lib/styles/tab.d.ts.map +0 -1
  302. package/lib/styles/tab.js.map +0 -1
  303. package/lib/styles/tabs.d.ts.map +0 -1
  304. package/lib/styles/tabs.js.map +0 -1
  305. package/lib/styles/tag.d.ts.map +0 -1
  306. package/lib/styles/tag.js.map +0 -1
  307. package/lib/styles/textarea.d.ts.map +0 -1
  308. package/lib/styles/textarea.js.map +0 -1
  309. package/lib/styles/tooltip.d.ts.map +0 -1
  310. package/lib/styles/tooltip.js.map +0 -1
  311. package/lib/styles/wizard.d.ts.map +0 -1
  312. package/lib/styles/wizard.js.map +0 -1
  313. package/lib/types/common.d.ts.map +0 -1
  314. package/lib/types/common.js.map +0 -1
  315. package/lib/utils/calendar-utils.d.ts.map +0 -1
  316. package/lib/utils/calendar-utils.js.map +0 -1
  317. package/lib/utils/custom-scrollbar.d.ts.map +0 -1
  318. package/lib/utils/custom-scrollbar.js.map +0 -1
  319. package/lib/utils/flavor-sheet.d.ts.map +0 -1
  320. package/lib/utils/flavor-sheet.js.map +0 -1
  321. package/lib/utils/icon-registry.d.ts.map +0 -1
  322. package/lib/utils/icon-registry.js.map +0 -1
  323. package/lib/utils/icons.d.ts.map +0 -1
  324. package/lib/utils/icons.js.map +0 -1
  325. package/lib/utils/loader-registry.d.ts.map +0 -1
  326. package/lib/utils/loader-registry.js.map +0 -1
  327. package/lib/utils/locale.d.ts.map +0 -1
  328. package/lib/utils/locale.js.map +0 -1
  329. package/lib/utils/mobile.d.ts.map +0 -1
  330. package/lib/utils/mobile.js.map +0 -1
  331. package/lib/utils/number-format.d.ts.map +0 -1
  332. package/lib/utils/number-format.js.map +0 -1
  333. package/lib/utils/parse-boolean.d.ts +0 -39
  334. package/lib/utils/parse-boolean.d.ts.map +0 -1
  335. package/lib/utils/parse-boolean.js +0 -58
  336. package/lib/utils/parse-boolean.js.map +0 -1
  337. package/lib/utils/positioning.d.ts.map +0 -1
  338. package/lib/utils/positioning.js.map +0 -1
  339. package/lib/utils/property-capture.d.ts.map +0 -1
  340. package/lib/utils/property-capture.js.map +0 -1
  341. package/lib/utils/property-manager.d.ts.map +0 -1
  342. package/lib/utils/property-manager.js.map +0 -1
  343. package/lib/utils/resize-observer.d.ts.map +0 -1
  344. package/lib/utils/resize-observer.js.map +0 -1
  345. package/lib/utils/scroll-lock.d.ts.map +0 -1
  346. package/lib/utils/scroll-lock.js.map +0 -1
  347. package/lib/utils/styles.d.ts.map +0 -1
  348. package/lib/utils/styles.js.map +0 -1
  349. package/lib/version.d.ts.map +0 -1
  350. package/lib/version.js.map +0 -1
@@ -1,79 +1,7 @@
1
- /**
2
- * Tabs Component
3
- *
4
- * A carousel-based tabs component with smooth animations and fixed container dimensions.
5
- * Features horizontal sliding transitions, smart positioning marker, and independent panel scrolling.
6
- *
7
- * Features:
8
- * - Carousel animation with smooth sliding transitions
9
- * - Fixed dimensions prevent layout shift between tabs
10
- * - Animated marker that follows active tab button
11
- * - Rich label support via slots (icons, badges, custom content)
12
- * - Custom marker slot for complete active tab styling control
13
- * - Independent panel scrolling with scroll position reset
14
- * - ResizeObserver for responsive percentage widths
15
- * - Smart rendering - only updates DOM when necessary
16
- * - Accessibility with ARIA roles and attributes
17
- * - Top/bottom placement for tab buttons
18
- *
19
- * @example
20
- * <!-- Basic tabs with text labels -->
21
- * <ty-tabs width="800px" height="600px" active="general">
22
- * <ty-tab id="general" label="General Settings">
23
- * <div class="p-6">General content...</div>
24
- * </ty-tab>
25
- * <ty-tab id="advanced" label="Advanced Settings">
26
- * <div class="p-6">Advanced content...</div>
27
- * </ty-tab>
28
- * </ty-tabs>
29
- *
30
- * @example
31
- * <!-- Rich labels with icons and badges -->
32
- * <ty-tabs width="800px" height="600px">
33
- * <!-- Rich label as direct child -->
34
- * <span slot="label-profile" class="flex items-center gap-2">
35
- * <ty-icon name="user" size="sm"></ty-icon>
36
- * Profile
37
- * </span>
38
- *
39
- * <span slot="label-notifications" class="flex items-center gap-2">
40
- * <ty-icon name="bell" size="sm"></ty-icon>
41
- * Notifications
42
- * <span class="ty-bg-danger ty-text-danger++ px-2 py-0.5 rounded-full text-xs">5</span>
43
- * </span>
44
- *
45
- * <!-- Tab panels -->
46
- * <ty-tab id="profile">...</ty-tab>
47
- * <ty-tab id="notifications">...</ty-tab>
48
- * </ty-tabs>
49
- *
50
- * @example
51
- * <!-- Custom marker styling -->
52
- * <ty-tabs width="100%" height="400px">
53
- * <!-- Gradient marker slot -->
54
- * <div slot="marker" style="
55
- * background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
56
- * border-radius: 0.5rem;
57
- * box-shadow: 0 2px 8px rgba(0,0,0,0.15);
58
- * "></div>
59
- *
60
- * <ty-tab id="tab1" label="Dashboard">...</ty-tab>
61
- * <ty-tab id="tab2" label="Analytics">...</ty-tab>
62
- * </ty-tabs>
63
- */
64
1
  import { ensureStyles } from '../utils/styles.js';
65
2
  import { tabsStyles } from '../styles/tabs.js';
66
- // ============================================================================
67
- // WeakMaps for State Management
68
- // ============================================================================
69
3
  const eventHandlers = new WeakMap();
70
4
  const resizeObservers = new WeakMap();
71
- // ============================================================================
72
- // Helper Functions - Attribute Parsing
73
- // ============================================================================
74
- /**
75
- * Extract tabs configuration from element attributes
76
- */
77
5
  function getTabsAttributes(el) {
78
6
  return {
79
7
  width: el.getAttribute('width') || '100%',
@@ -82,29 +10,15 @@ function getTabsAttributes(el) {
82
10
  placement: (el.getAttribute('placement') || 'top'),
83
11
  };
84
12
  }
85
- /**
86
- * Get all ty-tab child elements
87
- */
88
13
  function getChildTabs(el) {
89
14
  return Array.from(el.querySelectorAll('ty-tab'));
90
15
  }
91
- /**
92
- * Get ID from a ty-tab element
93
- */
94
16
  function getTabId(tab) {
95
17
  return tab.getAttribute('id');
96
18
  }
97
- /**
98
- * Check if ty-tabs has a direct child label slot for this tab-id.
99
- * Looks in ty-tabs' light DOM for slot='label-{tab-id}' elements.
100
- */
101
19
  function hasSlotLabel(tabsEl, tabId) {
102
20
  return tabsEl.querySelector(`[slot='label-${tabId}']`) !== null;
103
21
  }
104
- /**
105
- * Determine label type: 'slot' (rich content) or 'text' (simple attribute).
106
- * Checks ty-tabs element for slot='label-{tab-id}' direct children.
107
- */
108
22
  function getTabLabelType(tabsEl, tab) {
109
23
  const tabId = getTabId(tab);
110
24
  if (tabId && hasSlotLabel(tabsEl, tabId)) {
@@ -112,51 +26,29 @@ function getTabLabelType(tabsEl, tab) {
112
26
  }
113
27
  return 'text';
114
28
  }
115
- /**
116
- * Check if tab is disabled
117
- */
118
29
  function isTabDisabled(tab) {
119
30
  return tab.hasAttribute('disabled');
120
31
  }
121
- /**
122
- * Check if user provided custom marker content via slot
123
- */
124
32
  function hasCustomMarker(el) {
125
33
  return el.querySelector('[slot="marker"]') !== null;
126
34
  }
127
- // ============================================================================
128
- // Helper Functions - Active Tab Management
129
- // ============================================================================
130
- /**
131
- * Find index of tab with given ID
132
- */
133
35
  function findTabIndex(tabs, tabId) {
134
36
  const index = tabs.findIndex(tab => getTabId(tab) === tabId);
135
37
  return index >= 0 ? index : undefined;
136
38
  }
137
- /**
138
- * Get the active tab ID, defaulting to first tab if not specified
139
- */
140
39
  function getActiveTabId(el, tabs) {
141
40
  const activeAttr = el.getAttribute('active');
142
41
  if (activeAttr && findTabIndex(tabs, activeAttr) !== undefined) {
143
42
  return activeAttr;
144
43
  }
145
- // Default to first tab
146
44
  if (tabs.length > 0) {
147
45
  return getTabId(tabs[0]);
148
46
  }
149
47
  return null;
150
48
  }
151
- /**
152
- * Set the active tab by ID
153
- */
154
49
  function setActiveTab(el, tabId) {
155
50
  el.setAttribute('active', tabId);
156
51
  }
157
- /**
158
- * Dispatch ty-tab-change event
159
- */
160
52
  function dispatchTabChangeEvent(el, activeId, activeIndex, previousId, previousIndex) {
161
53
  const event = new CustomEvent('ty-tab-change', {
162
54
  detail: {
@@ -170,21 +62,11 @@ function dispatchTabChangeEvent(el, activeId, activeIndex, previousId, previousI
170
62
  });
171
63
  el.dispatchEvent(event);
172
64
  }
173
- // ============================================================================
174
- // Event Handlers - Tab Button Click
175
- // ============================================================================
176
- /**
177
- * Handle tab button click
178
- */
179
65
  function handleTabClick(el, tabId, event) {
180
66
  event.preventDefault();
181
67
  event.stopPropagation();
182
- // Set the active attribute (which will trigger state update)
183
68
  setActiveTab(el, tabId);
184
69
  }
185
- /**
186
- * Cleanup existing event listeners
187
- */
188
70
  function cleanupEventListeners(el) {
189
71
  const handlers = eventHandlers.get(el);
190
72
  if (!handlers)
@@ -192,7 +74,6 @@ function cleanupEventListeners(el) {
192
74
  const shadowRoot = el.shadowRoot;
193
75
  if (!shadowRoot)
194
76
  return;
195
- // Remove all tab click handlers
196
77
  handlers.tabClickHandlers.forEach((handler, tabId) => {
197
78
  const button = shadowRoot.querySelector(`[data-tab-id='${tabId}']`);
198
79
  if (button) {
@@ -208,18 +89,12 @@ function cleanupEventListeners(el) {
208
89
  handlers.tabClickHandlers.clear();
209
90
  handlers.tabKeyClickHandlers.clear();
210
91
  }
211
- /**
212
- * Setup event listeners for tab button clicks
213
- */
214
92
  function setupEventListeners(el, shadowRoot, tabs) {
215
- // Clean up any existing listeners first
216
93
  cleanupEventListeners(el);
217
- // Initialize handlers storage
218
94
  const handlers = {
219
95
  tabClickHandlers: new Map(),
220
96
  tabKeyClickHandlers: new Map(),
221
97
  };
222
- // Add click listener for each tab button
223
98
  tabs.forEach((tab) => {
224
99
  const tabId = getTabId(tab);
225
100
  if (!tabId)
@@ -227,13 +102,6 @@ function setupEventListeners(el, shadowRoot, tabs) {
227
102
  const button = shadowRoot.querySelector(`[data-tab-id='${tabId}']`);
228
103
  if (button) {
229
104
  const handler = (e) => handleTabClick(el, tabId, e);
230
- // pointerdown: snappy mouse/touch activation (no native click delay).
231
- // click, gated to event.detail === 0: native <button> keyboard
232
- // activation (Enter/Space) fires 'click' with detail 0 — real mouse
233
- // clicks report detail >= 1 — so this covers keyboard users without
234
- // double-firing handleTabClick on an actual pointer click (which
235
- // already fired via pointerdown above). Without this, keyboard-only
236
- // users could Tab to a tab button but had no way to activate it.
237
105
  const keyClickHandler = (e) => {
238
106
  if (e.detail === 0)
239
107
  handleTabClick(el, tabId, e);
@@ -244,15 +112,8 @@ function setupEventListeners(el, shadowRoot, tabs) {
244
112
  handlers.tabKeyClickHandlers.set(tabId, keyClickHandler);
245
113
  }
246
114
  });
247
- // Store handlers for cleanup
248
115
  eventHandlers.set(el, handlers);
249
116
  }
250
- // ============================================================================
251
- // Transform & Positioning Updates
252
- // ============================================================================
253
- /**
254
- * Update the transform on panels-wrapper based on active index and measured width
255
- */
256
117
  function updateTransform(el, activeIndex) {
257
118
  const shadowRoot = el.shadowRoot;
258
119
  if (!shadowRoot)
@@ -260,15 +121,10 @@ function updateTransform(el, activeIndex) {
260
121
  const panelsWrapper = shadowRoot.querySelector('.panels-wrapper');
261
122
  if (!panelsWrapper)
262
123
  return;
263
- // Measure the actual width of the container
264
124
  const containerWidth = el.offsetWidth;
265
125
  const offsetPx = activeIndex * containerWidth;
266
- // Apply transform directly in pixels
267
126
  panelsWrapper.style.transform = `translateX(-${offsetPx}px)`;
268
127
  }
269
- /**
270
- * Update ARIA attributes on tab buttons without re-rendering
271
- */
272
128
  function updateAriaAttributes(el, shadowRoot, activeId) {
273
129
  const tabs = getChildTabs(el);
274
130
  tabs.forEach((tab, idx) => {
@@ -280,9 +136,7 @@ function updateAriaAttributes(el, shadowRoot, activeId) {
280
136
  if (button) {
281
137
  button.setAttribute('aria-selected', String(isActive));
282
138
  button.setAttribute('tabindex', isActive ? '0' : '-1');
283
- // Add data-active attribute for styling slotted content
284
139
  button.setAttribute('data-active', String(isActive));
285
- // Also set data-active on the slotted label element in light DOM
286
140
  const slottedLabel = el.querySelector(`[slot='label-${tabId}']`);
287
141
  if (slottedLabel) {
288
142
  slottedLabel.setAttribute('data-active', String(isActive));
@@ -290,9 +144,6 @@ function updateAriaAttributes(el, shadowRoot, activeId) {
290
144
  }
291
145
  });
292
146
  }
293
- /**
294
- * Update pointer-events, opacity, and data-active on tab panels without re-rendering
295
- */
296
147
  function updatePanelInteraction(el, activeId) {
297
148
  const tabs = getChildTabs(el);
298
149
  tabs.forEach((tab) => {
@@ -300,7 +151,6 @@ function updatePanelInteraction(el, activeId) {
300
151
  if (!tabId)
301
152
  return;
302
153
  const isActive = tabId === activeId;
303
- // Set data-active attribute for framework conditional rendering
304
154
  tab.setAttribute('data-active', String(isActive));
305
155
  if (isActive) {
306
156
  tab.style.pointerEvents = 'auto';
@@ -312,17 +162,12 @@ function updatePanelInteraction(el, activeId) {
312
162
  }
313
163
  });
314
164
  }
315
- /**
316
- * Calculate marker position and dimensions based on active tab button.
317
- * Returns position object with left, top, width, height in pixels, or null if button not found.
318
- */
319
165
  function calculateMarkerPosition(el, shadowRoot, activeId) {
320
166
  const button = shadowRoot.querySelector(`[data-tab-id='${activeId}']`);
321
167
  const buttonsContainer = shadowRoot.querySelector('.tab-buttons');
322
168
  if (!button || !buttonsContainer)
323
169
  return null;
324
170
  const buttonRect = button.getBoundingClientRect();
325
- // Use offset properties for position relative to container (accounts for padding)
326
171
  const left = button.offsetLeft;
327
172
  const top = button.offsetTop;
328
173
  return {
@@ -332,9 +177,6 @@ function calculateMarkerPosition(el, shadowRoot, activeId) {
332
177
  height: buttonRect.height,
333
178
  };
334
179
  }
335
- /**
336
- * Update marker wrapper position and dimensions to match active tab button
337
- */
338
180
  function updateMarker(el, activeId) {
339
181
  const shadowRoot = el.shadowRoot;
340
182
  if (!shadowRoot)
@@ -345,9 +187,6 @@ function updateMarker(el, activeId) {
345
187
  const position = calculateMarkerPosition(el, shadowRoot, activeId);
346
188
  if (!position)
347
189
  return;
348
- // Snap without animating when the marker was never positioned, or was
349
- // positioned while the tabs were hidden (zero-size rects) — otherwise it
350
- // visibly glides in from 0,0 on first display.
351
190
  const snap = !marker.style.left || marker.offsetWidth === 0;
352
191
  if (snap)
353
192
  marker.style.transition = 'none';
@@ -356,20 +195,10 @@ function updateMarker(el, activeId) {
356
195
  marker.style.width = `${position.width}px`;
357
196
  marker.style.height = `${position.height}px`;
358
197
  if (snap) {
359
- marker.offsetWidth; // flush styles so the snap isn't animated
198
+ marker.offsetWidth;
360
199
  marker.style.transition = '';
361
200
  }
362
201
  }
363
- /**
364
- * Update only the active tab state without re-rendering DOM.
365
- * Called from `attributeChangedCallback` when the `active` attribute changes.
366
- *
367
- * `previousId` MUST be the value the attribute held *before* it was changed —
368
- * never call `getActiveTabId(el, ...)` here, because the attribute has already
369
- * been updated to `tabId` by the time this runs. Reading it back would always
370
- * yield the new value, making the change-detection guard below trip on every
371
- * click and silently suppress `ty-tab-change` dispatch.
372
- */
373
202
  function updateActiveTabState(el, tabId, previousId) {
374
203
  const tabs = getChildTabs(el);
375
204
  const shadowRoot = el.shadowRoot;
@@ -377,56 +206,36 @@ function updateActiveTabState(el, tabId, previousId) {
377
206
  return;
378
207
  const currentIndex = previousId ? findTabIndex(tabs, previousId) : undefined;
379
208
  const newIndex = findTabIndex(tabs, tabId);
380
- // Only update if different tab and valid
381
209
  if (previousId === tabId || newIndex === undefined)
382
210
  return;
383
- // Update CSS variable for transform
384
211
  el.style.setProperty('--active-index', String(newIndex));
385
- // Update transform directly
386
212
  updateTransform(el, newIndex);
387
- // Update ARIA attributes on buttons
388
213
  updateAriaAttributes(el, shadowRoot, tabId);
389
- // Update pointer-events on panels
390
214
  updatePanelInteraction(el, tabId);
391
- // Update marker position to match new active tab
392
215
  updateMarker(el, tabId);
393
- // Reset scroll position of new active panel
394
216
  const newPanel = tabs[newIndex];
395
217
  if (newPanel?.resetScroll) {
396
218
  newPanel.resetScroll();
397
219
  }
398
- // Dispatch change event
399
220
  dispatchTabChangeEvent(el, tabId, newIndex, previousId, currentIndex ?? null);
400
221
  }
401
- // ============================================================================
402
- // ResizeObserver for Responsive Width
403
- // ============================================================================
404
- /**
405
- * Setup ResizeObserver for percentage widths and marker updates
406
- */
407
222
  function setupResizeObserver(el) {
408
- // Clean up old observer
409
223
  const oldObserver = resizeObservers.get(el);
410
224
  if (oldObserver) {
411
225
  oldObserver.disconnect();
412
226
  }
413
- // Observe unconditionally (not just for '%' widths) — overflow can happen
414
- // at any fixed width too, and needs to react to container resizes.
415
227
  const observer = new ResizeObserver((entries) => {
416
228
  const entry = entries[0];
417
229
  const { width } = getTabsAttributes(el);
418
230
  const tabs = getChildTabs(el);
419
231
  const activeId = getActiveTabId(el, tabs);
420
232
  const activeIndex = activeId ? findTabIndex(tabs, activeId) : 0;
421
- // Update CSS variable with measured width (percentage widths only)
422
233
  if (width.includes('%')) {
423
234
  el.style.setProperty('--tabs-width', `${entry.contentRect.width}px`);
424
235
  }
425
- // Update transform with new width
426
236
  if (activeIndex !== undefined) {
427
237
  updateTransform(el, activeIndex);
428
238
  }
429
- // Update marker position (tab button positions may have changed)
430
239
  if (activeId) {
431
240
  updateMarker(el, activeId);
432
241
  }
@@ -435,9 +244,6 @@ function setupResizeObserver(el) {
435
244
  observer.observe(el);
436
245
  resizeObservers.set(el, observer);
437
246
  }
438
- /**
439
- * Cleanup ResizeObserver
440
- */
441
247
  function cleanupResizeObserver(el) {
442
248
  const observer = resizeObservers.get(el);
443
249
  if (observer) {
@@ -445,14 +251,6 @@ function cleanupResizeObserver(el) {
445
251
  resizeObservers.delete(el);
446
252
  }
447
253
  }
448
- // ============================================================================
449
- // Rendering Functions
450
- // ============================================================================
451
- /**
452
- * Generate HTML for tab buttons using slots for rich labels.
453
- * Slots are looked up as direct children of tabs-el with slot='label-{tab-id}'.
454
- * Also renders marker wrapper with optional default underline (only if no custom marker).
455
- */
456
254
  function renderTabButtons(tabsEl, tabs, activeId) {
457
255
  const buttons = tabs.map((tab) => {
458
256
  const tabId = getTabId(tab);
@@ -485,10 +283,6 @@ function renderTabButtons(tabsEl, tabs, activeId) {
485
283
  </div>
486
284
  `;
487
285
  }
488
- /**
489
- * Generate HTML for ONLY the button elements (no marker wrapper).
490
- * Used during smart updates to preserve the marker wrapper.
491
- */
492
286
  function renderButtonsOnly(tabsEl, tabs, activeId) {
493
287
  return tabs.map((tab) => {
494
288
  const tabId = getTabId(tab);
@@ -512,11 +306,7 @@ function renderButtonsOnly(tabsEl, tabs, activeId) {
512
306
  </button>`;
513
307
  }).join('');
514
308
  }
515
- // ============================================================================
516
- // Overflow Handling (collapse tabs that don't fit into a "more" menu)
517
- // ============================================================================
518
309
  const OVERFLOW_TRIGGER_WIDTH = 40;
519
- /** Build a display label for a hidden tab's overflow-menu entry. */
520
310
  function getTabMenuLabel(tabsEl, tab) {
521
311
  const tabId = getTabId(tab);
522
312
  if (tabId && getTabLabelType(tabsEl, tab) === 'slot') {
@@ -525,15 +315,9 @@ function getTabMenuLabel(tabsEl, tab) {
525
315
  }
526
316
  return tab.getAttribute('label') || 'Tab';
527
317
  }
528
- /** Remove the "more" trigger + its popup, if present. */
529
318
  function removeOverflowMenu(shadowRoot) {
530
319
  shadowRoot.querySelector('.tab-overflow-trigger')?.remove();
531
320
  }
532
- /**
533
- * Build the "more" trigger button + its ty-popup menu for the given
534
- * (already-hidden) tab buttons, and append it to the buttons container.
535
- * Reuses ty-popup instead of a second floating-menu implementation.
536
- */
537
321
  function renderOverflowMenu(el, container, hiddenButtons, allTabs) {
538
322
  if (hiddenButtons.length === 0)
539
323
  return;
@@ -573,12 +357,6 @@ function renderOverflowMenu(el, container, hiddenButtons, allTabs) {
573
357
  trigger.appendChild(popup);
574
358
  container.appendChild(trigger);
575
359
  }
576
- /**
577
- * Measure the tab buttons against the available width and collapse whatever
578
- * doesn't fit behind a "more" trigger + popup menu. The active tab is always
579
- * kept visible — if it would be the one to overflow, the last visible button
580
- * is hidden in its place instead.
581
- */
582
360
  function updateOverflow(el) {
583
361
  const shadowRoot = el.shadowRoot;
584
362
  if (!shadowRoot)
@@ -602,8 +380,7 @@ function updateOverflow(el) {
602
380
  }
603
381
  }
604
382
  if (cutoff >= buttons.length)
605
- return; // everything fits
606
- // Recompute the cutoff leaving room for the "more" trigger itself.
383
+ return;
607
384
  total = 0;
608
385
  cutoff = buttons.length;
609
386
  for (let i = 0; i < buttons.length; i++) {
@@ -626,10 +403,6 @@ function updateOverflow(el) {
626
403
  buttons.forEach((b) => b.classList.toggle('overflow-hidden', hidden.has(b)));
627
404
  renderOverflowMenu(el, container, buttons.filter((b) => hidden.has(b)), tabs);
628
405
  }
629
- /**
630
- * Render the tabs container with buttons and panel viewport.
631
- * Smart rendering: checks if structure exists and only updates when needed.
632
- */
633
406
  function render(el) {
634
407
  const shadowRoot = el.shadowRoot;
635
408
  if (!shadowRoot)
@@ -638,16 +411,6 @@ function render(el) {
638
411
  const tabs = getChildTabs(el);
639
412
  const activeId = getActiveTabId(el, tabs);
640
413
  const activeIndex = activeId ? (findTabIndex(tabs, activeId) ?? 0) : 0;
641
- // Each <ty-tab> IS its tabpanel (no separate wrapper — there's just one
642
- // catch-all <slot> in the carousel viewport below). role/aria-labelledby/
643
- // tabindex go directly on the light-DOM host; its `id` attribute is ALSO
644
- // its tabId (getTabId reads it directly), so it's never touched here.
645
- // No aria-controls on the tab BUTTON pointing at this: it lives in the
646
- // shadow root while the panel is in light DOM, and ARIA id-references
647
- // don't resolve across a shadow-tree boundary — axe's aria-valid-attr-value
648
- // correctly flags that as invalid even though getElementById can find the
649
- // target. role=tab + aria-selected + aria-labelledby (this direction) is
650
- // what's actually load-bearing; caught by axe once ty-tabs got coverage.
651
414
  tabs.forEach((tab) => {
652
415
  const tabId = getTabId(tab);
653
416
  if (!tabId)
@@ -656,63 +419,48 @@ function render(el) {
656
419
  tab.setAttribute('aria-labelledby', `tab-${tabId}`);
657
420
  tab.setAttribute('tabindex', '0');
658
421
  });
659
- // Check if structure already exists
660
422
  const existingContainer = shadowRoot.querySelector('.tabs-container');
661
423
  const existingButtons = shadowRoot.querySelector('.tab-buttons');
662
424
  const existingViewport = shadowRoot.querySelector('.panels-viewport');
663
- // Ensure styles are loaded
664
425
  ensureStyles(shadowRoot, { css: tabsStyles, id: 'ty-tabs' });
665
- // Dev warning for too many tabs
666
426
  if (tabs.length > 7) {
667
427
  console.warn(`[ty-tabs] More than 7 tabs detected (${tabs.length} tabs). ` +
668
428
  'This may cause overflow and poor UX. ' +
669
429
  'Consider using sidebar navigation, accordion menu, or other patterns.');
670
430
  }
671
- // Set CSS variables for dimensions
672
431
  el.style.setProperty('--tabs-width', width.includes('%') ? '100%' : width);
673
432
  el.style.setProperty('--tabs-height', height);
674
433
  el.style.setProperty('--active-index', String(activeIndex));
675
434
  if (existingContainer && existingButtons && existingViewport) {
676
- // === SMART UPDATE: Structure exists, only update what changed ===
677
- // Update placement if changed
678
435
  existingContainer.setAttribute('data-placement', placement);
679
- // SMART UPDATE: Preserve marker wrapper, only update buttons
680
436
  existingButtons.querySelector('.tab-overflow-trigger')?.remove();
681
437
  const allButtons = existingButtons.querySelectorAll('.tab-button');
682
438
  allButtons.forEach(button => button.remove());
683
- // Generate and append new buttons after the marker wrapper
684
439
  const buttonsHtml = renderButtonsOnly(el, tabs, activeId);
685
440
  const tempDiv = document.createElement('div');
686
441
  tempDiv.innerHTML = buttonsHtml;
687
442
  Array.from(tempDiv.children).forEach(button => {
688
443
  existingButtons.appendChild(button);
689
444
  });
690
- // Re-setup event listeners (buttons were recreated)
691
445
  setupEventListeners(el, shadowRoot, tabs);
692
- // Update ARIA and data-active attributes on initial render
693
446
  updateAriaAttributes(el, shadowRoot, activeId || '');
694
- // Measure button height and update marker after update
695
447
  requestAnimationFrame(() => {
696
448
  const buttons = shadowRoot.querySelector('.tab-buttons');
697
449
  if (buttons) {
698
450
  const buttonsHeight = buttons.offsetHeight;
699
451
  el.style.setProperty('--buttons-height', `${buttonsHeight}px`);
700
452
  }
701
- // Update transform with current active index
702
453
  updateTransform(el, activeIndex);
703
- // Update marker position to match active tab
704
454
  if (activeId) {
705
455
  updateMarker(el, activeId);
706
456
  }
707
457
  updateOverflow(el);
708
458
  });
709
- // Update panel interaction states
710
459
  if (activeId) {
711
460
  updatePanelInteraction(el, activeId);
712
461
  }
713
462
  }
714
463
  else {
715
- // === FULL RENDER: First time or structure missing ===
716
464
  shadowRoot.innerHTML = `
717
465
  <div class="tabs-container" data-placement="${placement}">
718
466
  ${renderTabButtons(el, tabs, activeId)}
@@ -723,32 +471,24 @@ function render(el) {
723
471
  </div>
724
472
  </div>
725
473
  `;
726
- // Measure button height, update transform and marker after render
727
474
  requestAnimationFrame(() => {
728
475
  const buttons = shadowRoot.querySelector('.tab-buttons');
729
476
  if (buttons) {
730
477
  const buttonsHeight = buttons.offsetHeight;
731
478
  el.style.setProperty('--buttons-height', `${buttonsHeight}px`);
732
479
  }
733
- // Update transform with measured width
734
480
  updateTransform(el, activeIndex);
735
- // Update marker position to match active tab
736
481
  if (activeId) {
737
482
  updateMarker(el, activeId);
738
483
  }
739
484
  updateOverflow(el);
740
485
  });
741
- // Setup event listeners
742
486
  setupEventListeners(el, shadowRoot, tabs);
743
- // Update ARIA and data-active attributes on initial render
744
487
  updateAriaAttributes(el, shadowRoot, activeId || '');
745
- // Setup ResizeObserver for responsive width
746
488
  setupResizeObserver(el);
747
- // Update tab panel states
748
489
  if (activeId) {
749
490
  updatePanelInteraction(el, activeId);
750
491
  }
751
- // Listen for marker slot changes to hide default marker
752
492
  const markerSlot = shadowRoot.querySelector('slot[name="marker"]');
753
493
  if (markerSlot) {
754
494
  const updateDefaultMarkerVisibility = () => {
@@ -759,26 +499,15 @@ function render(el) {
759
499
  }
760
500
  };
761
501
  markerSlot.addEventListener('slotchange', updateDefaultMarkerVisibility);
762
- // Check immediately in case content is already slotted
763
502
  updateDefaultMarkerVisibility();
764
503
  }
765
504
  }
766
505
  }
767
- /**
768
- * Cleanup when tabs component is disconnected
769
- */
770
506
  function cleanup(el) {
771
507
  cleanupEventListeners(el);
772
508
  cleanupResizeObserver(el);
773
509
  }
774
- // ============================================================================
775
- // Component Definition
776
- // ============================================================================
777
- /**
778
- * TyTabs Web Component
779
- */
780
510
  export class TyTabs extends HTMLElement {
781
- /** Observed attributes */
782
511
  static get observedAttributes() {
783
512
  return ['width', 'height', 'active', 'placement'];
784
513
  }
@@ -793,26 +522,17 @@ export class TyTabs extends HTMLElement {
793
522
  cleanup(this);
794
523
  }
795
524
  attributeChangedCallback(name, oldValue, newValue) {
796
- // Smart rendering: only full render when structural attributes change
797
525
  if (name === 'active') {
798
- // Active tab changed - update state, then do smart render
799
526
  if (newValue) {
800
- // Pass `oldValue` explicitly so the change-detection inside
801
- // `updateActiveTabState` compares against the previous value
802
- // rather than re-reading the (already updated) attribute.
803
527
  updateActiveTabState(this, newValue, oldValue);
804
528
  }
805
- // Always call render after active change to update button states
806
529
  render(this);
807
530
  }
808
531
  else {
809
- // Other attributes changed (width, height, placement) - full render
810
532
  render(this);
811
533
  }
812
534
  }
813
535
  }
814
- // Register the custom element
815
536
  if (!customElements.get('ty-tabs')) {
816
537
  customElements.define('ty-tabs', TyTabs);
817
538
  }
818
- //# sourceMappingURL=tabs.js.map