@ds-mo/ui 14.18.1 → 14.19.1

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 (359) hide show
  1. package/dist/.package-ready.json +1 -1
  2. package/dist/agent-patterns.json +1 -1
  3. package/dist/agent.json +16 -9
  4. package/dist/angular/ds-pagination.d.ts +1 -1
  5. package/dist/angular/ds-pagination.js +3 -3
  6. package/dist/components/ds-agent-activity.js +1 -1
  7. package/dist/components/ds-agent-questionnaire.js +1 -1
  8. package/dist/components/ds-agent-response.js +1 -1
  9. package/dist/components/ds-agent-source-list.js +1 -1
  10. package/dist/components/ds-agent-tool-call.js +1 -1
  11. package/dist/components/ds-attachment-list.js +1 -1
  12. package/dist/components/ds-avatar.js +1 -1
  13. package/dist/components/ds-badge.js +1 -1
  14. package/dist/components/ds-banner.js +1 -1
  15. package/dist/components/ds-banner.js.map +1 -1
  16. package/dist/components/ds-bar-action.js +1 -1
  17. package/dist/components/ds-bar-action.js.map +1 -1
  18. package/dist/components/ds-bar-nav.js +1 -1
  19. package/dist/components/ds-bar-title.js +1 -1
  20. package/dist/components/ds-bar-workflow.js +1 -1
  21. package/dist/components/ds-bar-workflow.js.map +1 -1
  22. package/dist/components/ds-breadcrumb.js +1 -1
  23. package/dist/components/ds-button-filled.js +1 -1
  24. package/dist/components/ds-button-unfilled.js +1 -1
  25. package/dist/components/ds-card-chart.js +1 -1
  26. package/dist/components/ds-card-chart.js.map +1 -1
  27. package/dist/components/ds-card-overview.js +1 -1
  28. package/dist/components/ds-card-overview.js.map +1 -1
  29. package/dist/components/ds-card-setting.js +1 -1
  30. package/dist/components/ds-card-setting.js.map +1 -1
  31. package/dist/components/ds-chart-legend.js +1 -1
  32. package/dist/components/ds-chart-legend.js.map +1 -1
  33. package/dist/components/ds-chart.js +1 -1
  34. package/dist/components/ds-chart.js.map +1 -1
  35. package/dist/components/ds-checkbox.js +1 -1
  36. package/dist/components/ds-chip.js +1 -1
  37. package/dist/components/ds-chip.js.map +1 -1
  38. package/dist/components/ds-code-block.js +1 -1
  39. package/dist/components/ds-conversation-list-item.js +1 -1
  40. package/dist/components/ds-conversation-list-item.js.map +1 -1
  41. package/dist/components/ds-conversation-list-section.js +1 -1
  42. package/dist/components/ds-conversation-list.js +1 -1
  43. package/dist/components/ds-conversation-list.js.map +1 -1
  44. package/dist/components/ds-divider.js +1 -1
  45. package/dist/components/ds-empty-state.js +1 -1
  46. package/dist/components/ds-field.js +1 -1
  47. package/dist/components/ds-filter-menu.js +1 -1
  48. package/dist/components/ds-icon.js +1 -1
  49. package/dist/components/ds-input.js +1 -1
  50. package/dist/components/ds-loader.js +1 -1
  51. package/dist/components/ds-markdown.js +1 -1
  52. package/dist/components/ds-menu.js +1 -1
  53. package/dist/components/ds-message-actions.js +1 -1
  54. package/dist/components/ds-message-bubble.js +1 -1
  55. package/dist/components/ds-message-bubble.js.map +1 -1
  56. package/dist/components/ds-message-composer.js +1 -1
  57. package/dist/components/ds-message-composer.js.map +1 -1
  58. package/dist/components/ds-message-scroller.js +1 -1
  59. package/dist/components/ds-message-scroller.js.map +1 -1
  60. package/dist/components/ds-message.js +1 -1
  61. package/dist/components/ds-mobile-bar-nav.js +1 -1
  62. package/dist/components/ds-mobile-header.js +1 -1
  63. package/dist/components/ds-mobile-section-switcher.js +1 -1
  64. package/dist/components/ds-mobile-sheet-nav.js +1 -1
  65. package/dist/components/ds-modal.js +1 -1
  66. package/dist/components/ds-pagination.js +1 -1
  67. package/dist/components/ds-panel-nav.js +1 -1
  68. package/dist/components/ds-panel-sub-nav.js +1 -1
  69. package/dist/components/ds-panel-sub-nav.js.map +1 -1
  70. package/dist/components/ds-panel-tool-header.js +1 -1
  71. package/dist/components/ds-panel-tool-search.js +1 -1
  72. package/dist/components/ds-panel-tool-search.js.map +1 -1
  73. package/dist/components/ds-panel-tools.js +1 -1
  74. package/dist/components/ds-paper-texture.js +1 -1
  75. package/dist/components/ds-radio.js +1 -1
  76. package/dist/components/ds-scroll-overlay.js +1 -1
  77. package/dist/components/ds-scroll-overlay.js.map +1 -1
  78. package/dist/components/ds-select.js +1 -1
  79. package/dist/components/ds-shell-app.js +1 -1
  80. package/dist/components/ds-shell-app.js.map +1 -1
  81. package/dist/components/ds-shell-page.js +1 -1
  82. package/dist/components/ds-shell-tools.js +1 -1
  83. package/dist/components/ds-skeleton.js +1 -1
  84. package/dist/components/ds-slider.js +1 -1
  85. package/dist/components/ds-slider.js.map +1 -1
  86. package/dist/components/ds-swatch-picker.js +1 -1
  87. package/dist/components/ds-switch.js +1 -1
  88. package/dist/components/ds-tab-group.js +1 -1
  89. package/dist/components/ds-table-filter.js +1 -1
  90. package/dist/components/ds-table-saved-views.js +1 -1
  91. package/dist/components/ds-table-sort.js +1 -1
  92. package/dist/components/ds-table-sort.js.map +1 -1
  93. package/dist/components/ds-table-toolbar.js +1 -1
  94. package/dist/components/ds-table.js +1 -1
  95. package/dist/components/ds-table.js.map +1 -1
  96. package/dist/components/ds-tag.js +1 -1
  97. package/dist/components/ds-text.js +1 -1
  98. package/dist/components/ds-toast.js +1 -1
  99. package/dist/components/ds-toast.js.map +1 -1
  100. package/dist/components/ds-tooltip-chart.js +1 -1
  101. package/dist/components/ds-tooltip.js +1 -1
  102. package/dist/components/ds-typing-indicator.js +1 -1
  103. package/dist/components/p-0XTyQNRG.js +2 -0
  104. package/dist/components/{p-JPTkZ3Ji.js.map → p-0XTyQNRG.js.map} +1 -1
  105. package/dist/components/{p-DFQ9mcbK.js → p-1YHFQeI7.js} +1 -1
  106. package/dist/components/p-1YHFQeI7.js.map +1 -0
  107. package/dist/components/p-B-HsfQOK.js +2 -0
  108. package/dist/components/p-B-HsfQOK.js.map +1 -0
  109. package/dist/components/p-B4wTnsUL.js +2 -0
  110. package/dist/components/p-B4wTnsUL.js.map +1 -0
  111. package/dist/components/p-B6WQre5D.js +2 -0
  112. package/dist/components/p-B6WQre5D.js.map +1 -0
  113. package/dist/components/{p-B1JRCDHS.js → p-BHhmB9-4.js} +2 -2
  114. package/dist/components/{p-B1JRCDHS.js.map → p-BHhmB9-4.js.map} +1 -1
  115. package/dist/components/{p-CqQFowZd.js → p-BNq9fr9S.js} +2 -2
  116. package/dist/components/p-BNq9fr9S.js.map +1 -0
  117. package/dist/components/{p-BKp__CQX.js → p-BWwiCvep.js} +2 -2
  118. package/dist/components/p-BWwiCvep.js.map +1 -0
  119. package/dist/components/{p-BBG6Sswb.js → p-BYFS2qWb.js} +2 -2
  120. package/dist/components/{p-BBG6Sswb.js.map → p-BYFS2qWb.js.map} +1 -1
  121. package/dist/components/{p-uI6z53BE.js → p-Bb4KnRy0.js} +2 -2
  122. package/dist/components/{p-HB_cf_aR.js → p-BeA6Jv-3.js} +2 -2
  123. package/dist/components/{p-HB_cf_aR.js.map → p-BeA6Jv-3.js.map} +1 -1
  124. package/dist/components/{p-1Hu9jlju.js → p-Bf7PZJPT.js} +2 -2
  125. package/dist/components/{p-Bbb9A3NV.js → p-BjRD57w3.js} +2 -2
  126. package/dist/components/p-BjRD57w3.js.map +1 -0
  127. package/dist/components/{p-CKVHrOEp.js → p-BmlGeaY8.js} +2 -2
  128. package/dist/components/p-BmlGeaY8.js.map +1 -0
  129. package/dist/components/{p-CLVxgTme.js → p-BubgDQlb.js} +2 -2
  130. package/dist/components/p-BubgDQlb.js.map +1 -0
  131. package/dist/components/{p-BNkU9dcc.js → p-Bv6FSDPR.js} +2 -2
  132. package/dist/components/{p-BNkU9dcc.js.map → p-Bv6FSDPR.js.map} +1 -1
  133. package/dist/components/p-Bx1IHqJb.js +2 -0
  134. package/dist/components/p-Bx1IHqJb.js.map +1 -0
  135. package/dist/components/{p-D3fMaUJ3.js → p-Bx5BSd6F.js} +2 -2
  136. package/dist/components/p-Bx5BSd6F.js.map +1 -0
  137. package/dist/components/{p-B8q-6a03.js → p-C0qN3PSb.js} +1 -1
  138. package/dist/components/{p-B8q-6a03.js.map → p-C0qN3PSb.js.map} +1 -1
  139. package/dist/components/{p-H5T258T-.js → p-CFsLF-ts.js} +2 -2
  140. package/dist/components/p-CFsLF-ts.js.map +1 -0
  141. package/dist/components/p-CKtJ6FHM.js +2 -0
  142. package/dist/components/p-CKtJ6FHM.js.map +1 -0
  143. package/dist/components/p-CRSwNbPk.js +2 -0
  144. package/dist/components/p-CRSwNbPk.js.map +1 -0
  145. package/dist/components/{p-BFfLvMZd.js → p-Cea7rROU.js} +2 -2
  146. package/dist/components/{p-BFfLvMZd.js.map → p-Cea7rROU.js.map} +1 -1
  147. package/dist/components/{p-B0Byw6OX.js → p-CgGcgMjs.js} +1 -1
  148. package/dist/components/{p-B0Byw6OX.js.map → p-CgGcgMjs.js.map} +1 -1
  149. package/dist/components/{p-C-Y7gYgz.js → p-ChfVRPHw.js} +2 -2
  150. package/dist/components/p-ChfVRPHw.js.map +1 -0
  151. package/dist/components/{p-CJq3n5kA.js → p-Ck1hgD6g.js} +2 -2
  152. package/dist/components/p-Ck1hgD6g.js.map +1 -0
  153. package/dist/components/{p-BMc0DE59.js → p-CochmPyg.js} +1 -1
  154. package/dist/components/p-CochmPyg.js.map +1 -0
  155. package/dist/components/{p-iMuFgSoM.js → p-Cp5Hr7Vz.js} +2 -2
  156. package/dist/components/p-Cp5Hr7Vz.js.map +1 -0
  157. package/dist/components/p-CpUXubax.js +2 -0
  158. package/dist/components/{p-DtndC9f1.js.map → p-CpUXubax.js.map} +1 -1
  159. package/dist/components/p-Crz606uD.js +2 -0
  160. package/dist/components/p-Crz606uD.js.map +1 -0
  161. package/dist/components/p-Cxr0IQjC.js.map +1 -1
  162. package/dist/components/p-D-MWvpg6.js +2 -0
  163. package/dist/components/p-D-MWvpg6.js.map +1 -0
  164. package/dist/components/{p-Bu3dOjpW.js → p-D7v6Na7U.js} +2 -2
  165. package/dist/components/{p-Bu3dOjpW.js.map → p-D7v6Na7U.js.map} +1 -1
  166. package/dist/components/{p-C6DNhlES.js → p-DA5H_Iq_.js} +1 -1
  167. package/dist/components/p-DA5H_Iq_.js.map +1 -0
  168. package/dist/components/p-DBYCwq9L.js +2 -0
  169. package/dist/components/{p-BXN-e7vF.js.map → p-DBYCwq9L.js.map} +1 -1
  170. package/dist/components/{p-CY4YFAT3.js → p-DCbKsABt.js} +1 -1
  171. package/dist/components/p-DCbKsABt.js.map +1 -0
  172. package/dist/components/{p-C6R5Lft0.js → p-DFC0sRM-.js} +2 -2
  173. package/dist/components/p-DFC0sRM-.js.map +1 -0
  174. package/dist/components/{p-CsBICYmt.js → p-DGkn8FwV.js} +2 -2
  175. package/dist/components/p-DGkn8FwV.js.map +1 -0
  176. package/dist/components/p-DIqT1m2r.js +2 -0
  177. package/dist/components/p-DIqT1m2r.js.map +1 -0
  178. package/dist/components/p-DKt_8s-U.js +2 -0
  179. package/dist/components/p-DKt_8s-U.js.map +1 -0
  180. package/dist/components/p-DOUk4KoT.js.map +1 -1
  181. package/dist/components/p-DQBT1Jkf.js +2 -0
  182. package/dist/components/p-DQBT1Jkf.js.map +1 -0
  183. package/dist/components/p-DW5Ita-P.js +2 -0
  184. package/dist/components/{p-DUdOmSFR.js.map → p-DW5Ita-P.js.map} +1 -1
  185. package/dist/components/{p-DNpLDbWY.js → p-DZ58Sg5a.js} +1 -1
  186. package/dist/components/p-DZ58Sg5a.js.map +1 -0
  187. package/dist/components/{p-DDgeXGbB.js → p-DZv2g9Pe.js} +2 -2
  188. package/dist/components/p-DZv2g9Pe.js.map +1 -0
  189. package/dist/components/{p-BknXYAEc.js → p-DdH7b4JF.js} +2 -2
  190. package/dist/components/p-DdH7b4JF.js.map +1 -0
  191. package/dist/components/{p-DBue3ISk.js → p-Demfba7T.js} +2 -2
  192. package/dist/components/p-Demfba7T.js.map +1 -0
  193. package/dist/components/p-Djxw2FjZ.js +2 -0
  194. package/dist/components/{p-CwjVgUqy.js.map → p-Djxw2FjZ.js.map} +1 -1
  195. package/dist/components/p-DmWoFSGs.js +2 -0
  196. package/dist/components/p-DmWoFSGs.js.map +1 -0
  197. package/dist/components/p-Do8wbFff.js +2 -0
  198. package/dist/components/p-Do8wbFff.js.map +1 -0
  199. package/dist/components/p-Drnyq8z7.js +2 -0
  200. package/dist/components/p-Drnyq8z7.js.map +1 -0
  201. package/dist/components/p-DsgEY6mZ.js +2 -0
  202. package/dist/components/p-DsgEY6mZ.js.map +1 -0
  203. package/dist/components/p-Is1lFmwp.js +2 -0
  204. package/dist/components/p-Is1lFmwp.js.map +1 -0
  205. package/dist/components/{p-BUDAwJVV.js → p-caLOcQUK.js} +2 -2
  206. package/dist/components/p-caLOcQUK.js.map +1 -0
  207. package/dist/components/{p-D1eEgwcP.js → p-etg-Uk1u.js} +1 -1
  208. package/dist/components/{p-D1eEgwcP.js.map → p-etg-Uk1u.js.map} +1 -1
  209. package/dist/components/{p-C9gC4haA.js → p-fRDzpfi9.js} +2 -2
  210. package/dist/components/p-fRDzpfi9.js.map +1 -0
  211. package/dist/components/{p-C-vRkDyn.js → p-rCeztgVn.js} +1 -1
  212. package/dist/components/{p-C-vRkDyn.js.map → p-rCeztgVn.js.map} +1 -1
  213. package/dist/components/{p-B_jZIC9R.js → p-vrotWZxt.js} +2 -2
  214. package/dist/components/{p-B_jZIC9R.js.map → p-vrotWZxt.js.map} +1 -1
  215. package/dist/docs/components.json +35 -6
  216. package/dist/lib/shell/index.d.ts +2 -2
  217. package/dist/lib/shell/index.js +8 -6
  218. package/dist/lib/shell/index.js.map +2 -2
  219. package/dist/lib/toast/index.js +1 -4
  220. package/dist/lib/toast/index.js.map +2 -2
  221. package/dist/lib/utils/index.d.ts +1 -1
  222. package/dist/lib/utils/index.js +104 -22
  223. package/dist/lib/utils/index.js.map +2 -2
  224. package/dist/mcp/cli.js +70 -31
  225. package/dist/mcp-data/registry/agent-activity.json +1 -1
  226. package/dist/mcp-data/registry/agent-questionnaire.json +3 -3
  227. package/dist/mcp-data/registry/agent-source-list.json +2 -2
  228. package/dist/mcp-data/registry/agent-tool-call.json +2 -2
  229. package/dist/mcp-data/registry/attachment-list.json +1 -1
  230. package/dist/mcp-data/registry/badge.json +1 -1
  231. package/dist/mcp-data/registry/banner.json +2 -2
  232. package/dist/mcp-data/registry/bar-action.json +1 -1
  233. package/dist/mcp-data/registry/bar-nav.json +3 -3
  234. package/dist/mcp-data/registry/bar-workflow.json +1 -1
  235. package/dist/mcp-data/registry/breadcrumb.json +1 -1
  236. package/dist/mcp-data/registry/button-filled.json +1 -1
  237. package/dist/mcp-data/registry/button-unfilled.json +1 -1
  238. package/dist/mcp-data/registry/card-chart.json +2 -2
  239. package/dist/mcp-data/registry/card-overview.json +3 -3
  240. package/dist/mcp-data/registry/card-setting.json +1 -1
  241. package/dist/mcp-data/registry/chart-legend.json +1 -1
  242. package/dist/mcp-data/registry/chart.json +1 -1
  243. package/dist/mcp-data/registry/checkbox.json +1 -1
  244. package/dist/mcp-data/registry/chip.json +1 -1
  245. package/dist/mcp-data/registry/code-block.json +1 -1
  246. package/dist/mcp-data/registry/conversation-list-item.json +1 -1
  247. package/dist/mcp-data/registry/conversation-list.json +2 -2
  248. package/dist/mcp-data/registry/divider.json +1 -1
  249. package/dist/mcp-data/registry/empty-state.json +1 -1
  250. package/dist/mcp-data/registry/field.json +1 -1
  251. package/dist/mcp-data/registry/filter-menu.json +1 -1
  252. package/dist/mcp-data/registry/icon.json +2 -2
  253. package/dist/mcp-data/registry/input.json +2 -2
  254. package/dist/mcp-data/registry/loader.json +1 -1
  255. package/dist/mcp-data/registry/menu.json +3 -3
  256. package/dist/mcp-data/registry/message-bubble.json +1 -1
  257. package/dist/mcp-data/registry/message-composer.json +2 -2
  258. package/dist/mcp-data/registry/message-scroller.json +3 -3
  259. package/dist/mcp-data/registry/mobile-bar-nav.json +1 -1
  260. package/dist/mcp-data/registry/mobile-header.json +2 -2
  261. package/dist/mcp-data/registry/mobile-section-switcher.json +2 -2
  262. package/dist/mcp-data/registry/mobile-sheet-nav.json +1 -1
  263. package/dist/mcp-data/registry/modal.json +2 -2
  264. package/dist/mcp-data/registry/pagination.json +32 -11
  265. package/dist/mcp-data/registry/panel-nav.json +2 -2
  266. package/dist/mcp-data/registry/panel-sub-nav.json +1 -1
  267. package/dist/mcp-data/registry/panel-tool-header.json +1 -1
  268. package/dist/mcp-data/registry/panel-tool-search.json +1 -1
  269. package/dist/mcp-data/registry/panel-tools.json +2 -2
  270. package/dist/mcp-data/registry/radio.json +1 -1
  271. package/dist/mcp-data/registry/registry.json +15 -8
  272. package/dist/mcp-data/registry/select.json +1 -1
  273. package/dist/mcp-data/registry/shell-app.json +2 -2
  274. package/dist/mcp-data/registry/shell-page.json +1 -1
  275. package/dist/mcp-data/registry/shell-tools.json +1 -1
  276. package/dist/mcp-data/registry/skeleton.json +2 -2
  277. package/dist/mcp-data/registry/slider.json +2 -2
  278. package/dist/mcp-data/registry/swatch-picker.json +3 -3
  279. package/dist/mcp-data/registry/switch.json +2 -2
  280. package/dist/mcp-data/registry/tab-group.json +2 -2
  281. package/dist/mcp-data/registry/table-sort.json +1 -1
  282. package/dist/mcp-data/registry/table.json +18 -18
  283. package/dist/mcp-data/registry/tag.json +2 -2
  284. package/dist/mcp-data/registry/text.json +4 -4
  285. package/dist/mcp-data/registry/toast.json +2 -2
  286. package/dist/mcp-data/registry/tooltip-chart.json +2 -2
  287. package/dist/mcp-data/registry/tooltip.json +2 -2
  288. package/dist/styles/prose.css +42 -38
  289. package/dist/styles/table.css +39 -67
  290. package/dist/types/components/CardSetting/index.d.ts +1 -1
  291. package/dist/types/components/Pagination/Pagination.d.ts +2 -0
  292. package/dist/types/components/Pagination/pagination-types.d.ts +2 -0
  293. package/dist/types/components/TooltipChart/index.d.ts +1 -1
  294. package/dist/types/components.d.ts +11 -0
  295. package/dist/types/shell/index.d.ts +2 -2
  296. package/dist/types/utils/index.d.ts +1 -1
  297. package/dist/vue/ds-pagination.js +1 -0
  298. package/package.json +11 -1
  299. package/dist/components/p-B00PEE6t.js +0 -2
  300. package/dist/components/p-B00PEE6t.js.map +0 -1
  301. package/dist/components/p-BCOl1x0_.js +0 -2
  302. package/dist/components/p-BCOl1x0_.js.map +0 -1
  303. package/dist/components/p-BHVOiWz5.js +0 -2
  304. package/dist/components/p-BHVOiWz5.js.map +0 -1
  305. package/dist/components/p-BKp__CQX.js.map +0 -1
  306. package/dist/components/p-BMc0DE59.js.map +0 -1
  307. package/dist/components/p-BPG0r_mC.js +0 -2
  308. package/dist/components/p-BPG0r_mC.js.map +0 -1
  309. package/dist/components/p-BUDAwJVV.js.map +0 -1
  310. package/dist/components/p-BXN-e7vF.js +0 -2
  311. package/dist/components/p-Bbb9A3NV.js.map +0 -1
  312. package/dist/components/p-Bece5KH3.js +0 -2
  313. package/dist/components/p-Bece5KH3.js.map +0 -1
  314. package/dist/components/p-BhYS_Ump.js +0 -2
  315. package/dist/components/p-BhYS_Ump.js.map +0 -1
  316. package/dist/components/p-Bkh7scMQ.js +0 -2
  317. package/dist/components/p-Bkh7scMQ.js.map +0 -1
  318. package/dist/components/p-BknXYAEc.js.map +0 -1
  319. package/dist/components/p-C-Y7gYgz.js.map +0 -1
  320. package/dist/components/p-C6DNhlES.js.map +0 -1
  321. package/dist/components/p-C6R5Lft0.js.map +0 -1
  322. package/dist/components/p-C9gC4haA.js.map +0 -1
  323. package/dist/components/p-CAnnQL1F.js +0 -2
  324. package/dist/components/p-CAnnQL1F.js.map +0 -1
  325. package/dist/components/p-CE2IKOsy.js +0 -2
  326. package/dist/components/p-CE2IKOsy.js.map +0 -1
  327. package/dist/components/p-CJq3n5kA.js.map +0 -1
  328. package/dist/components/p-CKVHrOEp.js.map +0 -1
  329. package/dist/components/p-CLVxgTme.js.map +0 -1
  330. package/dist/components/p-CY4YFAT3.js.map +0 -1
  331. package/dist/components/p-Cg0kimXZ.js +0 -2
  332. package/dist/components/p-Cg0kimXZ.js.map +0 -1
  333. package/dist/components/p-CqQFowZd.js.map +0 -1
  334. package/dist/components/p-CsBICYmt.js.map +0 -1
  335. package/dist/components/p-CwjVgUqy.js +0 -2
  336. package/dist/components/p-D123Jvp-.js +0 -2
  337. package/dist/components/p-D123Jvp-.js.map +0 -1
  338. package/dist/components/p-D3fMaUJ3.js.map +0 -1
  339. package/dist/components/p-D7R4yE2C.js +0 -2
  340. package/dist/components/p-D7R4yE2C.js.map +0 -1
  341. package/dist/components/p-D7haeL5y.js +0 -2
  342. package/dist/components/p-D7haeL5y.js.map +0 -1
  343. package/dist/components/p-DAvh38E4.js +0 -2
  344. package/dist/components/p-DAvh38E4.js.map +0 -1
  345. package/dist/components/p-DBue3ISk.js.map +0 -1
  346. package/dist/components/p-DDgeXGbB.js.map +0 -1
  347. package/dist/components/p-DFQ9mcbK.js.map +0 -1
  348. package/dist/components/p-DNpLDbWY.js.map +0 -1
  349. package/dist/components/p-DUdOmSFR.js +0 -2
  350. package/dist/components/p-DtndC9f1.js +0 -2
  351. package/dist/components/p-H5T258T-.js.map +0 -1
  352. package/dist/components/p-JPTkZ3Ji.js +0 -2
  353. package/dist/components/p-LK-jpxwq.js +0 -2
  354. package/dist/components/p-LK-jpxwq.js.map +0 -1
  355. package/dist/components/p-ag6Io-kL.js +0 -2
  356. package/dist/components/p-ag6Io-kL.js.map +0 -1
  357. package/dist/components/p-iMuFgSoM.js.map +0 -1
  358. /package/dist/components/{p-uI6z53BE.js.map → p-Bb4KnRy0.js.map} +0 -0
  359. /package/dist/components/{p-1Hu9jlju.js.map → p-Bf7PZJPT.js.map} +0 -0
@@ -219,17 +219,17 @@
219
219
  "files": [
220
220
  {
221
221
  "path": "src/wc/components/SwatchPicker/SwatchPicker.css",
222
- "content": "@import '../../utils/focus-ring.css';\n@import '../../utils/control-inactive.css';\n@import '../../utils/forced-colors.css';\n\n:host {\n box-sizing: border-box;\n display: block;\n width: 100%;\n --_swatch-inner-inset: calc(\n var(--dimension-stroke-width-025) + var(--dimension-space-025)\n );\n --_swatch-ring-bg: var(--color-background-primary);\n --_swatch-select-motion: var(--effect-motion-short-3);\n}\n\n.swatch-picker {\n box-sizing: border-box;\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n width: 100%;\n min-height: var(--dimension-size-400);\n gap: var(--dimension-space-100);\n padding: var(--dimension-space-100);\n}\n\n.swatch-picker__option {\n position: relative;\n box-sizing: border-box;\n flex: 0 0 auto;\n width: var(--dimension-size-300);\n height: var(--dimension-size-300);\n padding: 0;\n border: none;\n border-radius: var(--dimension-radius-half);\n background: transparent;\n cursor: default;\n isolation: isolate;\n}\n\n.swatch-picker__fill,\n.swatch-picker__border,\n.swatch-picker__interaction,\n.swatch-picker__halo,\n.swatch-picker__stroke {\n position: absolute;\n inset: 0;\n border-radius: inherit;\n pointer-events: none;\n}\n\n.swatch-picker__fill {\n z-index: 1;\n background-color: var(--_swatch-preview-color);\n background-image: var(--_swatch-preview-image);\n background-repeat: no-repeat;\n background-position: center;\n background-size: cover;\n opacity: var(--_swatch-preview-opacity);\n transition:\n inset var(--_swatch-select-motion);\n}\n\n.swatch-picker__border {\n z-index: 2;\n box-shadow: inset 0 0 0 var(--dimension-stroke-width-012) var(--color-border-secondary);\n transition:\n inset var(--_swatch-select-motion);\n}\n\n.swatch-picker__interaction {\n z-index: 3;\n background-color: transparent;\n transition:\n inset var(--_swatch-select-motion),\n background-color 0s;\n}\n\n@media (hover: hover) and (pointer: fine) {\n .swatch-picker__option:hover:not(:disabled) .swatch-picker__interaction {\n background-color: var(--color-interaction-hover);\n }\n}\n\n.swatch-picker__option:active:not(:disabled) .swatch-picker__interaction {\n background-color: var(--color-interaction-pressed);\n}\n\n.swatch-picker__halo,\n.swatch-picker__stroke {\n opacity: 0;\n transform: scale(1.2);\n transform-origin: center;\n transition:\n opacity var(--_swatch-select-motion),\n transform var(--_swatch-select-motion);\n}\n\n.swatch-picker__halo {\n z-index: 4;\n box-shadow: inset 0 0 0 var(--_swatch-inner-inset) var(--_swatch-ring-bg);\n}\n\n.swatch-picker__stroke {\n z-index: 5;\n box-shadow: inset 0 0 0 var(--dimension-stroke-width-025) var(--color-border-bold-brand);\n}\n\n.swatch-picker__option--selected .swatch-picker__fill,\n.swatch-picker__option--selected .swatch-picker__border,\n.swatch-picker__option--selected .swatch-picker__interaction {\n inset: var(--_swatch-inner-inset);\n}\n\n.swatch-picker__option--selected .swatch-picker__halo,\n.swatch-picker__option--selected .swatch-picker__stroke {\n opacity: 1;\n transform: scale(1);\n}\n\n.swatch-picker__divider {\n flex: 0 0 auto;\n align-self: center;\n width: var(--dimension-stroke-width-012);\n height: var(--dimension-size-300);\n background-color: var(--color-border-tertiary);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .swatch-picker__fill,\n .swatch-picker__border,\n .swatch-picker__interaction,\n .swatch-picker__halo,\n .swatch-picker__stroke {\n transition: none;\n }\n}\n\n@media (forced-colors: active) {\n .swatch-picker__fill {\n forced-color-adjust: none;\n }\n\n .swatch-picker__border {\n box-shadow: none;\n outline: var(--dimension-stroke-width-012) solid var(--ds-forced-color-content);\n outline-offset: calc(-1 * var(--dimension-stroke-width-012));\n }\n\n .swatch-picker__halo {\n box-shadow: none;\n }\n\n .swatch-picker__stroke {\n box-shadow: none;\n outline: var(--dimension-stroke-width-025) solid var(--ds-forced-color-selected);\n outline-offset: calc(-1 * var(--dimension-stroke-width-025));\n }\n\n .swatch-picker__divider {\n background-color: var(--ds-forced-color-content);\n }\n}\n",
222
+ "content": "@import '../../utils/focus-ring.css';\n@import '../../utils/control-inactive.css';\n@import '../../utils/forced-colors.css';\n\n:host {\n box-sizing: border-box;\n display: block;\n width: 100%;\n --_swatch-inner-inset: calc(var(--dimension-stroke-width-025) + var(--dimension-space-025));\n --_swatch-ring-bg: var(--color-background-primary);\n --_swatch-select-motion: var(--effect-motion-short-3);\n}\n\n.swatch-picker {\n box-sizing: border-box;\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n width: 100%;\n min-height: var(--dimension-size-400);\n gap: var(--dimension-space-100);\n padding: var(--dimension-space-100);\n}\n\n.swatch-picker__option {\n position: relative;\n box-sizing: border-box;\n flex: 0 0 auto;\n width: var(--dimension-size-300);\n height: var(--dimension-size-300);\n padding: 0;\n border: none;\n border-radius: var(--dimension-radius-half);\n background: transparent;\n cursor: default;\n isolation: isolate;\n}\n\n.swatch-picker__fill,\n.swatch-picker__border,\n.swatch-picker__interaction,\n.swatch-picker__halo,\n.swatch-picker__stroke {\n position: absolute;\n inset: 0;\n border-radius: inherit;\n pointer-events: none;\n}\n\n.swatch-picker__fill {\n z-index: 1;\n background-color: var(--_swatch-preview-color);\n background-image: var(--_swatch-preview-image);\n background-repeat: no-repeat;\n background-position: center;\n background-size: cover;\n opacity: var(--_swatch-preview-opacity);\n transition: inset var(--_swatch-select-motion);\n}\n\n.swatch-picker__border {\n z-index: 2;\n box-shadow: inset 0 0 0 var(--dimension-stroke-width-012) var(--color-border-secondary);\n transition: inset var(--_swatch-select-motion);\n}\n\n.swatch-picker__interaction {\n z-index: 3;\n background-color: transparent;\n transition:\n inset var(--_swatch-select-motion),\n background-color 0s;\n}\n\n@media (hover: hover) and (pointer: fine) {\n .swatch-picker__option:hover:not(:disabled) .swatch-picker__interaction {\n background-color: var(--color-interaction-hover);\n }\n}\n\n.swatch-picker__option:active:not(:disabled) .swatch-picker__interaction {\n background-color: var(--color-interaction-pressed);\n}\n\n.swatch-picker__halo,\n.swatch-picker__stroke {\n opacity: 0;\n transform: scale(1.2);\n transform-origin: center;\n transition:\n opacity var(--_swatch-select-motion),\n transform var(--_swatch-select-motion);\n}\n\n.swatch-picker__halo {\n z-index: 4;\n box-shadow: inset 0 0 0 var(--_swatch-inner-inset) var(--_swatch-ring-bg);\n}\n\n.swatch-picker__stroke {\n z-index: 5;\n box-shadow: inset 0 0 0 var(--dimension-stroke-width-025) var(--color-border-bold-brand);\n}\n\n.swatch-picker__option--selected .swatch-picker__fill,\n.swatch-picker__option--selected .swatch-picker__border,\n.swatch-picker__option--selected .swatch-picker__interaction {\n inset: var(--_swatch-inner-inset);\n}\n\n.swatch-picker__option--selected .swatch-picker__halo,\n.swatch-picker__option--selected .swatch-picker__stroke {\n opacity: 1;\n transform: scale(1);\n}\n\n.swatch-picker__divider {\n flex: 0 0 auto;\n align-self: center;\n width: var(--dimension-stroke-width-012);\n height: var(--dimension-size-300);\n background-color: var(--color-border-tertiary);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .swatch-picker__fill,\n .swatch-picker__border,\n .swatch-picker__interaction,\n .swatch-picker__halo,\n .swatch-picker__stroke {\n transition: none;\n }\n}\n\n@media (forced-colors: active) {\n .swatch-picker__fill {\n forced-color-adjust: none;\n }\n\n .swatch-picker__border {\n box-shadow: none;\n outline: var(--dimension-stroke-width-012) solid var(--ds-forced-color-content);\n outline-offset: calc(-1 * var(--dimension-stroke-width-012));\n }\n\n .swatch-picker__halo {\n box-shadow: none;\n }\n\n .swatch-picker__stroke {\n box-shadow: none;\n outline: var(--dimension-stroke-width-025) solid var(--ds-forced-color-selected);\n outline-offset: calc(-1 * var(--dimension-stroke-width-025));\n }\n\n .swatch-picker__divider {\n background-color: var(--ds-forced-color-content);\n }\n}\n",
223
223
  "type": "registry:ui"
224
224
  },
225
225
  {
226
226
  "path": "src/wc/components/SwatchPicker/SwatchPicker.tsx",
227
- "content": "import { Component, Element, Event, EventEmitter, Prop, h, Host } from '@stencil/core';\nimport {\n flattenSwatchPickerOptions,\n normalizeSwatchPickerOpacity,\n resolveSwatchPickerNavigationIndex,\n resolveSwatchPickerTabIndex,\n type SwatchPickerNavigationKey,\n type SwatchPickerOption,\n type SwatchPickerSection,\n} from './swatch-picker-types';\n\n@Component({\n tag: 'ds-swatch-picker',\n styleUrl: 'SwatchPicker.css',\n scoped: true,\n})\nexport class SwatchPicker {\n @Element() el!: HTMLElement;\n\n /** Selected option value. */\n @Prop({ mutable: true, reflect: true }) value: string = '';\n\n /** Flat option list. Sections take precedence when supplied. Assign as a JavaScript property. */\n @Prop() options: SwatchPickerOption[] = [];\n\n /** Visually separated option groups that remain one radio group. Assign as a JavaScript property. */\n @Prop() sections: SwatchPickerSection[] = [];\n\n /** Accessible name for the complete one-of-many choice. */\n @Prop() groupLabel: string = 'Swatch options';\n\n @Event() dsChange!: EventEmitter<string>;\n\n private get activeOptions(): SwatchPickerOption[] {\n return flattenSwatchPickerOptions(this.options, this.sections);\n }\n\n private selectOption(option: SwatchPickerOption) {\n if (option.isInactive || option.value === this.value) return;\n this.value = option.value;\n this.dsChange.emit(option.value);\n }\n\n private focusOption(index: number) {\n requestAnimationFrame(() => {\n const optionButtons = this.el\n .querySelectorAll<HTMLButtonElement>('.swatch-picker__option');\n optionButtons[index]?.focus();\n });\n }\n\n private handleOptionKeyDown(\n event: KeyboardEvent,\n currentIndex: number,\n ) {\n const key = event.key as SwatchPickerNavigationKey;\n if (!['ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'Home', 'End'].includes(key)) {\n return;\n }\n\n const nextIndex = resolveSwatchPickerNavigationIndex(\n this.activeOptions,\n currentIndex,\n key,\n );\n if (nextIndex === null) return;\n\n event.preventDefault();\n const nextOption = this.activeOptions[nextIndex];\n this.selectOption(nextOption);\n this.focusOption(nextIndex);\n }\n\n private renderOption(\n option: SwatchPickerOption,\n index: number,\n tabIndex: number,\n ) {\n const selected = option.value === this.value;\n const opacity = normalizeSwatchPickerOpacity(option.preview.opacity);\n\n return (\n <button\n key={option.value}\n type=\"button\"\n class={{\n 'swatch-picker__option': true,\n 'swatch-picker__option--selected': selected,\n 'ds-focus-ring-inset': true,\n 'ds-control-inactive': !!option.isInactive,\n }}\n style={{\n '--_swatch-preview-color': option.preview.backgroundColor ?? 'var(--color-background-secondary)',\n '--_swatch-preview-image': option.preview.backgroundImage ?? 'none',\n '--_swatch-preview-opacity': String(opacity),\n }}\n role=\"radio\"\n aria-label={option.label}\n aria-checked={selected ? 'true' : 'false'}\n tabIndex={index === tabIndex ? 0 : -1}\n disabled={option.isInactive}\n onClick={() => this.selectOption(option)}\n onKeyDown={(event: KeyboardEvent) => this.handleOptionKeyDown(event, index)}\n >\n <span class=\"swatch-picker__fill\" aria-hidden=\"true\" />\n <span class=\"swatch-picker__border\" aria-hidden=\"true\" />\n <span class=\"swatch-picker__interaction\" aria-hidden=\"true\" />\n <span class=\"swatch-picker__halo\" aria-hidden=\"true\" />\n <span class=\"swatch-picker__stroke\" aria-hidden=\"true\" />\n </button>\n );\n }\n\n render() {\n const activeOptions = this.activeOptions;\n const tabIndex = resolveSwatchPickerTabIndex(activeOptions, this.value);\n let optionIndex = 0;\n\n return (\n <Host>\n <div\n class=\"swatch-picker\"\n role=\"radiogroup\"\n aria-label={this.groupLabel}\n >\n {(this.sections.length > 0 ? this.sections : [{ options: this.options }]).map((section, sectionIndex) => [\n section.options.map(option => {\n const currentIndex = optionIndex++;\n return this.renderOption(option, currentIndex, tabIndex);\n }),\n sectionIndex < (this.sections.length > 0 ? this.sections.length : 1) - 1 && (\n <span class=\"swatch-picker__divider\" aria-hidden=\"true\" />\n ),\n ])}\n </div>\n </Host>\n );\n }\n}\n",
227
+ "content": "import { Component, Element, Event, EventEmitter, Prop, h, Host } from '@stencil/core';\nimport {\n flattenSwatchPickerOptions,\n normalizeSwatchPickerOpacity,\n resolveSwatchPickerNavigationIndex,\n resolveSwatchPickerTabIndex,\n type SwatchPickerNavigationKey,\n type SwatchPickerOption,\n type SwatchPickerSection,\n} from './swatch-picker-types';\n\n@Component({\n tag: 'ds-swatch-picker',\n styleUrl: 'SwatchPicker.css',\n scoped: true,\n})\nexport class SwatchPicker {\n @Element() el!: HTMLElement;\n\n /** Selected option value. */\n @Prop({ mutable: true, reflect: true }) value: string = '';\n\n /** Flat option list. Sections take precedence when supplied. Assign as a JavaScript property. */\n @Prop() options: SwatchPickerOption[] = [];\n\n /** Visually separated option groups that remain one radio group. Assign as a JavaScript property. */\n @Prop() sections: SwatchPickerSection[] = [];\n\n /** Accessible name for the complete one-of-many choice. */\n @Prop() groupLabel: string = 'Swatch options';\n\n @Event() dsChange!: EventEmitter<string>;\n\n private get activeOptions(): SwatchPickerOption[] {\n return flattenSwatchPickerOptions(this.options, this.sections);\n }\n\n private selectOption(option: SwatchPickerOption) {\n if (option.isInactive || option.value === this.value) return;\n this.value = option.value;\n this.dsChange.emit(option.value);\n }\n\n private focusOption(index: number) {\n requestAnimationFrame(() => {\n const optionButtons = this.el.querySelectorAll<HTMLButtonElement>('.swatch-picker__option');\n optionButtons[index]?.focus();\n });\n }\n\n private handleOptionKeyDown(event: KeyboardEvent, currentIndex: number) {\n const key = event.key as SwatchPickerNavigationKey;\n if (!['ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'Home', 'End'].includes(key)) {\n return;\n }\n\n const nextIndex = resolveSwatchPickerNavigationIndex(this.activeOptions, currentIndex, key);\n if (nextIndex === null) return;\n\n event.preventDefault();\n const nextOption = this.activeOptions[nextIndex];\n this.selectOption(nextOption);\n this.focusOption(nextIndex);\n }\n\n private renderOption(option: SwatchPickerOption, index: number, tabIndex: number) {\n const selected = option.value === this.value;\n const opacity = normalizeSwatchPickerOpacity(option.preview.opacity);\n\n return (\n <button\n key={option.value}\n type=\"button\"\n class={{\n 'swatch-picker__option': true,\n 'swatch-picker__option--selected': selected,\n 'ds-focus-ring-inset': true,\n 'ds-control-inactive': !!option.isInactive,\n }}\n style={{\n '--_swatch-preview-color':\n option.preview.backgroundColor ?? 'var(--color-background-secondary)',\n '--_swatch-preview-image': option.preview.backgroundImage ?? 'none',\n '--_swatch-preview-opacity': String(opacity),\n }}\n role=\"radio\"\n aria-label={option.label}\n aria-checked={selected ? 'true' : 'false'}\n tabIndex={index === tabIndex ? 0 : -1}\n disabled={option.isInactive}\n onClick={() => this.selectOption(option)}\n onKeyDown={(event: KeyboardEvent) => this.handleOptionKeyDown(event, index)}\n >\n <span class=\"swatch-picker__fill\" aria-hidden=\"true\" />\n <span class=\"swatch-picker__border\" aria-hidden=\"true\" />\n <span class=\"swatch-picker__interaction\" aria-hidden=\"true\" />\n <span class=\"swatch-picker__halo\" aria-hidden=\"true\" />\n <span class=\"swatch-picker__stroke\" aria-hidden=\"true\" />\n </button>\n );\n }\n\n render() {\n const activeOptions = this.activeOptions;\n const tabIndex = resolveSwatchPickerTabIndex(activeOptions, this.value);\n let optionIndex = 0;\n\n return (\n <Host>\n <div class=\"swatch-picker\" role=\"radiogroup\" aria-label={this.groupLabel}>\n {(this.sections.length > 0 ? this.sections : [{ options: this.options }]).map(\n (section, sectionIndex) => [\n section.options.map(option => {\n const currentIndex = optionIndex++;\n return this.renderOption(option, currentIndex, tabIndex);\n }),\n sectionIndex < (this.sections.length > 0 ? this.sections.length : 1) - 1 && (\n <span class=\"swatch-picker__divider\" aria-hidden=\"true\" />\n ),\n ]\n )}\n </div>\n </Host>\n );\n }\n}\n",
228
228
  "type": "registry:ui"
229
229
  },
230
230
  {
231
231
  "path": "src/wc/components/SwatchPicker/swatch-picker-types.ts",
232
- "content": "export interface SwatchPickerPreview {\n /** CSS color used as the swatch base or as a complete flat-color preview. */\n backgroundColor?: string;\n /** CSS gradient used as the swatch image. */\n backgroundImage?: string;\n /** Optional preview-layer opacity, clamped to the inclusive 0–1 range. */\n opacity?: number;\n}\n\nexport interface SwatchPickerOption {\n /** Stable value emitted when this option is selected. */\n value: string;\n /** Accessible option name; the visual preview never replaces this label. */\n label: string;\n preview: SwatchPickerPreview;\n isInactive?: boolean;\n}\n\nexport interface SwatchPickerSection {\n /** Sections remain one radio group and are separated visually. */\n options: SwatchPickerOption[];\n}\n\nexport type SwatchPickerNavigationKey =\n | 'ArrowDown'\n | 'ArrowLeft'\n | 'ArrowRight'\n | 'ArrowUp'\n | 'Home'\n | 'End';\n\nexport function flattenSwatchPickerOptions(\n options: readonly SwatchPickerOption[],\n sections: readonly SwatchPickerSection[],\n): SwatchPickerOption[] {\n return sections.length > 0\n ? sections.flatMap(section => section.options)\n : [...options];\n}\n\nexport function resolveSwatchPickerTabIndex(\n options: readonly SwatchPickerOption[],\n value: string,\n): number {\n const selectedIndex = options.findIndex(option => (\n option.value === value && !option.isInactive\n ));\n if (selectedIndex >= 0) return selectedIndex;\n return options.findIndex(option => !option.isInactive);\n}\n\nexport function resolveSwatchPickerNavigationIndex(\n options: readonly SwatchPickerOption[],\n currentIndex: number,\n key: SwatchPickerNavigationKey,\n): number | null {\n const enabledIndexes = options\n .map((option, index) => ({ option, index }))\n .filter(({ option }) => !option.isInactive)\n .map(({ index }) => index);\n\n if (enabledIndexes.length === 0) return null;\n if (key === 'Home') return enabledIndexes[0];\n if (key === 'End') return enabledIndexes[enabledIndexes.length - 1];\n\n const direction = key === 'ArrowRight' || key === 'ArrowDown' ? 1 : -1;\n const currentEnabledIndex = enabledIndexes.indexOf(currentIndex);\n if (currentEnabledIndex < 0) {\n return direction > 0\n ? enabledIndexes[0]\n : enabledIndexes[enabledIndexes.length - 1];\n }\n\n return enabledIndexes[\n (currentEnabledIndex + direction + enabledIndexes.length) % enabledIndexes.length\n ];\n}\n\nexport function normalizeSwatchPickerOpacity(opacity: number | undefined): number {\n if (opacity === undefined || !Number.isFinite(opacity)) return 1;\n return Math.min(1, Math.max(0, opacity));\n}\n",
232
+ "content": "export interface SwatchPickerPreview {\n /** CSS color used as the swatch base or as a complete flat-color preview. */\n backgroundColor?: string;\n /** CSS gradient used as the swatch image. */\n backgroundImage?: string;\n /** Optional preview-layer opacity, clamped to the inclusive 0–1 range. */\n opacity?: number;\n}\n\nexport interface SwatchPickerOption {\n /** Stable value emitted when this option is selected. */\n value: string;\n /** Accessible option name; the visual preview never replaces this label. */\n label: string;\n preview: SwatchPickerPreview;\n isInactive?: boolean;\n}\n\nexport interface SwatchPickerSection {\n /** Sections remain one radio group and are separated visually. */\n options: SwatchPickerOption[];\n}\n\nexport type SwatchPickerNavigationKey =\n | 'ArrowDown'\n | 'ArrowLeft'\n | 'ArrowRight'\n | 'ArrowUp'\n | 'Home'\n | 'End';\n\nexport function flattenSwatchPickerOptions(\n options: readonly SwatchPickerOption[],\n sections: readonly SwatchPickerSection[]\n): SwatchPickerOption[] {\n return sections.length > 0 ? sections.flatMap(section => section.options) : [...options];\n}\n\nexport function resolveSwatchPickerTabIndex(\n options: readonly SwatchPickerOption[],\n value: string\n): number {\n const selectedIndex = options.findIndex(option => option.value === value && !option.isInactive);\n if (selectedIndex >= 0) return selectedIndex;\n return options.findIndex(option => !option.isInactive);\n}\n\nexport function resolveSwatchPickerNavigationIndex(\n options: readonly SwatchPickerOption[],\n currentIndex: number,\n key: SwatchPickerNavigationKey\n): number | null {\n const enabledIndexes = options\n .map((option, index) => ({ option, index }))\n .filter(({ option }) => !option.isInactive)\n .map(({ index }) => index);\n\n if (enabledIndexes.length === 0) return null;\n if (key === 'Home') return enabledIndexes[0];\n if (key === 'End') return enabledIndexes[enabledIndexes.length - 1];\n\n const direction = key === 'ArrowRight' || key === 'ArrowDown' ? 1 : -1;\n const currentEnabledIndex = enabledIndexes.indexOf(currentIndex);\n if (currentEnabledIndex < 0) {\n return direction > 0 ? enabledIndexes[0] : enabledIndexes[enabledIndexes.length - 1];\n }\n\n return enabledIndexes[\n (currentEnabledIndex + direction + enabledIndexes.length) % enabledIndexes.length\n ];\n}\n\nexport function normalizeSwatchPickerOpacity(opacity: number | undefined): number {\n if (opacity === undefined || !Number.isFinite(opacity)) return 1;\n return Math.min(1, Math.max(0, opacity));\n}\n",
233
233
  "type": "registry:ui"
234
234
  }
235
235
  ]
@@ -357,12 +357,12 @@
357
357
  "files": [
358
358
  {
359
359
  "path": "src/wc/components/Switch/Switch.css",
360
- "content": "@import '../../utils/focus-ring.css';\n@import '../../utils/control-inactive.css';\n@import '../../utils/forced-colors.css';\n\n:host {\n --ds-switch-width: var(--dimension-size-400);\n --ds-switch-height: var(--dimension-size-250);\n --ds-switch-thumb-size: var(--dimension-size-150);\n --ds-switch-gap: var(--dimension-space-050);\n --ds-switch-stroke: var(--dimension-stroke-width-012);\n --ds-switch-inset: var(--ds-switch-gap);\n --ds-switch-thumb-hover: var(--color-interaction-hover);\n --ds-switch-thumb-pressed: var(--color-interaction-pressed);\n --ds-switch-travel: calc(\n var(--ds-switch-width) - var(--ds-switch-thumb-size) - var(--ds-switch-inset) -\n var(--ds-switch-inset)\n );\n position: relative;\n display: inline-flex;\n width: var(--ds-switch-width);\n height: var(--ds-switch-height);\n padding: 0;\n border: none;\n border-radius: var(--dimension-radius-half);\n box-sizing: border-box;\n flex-shrink: 0;\n overflow: visible;\n cursor: default;\n user-select: none;\n outline: none;\n background-color: transparent;\n box-shadow: inset 0 0 0 var(--ds-switch-stroke) var(--color-border-secondary);\n transition:\n background-color var(--effect-motion-short-3),\n box-shadow var(--effect-motion-short-3);\n}\n\n:host(.switch--lg) {\n --ds-switch-width: var(--dimension-size-500);\n --ds-switch-height: var(--dimension-size-300);\n --ds-switch-thumb-size: var(--dimension-iconography-sm);\n --ds-switch-gap: var(--dimension-space-050);\n}\n\n:host(.switch--sm) {\n --ds-switch-width: var(--dimension-size-300);\n --ds-switch-height: var(--dimension-size-200);\n --ds-switch-thumb-size: calc(var(--dimension-size-base) * 5 / 4);\n --ds-switch-gap: calc(var(--dimension-space-025) + var(--dimension-space-012));\n}\n\n:host(.switch--xs) {\n --ds-switch-width: var(--dimension-size-250);\n --ds-switch-height: var(--dimension-size-150);\n --ds-switch-thumb-size: var(--dimension-size-100);\n --ds-switch-gap: var(--dimension-space-025);\n}\n\n:host(.checked) {\n background-color: var(--color-background-bold-brand);\n box-shadow: none;\n}\n\n:host(.switch--readonly),\n:host(.switch--presentation) {\n cursor: default;\n}\n\n:host(.switch--presentation) {\n pointer-events: none;\n}\n\n/* Inactive: `.ds-control-inactive` (50% opacity). */\n\n.thumb {\n position: absolute;\n top: 50%;\n left: var(--ds-switch-inset);\n width: var(--ds-switch-thumb-size);\n height: var(--ds-switch-thumb-size);\n border-radius: var(--dimension-radius-half);\n background-color: var(--color-foreground-tertiary);\n box-shadow: none;\n transform: translateY(-50%);\n transition:\n background-color var(--effect-motion-short-3),\n transform var(--effect-motion-short-3);\n pointer-events: none;\n}\n\n.thumb::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: inherit;\n background: transparent;\n pointer-events: none;\n}\n\n@media (hover: hover) and (pointer: fine) {\n :host(:hover:not(:disabled):not(.ds-control-inactive):not(.switch--readonly):not(.switch--presentation))\n .thumb::after {\n background: var(--ds-switch-thumb-hover);\n }\n}\n\n:host(:active:not(:disabled):not(.ds-control-inactive):not(.switch--readonly):not(.switch--presentation))\n .thumb::after {\n background: var(--ds-switch-thumb-pressed);\n}\n\n:host(.checked) .thumb {\n background-color: var(--color-background-primary);\n box-shadow: none;\n transform: translate3d(var(--ds-switch-travel), -50%, 0);\n}\n\n@media (prefers-reduced-motion: reduce) {\n :host {\n transition: none;\n }\n\n .thumb {\n transition: none;\n }\n}\n\n@media (forced-colors: active) {\n :host {\n border: var(--ds-switch-stroke) solid var(--ds-forced-color-content);\n box-shadow: none;\n }\n\n :host(.checked) {\n background-color: var(--ds-forced-color-selected);\n border-color: var(--ds-forced-color-selected);\n }\n\n .thumb {\n background-color: var(--ds-forced-color-content);\n }\n\n :host(.checked) .thumb {\n background-color: var(--ds-forced-color-on-selected);\n }\n}\n",
360
+ "content": "@import '../../utils/focus-ring.css';\n@import '../../utils/control-inactive.css';\n@import '../../utils/forced-colors.css';\n\n:host {\n --ds-switch-width: var(--dimension-size-400);\n --ds-switch-height: var(--dimension-size-250);\n --ds-switch-thumb-size: var(--dimension-size-150);\n --ds-switch-gap: var(--dimension-space-050);\n --ds-switch-stroke: var(--dimension-stroke-width-012);\n --ds-switch-inset: var(--ds-switch-gap);\n --ds-switch-thumb-hover: var(--color-interaction-hover);\n --ds-switch-thumb-pressed: var(--color-interaction-pressed);\n --ds-switch-travel: calc(\n var(--ds-switch-width) - var(--ds-switch-thumb-size) - var(--ds-switch-inset) -\n var(--ds-switch-inset)\n );\n position: relative;\n display: inline-flex;\n width: var(--ds-switch-width);\n height: var(--ds-switch-height);\n padding: 0;\n border: none;\n border-radius: var(--dimension-radius-half);\n box-sizing: border-box;\n flex-shrink: 0;\n overflow: visible;\n cursor: default;\n user-select: none;\n outline: none;\n background-color: transparent;\n box-shadow: inset 0 0 0 var(--ds-switch-stroke) var(--color-border-secondary);\n transition:\n background-color var(--effect-motion-short-3),\n box-shadow var(--effect-motion-short-3);\n}\n\n:host(.switch--lg) {\n --ds-switch-width: var(--dimension-size-500);\n --ds-switch-height: var(--dimension-size-300);\n --ds-switch-thumb-size: var(--dimension-iconography-sm);\n --ds-switch-gap: var(--dimension-space-050);\n}\n\n:host(.switch--sm) {\n --ds-switch-width: var(--dimension-size-300);\n --ds-switch-height: var(--dimension-size-200);\n --ds-switch-thumb-size: calc(var(--dimension-size-base) * 5 / 4);\n --ds-switch-gap: calc(var(--dimension-space-025) + var(--dimension-space-012));\n}\n\n:host(.switch--xs) {\n --ds-switch-width: var(--dimension-size-250);\n --ds-switch-height: var(--dimension-size-150);\n --ds-switch-thumb-size: var(--dimension-size-100);\n --ds-switch-gap: var(--dimension-space-025);\n}\n\n:host(.checked) {\n background-color: var(--color-background-bold-brand);\n box-shadow: none;\n}\n\n:host(.switch--readonly),\n:host(.switch--presentation) {\n cursor: default;\n}\n\n:host(.switch--presentation) {\n pointer-events: none;\n}\n\n/* Inactive: `.ds-control-inactive` (50% opacity). */\n\n.thumb {\n position: absolute;\n top: 50%;\n left: var(--ds-switch-inset);\n width: var(--ds-switch-thumb-size);\n height: var(--ds-switch-thumb-size);\n border-radius: var(--dimension-radius-half);\n background-color: var(--color-foreground-tertiary);\n box-shadow: none;\n transform: translateY(-50%);\n transition:\n background-color var(--effect-motion-short-3),\n transform var(--effect-motion-short-3);\n pointer-events: none;\n}\n\n.thumb::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: inherit;\n background: transparent;\n pointer-events: none;\n}\n\n@media (hover: hover) and (pointer: fine) {\n :host(\n :hover:not(:disabled):not(.ds-control-inactive):not(.switch--readonly):not(\n .switch--presentation\n )\n )\n .thumb::after {\n background: var(--ds-switch-thumb-hover);\n }\n}\n\n:host(\n :active:not(:disabled):not(.ds-control-inactive):not(.switch--readonly):not(\n .switch--presentation\n )\n )\n .thumb::after {\n background: var(--ds-switch-thumb-pressed);\n}\n\n:host(.checked) .thumb {\n background-color: var(--color-background-primary);\n box-shadow: none;\n transform: translate3d(var(--ds-switch-travel), -50%, 0);\n}\n\n@media (prefers-reduced-motion: reduce) {\n :host {\n transition: none;\n }\n\n .thumb {\n transition: none;\n }\n}\n\n@media (forced-colors: active) {\n :host {\n border: var(--ds-switch-stroke) solid var(--ds-forced-color-content);\n box-shadow: none;\n }\n\n :host(.checked) {\n background-color: var(--ds-forced-color-selected);\n border-color: var(--ds-forced-color-selected);\n }\n\n .thumb {\n background-color: var(--ds-forced-color-content);\n }\n\n :host(.checked) .thumb {\n background-color: var(--ds-forced-color-on-selected);\n }\n}\n",
361
361
  "type": "registry:ui"
362
362
  },
363
363
  {
364
364
  "path": "src/wc/components/Switch/Switch.tsx",
365
- "content": "import { AttachInternals, Component, Element, Prop, State, Event, EventEmitter, Watch, h, Host } from '@stencil/core';\nimport { DEFAULT_REQUIRED_MESSAGE, setFormControlValue, setRequiredValidity } from '../../utils';\n\nexport type SwitchSize = 'lg' | 'md' | 'sm' | 'xs';\n\nlet generatedLabelId = 0;\n\n@Component({\n tag: 'ds-switch',\n styleUrl: 'Switch.css',\n shadow: true,\n formAssociated: true,\n})\nexport class Switch {\n @Element() el!: HTMLElement;\n @AttachInternals() internals!: ElementInternals;\n\n /** Current on/off state. The initial value is restored when the owning form resets. */\n @Prop({ mutable: true }) checked: boolean = false;\n\n @Prop({ reflect: true }) name: string | undefined;\n @Prop() value: string = 'on';\n\n /** Optional value submitted when unchecked; omitted by default like a native checkbox. */\n @Prop() uncheckedValue: string | undefined;\n\n /** Associates the switch with a form by id when it is rendered outside that form. */\n @Prop({ reflect: true }) form: string | undefined;\n\n @Prop({ reflect: true }) disabled: boolean = false;\n @Prop({ reflect: true }) readOnly: boolean = false;\n @Prop({ reflect: true }) required: boolean = false;\n @Prop() requiredMessage: string = DEFAULT_REQUIRED_MESSAGE;\n @Prop() isInactive: boolean = false;\n\n /** Compact track size for placement inside controls, menus, and form rows. */\n @Prop() size: SwitchSize = 'md';\n\n /**\n * Removes standalone control semantics so a composite owner such as a\n * `menuitemcheckbox` can use the switch as an aria-hidden visual indicator.\n */\n @Prop() presentation: boolean = false;\n\n @Event() dsChange!: EventEmitter<boolean>;\n\n private initialChecked = false;\n @State() private formDisabled = false;\n @State() private focused = false;\n @State() private touched = false;\n\n componentWillLoad() {\n this.initialChecked = this.checked;\n this.syncFormValue();\n }\n\n componentDidLoad() {\n this.syncNativeLabels();\n }\n\n @Watch('checked')\n @Watch('value')\n @Watch('uncheckedValue')\n @Watch('disabled')\n @Watch('isInactive')\n @Watch('presentation')\n @Watch('required')\n syncFormValue() {\n const inactive = this.presentation || this.isInactive || this.disabled || this.formDisabled;\n const submissionValue = this.checked ? this.value : this.uncheckedValue ?? null;\n const state = this.checked ? 'checked' : 'unchecked';\n setFormControlValue(this.internals, submissionValue, { inactive, state });\n\n const missing = this.required && !inactive && !this.checked;\n setRequiredValidity(this.internals, missing, this.requiredMessage);\n }\n\n formDisabledCallback(disabled: boolean) {\n this.formDisabled = disabled;\n this.syncFormValue();\n }\n\n formResetCallback() {\n this.checked = this.initialChecked;\n this.focused = false;\n this.touched = false;\n }\n\n formStateRestoreCallback(state: string | File | FormData | null) {\n this.checked = state === 'checked';\n }\n\n private syncNativeLabels() {\n if (this.presentation) return;\n if (this.el.hasAttribute('aria-label') || this.el.hasAttribute('aria-labelledby')) return;\n\n const labelIds = (Array.from(this.internals.labels) as HTMLLabelElement[]).map(label => {\n if (!label.id) label.id = `ds-switch-label-${++generatedLabelId}`;\n return label.id;\n });\n if (labelIds.length) this.el.setAttribute('aria-labelledby', labelIds.join(' '));\n }\n\n private handleClick = () => {\n if (this.presentation || this.readOnly || this.isInactive || this.disabled || this.formDisabled) return;\n this.checked = !this.checked;\n this.dsChange.emit(this.checked);\n };\n\n private handleKeyDown = (event: KeyboardEvent) => {\n if (this.presentation || this.readOnly || this.isInactive || this.disabled || this.formDisabled) return;\n if (event.key === ' ' || event.key === 'Enter') {\n event.preventDefault();\n this.handleClick();\n }\n };\n\n private handleFocus = () => {\n if (!this.presentation) this.focused = true;\n };\n\n private handleBlur = () => {\n if (this.presentation) return;\n this.focused = false;\n this.touched = true;\n };\n\n render() {\n const inactive = this.isInactive || this.disabled || this.formDisabled;\n const invalid = this.required && !inactive && !this.checked;\n const dirty = this.checked !== this.initialChecked;\n // Switch owns its structural track and thumb strokes so nested interaction layers cannot cover them.\n return (\n <Host\n role={this.presentation ? undefined : 'switch'}\n aria-checked={this.presentation ? undefined : String(this.checked)}\n aria-disabled={!this.presentation && inactive ? 'true' : undefined}\n aria-readonly={!this.presentation && this.readOnly ? 'true' : undefined}\n aria-required={!this.presentation && this.required ? 'true' : undefined}\n aria-invalid={!this.presentation && invalid ? 'true' : undefined}\n aria-hidden={this.presentation ? 'true' : undefined}\n data-checked={this.checked ? '' : undefined}\n data-unchecked={!this.checked ? '' : undefined}\n data-disabled={inactive ? '' : undefined}\n data-readonly={this.readOnly ? '' : undefined}\n data-required={this.required ? '' : undefined}\n data-focused={!this.presentation && this.focused ? '' : undefined}\n data-filled={!this.presentation && this.checked ? '' : undefined}\n data-dirty={!this.presentation && dirty ? '' : undefined}\n data-touched={!this.presentation && this.touched ? '' : undefined}\n data-valid={!this.presentation && !inactive && !invalid ? '' : undefined}\n data-invalid={!this.presentation && invalid ? '' : undefined}\n tabIndex={this.presentation || inactive ? -1 : 0}\n class={{\n switch: true,\n checked: this.checked,\n 'switch--lg': this.size === 'lg',\n 'switch--md': this.size === 'md',\n 'switch--sm': this.size === 'sm',\n 'switch--xs': this.size === 'xs',\n 'switch--readonly': this.readOnly,\n 'switch--presentation': this.presentation,\n 'ds-focus-ring': true,\n 'ds-control-inactive': inactive,\n }}\n onClick={this.handleClick}\n onKeyDown={this.handleKeyDown}\n onFocus={this.handleFocus}\n onBlur={this.handleBlur}\n >\n <span class=\"thumb\" />\n </Host>\n );\n }\n}\n",
365
+ "content": "import {\n AttachInternals,\n Component,\n Element,\n Prop,\n State,\n Event,\n EventEmitter,\n Watch,\n h,\n Host,\n} from '@stencil/core';\nimport { DEFAULT_REQUIRED_MESSAGE, setFormControlValue, setRequiredValidity } from '../../utils';\n\nexport type SwitchSize = 'lg' | 'md' | 'sm' | 'xs';\n\nlet generatedLabelId = 0;\n\n@Component({\n tag: 'ds-switch',\n styleUrl: 'Switch.css',\n shadow: true,\n formAssociated: true,\n})\nexport class Switch {\n @Element() el!: HTMLElement;\n @AttachInternals() internals!: ElementInternals;\n\n /** Current on/off state. The initial value is restored when the owning form resets. */\n @Prop({ mutable: true }) checked: boolean = false;\n\n @Prop({ reflect: true }) name: string | undefined;\n @Prop() value: string = 'on';\n\n /** Optional value submitted when unchecked; omitted by default like a native checkbox. */\n @Prop() uncheckedValue: string | undefined;\n\n /** Associates the switch with a form by id when it is rendered outside that form. */\n @Prop({ reflect: true }) form: string | undefined;\n\n @Prop({ reflect: true }) disabled: boolean = false;\n @Prop({ reflect: true }) readOnly: boolean = false;\n @Prop({ reflect: true }) required: boolean = false;\n @Prop() requiredMessage: string = DEFAULT_REQUIRED_MESSAGE;\n @Prop() isInactive: boolean = false;\n\n /** Compact track size for placement inside controls, menus, and form rows. */\n @Prop() size: SwitchSize = 'md';\n\n /**\n * Removes standalone control semantics so a composite owner such as a\n * `menuitemcheckbox` can use the switch as an aria-hidden visual indicator.\n */\n @Prop() presentation: boolean = false;\n\n @Event() dsChange!: EventEmitter<boolean>;\n\n private initialChecked = false;\n @State() private formDisabled = false;\n @State() private focused = false;\n @State() private touched = false;\n\n componentWillLoad() {\n this.initialChecked = this.checked;\n this.syncFormValue();\n }\n\n componentDidLoad() {\n this.syncNativeLabels();\n }\n\n @Watch('checked')\n @Watch('value')\n @Watch('uncheckedValue')\n @Watch('disabled')\n @Watch('isInactive')\n @Watch('presentation')\n @Watch('required')\n syncFormValue() {\n const inactive = this.presentation || this.isInactive || this.disabled || this.formDisabled;\n const submissionValue = this.checked ? this.value : (this.uncheckedValue ?? null);\n const state = this.checked ? 'checked' : 'unchecked';\n setFormControlValue(this.internals, submissionValue, { inactive, state });\n\n const missing = this.required && !inactive && !this.checked;\n setRequiredValidity(this.internals, missing, this.requiredMessage);\n }\n\n formDisabledCallback(disabled: boolean) {\n this.formDisabled = disabled;\n this.syncFormValue();\n }\n\n formResetCallback() {\n this.checked = this.initialChecked;\n this.focused = false;\n this.touched = false;\n }\n\n formStateRestoreCallback(state: string | File | FormData | null) {\n this.checked = state === 'checked';\n }\n\n private syncNativeLabels() {\n if (this.presentation) return;\n if (this.el.hasAttribute('aria-label') || this.el.hasAttribute('aria-labelledby')) return;\n\n const labelIds = (Array.from(this.internals.labels) as HTMLLabelElement[]).map(label => {\n if (!label.id) label.id = `ds-switch-label-${++generatedLabelId}`;\n return label.id;\n });\n if (labelIds.length) this.el.setAttribute('aria-labelledby', labelIds.join(' '));\n }\n\n private handleClick = () => {\n if (this.presentation || this.readOnly || this.isInactive || this.disabled || this.formDisabled)\n return;\n this.checked = !this.checked;\n this.dsChange.emit(this.checked);\n };\n\n private handleKeyDown = (event: KeyboardEvent) => {\n if (this.presentation || this.readOnly || this.isInactive || this.disabled || this.formDisabled)\n return;\n if (event.key === ' ' || event.key === 'Enter') {\n event.preventDefault();\n this.handleClick();\n }\n };\n\n private handleFocus = () => {\n if (!this.presentation) this.focused = true;\n };\n\n private handleBlur = () => {\n if (this.presentation) return;\n this.focused = false;\n this.touched = true;\n };\n\n render() {\n const inactive = this.isInactive || this.disabled || this.formDisabled;\n const invalid = this.required && !inactive && !this.checked;\n const dirty = this.checked !== this.initialChecked;\n // Switch owns its structural track and thumb strokes so nested interaction layers cannot cover them.\n return (\n <Host\n role={this.presentation ? undefined : 'switch'}\n aria-checked={this.presentation ? undefined : String(this.checked)}\n aria-disabled={!this.presentation && inactive ? 'true' : undefined}\n aria-readonly={!this.presentation && this.readOnly ? 'true' : undefined}\n aria-required={!this.presentation && this.required ? 'true' : undefined}\n aria-invalid={!this.presentation && invalid ? 'true' : undefined}\n aria-hidden={this.presentation ? 'true' : undefined}\n data-checked={this.checked ? '' : undefined}\n data-unchecked={!this.checked ? '' : undefined}\n data-disabled={inactive ? '' : undefined}\n data-readonly={this.readOnly ? '' : undefined}\n data-required={this.required ? '' : undefined}\n data-focused={!this.presentation && this.focused ? '' : undefined}\n data-filled={!this.presentation && this.checked ? '' : undefined}\n data-dirty={!this.presentation && dirty ? '' : undefined}\n data-touched={!this.presentation && this.touched ? '' : undefined}\n data-valid={!this.presentation && !inactive && !invalid ? '' : undefined}\n data-invalid={!this.presentation && invalid ? '' : undefined}\n tabIndex={this.presentation || inactive ? -1 : 0}\n class={{\n switch: true,\n checked: this.checked,\n 'switch--lg': this.size === 'lg',\n 'switch--md': this.size === 'md',\n 'switch--sm': this.size === 'sm',\n 'switch--xs': this.size === 'xs',\n 'switch--readonly': this.readOnly,\n 'switch--presentation': this.presentation,\n 'ds-focus-ring': true,\n 'ds-control-inactive': inactive,\n }}\n onClick={this.handleClick}\n onKeyDown={this.handleKeyDown}\n onFocus={this.handleFocus}\n onBlur={this.handleBlur}\n >\n <span class=\"thumb\" />\n </Host>\n );\n }\n}\n",
366
366
  "type": "registry:ui"
367
367
  }
368
368
  ]
@@ -282,12 +282,12 @@
282
282
  "files": [
283
283
  {
284
284
  "path": "src/wc/components/TabGroup/TabGroup.css",
285
- "content": "@import '../../utils/control-density.css';\n@import '../../utils/control-density-inset.css';\n@import '../../utils/control-width.css';\n@import '../../utils/focus-ring.css';\n@import '../../utils/interaction-fill.css';\n@import '../../utils/control-inactive.css';\n\n:host {\n display: block;\n --_tab-group-surface-border: var(--color-border-tertiary);\n --_tab-group-divider: var(--color-border-secondary);\n}\n\n:host(.tab-group-host--on-faint) {\n --_tab-group-selected-fill: var(--color-interaction-active);\n}\n\n:host(.tab-group-host--on-medium) {\n --_tab-group-surface-border: var(--color-border-on-medium-background-tertiary);\n --_tab-group-divider: var(--color-border-on-medium-background-secondary);\n --_tab-group-selected-fill: var(--color-interaction-on-medium-background-active);\n}\n\n:host(.tab-group-host--on-bold) {\n --_tab-group-surface-border: var(--color-border-on-bold-background-tertiary);\n --_tab-group-divider: var(--color-border-on-bold-background-secondary);\n --_tab-group-selected-fill: var(--color-interaction-on-bold-background-active);\n}\n\n:host(.tab-group-host--on-strong) {\n --_tab-group-surface-border: var(--color-border-on-strong-background-tertiary);\n --_tab-group-divider: var(--color-border-on-strong-background-secondary);\n --_tab-group-selected-fill: var(--color-interaction-on-strong-background-active);\n}\n\n:host(.tab-group-host--on-translucent) {\n --_tab-group-surface-border: var(--color-translucent-border-tertiary);\n --_tab-group-divider: var(--color-translucent-border-secondary);\n --_tab-group-selected-fill: var(--color-translucent-interaction-active);\n}\n\n:host(.tab-group-host--on-inverted) {\n --_tab-group-surface-border: var(--color-inverted-border-tertiary);\n --_tab-group-divider: var(--color-inverted-border-secondary);\n --_tab-group-selected-fill: var(--color-inverted-interaction-active);\n}\n\n:host(.tab-group-host--on-media) {\n --_tab-group-surface-border: var(--color-media-border-tertiary);\n --_tab-group-divider: var(--color-media-border-secondary);\n --_tab-group-selected-fill: var(--color-media-interaction-active);\n}\n\n:host(.tab-group-host--on-always-dark) {\n --_tab-group-surface-border: var(--color-always-dark-border-tertiary);\n --_tab-group-divider: var(--color-always-dark-border-secondary);\n --_tab-group-selected-fill: var(--color-always-dark-interaction-active);\n}\n\n.tab-list {\n display: flex;\n align-items: center;\n gap: var(--ds-control-gap);\n position: relative;\n width: max-content;\n max-width: 100%;\n height: var(--ds-control-height);\n padding: calc(var(--dimension-space-025) - var(--dimension-stroke-width-012));\n background-color: var(--color-background-secondary);\n border: var(--dimension-stroke-width-012) solid var(--_tab-group-surface-border);\n border-radius: var(--dimension-radius-half);\n box-sizing: border-box;\n}\n\n:host(.ds-control-width--fill) .tab-list {\n width: 100%;\n}\n\n:host(.tab-group-host--surface) .tab-list {\n background-color: transparent;\n}\n\n.tab {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--ds-control-gap);\n padding: 0 var(--ds-control-padding-inline);\n background-color: transparent;\n border: none;\n border-radius: var(--dimension-radius-half);\n cursor: default;\n -webkit-user-select: none;\n user-select: none;\n color: var(--color-foreground-secondary);\n transition: color var(--effect-motion-short-2);\n flex-shrink: 0;\n height: var(--ds-control-height);\n min-width: var(--ds-control-height);\n box-sizing: border-box;\n --_dot: var(--color-foreground-bold-brand);\n /* Hover/press: `.ds-interaction-fill` fills cover the complete segment. */\n}\n\n:host(.ds-control-width--fill) .tab {\n flex: 1 1 0;\n min-width: 0;\n overflow: hidden;\n}\n\n:host(.ds-control-width--fill) .tab__content {\n max-width: 100%;\n overflow: hidden;\n}\n\n:host(.ds-control-width--fill) .tab__label {\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.tab--selected {\n color: var(--color-foreground-primary);\n background-color: var(--color-background-primary);\n /* Inset stroke on interaction-fill ::after — stays above hover wash. */\n --ds-interaction-border-width: var(--dimension-stroke-width-012);\n --ds-interaction-border-color: var(--_tab-group-surface-border);\n}\n\n:host(.tab-group-host--surface) .tab--selected {\n background-color: var(--_tab-group-selected-fill);\n}\n\n.tab--selected:not(.ds-interaction-fill) {\n box-shadow: inset 0 0 0 var(--ds-interaction-border-width) var(--ds-interaction-border-color);\n}\n\n/* Inactive: `.ds-control-inactive` (50% opacity) — applied via class in TSX. */\n\n.tab__content {\n position: relative;\n z-index: 2;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--ds-control-gap);\n min-width: 0;\n}\n\n.tab__content--dot {\n padding-right: var(--dimension-space-050);\n}\n\n.tab__label {\n padding: 0 var(--ds-control-label-inset);\n white-space: nowrap;\n}\n\n.tab__icon {\n flex-shrink: 0;\n}\n\n.tab__dot {\n position: absolute;\n top: 0;\n right: 0;\n z-index: 2;\n pointer-events: none;\n}\n\n.on-medium {\n color: var(--color-foreground-on-medium-background-secondary);\n}\n\n.on-medium.tab--selected { color: var(--color-foreground-on-medium-background-primary); }\n\n.on-bold {\n color: var(--color-foreground-on-bold-background-secondary);\n}\n\n.on-bold.tab--selected { color: var(--color-foreground-on-bold-background-primary); }\n\n.on-strong {\n color: var(--color-foreground-on-strong-background-secondary);\n}\n\n.on-strong.tab--selected { color: var(--color-foreground-on-strong-background-primary); }\n\n.on-translucent { color: var(--color-translucent-foreground-secondary); }\n\n.on-translucent.tab--selected { color: var(--color-translucent-foreground-primary); }\n\n.on-inverted { color: var(--color-inverted-foreground-secondary); }\n\n.on-inverted.tab--selected { color: var(--color-inverted-foreground-primary); }\n\n.on-media { color: var(--color-media-foreground-secondary); }\n\n.on-media.tab--selected { color: var(--color-media-foreground-primary); }\n\n.on-always-dark {\n color: var(--color-always-dark-foreground-secondary);\n}\n\n.on-always-dark.tab--selected { color: var(--color-always-dark-foreground-primary); }\n\n.tab-divider {\n display: flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n width: var(--dimension-stroke-width-012);\n align-self: center;\n padding: 0;\n flex-shrink: 0;\n pointer-events: none;\n}\n\n.tab-divider__line {\n width: var(--dimension-stroke-width-012);\n height: var(--typography-lineheight-sm);\n flex-shrink: 0;\n background-color: var(--_tab-group-divider);\n}\n",
285
+ "content": "@import '../../utils/control-density.css';\n@import '../../utils/control-density-inset.css';\n@import '../../utils/control-width.css';\n@import '../../utils/focus-ring.css';\n@import '../../utils/interaction-fill.css';\n@import '../../utils/control-inactive.css';\n\n:host {\n display: block;\n --_tab-group-surface-border: var(--color-border-tertiary);\n --_tab-group-divider: var(--color-border-secondary);\n}\n\n:host(.tab-group-host--on-faint) {\n --_tab-group-selected-fill: var(--color-interaction-active);\n}\n\n:host(.tab-group-host--on-medium) {\n --_tab-group-surface-border: var(--color-border-on-medium-background-tertiary);\n --_tab-group-divider: var(--color-border-on-medium-background-secondary);\n --_tab-group-selected-fill: var(--color-interaction-on-medium-background-active);\n}\n\n:host(.tab-group-host--on-bold) {\n --_tab-group-surface-border: var(--color-border-on-bold-background-tertiary);\n --_tab-group-divider: var(--color-border-on-bold-background-secondary);\n --_tab-group-selected-fill: var(--color-interaction-on-bold-background-active);\n}\n\n:host(.tab-group-host--on-strong) {\n --_tab-group-surface-border: var(--color-border-on-strong-background-tertiary);\n --_tab-group-divider: var(--color-border-on-strong-background-secondary);\n --_tab-group-selected-fill: var(--color-interaction-on-strong-background-active);\n}\n\n:host(.tab-group-host--on-translucent) {\n --_tab-group-surface-border: var(--color-translucent-border-tertiary);\n --_tab-group-divider: var(--color-translucent-border-secondary);\n --_tab-group-selected-fill: var(--color-translucent-interaction-active);\n}\n\n:host(.tab-group-host--on-inverted) {\n --_tab-group-surface-border: var(--color-inverted-border-tertiary);\n --_tab-group-divider: var(--color-inverted-border-secondary);\n --_tab-group-selected-fill: var(--color-inverted-interaction-active);\n}\n\n:host(.tab-group-host--on-media) {\n --_tab-group-surface-border: var(--color-media-border-tertiary);\n --_tab-group-divider: var(--color-media-border-secondary);\n --_tab-group-selected-fill: var(--color-media-interaction-active);\n}\n\n:host(.tab-group-host--on-always-dark) {\n --_tab-group-surface-border: var(--color-always-dark-border-tertiary);\n --_tab-group-divider: var(--color-always-dark-border-secondary);\n --_tab-group-selected-fill: var(--color-always-dark-interaction-active);\n}\n\n.tab-list {\n display: flex;\n align-items: center;\n gap: var(--ds-control-gap);\n position: relative;\n width: max-content;\n max-width: 100%;\n height: var(--ds-control-height);\n padding: calc(var(--dimension-space-025) - var(--dimension-stroke-width-012));\n background-color: var(--color-background-secondary);\n border: var(--dimension-stroke-width-012) solid var(--_tab-group-surface-border);\n border-radius: var(--dimension-radius-half);\n box-sizing: border-box;\n}\n\n:host(.ds-control-width--fill) .tab-list {\n width: 100%;\n}\n\n:host(.tab-group-host--surface) .tab-list {\n background-color: transparent;\n}\n\n.tab {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--ds-control-gap);\n padding: 0 var(--ds-control-padding-inline);\n background-color: transparent;\n border: none;\n border-radius: var(--dimension-radius-half);\n cursor: default;\n -webkit-user-select: none;\n user-select: none;\n color: var(--color-foreground-secondary);\n transition: color var(--effect-motion-short-2);\n flex-shrink: 0;\n height: var(--ds-control-height);\n min-width: var(--ds-control-height);\n box-sizing: border-box;\n --_dot: var(--color-foreground-bold-brand);\n /* Hover/press: `.ds-interaction-fill` fills cover the complete segment. */\n}\n\n:host(.ds-control-width--fill) .tab {\n flex: 1 1 0;\n min-width: 0;\n overflow: hidden;\n}\n\n:host(.ds-control-width--fill) .tab__content {\n max-width: 100%;\n overflow: hidden;\n}\n\n:host(.ds-control-width--fill) .tab__label {\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.tab--selected {\n color: var(--color-foreground-primary);\n background-color: var(--color-background-primary);\n /* Inset stroke on interaction-fill ::after — stays above hover wash. */\n --ds-interaction-border-width: var(--dimension-stroke-width-012);\n --ds-interaction-border-color: var(--_tab-group-surface-border);\n}\n\n:host(.tab-group-host--surface) .tab--selected {\n background-color: var(--_tab-group-selected-fill);\n}\n\n.tab--selected:not(.ds-interaction-fill) {\n box-shadow: inset 0 0 0 var(--ds-interaction-border-width) var(--ds-interaction-border-color);\n}\n\n/* Inactive: `.ds-control-inactive` (50% opacity) — applied via class in TSX. */\n\n.tab__content {\n position: relative;\n z-index: 2;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--ds-control-gap);\n min-width: 0;\n}\n\n.tab__content--dot {\n padding-right: var(--dimension-space-050);\n}\n\n.tab__label {\n padding: 0 var(--ds-control-label-inset);\n white-space: nowrap;\n}\n\n.tab__icon {\n flex-shrink: 0;\n}\n\n.tab__dot {\n position: absolute;\n top: 0;\n right: 0;\n z-index: 2;\n pointer-events: none;\n}\n\n.on-medium {\n color: var(--color-foreground-on-medium-background-secondary);\n}\n\n.on-medium.tab--selected {\n color: var(--color-foreground-on-medium-background-primary);\n}\n\n.on-bold {\n color: var(--color-foreground-on-bold-background-secondary);\n}\n\n.on-bold.tab--selected {\n color: var(--color-foreground-on-bold-background-primary);\n}\n\n.on-strong {\n color: var(--color-foreground-on-strong-background-secondary);\n}\n\n.on-strong.tab--selected {\n color: var(--color-foreground-on-strong-background-primary);\n}\n\n.on-translucent {\n color: var(--color-translucent-foreground-secondary);\n}\n\n.on-translucent.tab--selected {\n color: var(--color-translucent-foreground-primary);\n}\n\n.on-inverted {\n color: var(--color-inverted-foreground-secondary);\n}\n\n.on-inverted.tab--selected {\n color: var(--color-inverted-foreground-primary);\n}\n\n.on-media {\n color: var(--color-media-foreground-secondary);\n}\n\n.on-media.tab--selected {\n color: var(--color-media-foreground-primary);\n}\n\n.on-always-dark {\n color: var(--color-always-dark-foreground-secondary);\n}\n\n.on-always-dark.tab--selected {\n color: var(--color-always-dark-foreground-primary);\n}\n\n.tab-divider {\n display: flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n width: var(--dimension-stroke-width-012);\n align-self: center;\n padding: 0;\n flex-shrink: 0;\n pointer-events: none;\n}\n\n.tab-divider__line {\n width: var(--dimension-stroke-width-012);\n height: var(--typography-lineheight-sm);\n flex-shrink: 0;\n background-color: var(--_tab-group-divider);\n}\n",
286
286
  "type": "registry:ui"
287
287
  },
288
288
  {
289
289
  "path": "src/wc/components/TabGroup/TabGroup.tsx",
290
- "content": "import { Component, Prop, Event, EventEmitter, Element, Listen, Watch, h, Host } from '@stencil/core';\nimport { controlWidthClass, CONTROL_TEXT_VARIANT, type ControlWidth } from '../../utils';\nimport {\n getSelectableTabs,\n isTabDivider,\n type TabGroupItem,\n type TabItemTab,\n} from './tab-item-utils';\n\nexport type TabBackground =\n | 'faint'\n | 'medium'\n | 'bold'\n | 'strong'\n | 'translucent'\n | 'inverted'\n | 'media'\n | 'always-dark';\n\nexport type TabGroupSize = 'sm' | 'md' | 'lg';\nexport type TabGroupWidth = ControlWidth;\n\n@Component({\n tag: 'ds-tab-group',\n styleUrl: 'TabGroup.css',\n scoped: true,\n})\nexport class TabGroup {\n @Element() el!: HTMLElement;\n\n @Prop({ mutable: true }) value: string = '';\n @Prop() tabs: TabGroupItem[] = [];\n /** Control density: 24px small, 32px medium, or 40px large track. */\n @Prop() size: TabGroupSize = 'md';\n /** Width fit — hug content (default) or fill the parent with equal-width segments. */\n @Prop() width: TabGroupWidth = 'hug';\n @Prop() background: TabBackground | undefined;\n @Prop({ attribute: 'aria-label' }) ariaLabel: string | null = null;\n @Prop({ attribute: 'aria-labelledby' }) ariaLabelledby: string | undefined;\n\n @Event() dsChange!: EventEmitter<string>;\n\n private get selectableTabs(): TabItemTab[] {\n return getSelectableTabs(this.tabs);\n }\n\n private get focusableTabId(): string {\n return this.selectableTabs.some(tab => tab.id === this.value && !tab.isInactive)\n ? this.value\n : (this.selectableTabs.find(tab => !tab.isInactive)?.id ?? '');\n }\n\n private selectTab(id: string, options?: { focus?: boolean }) {\n this.value = id;\n this.dsChange.emit(id);\n if (options?.focus !== false) {\n this.focusTab(id);\n }\n }\n\n private focusTab(id: string) {\n const btn = this.el.querySelector(`[data-tab-id=\"${id}\"]`) as HTMLElement | null;\n btn?.focus({ preventScroll: true });\n }\n\n /** Keep focus on the selected tab when value changes externally (e.g. BarNav menu). */\n @Watch('value')\n onValueChange(next: string, prev: string | undefined) {\n if (prev === undefined || next === prev) return;\n\n requestAnimationFrame(() => {\n const active = document.activeElement as HTMLElement | null;\n if (!active || !this.el.contains(active)) return;\n\n const activeId = active.getAttribute('data-tab-id');\n if (activeId === next) return;\n\n const focusId = this.selectableTabs.some(tab => tab.id === next && !tab.isInactive)\n ? next\n : this.focusableTabId;\n const nextTab = this.el.querySelector(`[data-tab-id=\"${focusId}\"]`) as HTMLElement | null;\n if (nextTab) {\n nextTab.focus({ preventScroll: true });\n } else {\n active.blur();\n }\n });\n }\n\n /**\n * Find the next non-inactive tab index relative to `from`, stepping by `step`.\n * Returns `from` unchanged if no other enabled tab exists.\n */\n private findEnabled(from: number, step: 1 | -1): number {\n const tabs = this.selectableTabs;\n const len = tabs.length;\n for (let n = 0; n < len; n++) {\n const i = (from + step * (n + 1) + len * (n + 1)) % len;\n if (!tabs[i]?.isInactive) return i;\n }\n return from;\n }\n\n @Listen('keydown')\n handleKeyDown(e: KeyboardEvent) {\n const tabs = this.selectableTabs;\n if (!tabs.length) return;\n\n const focusedId = (e.target as Element | null)?.closest<HTMLElement>('[data-tab-id]')?.dataset['tabId'];\n const currentIdx = tabs.findIndex(t => t.id === (focusedId ?? this.focusableTabId));\n\n let nextIdx: number | null = null;\n\n if (e.key === 'ArrowRight') {\n nextIdx = this.findEnabled(currentIdx, 1);\n } else if (e.key === 'ArrowLeft') {\n nextIdx = this.findEnabled(currentIdx, -1);\n }\n\n if (e.key === 'Home') {\n const first = tabs.findIndex(t => !t.isInactive);\n nextIdx = first === -1 ? null : first;\n } else if (e.key === 'End') {\n const lastEnabled = [...tabs].reverse().findIndex(t => !t.isInactive);\n nextIdx = lastEnabled === -1 ? null : tabs.length - 1 - lastEnabled;\n }\n\n if (nextIdx !== null) {\n e.preventDefault();\n const next = tabs[nextIdx];\n this.selectTab(next.id);\n }\n }\n\n private getBgClass(): string {\n if (!this.background) return '';\n if (this.background === 'always-dark') return 'on-always-dark';\n return `on-${this.background}`;\n }\n\n render() {\n const bgClass = this.getBgClass();\n const focusableTabId = this.focusableTabId;\n\n return (\n <Host\n class={{\n 'tab-group-host': true,\n [`tab-group-host--${this.size}`]: true,\n [`ds-control--${this.size}`]: true,\n 'tab-group-host--surface': !!bgClass,\n [`tab-group-host--${bgClass}`]: !!bgClass,\n ...controlWidthClass(this.width),\n }}\n >\n <div\n role=\"tablist\"\n class=\"tab-list\"\n aria-label={this.ariaLabel}\n aria-labelledby={this.ariaLabelledby}\n >\n {this.tabs.map((tab, index) => {\n if (isTabDivider(tab)) {\n return (\n <div\n key={`divider-${index}`}\n class=\"tab-divider\"\n aria-hidden=\"true\"\n >\n <div class=\"tab-divider__line\" />\n </div>\n );\n }\n\n const isSelected = tab.id === this.value;\n const variant = tab.variant ?? 'label';\n const showIcon = variant === 'icon' || variant === 'icon-label';\n const showLabel = variant === 'label' || variant === 'icon-label';\n const emphasizeLabel = isSelected && !bgClass;\n return (\n <button\n key={tab.id}\n type=\"button\"\n role=\"tab\"\n data-tab-id={tab.id}\n class={{\n tab: true,\n 'tab--selected': isSelected,\n [`ds-control--${this.size}`]: true,\n 'ds-control--inset': true,\n 'ds-focus-ring-inset': true,\n 'ds-interaction-fill': !tab.isInactive,\n 'ds-interaction-fill--on-faint': bgClass === 'on-faint',\n 'ds-interaction-fill--on-medium': bgClass === 'on-medium',\n 'ds-interaction-fill--on-bold': bgClass === 'on-bold',\n 'ds-interaction-fill--on-strong': bgClass === 'on-strong',\n 'ds-interaction-fill--on-translucent': bgClass === 'on-translucent',\n 'ds-interaction-fill--on-inverted': bgClass === 'on-inverted',\n 'ds-interaction-fill--on-media': bgClass === 'on-media',\n 'ds-interaction-fill--on-always-dark': bgClass === 'on-always-dark',\n 'ds-control-inactive': !!tab.isInactive,\n [bgClass]: !!bgClass,\n }}\n aria-label={variant === 'icon' ? tab.label : undefined}\n aria-selected={isSelected}\n aria-disabled={tab.isInactive ? 'true' : undefined}\n aria-controls={tab.panelId ?? undefined}\n disabled={tab.isInactive}\n tabIndex={tab.id === focusableTabId ? 0 : -1}\n onClick={() => !tab.isInactive && this.selectTab(tab.id)}\n >\n <span class={{\n tab__content: true,\n [`tab__content--${variant}`]: true,\n 'tab__content--dot': !!tab.dot,\n }}>\n {showIcon && (\n <ds-icon\n class=\"tab__icon\"\n name={tab.icon}\n size={this.size}\n color=\"inherit\"\n />\n )}\n {showLabel && (\n <ds-text\n class=\"tab__label\"\n as=\"span\"\n variant={CONTROL_TEXT_VARIANT[this.size]}\n emphasis={emphasizeLabel}\n color=\"inherit\"\n >\n {tab.label}\n </ds-text>\n )}\n {tab.dot && (\n <ds-badge\n class=\"tab__dot\"\n variant=\"dot\"\n hasRing={false}\n label=\"\"\n aria-hidden=\"true\"\n />\n )}\n </span>\n </button>\n );\n })}\n </div>\n </Host>\n );\n }\n}\n",
290
+ "content": "import {\n Component,\n Prop,\n Event,\n EventEmitter,\n Element,\n Listen,\n Watch,\n h,\n Host,\n} from '@stencil/core';\nimport { controlWidthClass, CONTROL_TEXT_VARIANT, type ControlWidth } from '../../utils';\nimport {\n getSelectableTabs,\n isTabDivider,\n type TabGroupItem,\n type TabItemTab,\n} from './tab-item-utils';\n\nexport type TabBackground =\n | 'faint'\n | 'medium'\n | 'bold'\n | 'strong'\n | 'translucent'\n | 'inverted'\n | 'media'\n | 'always-dark';\n\nexport type TabGroupSize = 'sm' | 'md' | 'lg';\nexport type TabGroupWidth = ControlWidth;\n\n@Component({\n tag: 'ds-tab-group',\n styleUrl: 'TabGroup.css',\n scoped: true,\n})\nexport class TabGroup {\n @Element() el!: HTMLElement;\n\n @Prop({ mutable: true }) value: string = '';\n @Prop() tabs: TabGroupItem[] = [];\n /** Control density: 24px small, 32px medium, or 40px large track. */\n @Prop() size: TabGroupSize = 'md';\n /** Width fit — hug content (default) or fill the parent with equal-width segments. */\n @Prop() width: TabGroupWidth = 'hug';\n @Prop() background: TabBackground | undefined;\n @Prop({ attribute: 'aria-label' }) ariaLabel: string | null = null;\n @Prop({ attribute: 'aria-labelledby' }) ariaLabelledby: string | undefined;\n\n @Event() dsChange!: EventEmitter<string>;\n\n private get selectableTabs(): TabItemTab[] {\n return getSelectableTabs(this.tabs);\n }\n\n private get focusableTabId(): string {\n return this.selectableTabs.some(tab => tab.id === this.value && !tab.isInactive)\n ? this.value\n : (this.selectableTabs.find(tab => !tab.isInactive)?.id ?? '');\n }\n\n private selectTab(id: string, options?: { focus?: boolean }) {\n this.value = id;\n this.dsChange.emit(id);\n if (options?.focus !== false) {\n this.focusTab(id);\n }\n }\n\n private focusTab(id: string) {\n const btn = this.el.querySelector(`[data-tab-id=\"${id}\"]`) as HTMLElement | null;\n btn?.focus({ preventScroll: true });\n }\n\n /** Keep focus on the selected tab when value changes externally (e.g. BarNav menu). */\n @Watch('value')\n onValueChange(next: string, prev: string | undefined) {\n if (prev === undefined || next === prev) return;\n\n requestAnimationFrame(() => {\n const active = document.activeElement as HTMLElement | null;\n if (!active || !this.el.contains(active)) return;\n\n const activeId = active.getAttribute('data-tab-id');\n if (activeId === next) return;\n\n const focusId = this.selectableTabs.some(tab => tab.id === next && !tab.isInactive)\n ? next\n : this.focusableTabId;\n const nextTab = this.el.querySelector(`[data-tab-id=\"${focusId}\"]`) as HTMLElement | null;\n if (nextTab) {\n nextTab.focus({ preventScroll: true });\n } else {\n active.blur();\n }\n });\n }\n\n /**\n * Find the next non-inactive tab index relative to `from`, stepping by `step`.\n * Returns `from` unchanged if no other enabled tab exists.\n */\n private findEnabled(from: number, step: 1 | -1): number {\n const tabs = this.selectableTabs;\n const len = tabs.length;\n for (let n = 0; n < len; n++) {\n const i = (from + step * (n + 1) + len * (n + 1)) % len;\n if (!tabs[i]?.isInactive) return i;\n }\n return from;\n }\n\n @Listen('keydown')\n handleKeyDown(e: KeyboardEvent) {\n const tabs = this.selectableTabs;\n if (!tabs.length) return;\n\n const focusedId = (e.target as Element | null)?.closest<HTMLElement>('[data-tab-id]')?.dataset[\n 'tabId'\n ];\n const currentIdx = tabs.findIndex(t => t.id === (focusedId ?? this.focusableTabId));\n\n let nextIdx: number | null = null;\n\n if (e.key === 'ArrowRight') {\n nextIdx = this.findEnabled(currentIdx, 1);\n } else if (e.key === 'ArrowLeft') {\n nextIdx = this.findEnabled(currentIdx, -1);\n }\n\n if (e.key === 'Home') {\n const first = tabs.findIndex(t => !t.isInactive);\n nextIdx = first === -1 ? null : first;\n } else if (e.key === 'End') {\n const lastEnabled = [...tabs].reverse().findIndex(t => !t.isInactive);\n nextIdx = lastEnabled === -1 ? null : tabs.length - 1 - lastEnabled;\n }\n\n if (nextIdx !== null) {\n e.preventDefault();\n const next = tabs[nextIdx];\n this.selectTab(next.id);\n }\n }\n\n private getBgClass(): string {\n if (!this.background) return '';\n if (this.background === 'always-dark') return 'on-always-dark';\n return `on-${this.background}`;\n }\n\n render() {\n const bgClass = this.getBgClass();\n const focusableTabId = this.focusableTabId;\n\n return (\n <Host\n class={{\n 'tab-group-host': true,\n [`tab-group-host--${this.size}`]: true,\n [`ds-control--${this.size}`]: true,\n 'tab-group-host--surface': !!bgClass,\n [`tab-group-host--${bgClass}`]: !!bgClass,\n ...controlWidthClass(this.width),\n }}\n >\n <div\n role=\"tablist\"\n class=\"tab-list\"\n aria-label={this.ariaLabel}\n aria-labelledby={this.ariaLabelledby}\n >\n {this.tabs.map((tab, index) => {\n if (isTabDivider(tab)) {\n return (\n <div key={`divider-${index}`} class=\"tab-divider\" aria-hidden=\"true\">\n <div class=\"tab-divider__line\" />\n </div>\n );\n }\n\n const isSelected = tab.id === this.value;\n const variant = tab.variant ?? 'label';\n const showIcon = variant === 'icon' || variant === 'icon-label';\n const showLabel = variant === 'label' || variant === 'icon-label';\n const emphasizeLabel = isSelected && !bgClass;\n return (\n <button\n key={tab.id}\n type=\"button\"\n role=\"tab\"\n data-tab-id={tab.id}\n class={{\n tab: true,\n 'tab--selected': isSelected,\n [`ds-control--${this.size}`]: true,\n 'ds-control--inset': true,\n 'ds-focus-ring-inset': true,\n 'ds-interaction-fill': !tab.isInactive,\n 'ds-interaction-fill--on-faint': bgClass === 'on-faint',\n 'ds-interaction-fill--on-medium': bgClass === 'on-medium',\n 'ds-interaction-fill--on-bold': bgClass === 'on-bold',\n 'ds-interaction-fill--on-strong': bgClass === 'on-strong',\n 'ds-interaction-fill--on-translucent': bgClass === 'on-translucent',\n 'ds-interaction-fill--on-inverted': bgClass === 'on-inverted',\n 'ds-interaction-fill--on-media': bgClass === 'on-media',\n 'ds-interaction-fill--on-always-dark': bgClass === 'on-always-dark',\n 'ds-control-inactive': !!tab.isInactive,\n [bgClass]: !!bgClass,\n }}\n aria-label={variant === 'icon' ? tab.label : undefined}\n aria-selected={isSelected}\n aria-disabled={tab.isInactive ? 'true' : undefined}\n aria-controls={tab.panelId ?? undefined}\n disabled={tab.isInactive}\n tabIndex={tab.id === focusableTabId ? 0 : -1}\n onClick={() => !tab.isInactive && this.selectTab(tab.id)}\n >\n <span\n class={{\n tab__content: true,\n [`tab__content--${variant}`]: true,\n 'tab__content--dot': !!tab.dot,\n }}\n >\n {showIcon && (\n <ds-icon class=\"tab__icon\" name={tab.icon} size={this.size} color=\"inherit\" />\n )}\n {showLabel && (\n <ds-text\n class=\"tab__label\"\n as=\"span\"\n variant={CONTROL_TEXT_VARIANT[this.size]}\n emphasis={emphasizeLabel}\n color=\"inherit\"\n >\n {tab.label}\n </ds-text>\n )}\n {tab.dot && (\n <ds-badge\n class=\"tab__dot\"\n variant=\"dot\"\n hasRing={false}\n label=\"\"\n aria-hidden=\"true\"\n />\n )}\n </span>\n </button>\n );\n })}\n </div>\n </Host>\n );\n }\n}\n",
291
291
  "type": "registry:ui"
292
292
  },
293
293
  {
@@ -218,7 +218,7 @@
218
218
  },
219
219
  {
220
220
  "path": "src/wc/components/TableSort/TableSort.tsx",
221
- "content": "import { Component, Element, Event, EventEmitter, h, Host, Method, Prop, State } from '@stencil/core';\nimport type { MenuItemData } from '../Menu/menu-types';\nimport type { TableColumn, TableSortChangeDetail, TableSortState } from '../Table/table-types';\nimport {\n nextTableSortStateFromMenuItem,\n tableSortFields,\n tableSortMenuSections,\n tableSortStatesEqual,\n} from './table-sort-menu';\n\nlet tableSortSeq = 0;\n\n@Component({\n tag: 'ds-table-sort',\n styleUrl: 'TableSort.css',\n scoped: true,\n})\nexport class TableSort {\n @Element() private el!: HTMLElement;\n\n /** Catalog used to derive sortable fields, including compound header segments. */\n @Prop() columns: TableColumn[] = [];\n /** Controlled table sort. Header sorting and this menu share the same value. */\n @Prop() sort: TableSortState | null = null;\n /** Accessible name for the trigger and menu. */\n @Prop({ attribute: 'aria-label' }) ariaLabel: string | null = null;\n\n @Event({ bubbles: false }) dsSortChange!: EventEmitter<TableSortChangeDetail>;\n\n @State() private menuOpen = false;\n @State() private initialFocusVisible = false;\n\n private readonly componentId = `ds-table-sort-${++tableSortSeq}`;\n private readonly triggerId = `${this.componentId}-trigger`;\n private readonly menuId = `${this.componentId}-menu`;\n\n @Method()\n async setFocus() {\n const trigger = this.el.querySelector<HTMLElement & { setFocus?: () => Promise<void> }>(\n `#${CSS.escape(this.triggerId)}`\n );\n await trigger?.setFocus?.();\n }\n\n render() {\n const fields = tableSortFields(this.columns);\n const name = this.ariaLabel?.trim() || 'Sort table';\n\n return (\n <Host hidden={fields.length === 0 ? true : undefined}>\n {fields.length === 0 ? null : (\n <ds-button-unfilled\n id={this.triggerId}\n variant=\"icon-label\"\n size=\"md\"\n icon=\"ArrowsVertical\"\n label=\"Sort\"\n labelEmphasis={false}\n pressScale={false}\n aria-label={name}\n hasMenu={true}\n collapseLabel={true}\n expanded={this.menuOpen}\n controls={this.menuId}\n onDsClick={(event: CustomEvent<MouseEvent>) => {\n this.toggle(event.detail.detail === 0);\n }}\n />\n )}\n {fields.length === 0 ? null : (\n <ds-menu\n id={this.menuId}\n open={this.menuOpen}\n anchorId={this.triggerId}\n align=\"start\"\n side=\"bottom\"\n menuLabel={name}\n selectionMode=\"none\"\n initialFocusVisible={this.initialFocusVisible}\n sections={tableSortMenuSections(this.columns, this.sort)}\n onDsClose={() => this.close()}\n onDsSelect={event => this.handleSelect(event.detail)}\n />\n )}\n </Host>\n );\n }\n\n private toggle(fromKeyboard = false): void {\n if (this.menuOpen) this.close();\n else this.open(fromKeyboard);\n }\n\n private open(fromKeyboard = false): void {\n if (this.menuOpen) return;\n this.initialFocusVisible = fromKeyboard;\n this.menuOpen = true;\n }\n\n private close(): void {\n this.menuOpen = false;\n }\n\n private handleSelect(item: MenuItemData): void {\n const next = nextTableSortStateFromMenuItem(this.columns, this.sort, item);\n if (tableSortStatesEqual(this.sort, next)) return;\n this.dsSortChange.emit({ sort: next });\n }\n}\n",
221
+ "content": "import {\n Component,\n Element,\n Event,\n EventEmitter,\n h,\n Host,\n Method,\n Prop,\n State,\n} from '@stencil/core';\nimport type { MenuItemData } from '../Menu/menu-types';\nimport type { TableColumn, TableSortChangeDetail, TableSortState } from '../Table/table-types';\nimport {\n nextTableSortStateFromMenuItem,\n tableSortFields,\n tableSortMenuSections,\n tableSortStatesEqual,\n} from './table-sort-menu';\n\nlet tableSortSeq = 0;\n\n@Component({\n tag: 'ds-table-sort',\n styleUrl: 'TableSort.css',\n scoped: true,\n})\nexport class TableSort {\n @Element() private el!: HTMLElement;\n\n /** Catalog used to derive sortable fields, including compound header segments. */\n @Prop() columns: TableColumn[] = [];\n /** Controlled table sort. Header sorting and this menu share the same value. */\n @Prop() sort: TableSortState | null = null;\n /** Accessible name for the trigger and menu. */\n @Prop({ attribute: 'aria-label' }) ariaLabel: string | null = null;\n\n @Event({ bubbles: false }) dsSortChange!: EventEmitter<TableSortChangeDetail>;\n\n @State() private menuOpen = false;\n @State() private initialFocusVisible = false;\n\n private readonly componentId = `ds-table-sort-${++tableSortSeq}`;\n private readonly triggerId = `${this.componentId}-trigger`;\n private readonly menuId = `${this.componentId}-menu`;\n\n @Method()\n async setFocus() {\n const trigger = this.el.querySelector<HTMLElement & { setFocus?: () => Promise<void> }>(\n `#${CSS.escape(this.triggerId)}`\n );\n await trigger?.setFocus?.();\n }\n\n render() {\n const fields = tableSortFields(this.columns);\n const name = this.ariaLabel?.trim() || 'Sort table';\n\n return (\n <Host hidden={fields.length === 0 ? true : undefined}>\n {fields.length === 0 ? null : (\n <ds-button-unfilled\n id={this.triggerId}\n variant=\"icon-label\"\n size=\"md\"\n icon=\"ArrowsVertical\"\n label=\"Sort\"\n labelEmphasis={false}\n pressScale={false}\n aria-label={name}\n hasMenu={true}\n collapseLabel={true}\n expanded={this.menuOpen}\n controls={this.menuId}\n onDsClick={(event: CustomEvent<MouseEvent>) => {\n this.toggle(event.detail.detail === 0);\n }}\n />\n )}\n {fields.length === 0 ? null : (\n <ds-menu\n id={this.menuId}\n open={this.menuOpen}\n anchorId={this.triggerId}\n align=\"start\"\n side=\"bottom\"\n menuLabel={name}\n selectionMode=\"none\"\n initialFocusVisible={this.initialFocusVisible}\n sections={tableSortMenuSections(this.columns, this.sort)}\n onDsClose={() => this.close()}\n onDsSelect={event => this.handleSelect(event.detail)}\n />\n )}\n </Host>\n );\n }\n\n private toggle(fromKeyboard = false): void {\n if (this.menuOpen) this.close();\n else this.open(fromKeyboard);\n }\n\n private open(fromKeyboard = false): void {\n if (this.menuOpen) return;\n this.initialFocusVisible = fromKeyboard;\n this.menuOpen = true;\n }\n\n private close(): void {\n this.menuOpen = false;\n }\n\n private handleSelect(item: MenuItemData): void {\n const next = nextTableSortStateFromMenuItem(this.columns, this.sort, item);\n if (tableSortStatesEqual(this.sort, next)) return;\n this.dsSortChange.emit({ sort: next });\n }\n}\n",
222
222
  "type": "registry:ui"
223
223
  },
224
224
  {