@ds-mo/ui 15.6.0 → 15.8.0

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 (297) hide show
  1. package/dist/.package-ready.json +1 -1
  2. package/dist/agent-patterns.json +1 -1
  3. package/dist/agent.json +328 -5
  4. package/dist/angular/ds-navigation-tab-group.d.ts +15 -0
  5. package/dist/angular/ds-navigation-tab-group.js +42 -0
  6. package/dist/angular/ds-select-toggle.d.ts +15 -0
  7. package/dist/angular/ds-select-toggle.js +42 -0
  8. package/dist/angular/ds-table.d.ts +1 -1
  9. package/dist/angular/ds-table.js +3 -3
  10. package/dist/angular/index.d.ts +1 -1
  11. package/dist/angular/index.js +2 -0
  12. package/dist/angular/proxies.d.ts +2 -0
  13. package/dist/angular/proxies.js +2 -0
  14. package/dist/components/ds-agent-activity.js +1 -1
  15. package/dist/components/ds-agent-questionnaire.js +1 -1
  16. package/dist/components/ds-agent-response.js +1 -1
  17. package/dist/components/ds-agent-source-list.js +1 -1
  18. package/dist/components/ds-agent-tool-call.js +1 -1
  19. package/dist/components/ds-attachment-list.js +1 -1
  20. package/dist/components/ds-avatar.js +1 -1
  21. package/dist/components/ds-avatar.js.map +1 -1
  22. package/dist/components/ds-badge.js +1 -1
  23. package/dist/components/ds-banner.js +1 -1
  24. package/dist/components/ds-bar-action.js +1 -1
  25. package/dist/components/ds-bar-action.js.map +1 -1
  26. package/dist/components/ds-bar-nav.js +1 -1
  27. package/dist/components/ds-bar-page-title.js +1 -1
  28. package/dist/components/ds-bar-title.js +1 -1
  29. package/dist/components/ds-bar-workflow.js +1 -1
  30. package/dist/components/ds-bar-workflow.js.map +1 -1
  31. package/dist/components/ds-breadcrumb.js +1 -1
  32. package/dist/components/ds-button-filled.js +1 -1
  33. package/dist/components/ds-button-inverted.js +1 -1
  34. package/dist/components/ds-button-unfilled.js +1 -1
  35. package/dist/components/ds-calendar.js +1 -1
  36. package/dist/components/ds-card-action-center.js +1 -1
  37. package/dist/components/ds-card-chart.js +1 -1
  38. package/dist/components/ds-card-navigation.js +1 -1
  39. package/dist/components/ds-card-overview.js +1 -1
  40. package/dist/components/ds-card-setting.js +1 -1
  41. package/dist/components/ds-card-settings-scope.js +1 -1
  42. package/dist/components/ds-chart-legend.js +1 -1
  43. package/dist/components/ds-chart.js +1 -1
  44. package/dist/components/ds-checkbox-group.js +1 -1
  45. package/dist/components/ds-checkbox.js +1 -1
  46. package/dist/components/ds-chip.js +1 -1
  47. package/dist/components/ds-code-block.js +1 -1
  48. package/dist/components/ds-conversation-list-item.js +1 -1
  49. package/dist/components/ds-conversation-list-item.js.map +1 -1
  50. package/dist/components/ds-conversation-list-section.js +1 -1
  51. package/dist/components/ds-conversation-list.js +1 -1
  52. package/dist/components/ds-data-customize.js +1 -1
  53. package/dist/components/ds-data-filter.js +1 -1
  54. package/dist/components/ds-data-group.js +1 -1
  55. package/dist/components/ds-data-preferences.js +1 -1
  56. package/dist/components/ds-data-preferences.js.map +1 -1
  57. package/dist/components/ds-data-saved-views.js +1 -1
  58. package/dist/components/ds-data-search.js +1 -1
  59. package/dist/components/ds-data-search.js.map +1 -1
  60. package/dist/components/ds-data-sort.js +1 -1
  61. package/dist/components/ds-data-toolbar.js +1 -1
  62. package/dist/components/ds-empty-state.js +1 -1
  63. package/dist/components/ds-field.js +1 -1
  64. package/dist/components/ds-filter-menu.js +1 -1
  65. package/dist/components/ds-icon.js +1 -1
  66. package/dist/components/ds-inline-banner-settings.js +1 -1
  67. package/dist/components/ds-input-date.js +1 -1
  68. package/dist/components/ds-input-time.js +1 -1
  69. package/dist/components/ds-input.js +1 -1
  70. package/dist/components/ds-map-cluster-marker.js +1 -1
  71. package/dist/components/ds-map-entity-marker.js +1 -1
  72. package/dist/components/ds-markdown.js +1 -1
  73. package/dist/components/ds-menu.js +1 -1
  74. package/dist/components/ds-message-actions.js +1 -1
  75. package/dist/components/ds-message-composer.js +1 -1
  76. package/dist/components/ds-message-scroller.js +1 -1
  77. package/dist/components/ds-message.js +1 -1
  78. package/dist/components/ds-mobile-bar-nav.js +1 -1
  79. package/dist/components/ds-mobile-header.js +1 -1
  80. package/dist/components/ds-mobile-section-switcher.js +1 -1
  81. package/dist/components/ds-mobile-sheet-nav.js +1 -1
  82. package/dist/components/ds-modal.js +1 -1
  83. package/dist/components/ds-navigation-tab-group.d.ts +11 -0
  84. package/dist/components/ds-navigation-tab-group.js +2 -0
  85. package/dist/components/ds-navigation-tab-group.js.map +1 -0
  86. package/dist/components/ds-pagination.js +1 -1
  87. package/dist/components/ds-panel-nav.js +1 -1
  88. package/dist/components/ds-panel-sub-nav.js +1 -1
  89. package/dist/components/ds-panel-tool-header.js +1 -1
  90. package/dist/components/ds-panel-tool-search.js +1 -1
  91. package/dist/components/ds-panel-tool-search.js.map +1 -1
  92. package/dist/components/ds-panel-tools.js +1 -1
  93. package/dist/components/ds-paper-texture.js +1 -1
  94. package/dist/components/ds-radio-tile.js +1 -1
  95. package/dist/components/ds-radio.js +1 -1
  96. package/dist/components/ds-score.js +1 -1
  97. package/dist/components/ds-select-toggle.d.ts +11 -0
  98. package/dist/components/ds-select-toggle.js +2 -0
  99. package/dist/components/ds-select-toggle.js.map +1 -0
  100. package/dist/components/ds-select.js +1 -1
  101. package/dist/components/ds-setting-row-checkbox.js +1 -1
  102. package/dist/components/ds-setting-row-radio.js +1 -1
  103. package/dist/components/ds-setting-row-radio.js.map +1 -1
  104. package/dist/components/ds-setting-row-toggle.js +1 -1
  105. package/dist/components/ds-shell-app.js +1 -1
  106. package/dist/components/ds-shell-app.js.map +1 -1
  107. package/dist/components/ds-shell-page.js +1 -1
  108. package/dist/components/ds-shell-tools.js +1 -1
  109. package/dist/components/ds-skeleton.js +1 -1
  110. package/dist/components/ds-slider.js +1 -1
  111. package/dist/components/ds-slider.js.map +1 -1
  112. package/dist/components/ds-swatch-picker.js +1 -1
  113. package/dist/components/ds-switch.js +1 -1
  114. package/dist/components/ds-tab-group.js +1 -1
  115. package/dist/components/ds-table.js +1 -1
  116. package/dist/components/ds-table.js.map +1 -1
  117. package/dist/components/ds-tag.js +1 -1
  118. package/dist/components/ds-text.js +1 -1
  119. package/dist/components/ds-textarea.js +1 -1
  120. package/dist/components/ds-time-picker.js +1 -1
  121. package/dist/components/ds-toast.js +1 -1
  122. package/dist/components/ds-tooltip-chart.js +1 -1
  123. package/dist/components/ds-tooltip.js +1 -1
  124. package/dist/components/ds-typing-indicator.js +1 -1
  125. package/dist/components/{p--pJyciTI.js → p--_bPiNCd.js} +2 -2
  126. package/dist/components/{p-UJMEz8_O.js → p-3SCy1PHa.js} +2 -2
  127. package/dist/components/{p-kwS-vkau.js → p-5aZVNYQz.js} +2 -2
  128. package/dist/components/{p-BVxK8ce0.js → p-B1dnisok.js} +2 -2
  129. package/dist/components/{p-BZY40-pE.js → p-B7EfrM6C.js} +2 -2
  130. package/dist/components/{p-DObhRLBa.js → p-B9ZhRwE3.js} +2 -2
  131. package/dist/components/{p-Dr5rkAhe.js → p-BAcdqJaa.js} +2 -2
  132. package/dist/components/{p-DbW8S5qe.js → p-BJf49edB.js} +2 -2
  133. package/dist/components/{p-S1Ct3FJV.js → p-BNN9KkXe.js} +2 -2
  134. package/dist/components/{p-7MwoWtTJ.js → p-BREDkBUv.js} +2 -2
  135. package/dist/components/p-BREDkBUv.js.map +1 -0
  136. package/dist/components/{p-pPtjNRqC.js → p-BVsVUnAw.js} +2 -2
  137. package/dist/components/p-BVsVUnAw.js.map +1 -0
  138. package/dist/components/{p-CwdRaJER.js → p-BWJb2rih.js} +2 -2
  139. package/dist/components/{p-d76Ry5nh.js → p-BWnL2SWn.js} +2 -2
  140. package/dist/components/{p-D8OWhnID.js → p-BbvAWFJA.js} +2 -2
  141. package/dist/components/p-BcmJBzB6.js +2 -0
  142. package/dist/components/{p-5-gQbIIz.js.map → p-BcmJBzB6.js.map} +1 -1
  143. package/dist/components/p-BjD1eFx6.js +2 -0
  144. package/dist/components/{p-DYtVsnDS.js.map → p-BjD1eFx6.js.map} +1 -1
  145. package/dist/components/{p-DKGW3gRa.js → p-Bjm-Rk_x.js} +2 -2
  146. package/dist/components/{p-CSxwWcwW.js → p-Bls48_RF.js} +2 -2
  147. package/dist/components/{p-OJ5WeR05.js → p-BqZWKYsS.js} +2 -2
  148. package/dist/components/p-C2e7BkNn.js +2 -0
  149. package/dist/components/p-C2e7BkNn.js.map +1 -0
  150. package/dist/components/p-C4YUkrr0.js +2 -0
  151. package/dist/components/{p-CUYLGe5d.js.map → p-C4YUkrr0.js.map} +1 -1
  152. package/dist/components/{p-BnuNaXR2.js → p-C4t7HnfZ.js} +2 -2
  153. package/dist/components/p-C62g3d5y.js +2 -0
  154. package/dist/components/{p-DdFeWC-e.js.map → p-C62g3d5y.js.map} +1 -1
  155. package/dist/components/p-C6EXrs1S.js +2 -0
  156. package/dist/components/{p-B3o9zn3Y.js.map → p-C6EXrs1S.js.map} +1 -1
  157. package/dist/components/{p-C1ZfBbJ_.js → p-C7_awFtr.js} +2 -2
  158. package/dist/components/p-CCoJ7CBB.js +2 -0
  159. package/dist/components/{p-BNarCFJZ.js.map → p-CCoJ7CBB.js.map} +1 -1
  160. package/dist/components/p-CG4vPDhg.js +2 -0
  161. package/dist/components/{p-BR9Pjp15.js.map → p-CG4vPDhg.js.map} +1 -1
  162. package/dist/components/{p-CElsKlxj.js → p-CJSIvfpo.js} +2 -2
  163. package/dist/components/{p-CElsKlxj.js.map → p-CJSIvfpo.js.map} +1 -1
  164. package/dist/components/p-CJuc20pQ.js +2 -0
  165. package/dist/components/p-CJuc20pQ.js.map +1 -0
  166. package/dist/components/{p-DVsGfz7J.js → p-CWAeg9jl.js} +2 -2
  167. package/dist/components/p-CWc6Blcp.js +2 -0
  168. package/dist/components/{p-B95UP66y.js.map → p-CWc6Blcp.js.map} +1 -1
  169. package/dist/components/{p-BmIllmq2.js → p-CantpOo3.js} +2 -2
  170. package/dist/components/p-D-TeAVxE.js +2 -0
  171. package/dist/components/{p-DcxY_pdJ.js.map → p-D-TeAVxE.js.map} +1 -1
  172. package/dist/components/{p-YC7UM4jH.js → p-D1tmszmN.js} +2 -2
  173. package/dist/components/p-D3JCih82.js +2 -0
  174. package/dist/components/{p-wXKIVUTn.js.map → p-D3JCih82.js.map} +1 -1
  175. package/dist/components/p-DHpcv_Os.js +2 -0
  176. package/dist/components/{p-xMBaOeKa.js.map → p-DHpcv_Os.js.map} +1 -1
  177. package/dist/components/p-DWXpMWIp.js +2 -0
  178. package/dist/components/{p-DP07JCed.js.map → p-DWXpMWIp.js.map} +1 -1
  179. package/dist/components/{p-C6kUn-vb.js → p-DagrkX2v.js} +2 -2
  180. package/dist/components/{p-CaFXrn_8.js → p-DcMbWqTd.js} +2 -2
  181. package/dist/components/{p-B-pkoIX9.js → p-DiyL2sI5.js} +2 -2
  182. package/dist/components/p-Dkh_40bl.js +2 -0
  183. package/dist/components/p-Dkh_40bl.js.map +1 -0
  184. package/dist/components/{p-DXL5D30P.js → p-Du8LT8JS.js} +2 -2
  185. package/dist/components/p-Dvw7N7TH.js +2 -0
  186. package/dist/components/{p-CgQB8Ct5.js.map → p-Dvw7N7TH.js.map} +1 -1
  187. package/dist/components/{p-DfMTDFcE.js → p-IfsszNF0.js} +2 -2
  188. package/dist/components/{p-CbCN9ew9.js → p-QGtBlTMt.js} +2 -2
  189. package/dist/components/p-QU4czL2k.js +2 -0
  190. package/dist/components/p-QU4czL2k.js.map +1 -0
  191. package/dist/components/{p-leZFu-cd.js → p-SXaH5TeC.js} +2 -2
  192. package/dist/components/{p-leZFu-cd.js.map → p-SXaH5TeC.js.map} +1 -1
  193. package/dist/components/p-Tf6odBOV.js +2 -0
  194. package/dist/components/{p-DYGllrVT.js.map → p-Tf6odBOV.js.map} +1 -1
  195. package/dist/components/p-UWU23pbX.js +2 -0
  196. package/dist/components/{p-KA8srEed.js.map → p-UWU23pbX.js.map} +1 -1
  197. package/dist/components/p-YFjXYN8s.js +2 -0
  198. package/dist/components/{p-Ouk6FCxc.js.map → p-YFjXYN8s.js.map} +1 -1
  199. package/dist/components/{p-B9y6azEw.js → p-eKnJNQtL.js} +2 -2
  200. package/dist/components/p-kL4EOsx5.js +2 -0
  201. package/dist/components/p-kL4EOsx5.js.map +1 -0
  202. package/dist/components/{p-DSmDyI2o.js → p-n_OBbh94.js} +2 -2
  203. package/dist/components/{p-CIMyD_6H.js → p-topqlGsI.js} +2 -2
  204. package/dist/components/{p-HfZ1gI2q.js → p-zopMP2ze.js} +2 -2
  205. package/dist/components/{p-HfZ1gI2q.js.map → p-zopMP2ze.js.map} +1 -1
  206. package/dist/docs/components.json +683 -7
  207. package/dist/lib/shell/index.js.map +2 -2
  208. package/dist/lib/shell/shell-responsive.d.ts +2 -0
  209. package/dist/lint/index.js +1 -1
  210. package/dist/mcp-data/registry/data-preferences.json +1 -1
  211. package/dist/mcp-data/registry/data-sort.json +1 -1
  212. package/dist/mcp-data/registry/navigation-tab-group.json +279 -0
  213. package/dist/mcp-data/registry/registry.json +333 -4
  214. package/dist/mcp-data/registry/select-toggle.json +351 -0
  215. package/dist/mcp-data/registry/shell-app.json +2 -1
  216. package/dist/mcp-data/registry/slider.json +6 -5
  217. package/dist/mcp-data/registry/switch.json +2 -2
  218. package/dist/mcp-data/registry/tab-group.json +14 -8
  219. package/dist/mcp-data/registry/table.json +22 -3
  220. package/dist/react/components.d.ts +2 -0
  221. package/dist/react/components.js +2 -0
  222. package/dist/react/ds-navigation-tab-group.d.ts +12 -0
  223. package/dist/react/ds-navigation-tab-group.js +12 -0
  224. package/dist/react/ds-select-toggle.d.ts +12 -0
  225. package/dist/react/ds-select-toggle.js +12 -0
  226. package/dist/styles/table.css +8 -0
  227. package/dist/types/components/NavigationTabGroup/NavigationTabGroup.d.ts +16 -0
  228. package/dist/types/components/SelectToggle/SelectToggle.d.ts +46 -0
  229. package/dist/types/components/ShellApp/ShellApp.d.ts +1 -0
  230. package/dist/types/components/Slider/Slider.d.ts +1 -1
  231. package/dist/types/components/TabGroup/TabGroup.d.ts +2 -2
  232. package/dist/types/components/Table/Table.d.ts +3 -0
  233. package/dist/types/components.d.ts +237 -15
  234. package/dist/types/shell/shell-responsive.d.ts +2 -0
  235. package/dist/vue/components.d.ts +2 -0
  236. package/dist/vue/components.js +2 -0
  237. package/dist/vue/ds-navigation-tab-group.d.ts +3 -0
  238. package/dist/vue/ds-navigation-tab-group.js +16 -0
  239. package/dist/vue/ds-select-toggle.d.ts +3 -0
  240. package/dist/vue/ds-select-toggle.js +19 -0
  241. package/dist/vue/ds-table.js +1 -0
  242. package/package.json +1 -1
  243. package/dist/components/p-5-gQbIIz.js +0 -2
  244. package/dist/components/p-7MwoWtTJ.js.map +0 -1
  245. package/dist/components/p-B3o9zn3Y.js +0 -2
  246. package/dist/components/p-B95UP66y.js +0 -2
  247. package/dist/components/p-BNarCFJZ.js +0 -2
  248. package/dist/components/p-BR9Pjp15.js +0 -2
  249. package/dist/components/p-Bf-BphXP.js +0 -2
  250. package/dist/components/p-Bf-BphXP.js.map +0 -1
  251. package/dist/components/p-BqipGGua.js +0 -2
  252. package/dist/components/p-BqipGGua.js.map +0 -1
  253. package/dist/components/p-CUYLGe5d.js +0 -2
  254. package/dist/components/p-CgGcgMjs.js +0 -2
  255. package/dist/components/p-CgGcgMjs.js.map +0 -1
  256. package/dist/components/p-CgQB8Ct5.js +0 -2
  257. package/dist/components/p-DP07JCed.js +0 -2
  258. package/dist/components/p-DYGllrVT.js +0 -2
  259. package/dist/components/p-DYtVsnDS.js +0 -2
  260. package/dist/components/p-DcxY_pdJ.js +0 -2
  261. package/dist/components/p-DdFeWC-e.js +0 -2
  262. package/dist/components/p-DlCLfwP4.js +0 -2
  263. package/dist/components/p-DlCLfwP4.js.map +0 -1
  264. package/dist/components/p-KA8srEed.js +0 -2
  265. package/dist/components/p-Ouk6FCxc.js +0 -2
  266. package/dist/components/p-pPtjNRqC.js.map +0 -1
  267. package/dist/components/p-wXKIVUTn.js +0 -2
  268. package/dist/components/p-xMBaOeKa.js +0 -2
  269. /package/dist/components/{p--pJyciTI.js.map → p--_bPiNCd.js.map} +0 -0
  270. /package/dist/components/{p-UJMEz8_O.js.map → p-3SCy1PHa.js.map} +0 -0
  271. /package/dist/components/{p-kwS-vkau.js.map → p-5aZVNYQz.js.map} +0 -0
  272. /package/dist/components/{p-BVxK8ce0.js.map → p-B1dnisok.js.map} +0 -0
  273. /package/dist/components/{p-BZY40-pE.js.map → p-B7EfrM6C.js.map} +0 -0
  274. /package/dist/components/{p-DObhRLBa.js.map → p-B9ZhRwE3.js.map} +0 -0
  275. /package/dist/components/{p-Dr5rkAhe.js.map → p-BAcdqJaa.js.map} +0 -0
  276. /package/dist/components/{p-DbW8S5qe.js.map → p-BJf49edB.js.map} +0 -0
  277. /package/dist/components/{p-S1Ct3FJV.js.map → p-BNN9KkXe.js.map} +0 -0
  278. /package/dist/components/{p-CwdRaJER.js.map → p-BWJb2rih.js.map} +0 -0
  279. /package/dist/components/{p-d76Ry5nh.js.map → p-BWnL2SWn.js.map} +0 -0
  280. /package/dist/components/{p-D8OWhnID.js.map → p-BbvAWFJA.js.map} +0 -0
  281. /package/dist/components/{p-DKGW3gRa.js.map → p-Bjm-Rk_x.js.map} +0 -0
  282. /package/dist/components/{p-CSxwWcwW.js.map → p-Bls48_RF.js.map} +0 -0
  283. /package/dist/components/{p-OJ5WeR05.js.map → p-BqZWKYsS.js.map} +0 -0
  284. /package/dist/components/{p-BnuNaXR2.js.map → p-C4t7HnfZ.js.map} +0 -0
  285. /package/dist/components/{p-C1ZfBbJ_.js.map → p-C7_awFtr.js.map} +0 -0
  286. /package/dist/components/{p-DVsGfz7J.js.map → p-CWAeg9jl.js.map} +0 -0
  287. /package/dist/components/{p-BmIllmq2.js.map → p-CantpOo3.js.map} +0 -0
  288. /package/dist/components/{p-YC7UM4jH.js.map → p-D1tmszmN.js.map} +0 -0
  289. /package/dist/components/{p-C6kUn-vb.js.map → p-DagrkX2v.js.map} +0 -0
  290. /package/dist/components/{p-CaFXrn_8.js.map → p-DcMbWqTd.js.map} +0 -0
  291. /package/dist/components/{p-B-pkoIX9.js.map → p-DiyL2sI5.js.map} +0 -0
  292. /package/dist/components/{p-DXL5D30P.js.map → p-Du8LT8JS.js.map} +0 -0
  293. /package/dist/components/{p-DfMTDFcE.js.map → p-IfsszNF0.js.map} +0 -0
  294. /package/dist/components/{p-CbCN9ew9.js.map → p-QGtBlTMt.js.map} +0 -0
  295. /package/dist/components/{p-B9y6azEw.js.map → p-eKnJNQtL.js.map} +0 -0
  296. /package/dist/components/{p-DSmDyI2o.js.map → p-n_OBbh94.js.map} +0 -0
  297. /package/dist/components/{p-CIMyD_6H.js.map → p-topqlGsI.js.map} +0 -0
@@ -20,6 +20,7 @@
20
20
  "A tab set needs concise labels, icons, or icon-label content with optional supplemental notification dots."
21
21
  ],
22
22
  "avoidWhen": [
23
+ "The group needs an outset shell around full-height control-sized tabs for local panel navigation; use NavigationTabGroup instead.",
23
24
  "The choices navigate application routes; use PanelNav or BarNav instead.",
24
25
  "The choices belong in a vertical panel or card side rail; use PanelSubNav instead.",
25
26
  "The choices are temporary commands or contextual options; use Menu instead.",
@@ -27,6 +28,11 @@
27
28
  "No enabled tab can remain selected while enabled choices exist."
28
29
  ],
29
30
  "alternatives": [
31
+ {
32
+ "when": "Local panel tabs need a roomy navigation shell rather than compact control-height chrome.",
33
+ "component": "component:ds-navigation-tab-group",
34
+ "reason": "NavigationTabGroup preserves tab behavior while giving selected tabs the full control-density height and an elevated primary surface."
35
+ },
30
36
  {
31
37
  "when": "Local views need persistent vertical side-rail navigation.",
32
38
  "component": "component:ds-panel-sub-nav",
@@ -179,13 +185,13 @@
179
185
  "mutable": false
180
186
  },
181
187
  "presentation": {
182
- "type": "'segmented' | 'tabs'",
183
- "resolvedType": "\"segmented\" | \"tabs\"",
188
+ "type": "'segmented' | 'tabs' | 'navigation'",
189
+ "resolvedType": "\"navigation\" | \"segmented\" | \"tabs\"",
184
190
  "attribute": "presentation",
185
191
  "default": "'segmented'",
186
192
  "required": false,
187
193
  "mutable": false,
188
- "description": "Show the segmented control's outer background, border, and inset."
194
+ "description": "Visual treatment; use NavigationTabGroup for the outset navigation presentation."
189
195
  },
190
196
  "size": {
191
197
  "type": "TabGroupSize",
@@ -265,13 +271,13 @@
265
271
  "mutable": false
266
272
  },
267
273
  "presentation": {
268
- "type": "'segmented' | 'tabs'",
269
- "resolvedType": "\"segmented\" | \"tabs\"",
274
+ "type": "'segmented' | 'tabs' | 'navigation'",
275
+ "resolvedType": "\"navigation\" | \"segmented\" | \"tabs\"",
270
276
  "attribute": "presentation",
271
277
  "default": "'segmented'",
272
278
  "required": false,
273
279
  "mutable": false,
274
- "description": "Show the segmented control's outer background, border, and inset."
280
+ "description": "Visual treatment; use NavigationTabGroup for the outset navigation presentation."
275
281
  },
276
282
  "size": {
277
283
  "type": "TabGroupSize",
@@ -366,12 +372,12 @@
366
372
  "files": [
367
373
  {
368
374
  "path": "src/wc/components/TabGroup/TabGroup.css",
369
- "content": "@import '../../utils/radius.css';\n@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:host(.tab-group-host--containerless) .tab-list {\n padding: 0;\n border: 0;\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\n:host(.tab-group-host--tabs) .tab-list {\n gap: var(--dimension-space-050);\n border-radius: 0;\n}\n\n:host(.tab-group-host--tabs) .tab {\n border-radius: var(--ds-control-radius);\n}\n\n:host(.tab-group-host--tabs) .tab--selected {\n background-color: transparent;\n --ds-interaction-border-width: 0px;\n}\n",
375
+ "content": "@import '../../utils/radius.css';\n@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:host(.tab-group-host--containerless) .tab-list {\n padding: 0;\n border: 0;\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\n:host(.tab-group-host--tabs) .tab-list {\n gap: var(--dimension-space-050);\n border-radius: 0;\n}\n\n:host(.tab-group-host--tabs) .tab {\n border-radius: var(--ds-control-radius);\n}\n\n:host(.tab-group-host--tabs) .tab--selected {\n background-color: transparent;\n --ds-interaction-border-width: 0px;\n}\n\n:host(.tab-group-host--navigation) .tab-list {\n gap: var(--dimension-space-050);\n height: calc(var(--ds-control-height) + var(--dimension-space-050) + var(--dimension-space-050));\n padding: var(--dimension-space-050);\n border: 0;\n box-shadow: inset 0 0 0 var(--dimension-stroke-width-012) var(--color-border-tertiary);\n}\n\n:host(.tab-group-host--navigation) .tab--selected {\n box-shadow: var(--effect-elevation-elevated-md);\n --ds-interaction-border-width: 0px;\n}\n\n@media (forced-colors: active) {\n :host(.tab-group-host--navigation) .tab-list {\n box-shadow: none;\n outline: var(--dimension-stroke-width-012) solid var(--ds-forced-color-control-boundary);\n outline-offset: calc(-1 * var(--dimension-stroke-width-012));\n }\n\n :host(.tab-group-host--navigation) .tab--selected {\n box-shadow: none;\n outline: var(--dimension-stroke-width-012) solid var(--ds-forced-color-selected);\n outline-offset: calc(-1 * var(--dimension-stroke-width-012));\n }\n}\n",
370
376
  "type": "registry:ui"
371
377
  },
372
378
  {
373
379
  "path": "src/wc/components/TabGroup/TabGroup.tsx",
374
- "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 /** Show the segmented control's outer background, border, and inset. */\n @Prop() presentation: 'segmented' | 'tabs' = 'segmented';\n @Prop() hasContainer: boolean = true;\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--containerless': !this.hasContainer || this.presentation === 'tabs',\n 'tab-group-host--tabs': this.presentation === 'tabs',\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': this.presentation !== 'tabs',\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={String(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",
380
+ "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 /** Visual treatment; use NavigationTabGroup for the outset navigation presentation. */\n @Prop() presentation: 'segmented' | 'tabs' | 'navigation' = 'segmented';\n @Prop() hasContainer: boolean = true;\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--containerless': !this.hasContainer || this.presentation === 'tabs',\n 'tab-group-host--tabs': this.presentation === 'tabs',\n 'tab-group-host--navigation': this.presentation === 'navigation',\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': this.presentation === 'segmented',\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={String(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",
375
381
  "type": "registry:ui"
376
382
  },
377
383
  {