@ds-mo/ui 14.19.1 → 14.21.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 (272) hide show
  1. package/dist/.package-ready.json +1 -1
  2. package/dist/agent-patterns.json +1 -1
  3. package/dist/agent.json +405 -36
  4. package/dist/angular/ds-chip.d.ts +1 -1
  5. package/dist/angular/ds-chip.js +3 -3
  6. package/dist/angular/ds-filter-menu.d.ts +7 -1
  7. package/dist/angular/ds-filter-menu.js +7 -4
  8. package/dist/angular/ds-input.d.ts +1 -1
  9. package/dist/angular/ds-input.js +3 -3
  10. package/dist/angular/ds-tab-group.d.ts +1 -1
  11. package/dist/angular/ds-tab-group.js +3 -3
  12. package/dist/angular/ds-table-filter.d.ts +7 -1
  13. package/dist/angular/ds-table-filter.js +7 -4
  14. package/dist/angular/ds-table-group.d.ts +29 -0
  15. package/dist/angular/ds-table-group.js +48 -0
  16. package/dist/angular/ds-table-search.d.ts +20 -0
  17. package/dist/angular/ds-table-search.js +49 -0
  18. package/dist/angular/ds-table.d.ts +1 -1
  19. package/dist/angular/ds-table.js +3 -3
  20. package/dist/angular/index.d.ts +1 -1
  21. package/dist/angular/index.js +2 -0
  22. package/dist/angular/proxies.d.ts +2 -0
  23. package/dist/angular/proxies.js +2 -0
  24. package/dist/components/ds-agent-activity.js +1 -1
  25. package/dist/components/ds-agent-questionnaire.js +1 -1
  26. package/dist/components/ds-agent-response.js +1 -1
  27. package/dist/components/ds-agent-source-list.js +1 -1
  28. package/dist/components/ds-agent-tool-call.js +1 -1
  29. package/dist/components/ds-attachment-list.js +1 -1
  30. package/dist/components/ds-badge.js +1 -1
  31. package/dist/components/ds-banner.js +1 -1
  32. package/dist/components/ds-bar-action.js +1 -1
  33. package/dist/components/ds-bar-nav.js +1 -1
  34. package/dist/components/ds-bar-title.js +1 -1
  35. package/dist/components/ds-bar-workflow.js +1 -1
  36. package/dist/components/ds-breadcrumb.js +1 -1
  37. package/dist/components/ds-button-filled.js +1 -1
  38. package/dist/components/ds-button-unfilled.js +1 -1
  39. package/dist/components/ds-card-chart.js +1 -1
  40. package/dist/components/ds-card-overview.js +1 -1
  41. package/dist/components/ds-card-setting.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.js +1 -1
  45. package/dist/components/ds-chip.js +1 -1
  46. package/dist/components/ds-chip.js.map +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-section.js +1 -1
  50. package/dist/components/ds-empty-state.js +1 -1
  51. package/dist/components/ds-field.js +1 -1
  52. package/dist/components/ds-filter-menu.js +1 -1
  53. package/dist/components/ds-input.js +1 -1
  54. package/dist/components/ds-markdown.js +1 -1
  55. package/dist/components/ds-menu.js +1 -1
  56. package/dist/components/ds-message-actions.js +1 -1
  57. package/dist/components/ds-message-composer.js +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-tool-header.js +1 -1
  70. package/dist/components/ds-panel-tool-search.js +1 -1
  71. package/dist/components/ds-panel-tool-search.js.map +1 -1
  72. package/dist/components/ds-panel-tools.js +1 -1
  73. package/dist/components/ds-paper-texture.js +1 -1
  74. package/dist/components/ds-radio.js +1 -1
  75. package/dist/components/ds-select.js +1 -1
  76. package/dist/components/ds-shell-app.js +1 -1
  77. package/dist/components/ds-shell-page.js +1 -1
  78. package/dist/components/ds-shell-tools.js +1 -1
  79. package/dist/components/ds-slider.js +1 -1
  80. package/dist/components/ds-swatch-picker.js +1 -1
  81. package/dist/components/ds-tab-group.js +1 -1
  82. package/dist/components/ds-table-filter.js +1 -1
  83. package/dist/components/ds-table-filter.js.map +1 -1
  84. package/dist/components/ds-table-group.d.ts +11 -0
  85. package/dist/components/ds-table-group.js +2 -0
  86. package/dist/components/ds-table-group.js.map +1 -0
  87. package/dist/components/ds-table-saved-views.js +1 -1
  88. package/dist/components/ds-table-search.d.ts +11 -0
  89. package/dist/components/ds-table-search.js +2 -0
  90. package/dist/components/ds-table-search.js.map +1 -0
  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-toolbar.js.map +1 -1
  95. package/dist/components/ds-table.js +1 -1
  96. package/dist/components/ds-table.js.map +1 -1
  97. package/dist/components/ds-tag.js +1 -1
  98. package/dist/components/ds-text.js +1 -1
  99. package/dist/components/ds-toast.js +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-Bf7PZJPT.js → p-5Z4oYuiK.js} +2 -2
  104. package/dist/components/{p-DsgEY6mZ.js → p-6rB0eAda.js} +2 -2
  105. package/dist/components/{p-DZv2g9Pe.js → p-9ykZwPlI.js} +2 -2
  106. package/dist/components/{p-BHhmB9-4.js → p-B0kSlcaw.js} +2 -2
  107. package/dist/components/{p-BmlGeaY8.js → p-B1wMpt1z.js} +2 -2
  108. package/dist/components/p-BEttT0UB.js +2 -0
  109. package/dist/components/p-BEttT0UB.js.map +1 -0
  110. package/dist/components/p-BFit8o0r.js +2 -0
  111. package/dist/components/p-BFit8o0r.js.map +1 -0
  112. package/dist/components/{p-fRDzpfi9.js → p-BXv6RpoF.js} +2 -2
  113. package/dist/components/{p-CFsLF-ts.js → p-BbG7nOcz.js} +2 -2
  114. package/dist/components/{p-vrotWZxt.js → p-Bc_z1UwJ.js} +2 -2
  115. package/dist/components/{p-Bx5BSd6F.js → p-BcxidW9k.js} +2 -2
  116. package/dist/components/{p-BNq9fr9S.js → p-Bd8I2oXN.js} +2 -2
  117. package/dist/components/{p-D-MWvpg6.js → p-Bjq1KP_Y.js} +2 -2
  118. package/dist/components/p-Bjq1KP_Y.js.map +1 -0
  119. package/dist/components/{p-DW5Ita-P.js → p-BnTPuV3x.js} +2 -2
  120. package/dist/components/{p-DW5Ita-P.js.map → p-BnTPuV3x.js.map} +1 -1
  121. package/dist/components/p-BqVXMEJm.js +2 -0
  122. package/dist/components/p-BqVXMEJm.js.map +1 -0
  123. package/dist/components/{p-DdH7b4JF.js → p-CBymzuOO.js} +2 -2
  124. package/dist/components/{p-BjRD57w3.js → p-CE2b5GPu.js} +2 -2
  125. package/dist/components/{p-CRSwNbPk.js → p-CEnoPUn5.js} +2 -2
  126. package/dist/components/p-CFkZSDwI.js +2 -0
  127. package/dist/components/{p-CKtJ6FHM.js.map → p-CFkZSDwI.js.map} +1 -1
  128. package/dist/components/{p-Bb4KnRy0.js → p-CTH3dSYg.js} +2 -2
  129. package/dist/components/{p-1YHFQeI7.js → p-CUL5_8JM.js} +2 -2
  130. package/dist/components/{p-BubgDQlb.js → p-CZ144LS5.js} +2 -2
  131. package/dist/components/{p-B-HsfQOK.js → p-Cd1JnnVK.js} +2 -2
  132. package/dist/components/p-CeKa4VbU.js +2 -0
  133. package/dist/components/{p-Crz606uD.js.map → p-CeKa4VbU.js.map} +1 -1
  134. package/dist/components/{p-Bx1IHqJb.js → p-Cnsvgx1M.js} +2 -2
  135. package/dist/components/{p-BYFS2qWb.js → p-CpeJMKJ5.js} +2 -2
  136. package/dist/components/{p-Cxr0IQjC.js → p-CzbsTZt7.js} +2 -2
  137. package/dist/components/{p-D7v6Na7U.js → p-D36EmETH.js} +2 -2
  138. package/dist/components/{p-DFC0sRM-.js → p-DI8DUozc.js} +2 -2
  139. package/dist/components/{p-DIqT1m2r.js → p-DSy1oOnd.js} +2 -2
  140. package/dist/components/{p-DIqT1m2r.js.map → p-DSy1oOnd.js.map} +1 -1
  141. package/dist/components/{p-DQBT1Jkf.js → p-DbLLH3hr.js} +2 -2
  142. package/dist/components/{p-Drnyq8z7.js → p-Dit6JmAK.js} +2 -2
  143. package/dist/components/{p-caLOcQUK.js → p-DzPtYdXy.js} +2 -2
  144. package/dist/components/{p-Do8wbFff.js → p-XaCYqck3.js} +2 -2
  145. package/dist/components/{p-DKt_8s-U.js → p-Y5_MkCFn.js} +2 -2
  146. package/dist/components/{p-DBYCwq9L.js → p-YDxDyedE.js} +2 -2
  147. package/dist/components/{p-DmWoFSGs.js → p-_C-XRpCB.js} +2 -2
  148. package/dist/components/{p-Demfba7T.js → p-bHnk8t5I.js} +2 -2
  149. package/dist/components/p-hYjmOPCI.js +2 -0
  150. package/dist/components/p-hYjmOPCI.js.map +1 -0
  151. package/dist/components/{p-ChfVRPHw.js → p-pUOA3iDG.js} +2 -2
  152. package/dist/components/p-pnO8tAYB.js +2 -0
  153. package/dist/components/{p-Cp5Hr7Vz.js.map → p-pnO8tAYB.js.map} +1 -1
  154. package/dist/components/{p-B6WQre5D.js → p-pxN8JGEh.js} +2 -2
  155. package/dist/components/p-pxN8JGEh.js.map +1 -0
  156. package/dist/components/{p-BWwiCvep.js → p-rw0ZxIE4.js} +2 -2
  157. package/dist/components/{p-B4wTnsUL.js → p-w9fkkwL2.js} +2 -2
  158. package/dist/components/p-x5u5Ueli.js +2 -0
  159. package/dist/components/{p-BeA6Jv-3.js.map → p-x5u5Ueli.js.map} +1 -1
  160. package/dist/docs/components.json +1400 -44
  161. package/dist/lib/utils/date-filter-value.d.ts +57 -0
  162. package/dist/lib/utils/index.d.ts +2 -0
  163. package/dist/lib/utils/index.js +67 -0
  164. package/dist/lib/utils/index.js.map +3 -3
  165. package/dist/mcp-data/registry/button-filled.json +1 -0
  166. package/dist/mcp-data/registry/button-unfilled.json +1 -0
  167. package/dist/mcp-data/registry/chip.json +39 -2
  168. package/dist/mcp-data/registry/filter-menu.json +134 -14
  169. package/dist/mcp-data/registry/input.json +54 -2
  170. package/dist/mcp-data/registry/panel-tool-search.json +4 -5
  171. package/dist/mcp-data/registry/registry.json +410 -35
  172. package/dist/mcp-data/registry/select.json +1 -0
  173. package/dist/mcp-data/registry/tab-group.json +21 -2
  174. package/dist/mcp-data/registry/table-filter.json +83 -11
  175. package/dist/mcp-data/registry/table-group.json +301 -0
  176. package/dist/mcp-data/registry/table-search.json +343 -0
  177. package/dist/mcp-data/registry/table-sort.json +1 -1
  178. package/dist/mcp-data/registry/table-toolbar.json +7 -6
  179. package/dist/mcp-data/registry/table.json +55 -17
  180. package/dist/react/components.d.ts +2 -0
  181. package/dist/react/components.js +2 -0
  182. package/dist/react/ds-filter-menu.d.ts +2 -1
  183. package/dist/react/ds-filter-menu.js +1 -0
  184. package/dist/react/ds-table-filter.d.ts +2 -1
  185. package/dist/react/ds-table-filter.js +1 -0
  186. package/dist/react/ds-table-group.d.ts +14 -0
  187. package/dist/react/ds-table-group.js +16 -0
  188. package/dist/react/ds-table-search.d.ts +14 -0
  189. package/dist/react/ds-table-search.js +16 -0
  190. package/dist/styles/table.css +46 -0
  191. package/dist/types/components/Chip/Chip.d.ts +5 -0
  192. package/dist/types/components/FilterMenu/FilterMenu.d.ts +46 -1
  193. package/dist/types/components/FilterMenu/filter-menu-calendar.d.ts +13 -0
  194. package/dist/types/components/Input/Input.d.ts +6 -0
  195. package/dist/types/components/TabGroup/TabGroup.d.ts +2 -0
  196. package/dist/types/components/Table/Table.d.ts +6 -0
  197. package/dist/types/components/Table/index.d.ts +1 -1
  198. package/dist/types/components/Table/table-cell-model.d.ts +11 -1
  199. package/dist/types/components/Table/table-highlight.d.ts +12 -0
  200. package/dist/types/components/Table/table-model.d.ts +6 -5
  201. package/dist/types/components/Table/table-render-model.d.ts +1 -1
  202. package/dist/types/components/Table/table-row-view.d.ts +4 -1
  203. package/dist/types/components/Table/table-types.d.ts +22 -4
  204. package/dist/types/components/Table/table-virtual-model.d.ts +2 -0
  205. package/dist/types/components/TableFilter/TableFilter.d.ts +9 -1
  206. package/dist/types/components/TableGroup/TableGroup.d.ts +60 -0
  207. package/dist/types/components/TableSearch/TableSearch.d.ts +56 -0
  208. package/dist/types/components/TableSearch/table-search-model.d.ts +10 -0
  209. package/dist/types/components/TableSearch/table-search-types.d.ts +10 -0
  210. package/dist/types/components.d.ts +410 -8
  211. package/dist/types/utils/choice-list-parts.d.ts +11 -1
  212. package/dist/types/utils/date-filter-value.d.ts +58 -0
  213. package/dist/types/utils/index.d.ts +2 -0
  214. package/dist/types/utils/select-controller.d.ts +1 -1
  215. package/dist/vue/components.d.ts +2 -0
  216. package/dist/vue/components.js +2 -0
  217. package/dist/vue/ds-chip.js +2 -0
  218. package/dist/vue/ds-filter-menu.js +7 -0
  219. package/dist/vue/ds-input.js +3 -0
  220. package/dist/vue/ds-tab-group.js +1 -0
  221. package/dist/vue/ds-table-filter.js +5 -0
  222. package/dist/vue/ds-table-group.d.ts +3 -0
  223. package/dist/vue/ds-table-group.js +20 -0
  224. package/dist/vue/ds-table-search.d.ts +3 -0
  225. package/dist/vue/ds-table-search.js +22 -0
  226. package/dist/vue/ds-table.js +2 -0
  227. package/package.json +1 -1
  228. package/dist/components/p-B6WQre5D.js.map +0 -1
  229. package/dist/components/p-BeA6Jv-3.js +0 -2
  230. package/dist/components/p-CKtJ6FHM.js +0 -2
  231. package/dist/components/p-Cp5Hr7Vz.js +0 -2
  232. package/dist/components/p-CpUXubax.js +0 -2
  233. package/dist/components/p-CpUXubax.js.map +0 -1
  234. package/dist/components/p-Crz606uD.js +0 -2
  235. package/dist/components/p-D-MWvpg6.js.map +0 -1
  236. package/dist/components/p-DA5H_Iq_.js +0 -2
  237. package/dist/components/p-DA5H_Iq_.js.map +0 -1
  238. package/dist/components/p-Djxw2FjZ.js +0 -2
  239. package/dist/components/p-Djxw2FjZ.js.map +0 -1
  240. /package/dist/components/{p-Bf7PZJPT.js.map → p-5Z4oYuiK.js.map} +0 -0
  241. /package/dist/components/{p-DsgEY6mZ.js.map → p-6rB0eAda.js.map} +0 -0
  242. /package/dist/components/{p-DZv2g9Pe.js.map → p-9ykZwPlI.js.map} +0 -0
  243. /package/dist/components/{p-BHhmB9-4.js.map → p-B0kSlcaw.js.map} +0 -0
  244. /package/dist/components/{p-BmlGeaY8.js.map → p-B1wMpt1z.js.map} +0 -0
  245. /package/dist/components/{p-fRDzpfi9.js.map → p-BXv6RpoF.js.map} +0 -0
  246. /package/dist/components/{p-CFsLF-ts.js.map → p-BbG7nOcz.js.map} +0 -0
  247. /package/dist/components/{p-vrotWZxt.js.map → p-Bc_z1UwJ.js.map} +0 -0
  248. /package/dist/components/{p-Bx5BSd6F.js.map → p-BcxidW9k.js.map} +0 -0
  249. /package/dist/components/{p-BNq9fr9S.js.map → p-Bd8I2oXN.js.map} +0 -0
  250. /package/dist/components/{p-DdH7b4JF.js.map → p-CBymzuOO.js.map} +0 -0
  251. /package/dist/components/{p-BjRD57w3.js.map → p-CE2b5GPu.js.map} +0 -0
  252. /package/dist/components/{p-CRSwNbPk.js.map → p-CEnoPUn5.js.map} +0 -0
  253. /package/dist/components/{p-Bb4KnRy0.js.map → p-CTH3dSYg.js.map} +0 -0
  254. /package/dist/components/{p-1YHFQeI7.js.map → p-CUL5_8JM.js.map} +0 -0
  255. /package/dist/components/{p-BubgDQlb.js.map → p-CZ144LS5.js.map} +0 -0
  256. /package/dist/components/{p-B-HsfQOK.js.map → p-Cd1JnnVK.js.map} +0 -0
  257. /package/dist/components/{p-Bx1IHqJb.js.map → p-Cnsvgx1M.js.map} +0 -0
  258. /package/dist/components/{p-BYFS2qWb.js.map → p-CpeJMKJ5.js.map} +0 -0
  259. /package/dist/components/{p-Cxr0IQjC.js.map → p-CzbsTZt7.js.map} +0 -0
  260. /package/dist/components/{p-D7v6Na7U.js.map → p-D36EmETH.js.map} +0 -0
  261. /package/dist/components/{p-DFC0sRM-.js.map → p-DI8DUozc.js.map} +0 -0
  262. /package/dist/components/{p-DQBT1Jkf.js.map → p-DbLLH3hr.js.map} +0 -0
  263. /package/dist/components/{p-Drnyq8z7.js.map → p-Dit6JmAK.js.map} +0 -0
  264. /package/dist/components/{p-caLOcQUK.js.map → p-DzPtYdXy.js.map} +0 -0
  265. /package/dist/components/{p-Do8wbFff.js.map → p-XaCYqck3.js.map} +0 -0
  266. /package/dist/components/{p-DKt_8s-U.js.map → p-Y5_MkCFn.js.map} +0 -0
  267. /package/dist/components/{p-DBYCwq9L.js.map → p-YDxDyedE.js.map} +0 -0
  268. /package/dist/components/{p-DmWoFSGs.js.map → p-_C-XRpCB.js.map} +0 -0
  269. /package/dist/components/{p-Demfba7T.js.map → p-bHnk8t5I.js.map} +0 -0
  270. /package/dist/components/{p-ChfVRPHw.js.map → p-pUOA3iDG.js.map} +0 -0
  271. /package/dist/components/{p-BWwiCvep.js.map → p-rw0ZxIE4.js.map} +0 -0
  272. /package/dist/components/{p-B4wTnsUL.js.map → p-w9fkkwL2.js.map} +0 -0
@@ -0,0 +1,343 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "table-search",
4
+ "title": "TableSearch",
5
+ "description": "Controlled table-search editor with slash-invoked field scoping, removable inset field Chips, and an input-focused listbox keyboard model.",
6
+ "type": "registry:ui",
7
+ "meta": {
8
+ "library": "@ds-mo/ui",
9
+ "distribution": "npm",
10
+ "storybook": "https://zainadeel.github.io/compomo/?path=/story/table-search",
11
+ "source": "src/wc/components/TableSearch/TableSearch.tsx",
12
+ "intentStatus": "complete",
13
+ "intent": {
14
+ "audience": "general",
15
+ "status": "stable",
16
+ "summary": "Controlled table-search editor with slash-invoked field scoping, removable inset field Chips, and an input-focused listbox keyboard model.",
17
+ "useWhen": [
18
+ "A data-table search needs optional field-specific scopes without replacing the application's filtering logic.",
19
+ "People should be able to add one or more searchable data points through a slash command while continuing to type one free-text query."
20
+ ],
21
+ "avoidWhen": [
22
+ "A plain global text search is sufficient; use Input with type search.",
23
+ "The choices are persistent filters with independent values; use TableFilter or Select instead.",
24
+ "The component is expected to inspect records, build a search index, or filter rows internally."
25
+ ],
26
+ "commonlyComposedWith": [
27
+ "component:ds-table",
28
+ "component:ds-table-toolbar",
29
+ "component:ds-chip",
30
+ "component:ds-button-unfilled",
31
+ "component:ds-icon"
32
+ ],
33
+ "accessibility": [
34
+ "The native search input remains the one Tab stop and exposes combobox, expanded, controls, and active-descendant relationships to the field listbox.",
35
+ "Typing slash opens the field listbox without inserting slash into the table query. Text typed while it is open filters data points by complete label, Up and Down change the active match, Enter selects it, Escape closes it, and Left and Right retain native text-cursor behavior because focus never leaves the input. Backspace edits the field-picker query first and closes the listbox when that query is empty.",
36
+ "The query disables browser autocomplete history, automatic capitalization, autocorrection, and spellchecking so browser-owned suggestions never obscure the product-owned field menu or table results.",
37
+ "Selected field Chips have individually labeled remove actions and are also described by the search input. Polite announcements report additions, removals, and clearing without duplicating the visible query.",
38
+ "The slash suffix is a labeled double-inset md button excluded from sequential keyboard navigation; the search input exposes the same action through aria-keyshortcuts and its description.",
39
+ "The clear action removes both free text and every selected field. It restores focus to the search input; each Chip's own remove action remains scoped to that field."
40
+ ],
41
+ "states": [
42
+ "value and selectedFieldIds are controlled. columns supplies the same table catalog used by Table and TableSort; dataLabel provides the complete out-of-context name while header and header segment label may remain compact. dsChange reports query edits, dsFieldsChange reports the next ordered field identities, and dsClear reports a complete clear. The component never filters rows or mutates consumer inputs.",
43
+ "The field menu excludes already-selected identities and closes after one selection. Typing slash again reopens the remaining fields so multiple scopes can be added.",
44
+ "Selected scopes render as neutral md Chips using the double-inset recipe so they remain 24px high inside the 32px md search control and each can be removed directly.",
45
+ "Backspace edits a non-empty field-picker query, then closes the open slash listbox when that query is empty. When the table query is empty and the listbox is already closed, the next Backspace removes the last selected field. Ordinary Backspace edits table query text when the listbox is closed.",
46
+ "Duplicate, blank, and unknown field identities are ignored for presentation. Selected fields preserve the consumer's order.",
47
+ "Inactive search leaves the tab order, closes the field listbox, and does not emit query or field changes."
48
+ ],
49
+ "responsiveBehavior": [
50
+ "The control fills the available inline size supplied by the table toolbar search slot. Selected Chips and the editable query share one horizontal editor lane so the md control does not grow vertically.",
51
+ "The field popup enters the top layer, aligns its first choice cell with the search control, flips or clamps to the viewport or nearest overlay boundary, and scrolls internally when its catalog is taller than available space.",
52
+ "Long selected field labels truncate inside their Chips while the input remains available at the inline end of the editor lane. Both suffix actions resolve to the same 24px height as selected Chips. Clear follows the standard md Input recipe—a non-inset sm icon button—while slash is a double-inset md label button. Slash remains rightmost; when query text exposes Clear, a vertical divider separates the two actions."
53
+ ],
54
+ "frameworkCaveats": {
55
+ "customElements": [
56
+ "Assign columns and selectedFieldIds through JavaScript properties and replace controlled values from dsChange and dsFieldsChange.",
57
+ "Keep field IDs aligned with the application search index; the component treats IDs as opaque values."
58
+ ],
59
+ "react": [
60
+ "Pass new array references for columns and selectedFieldIds and update controlled state from onDsChange and onDsFieldsChange."
61
+ ],
62
+ "angular": [
63
+ "Import DsTableSearch, bind columns, selectedFieldIds, and value, then update application signals from dsChange and dsFieldsChange."
64
+ ]
65
+ },
66
+ "references": [
67
+ {
68
+ "label": "Storybook interaction states",
69
+ "path": "src/wc/components/TableSearch/TableSearch.stories.ts"
70
+ },
71
+ {
72
+ "label": "Toolbar composition owner",
73
+ "path": "src/wc/components/TableToolbar/TableToolbar.agent.json"
74
+ }
75
+ ]
76
+ },
77
+ "api": {
78
+ "props": {
79
+ "ariaLabel": {
80
+ "type": "string",
81
+ "resolvedType": "string",
82
+ "attribute": "aria-label",
83
+ "default": "'Search table'",
84
+ "required": false,
85
+ "mutable": false
86
+ },
87
+ "clearLabel": {
88
+ "type": "string",
89
+ "resolvedType": "string",
90
+ "attribute": "clear-label",
91
+ "default": "'Clear search'",
92
+ "required": false,
93
+ "mutable": false
94
+ },
95
+ "columns": {
96
+ "type": "TableColumn[]",
97
+ "resolvedType": "TableColumn[]",
98
+ "default": "[]",
99
+ "required": false,
100
+ "mutable": false,
101
+ "description": "Table column catalog used to derive searchable data points and their complete labels."
102
+ },
103
+ "fieldMenuLabel": {
104
+ "type": "string",
105
+ "resolvedType": "string",
106
+ "attribute": "field-menu-label",
107
+ "default": "'Choose search fields'",
108
+ "required": false,
109
+ "mutable": false
110
+ },
111
+ "isInactive": {
112
+ "type": "boolean",
113
+ "resolvedType": "boolean",
114
+ "attribute": "is-inactive",
115
+ "default": "false",
116
+ "required": false,
117
+ "mutable": false
118
+ },
119
+ "placeholder": {
120
+ "type": "string",
121
+ "resolvedType": "string",
122
+ "attribute": "placeholder",
123
+ "default": "'Search'",
124
+ "required": false,
125
+ "mutable": false
126
+ },
127
+ "selectedFieldIds": {
128
+ "type": "string[]",
129
+ "resolvedType": "string[]",
130
+ "default": "[]",
131
+ "required": false,
132
+ "mutable": false,
133
+ "description": "Controlled ordered field scopes rendered as Tags."
134
+ },
135
+ "value": {
136
+ "type": "string",
137
+ "resolvedType": "string",
138
+ "attribute": "value",
139
+ "default": "''",
140
+ "required": false,
141
+ "mutable": false,
142
+ "description": "Controlled free-text query."
143
+ }
144
+ },
145
+ "events": [
146
+ {
147
+ "name": "dsChange",
148
+ "detail": "string",
149
+ "bubbles": false,
150
+ "cancelable": true,
151
+ "composed": true
152
+ },
153
+ {
154
+ "name": "dsClear",
155
+ "detail": "void",
156
+ "bubbles": false,
157
+ "cancelable": true,
158
+ "composed": true
159
+ },
160
+ {
161
+ "name": "dsFieldsChange",
162
+ "detail": "TableSearchFieldsChangeDetail",
163
+ "bubbles": false,
164
+ "cancelable": true,
165
+ "composed": true
166
+ }
167
+ ],
168
+ "methods": [
169
+ {
170
+ "name": "setFocus",
171
+ "signature": "setFocus() => Promise<void>"
172
+ }
173
+ ],
174
+ "slots": []
175
+ },
176
+ "props": {
177
+ "ariaLabel": {
178
+ "type": "string",
179
+ "resolvedType": "string",
180
+ "attribute": "aria-label",
181
+ "default": "'Search table'",
182
+ "required": false,
183
+ "mutable": false
184
+ },
185
+ "clearLabel": {
186
+ "type": "string",
187
+ "resolvedType": "string",
188
+ "attribute": "clear-label",
189
+ "default": "'Clear search'",
190
+ "required": false,
191
+ "mutable": false
192
+ },
193
+ "columns": {
194
+ "type": "TableColumn[]",
195
+ "resolvedType": "TableColumn[]",
196
+ "default": "[]",
197
+ "required": false,
198
+ "mutable": false,
199
+ "description": "Table column catalog used to derive searchable data points and their complete labels."
200
+ },
201
+ "fieldMenuLabel": {
202
+ "type": "string",
203
+ "resolvedType": "string",
204
+ "attribute": "field-menu-label",
205
+ "default": "'Choose search fields'",
206
+ "required": false,
207
+ "mutable": false
208
+ },
209
+ "isInactive": {
210
+ "type": "boolean",
211
+ "resolvedType": "boolean",
212
+ "attribute": "is-inactive",
213
+ "default": "false",
214
+ "required": false,
215
+ "mutable": false
216
+ },
217
+ "placeholder": {
218
+ "type": "string",
219
+ "resolvedType": "string",
220
+ "attribute": "placeholder",
221
+ "default": "'Search'",
222
+ "required": false,
223
+ "mutable": false
224
+ },
225
+ "selectedFieldIds": {
226
+ "type": "string[]",
227
+ "resolvedType": "string[]",
228
+ "default": "[]",
229
+ "required": false,
230
+ "mutable": false,
231
+ "description": "Controlled ordered field scopes rendered as Tags."
232
+ },
233
+ "value": {
234
+ "type": "string",
235
+ "resolvedType": "string",
236
+ "attribute": "value",
237
+ "default": "''",
238
+ "required": false,
239
+ "mutable": false,
240
+ "description": "Controlled free-text query."
241
+ }
242
+ },
243
+ "events": [
244
+ {
245
+ "name": "dsChange",
246
+ "detail": "string",
247
+ "bubbles": false,
248
+ "cancelable": true,
249
+ "composed": true
250
+ },
251
+ {
252
+ "name": "dsClear",
253
+ "detail": "void",
254
+ "bubbles": false,
255
+ "cancelable": true,
256
+ "composed": true
257
+ },
258
+ {
259
+ "name": "dsFieldsChange",
260
+ "detail": "TableSearchFieldsChangeDetail",
261
+ "bubbles": false,
262
+ "cancelable": true,
263
+ "composed": true
264
+ }
265
+ ],
266
+ "methods": [
267
+ {
268
+ "name": "setFocus",
269
+ "signature": "setFocus() => Promise<void>"
270
+ }
271
+ ],
272
+ "slots": [],
273
+ "exports": {
274
+ "customElement": "ds-table-search",
275
+ "react": "DsTableSearch",
276
+ "vue": "DsTableSearch",
277
+ "angular": "DsTableSearch"
278
+ },
279
+ "consumption": {
280
+ "install": "npm install @ds-mo/ui @ds-mo/tokens @ds-mo/icons",
281
+ "cssSetup": "import '@ds-mo/tokens';\nimport '@ds-mo/tokens/reset';\nimport '@ds-mo/tokens/globals';",
282
+ "customElements": {
283
+ "import": "import '@ds-mo/ui/dist/components/ds-table-search.js';",
284
+ "example": "<ds-table-search></ds-table-search>"
285
+ },
286
+ "react": {
287
+ "import": "import { DsTableSearch } from '@ds-mo/ui/react';",
288
+ "example": "<DsTableSearch />"
289
+ },
290
+ "vue": {
291
+ "import": "import { DsTableSearch } from '@ds-mo/ui/vue';",
292
+ "example": "<DsTableSearch />"
293
+ },
294
+ "angular": {
295
+ "import": "import { DsTableSearch } from '@ds-mo/ui/angular/ds-table-search';",
296
+ "example": "<ds-table-search></ds-table-search>"
297
+ },
298
+ "complexPropertyNote": "Assign these non-primitive values as JavaScript properties: columns, selectedFieldIds.",
299
+ "peerDependencies": {
300
+ "required": [
301
+ "@ds-mo/tokens ^6.5.0",
302
+ "@ds-mo/icons ^7.0.0"
303
+ ],
304
+ "frameworks": "Custom Elements; React 18/19 wrappers; Vue 3 wrappers; Angular 19-22 standalone adapters."
305
+ }
306
+ }
307
+ },
308
+ "dependencies": [
309
+ "@ds-mo/ui",
310
+ "@ds-mo/tokens",
311
+ "@ds-mo/icons"
312
+ ],
313
+ "registryDependencies": [
314
+ "button-unfilled",
315
+ "chip",
316
+ "divider",
317
+ "empty-state",
318
+ "icon",
319
+ "text"
320
+ ],
321
+ "files": [
322
+ {
323
+ "path": "src/wc/components/TableSearch/TableSearch.css",
324
+ "content": "@import '../../utils/choice-list.css';\n@import '../../utils/control-density-inset.css';\n@import '../../utils/typography.css';\n@import '../../utils/visually-hidden.css';\n\n:host(.table-search-host) {\n display: inline-flex;\n box-sizing: border-box;\n inline-size: 100%;\n min-inline-size: 0;\n --ds-table-search-foreground: var(--color-foreground-primary);\n --ds-table-search-adornment: var(--color-foreground-secondary);\n --ds-table-search-placeholder: var(--color-foreground-secondary);\n --ds-table-search-placeholder-active: var(--color-foreground-quaternary);\n --ds-table-search-border: var(--color-border-secondary);\n}\n\n.table-search__control {\n inline-size: 100%;\n min-inline-size: 0;\n padding-inline-end: var(--dimension-space-050);\n overflow: hidden;\n border: 0;\n background: transparent;\n color: var(--ds-table-search-foreground);\n --ds-interaction-border-width: var(--dimension-stroke-width-012);\n --ds-interaction-border-color: var(--ds-table-search-border);\n}\n\n.table-search__control:focus-within {\n --ds-interaction-border-width: var(--dimension-stroke-width-018);\n --ds-interaction-border-color: var(--color-border-bold-brand);\n}\n\n:host(.ds-control-inactive) .table-search__control::after {\n background: transparent;\n}\n\n.table-search__icon,\n.table-search__actions {\n position: relative;\n z-index: 2;\n flex: none;\n}\n\n.table-search__icon {\n color: var(--ds-table-search-adornment);\n}\n\n.table-search__editor {\n position: relative;\n z-index: 2;\n display: flex;\n flex: 1 1 auto;\n align-items: center;\n min-inline-size: 0;\n block-size: 100%;\n gap: var(--dimension-space-050);\n overflow-x: auto;\n overflow-y: hidden;\n scrollbar-width: none;\n}\n\n.table-search__editor::-webkit-scrollbar {\n display: none;\n}\n\n.table-search__chip {\n flex: none;\n max-inline-size: var(--dimension-panel-width-2xs);\n}\n\n.table-search__input {\n appearance: none;\n box-sizing: border-box;\n flex: 1 0 var(--dimension-size-600);\n min-inline-size: var(--dimension-size-600);\n block-size: 100%;\n margin: 0;\n padding: 0;\n border: 0;\n outline: 0;\n background: transparent;\n color: var(--ds-table-search-foreground);\n text-overflow: ellipsis;\n}\n\n.table-search__input::-webkit-search-cancel-button,\n.table-search__input::-webkit-search-decoration {\n display: none;\n}\n\n.table-search__input::placeholder {\n color: var(--ds-table-search-placeholder);\n}\n\n.table-search__input:focus::placeholder {\n color: var(--ds-table-search-placeholder-active);\n}\n\n.table-search__actions {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-050);\n}\n\n.table-search__clear,\n.table-search__slash,\n.table-search__action-divider {\n flex: none;\n}\n\n.table-search__slash {\n color: var(--ds-table-search-adornment);\n}\n\n.table-search__popup {\n --ds-choice-popup-min-inline-size: var(--dimension-menu-width-xs);\n}\n\n.table-search__list {\n padding: var(--dimension-space-050);\n}\n\n.table-search__option {\n flex: none;\n inline-size: 100%;\n}\n\n@media (forced-colors: active) {\n .table-search__control:focus-within::after {\n outline-color: var(--ds-forced-color-selected);\n }\n}\n",
325
+ "type": "registry:ui"
326
+ },
327
+ {
328
+ "path": "src/wc/components/TableSearch/TableSearch.tsx",
329
+ "content": "import {\n Component,\n Element,\n Event,\n EventEmitter,\n h,\n Host,\n Method,\n Prop,\n State,\n Watch,\n} from '@stencil/core';\nimport {\n choicePopupMinWidth,\n resolveChoicePopupAlignOffset,\n resolveCssLengthPx,\n TOKEN_DEFAULTS,\n} from '../../utils';\nimport { AnchoredPositionController } from '../../utils/anchored-position-controller';\nimport { AnchoredOverlayInteractionController } from '../../utils/anchored-overlay-interaction-controller';\nimport { resolveAnchoredOverlayBoundaryRect } from '../../utils/anchored-overlay-boundary';\nimport {\n availableTableSearchFields,\n filterTableSearchFields,\n nextTableSearchActiveIndex,\n selectedTableSearchFields,\n tableSearchFields,\n} from './table-search-model';\nimport type { TableColumn } from '../Table/table-types';\nimport type { TableSearchField, TableSearchFieldsChangeDetail } from './table-search-types';\n\nlet tableSearchSequence = 0;\n\n@Component({\n tag: 'ds-table-search',\n styleUrl: 'TableSearch.css',\n scoped: true,\n})\nexport class TableSearch {\n @Element() private el!: HTMLElement;\n\n /** Controlled free-text query. */\n @Prop() value: string = '';\n /** Table column catalog used to derive searchable data points and their complete labels. */\n @Prop() columns: TableColumn[] = [];\n /** Controlled ordered field scopes rendered as Tags. */\n @Prop() selectedFieldIds: string[] = [];\n @Prop() placeholder: string = 'Search';\n @Prop({ attribute: 'aria-label' }) ariaLabel: string = 'Search table';\n @Prop() clearLabel: string = 'Clear search';\n @Prop() fieldMenuLabel: string = 'Choose search fields';\n @Prop() isInactive: boolean = false;\n\n @Event({ bubbles: false }) dsChange!: EventEmitter<string>;\n @Event({ bubbles: false }) dsFieldsChange!: EventEmitter<TableSearchFieldsChangeDetail>;\n @Event({ bubbles: false }) dsClear!: EventEmitter<void>;\n\n @State() private menuOpen = false;\n @State() private menuQuery = '';\n @State() private activeIndex = 0;\n @State() private focusRingVisible = false;\n @State() private position = { x: 0, y: 0 };\n @State() private positionReady = false;\n @State() private availableHeight: number | null = null;\n @State() private announcement = '';\n\n private readonly componentId = `ds-table-search-${++tableSearchSequence}`;\n private readonly inputId = `${this.componentId}-input`;\n private readonly listboxId = `${this.componentId}-listbox`;\n private readonly descriptionId = `${this.componentId}-description`;\n private inputEl: HTMLInputElement | null = null;\n private controlEl: HTMLElement | null = null;\n private popupEl: HTMLElement | null = null;\n\n private readonly positionController = new AnchoredPositionController({\n getAnchor: () => this.controlEl,\n getPopup: () => this.popupEl,\n getOwnerDocument: () => this.el.ownerDocument,\n measure: (anchor, popup) => {\n if (!this.menuOpen || !popup.isConnected) return null;\n const inset = resolveCssLengthPx(TOKEN_DEFAULTS.space050, TOKEN_DEFAULTS.space050);\n popup.style.minWidth = `max(var(--ds-choice-popup-min-inline-size, ${TOKEN_DEFAULTS.menuWidthXs}), ${choicePopupMinWidth(anchor.offsetWidth, inset)}px)`;\n const anchorRect = anchor.getBoundingClientRect();\n return {\n anchorRect,\n popupWidth: popup.offsetWidth,\n popupHeight: popup.offsetHeight,\n side: 'bottom',\n align: 'start',\n sideOffsetPx: inset,\n alignOffsetPx: resolveChoicePopupAlignOffset({\n align: 'start',\n alignOffsetPx: 0,\n sectionInsetPx: inset,\n }),\n viewportPadPx: inset,\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n collisionRect: resolveAnchoredOverlayBoundaryRect(anchor),\n };\n },\n apply: ({ x, y, availableHeight }) => {\n this.position = { x, y };\n this.availableHeight = availableHeight;\n },\n onReady: () => {\n this.positionReady = true;\n },\n topLayer: true,\n liveUpdate: 'frame',\n observeResize: true,\n observeAnchorMotion: true,\n });\n\n private readonly interactionController = new AnchoredOverlayInteractionController({\n getAnchor: () => this.controlEl,\n getPopup: () => this.popupEl,\n getOwnerDocument: () => this.el.ownerDocument,\n onOutsideActivation: () => this.closeMenu(),\n });\n\n disconnectedCallback(): void {\n this.closeMenu();\n }\n\n @Watch('columns')\n @Watch('selectedFieldIds')\n syncAvailableFields(): void {\n const count = this.availableFields.length;\n if (count === 0) {\n this.closeMenu();\n return;\n }\n if (this.activeIndex >= count) this.activeIndex = count - 1;\n }\n\n @Watch('isInactive')\n syncInactive(isInactive: boolean): void {\n if (isInactive) this.closeMenu();\n }\n\n @Method()\n async setFocus(): Promise<void> {\n this.inputEl?.focus();\n }\n\n private get selectedFields(): TableSearchField[] {\n return selectedTableSearchFields(this.searchFields, this.selectedFieldIds);\n }\n\n private get availableFields(): TableSearchField[] {\n return availableTableSearchFields(this.searchFields, this.selectedFieldIds);\n }\n\n private get visibleFields(): TableSearchField[] {\n return filterTableSearchFields(this.availableFields, this.menuQuery);\n }\n\n private get searchFields(): TableSearchField[] {\n return tableSearchFields(this.columns);\n }\n\n private get activeOptionId(): string | undefined {\n return this.menuOpen && this.activeIndex >= 0\n ? `${this.componentId}-option-${this.activeIndex}`\n : undefined;\n }\n\n private openMenu(fromKeyboard: boolean): void {\n if (this.isInactive || this.menuOpen || this.availableFields.length === 0) return;\n this.menuQuery = '';\n this.activeIndex = 0;\n this.focusRingVisible = fromKeyboard;\n this.positionReady = false;\n this.menuOpen = true;\n requestAnimationFrame(() => {\n if (!this.menuOpen) return;\n this.interactionController.connect();\n this.positionController.observe();\n this.positionController.schedule();\n });\n }\n\n private closeMenu(): void {\n if (!this.menuOpen && !this.popupEl) return;\n if (this.popupEl?.matches(':popover-open')) this.popupEl.hidePopover();\n this.positionController.unobserve();\n this.interactionController.disconnect();\n this.popupEl = null;\n this.positionReady = false;\n this.availableHeight = null;\n this.menuOpen = false;\n this.menuQuery = '';\n }\n\n private handleInput(event: Event): void {\n const nextValue = (event.target as HTMLInputElement).value;\n if (this.menuOpen) {\n this.menuQuery = nextValue;\n this.activeIndex = filterTableSearchFields(this.availableFields, nextValue).length ? 0 : -1;\n this.focusRingVisible = false;\n this.positionController.schedule();\n return;\n }\n\n this.dsChange.emit(nextValue);\n }\n\n private selectField(field: TableSearchField): void {\n this.dsFieldsChange.emit({ selectedFieldIds: [...this.selectedFieldIds, field.id] });\n this.announcement = `${field.label} search field added.`;\n this.closeMenu();\n requestAnimationFrame(() => this.inputEl?.focus({ preventScroll: true }));\n }\n\n private removeLastField(): void {\n const selected = this.selectedFields;\n const removed = selected[selected.length - 1];\n if (!removed) return;\n this.dsFieldsChange.emit({\n selectedFieldIds: this.selectedFieldIds.filter(fieldId => fieldId !== removed.id),\n });\n this.announcement = `${removed.label} search field removed.`;\n }\n\n private removeField(field: TableSearchField): void {\n this.dsFieldsChange.emit({\n selectedFieldIds: this.selectedFieldIds.filter(fieldId => fieldId !== field.id),\n });\n this.announcement = `${field.label} search field removed.`;\n requestAnimationFrame(() => this.inputEl?.focus({ preventScroll: true }));\n }\n\n private clearSearch(): void {\n if (this.value) this.dsChange.emit('');\n if (this.selectedFieldIds.length) this.dsFieldsChange.emit({ selectedFieldIds: [] });\n this.dsClear.emit();\n this.announcement = 'Search cleared.';\n this.closeMenu();\n requestAnimationFrame(() => this.inputEl?.focus({ preventScroll: true }));\n }\n\n private handleKeyDown(event: KeyboardEvent): void {\n if (event.key === '/' && !this.menuOpen && !event.altKey && !event.ctrlKey && !event.metaKey) {\n event.preventDefault();\n this.openMenu(true);\n return;\n }\n\n if (event.key === 'Backspace' && this.menuOpen && this.menuQuery.length === 0) {\n event.preventDefault();\n this.closeMenu();\n return;\n }\n\n if (event.key === 'Backspace' && this.value.length === 0) {\n if (this.selectedFields.length) {\n event.preventDefault();\n this.removeLastField();\n }\n return;\n }\n\n if (!this.menuOpen) return;\n\n if (event.key === 'Escape') {\n event.preventDefault();\n this.closeMenu();\n return;\n }\n\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n this.focusRingVisible = true;\n this.activeIndex = nextTableSearchActiveIndex(\n this.activeIndex,\n this.visibleFields.length,\n event.key === 'ArrowDown' ? 1 : -1\n );\n this.el\n .querySelector<HTMLElement>(`#${CSS.escape(this.activeOptionId ?? '')}`)\n ?.scrollIntoView({ block: 'nearest' });\n return;\n }\n\n if (event.key === 'Enter') {\n const field = this.visibleFields[this.activeIndex];\n if (!field) return;\n event.preventDefault();\n this.selectField(field);\n return;\n }\n\n if (event.key === 'Tab') this.closeMenu();\n }\n\n render() {\n const selected = this.selectedFields;\n const available = this.availableFields;\n const visible = this.visibleFields;\n const inputValue = this.menuOpen ? this.menuQuery : this.value;\n const hasValue = this.value.length > 0 || selected.length > 0;\n const description = selected.length\n ? `Search limited to ${selected.map(field => field.label).join(' or ')}.`\n : 'Search includes every available table field. Type slash to choose fields.';\n const popupStyle: Record<string, string> = {\n position: 'fixed',\n left: '0',\n top: '0',\n transform: `translate(${Math.round(this.position.x)}px, ${Math.round(this.position.y)}px)`,\n zIndex: 'var(--dimension-z-index-floating)',\n visibility: this.positionReady ? 'visible' : 'hidden',\n };\n if (this.availableHeight !== null) {\n popupStyle['max-height'] = `${Math.floor(this.availableHeight)}px`;\n }\n\n return (\n <Host\n class={{\n 'table-search-host': true,\n 'ds-control--md': true,\n 'ds-control-inactive': this.isInactive,\n }}\n data-filled={hasValue ? '' : undefined}\n data-expanded={this.menuOpen ? '' : undefined}\n >\n <div\n ref={element => {\n this.controlEl = element ?? null;\n }}\n class=\"table-search__control ds-control-frame ds-control--md ds-interaction-fill\"\n >\n <span\n class=\"table-search__icon ds-control-icon-box ds-interaction-fill__content\"\n aria-hidden=\"true\"\n >\n <ds-icon name=\"MagnifyingGlass\" size=\"md\" color=\"inherit\" />\n </span>\n <div class=\"table-search__editor ds-interaction-fill__content\">\n {selected.map(field => (\n <ds-chip\n class=\"table-search__chip\"\n key={field.id}\n label={field.label}\n size=\"md\"\n state=\"default\"\n isInset\n insetDepth=\"double\"\n isInactive={this.isInactive}\n onDsRemove={() => this.removeField(field)}\n />\n ))}\n <input\n ref={element => {\n this.inputEl = element ?? null;\n }}\n id={this.inputId}\n class=\"table-search__input ds-text--body-medium ds-text--regular\"\n type=\"search\"\n role=\"combobox\"\n value={inputValue}\n placeholder={this.placeholder}\n disabled={this.isInactive}\n aria-label={this.ariaLabel}\n aria-describedby={this.descriptionId}\n aria-haspopup=\"listbox\"\n aria-expanded={String(this.menuOpen)}\n aria-controls={this.menuOpen ? this.listboxId : undefined}\n aria-activedescendant={this.activeOptionId}\n aria-keyshortcuts=\"/\"\n autoComplete=\"off\"\n autoCapitalize=\"none\"\n autoCorrect=\"off\"\n spellcheck={false}\n onInput={event => this.handleInput(event)}\n onKeyDown={event => this.handleKeyDown(event)}\n />\n </div>\n {this.searchFields.length > 0 && (\n <div class=\"table-search__actions ds-interaction-fill__content\">\n {inputValue.length > 0 && !this.isInactive && (\n <ds-button-unfilled\n class=\"table-search__clear\"\n variant=\"icon\"\n size=\"sm\"\n icon=\"CrossCircle\"\n hasBorder={false}\n rounded\n ariaLabel={this.clearLabel}\n onDsClick={() => this.clearSearch()}\n />\n )}\n {inputValue.length > 0 && !this.isInactive && (\n <ds-divider\n class=\"table-search__action-divider\"\n orientation=\"vertical\"\n length=\"var(--dimension-size-200)\"\n />\n )}\n <ds-button-unfilled\n class=\"table-search__slash\"\n variant=\"label\"\n size=\"md\"\n label=\"/\"\n labelEmphasis={false}\n isInset\n insetDepth=\"double\"\n hasBorder={false}\n ariaLabel={this.fieldMenuLabel}\n focusTabIndex={-1}\n haspopup=\"listbox\"\n controls={this.menuOpen ? this.listboxId : undefined}\n expanded={this.menuOpen}\n isInactive={this.isInactive || available.length === 0}\n onMouseDown={(event: MouseEvent) => event.preventDefault()}\n onDsClick={() => {\n this.inputEl?.focus({ preventScroll: true });\n this.openMenu(false);\n }}\n />\n </div>\n )}\n </div>\n <span id={this.descriptionId} class=\"ds-visually-hidden\">\n {description}\n </span>\n <span class=\"ds-visually-hidden\" aria-live=\"polite\">\n {this.announcement}\n </span>\n {this.menuOpen && (\n <div\n popover=\"manual\"\n ref={element => {\n this.popupEl = element ?? null;\n }}\n class=\"table-search__popup ds-choice-popup\"\n style={popupStyle}\n >\n <div\n id={this.listboxId}\n class=\"table-search__list ds-choice-list\"\n role=\"listbox\"\n aria-label={this.fieldMenuLabel}\n >\n {visible.map((field, index) => {\n const active = index === this.activeIndex;\n return (\n /* eslint-disable-next-line local/prefer-direct-ds-text -- The option owns listbox semantics and pointer interaction while ds-text remains its typography child. */\n <div\n id={`${this.componentId}-option-${index}`}\n key={field.id}\n class={{\n 'table-search__option': true,\n 'ds-choice-item': true,\n 'ds-control-frame': true,\n 'ds-control--md': true,\n 'ds-focus-ring-inset': true,\n 'ds-focus-ring--visible': active && this.focusRingVisible,\n 'ds-interaction-fill': true,\n 'ds-interaction-fill--selected': active,\n }}\n role=\"option\"\n aria-selected={String(active)}\n onMouseDown={event => event.preventDefault()}\n onMouseMove={() => {\n this.focusRingVisible = false;\n this.activeIndex = index;\n }}\n onClick={() => this.selectField(field)}\n >\n <ds-text\n class=\"ds-choice-item__label ds-control-label-box ds-interaction-fill__content\"\n as=\"span\"\n variant=\"text-body-medium\"\n color={active ? 'primary' : 'secondary'}\n >\n {field.label}\n </ds-text>\n </div>\n );\n })}\n {visible.length === 0 && (\n <div class=\"table-search__empty ds-choice-empty ds-empty-region\">\n <ds-empty-state body=\"No matching data points\" />\n </div>\n )}\n </div>\n </div>\n )}\n </Host>\n );\n }\n}\n",
330
+ "type": "registry:ui"
331
+ },
332
+ {
333
+ "path": "src/wc/components/TableSearch/table-search-model.ts",
334
+ "content": "import { isTableActionColumn } from '../Table/table-column-customizer';\nimport type { TableColumn } from '../Table/table-types';\nimport type { TableSearchField } from './table-search-types';\n\n/** Searchable data points from the table catalog, including compound header segments. */\nexport function tableSearchFields(columns: readonly TableColumn[]): TableSearchField[] {\n const resolved: TableSearchField[] = [];\n const seen = new Set<string>();\n\n for (const column of columns) {\n if (isTableActionColumn(column) || column.searchable === false) continue;\n const segments = column.headerSegments ?? [];\n if (segments.length > 0) {\n for (const segment of segments) {\n const id = segment.sortKey.trim();\n const label = segment.dataLabel?.trim() || segment.label.trim() || id;\n if (!id || !label || segment.searchable === false || seen.has(id)) continue;\n seen.add(id);\n resolved.push({ id, label });\n }\n continue;\n }\n\n const id = column.id.trim();\n const label =\n column.dataLabel?.trim() || column.header.trim() || column.headerLabel?.trim() || id;\n if (!id || !label || seen.has(id)) continue;\n seen.add(id);\n resolved.push({ id, label });\n }\n\n return resolved;\n}\n\nexport function selectedTableSearchFields(\n fields: readonly TableSearchField[],\n selectedFieldIds: string[]\n): TableSearchField[] {\n const byId = new Map(fields.map(field => [field.id, field]));\n const selected: TableSearchField[] = [];\n const seen = new Set<string>();\n\n for (const fieldId of selectedFieldIds) {\n if (seen.has(fieldId)) continue;\n const field = byId.get(fieldId);\n if (!field) continue;\n seen.add(fieldId);\n selected.push(field);\n }\n\n return selected;\n}\n\nexport function availableTableSearchFields(\n fields: readonly TableSearchField[],\n selectedFieldIds: string[]\n): TableSearchField[] {\n const selected = new Set(selectedFieldIds);\n return fields.filter(field => !selected.has(field.id));\n}\n\n/** Matches the field-picker query against the complete visible label and canonical identity. */\nexport function filterTableSearchFields(\n fields: readonly TableSearchField[],\n query: string\n): TableSearchField[] {\n const normalizedQuery = query.trim().toLocaleLowerCase();\n if (!normalizedQuery) return [...fields];\n\n return fields.filter(field =>\n `${field.label} ${field.id}`.toLocaleLowerCase().includes(normalizedQuery)\n );\n}\n\nexport function nextTableSearchActiveIndex(\n currentIndex: number,\n optionCount: number,\n direction: 1 | -1\n): number {\n if (optionCount <= 0) return -1;\n const safeIndex = currentIndex >= 0 && currentIndex < optionCount ? currentIndex : 0;\n return (safeIndex + direction + optionCount) % optionCount;\n}\n",
335
+ "type": "registry:ui"
336
+ },
337
+ {
338
+ "path": "src/wc/components/TableSearch/table-search-types.ts",
339
+ "content": "export interface TableSearchField {\n /** Stable application-owned field identity used to scope filtering. */\n id: string;\n /** Visible field label shown in the slash menu and selected Tag. */\n label: string;\n}\n\nexport interface TableSearchFieldsChangeDetail {\n selectedFieldIds: string[];\n}\n",
340
+ "type": "registry:ui"
341
+ }
342
+ ]
343
+ }
@@ -223,7 +223,7 @@
223
223
  },
224
224
  {
225
225
  "path": "src/wc/components/TableSort/table-sort-menu.ts",
226
- "content": "import type { MenuItemData, MenuSection } from '../Menu/menu-types';\nimport { isTableActionColumn } from '../Table/table-column-customizer';\nimport type { TableColumn, TableSortState } from '../Table/table-types';\n\nexport const TABLE_SORT_DIRECTION_ASC = 'direction:asc';\nexport const TABLE_SORT_DIRECTION_DESC = 'direction:desc';\nconst TABLE_SORT_FIELD_PREFIX = 'field:';\n\nexport interface TableSortField {\n id: string;\n label: string;\n}\n\n/** Sortable data fields from the catalog, including compound header segments. */\nexport function tableSortFields(columns: readonly TableColumn[]): TableSortField[] {\n const fields: TableSortField[] = [];\n const seen = new Set<string>();\n\n for (const column of columns) {\n if (isTableActionColumn(column) || !column.sortable) continue;\n const segments = column.headerSegments?.filter(segment => segment.sortKey.trim()) ?? [];\n if (segments.length > 0) {\n for (const segment of segments) {\n if (seen.has(segment.sortKey)) continue;\n seen.add(segment.sortKey);\n fields.push({\n id: segment.sortKey,\n label: segment.label.trim() || segment.sortKey,\n });\n }\n continue;\n }\n if (seen.has(column.id)) continue;\n seen.add(column.id);\n fields.push({\n id: column.id,\n label: column.header.trim() || column.headerLabel?.trim() || column.id,\n });\n }\n\n return fields;\n}\n\nexport function tableSortMenuSections(\n columns: readonly TableColumn[],\n sort: TableSortState | null | undefined\n): MenuSection[] {\n const fields = tableSortFields(columns);\n return [\n {\n header: 'Data',\n items: fields.map(field => ({\n label: field.label,\n value: `${TABLE_SORT_FIELD_PREFIX}${encodeURIComponent(field.id)}`,\n isSelected: sort?.columnId === field.id,\n })),\n },\n {\n header: 'Direction',\n items: [\n {\n label: 'Ascending',\n value: TABLE_SORT_DIRECTION_ASC,\n icon: 'ArrowUp',\n isSelected: sort?.direction === 'asc',\n },\n {\n label: 'Descending',\n value: TABLE_SORT_DIRECTION_DESC,\n icon: 'ArrowDown',\n isSelected: sort?.direction === 'desc',\n },\n ],\n },\n ];\n}\n\nexport function tableSortStatesEqual(\n left: TableSortState | null | undefined,\n right: TableSortState | null | undefined\n): boolean {\n if (left === right) return true;\n if (!left || !right) return false;\n return left.columnId === right.columnId && left.direction === right.direction;\n}\n\n/** Next controlled sort from a Sort menu selection. Does not toggle like a header click. */\nexport function nextTableSortStateFromMenuItem(\n columns: readonly TableColumn[],\n current: TableSortState | null | undefined,\n item: MenuItemData\n): TableSortState | null {\n const fields = tableSortFields(columns);\n if (fields.length === 0) return current ?? null;\n\n if (item.value === TABLE_SORT_DIRECTION_ASC || item.value === TABLE_SORT_DIRECTION_DESC) {\n const direction = item.value === TABLE_SORT_DIRECTION_ASC ? 'asc' : 'desc';\n const columnId = fields.some(field => field.id === current?.columnId)\n ? current!.columnId\n : fields[0]!.id;\n return { columnId, direction };\n }\n\n if (!item.value?.startsWith(TABLE_SORT_FIELD_PREFIX)) return current ?? null;\n let columnId: string;\n try {\n columnId = decodeURIComponent(item.value.slice(TABLE_SORT_FIELD_PREFIX.length));\n } catch {\n return current ?? null;\n }\n if (!fields.some(field => field.id === columnId)) return current ?? null;\n if (current?.columnId === columnId) return current ?? null;\n return { columnId, direction: current?.direction ?? 'asc' };\n}\n",
226
+ "content": "import type { MenuItemData, MenuSection } from '../Menu/menu-types';\nimport { isTableActionColumn } from '../Table/table-column-customizer';\nimport type { TableColumn, TableSortState } from '../Table/table-types';\n\nexport const TABLE_SORT_DIRECTION_ASC = 'direction:asc';\nexport const TABLE_SORT_DIRECTION_DESC = 'direction:desc';\nconst TABLE_SORT_FIELD_PREFIX = 'field:';\n\nexport interface TableSortField {\n id: string;\n label: string;\n}\n\n/** Sortable data fields from the catalog, including compound header segments. */\nexport function tableSortFields(columns: readonly TableColumn[]): TableSortField[] {\n const fields: TableSortField[] = [];\n const seen = new Set<string>();\n\n for (const column of columns) {\n if (isTableActionColumn(column) || !column.sortable) continue;\n const segments = column.headerSegments?.filter(segment => segment.sortKey.trim()) ?? [];\n if (segments.length > 0) {\n for (const segment of segments) {\n if (seen.has(segment.sortKey)) continue;\n seen.add(segment.sortKey);\n fields.push({\n id: segment.sortKey,\n label: segment.dataLabel?.trim() || segment.label.trim() || segment.sortKey,\n });\n }\n continue;\n }\n if (seen.has(column.id)) continue;\n seen.add(column.id);\n fields.push({\n id: column.id,\n label:\n column.dataLabel?.trim() || column.header.trim() || column.headerLabel?.trim() || column.id,\n });\n }\n\n return fields;\n}\n\nexport function tableSortMenuSections(\n columns: readonly TableColumn[],\n sort: TableSortState | null | undefined\n): MenuSection[] {\n const fields = tableSortFields(columns);\n return [\n {\n header: 'Data',\n items: fields.map(field => ({\n label: field.label,\n value: `${TABLE_SORT_FIELD_PREFIX}${encodeURIComponent(field.id)}`,\n isSelected: sort?.columnId === field.id,\n })),\n },\n {\n header: 'Direction',\n items: [\n {\n label: 'Ascending',\n value: TABLE_SORT_DIRECTION_ASC,\n icon: 'ArrowUp',\n isSelected: sort?.direction === 'asc',\n },\n {\n label: 'Descending',\n value: TABLE_SORT_DIRECTION_DESC,\n icon: 'ArrowDown',\n isSelected: sort?.direction === 'desc',\n },\n ],\n },\n ];\n}\n\nexport function tableSortStatesEqual(\n left: TableSortState | null | undefined,\n right: TableSortState | null | undefined\n): boolean {\n if (left === right) return true;\n if (!left || !right) return false;\n return left.columnId === right.columnId && left.direction === right.direction;\n}\n\n/** Next controlled sort from a Sort menu selection. Does not toggle like a header click. */\nexport function nextTableSortStateFromMenuItem(\n columns: readonly TableColumn[],\n current: TableSortState | null | undefined,\n item: MenuItemData\n): TableSortState | null {\n const fields = tableSortFields(columns);\n if (fields.length === 0) return current ?? null;\n\n if (item.value === TABLE_SORT_DIRECTION_ASC || item.value === TABLE_SORT_DIRECTION_DESC) {\n const direction = item.value === TABLE_SORT_DIRECTION_ASC ? 'asc' : 'desc';\n const columnId = fields.some(field => field.id === current?.columnId)\n ? current!.columnId\n : fields[0]!.id;\n return { columnId, direction };\n }\n\n if (!item.value?.startsWith(TABLE_SORT_FIELD_PREFIX)) return current ?? null;\n let columnId: string;\n try {\n columnId = decodeURIComponent(item.value.slice(TABLE_SORT_FIELD_PREFIX.length));\n } catch {\n return current ?? null;\n }\n if (!fields.some(field => field.id === columnId)) return current ?? null;\n if (current?.columnId === columnId) return current ?? null;\n return { columnId, direction: current?.direction ?? 'asc' };\n}\n",
227
227
  "type": "registry:ui"
228
228
  }
229
229
  ]
@@ -29,6 +29,7 @@
29
29
  "component:ds-divider",
30
30
  "component:ds-input",
31
31
  "component:ds-table-filter",
32
+ "component:ds-table-group",
32
33
  "component:ds-select",
33
34
  "component:ds-table-sort",
34
35
  "component:ds-button-unfilled"
@@ -42,24 +43,24 @@
42
43
  "states": [
43
44
  "The component owns layout only. It never mutates slotted control props, values, menus, loading, persistence, or consequences. Use ds-table-saved-views and ds-table-filter when those interactions need the shared CompoMo behavior while application data and persistence remain external.",
44
45
  "Buttons in the toolbar keep their hover and pressed interaction paint without physically scaling, so pointer feedback does not disturb the stable caption geometry.",
45
- "The start cluster is followed by a decorative vertical divider, then Search, optional leading controls, and one trailing action cluster. The start cluster and divider hide when nothing is projected. Search is a dedicated slot that hides when nothing is projected. Empty leading or trailing clusters add no placeholder chrome. Search is the only toolbar region that shrinks; all action controls retain their control geometry and align directly before the table-owned actions.",
46
+ "The start cluster is followed by a decorative vertical divider, then Search, optional leading controls, and one trailing action cluster. The start cluster and divider hide when nothing is projected. Search is a dedicated slot that hides when nothing is projected and grows to consume available row space. Empty leading or trailing clusters add no placeholder chrome. Search is the only toolbar region that grows and shrinks; all action controls retain their control geometry and align directly before the table-owned actions.",
46
47
  "Column customizer and data-mode switching stay table-owned caption actions. Do not recreate those menus in the toolbar. When slotted into a table header, the table caption owns overflow for the complete toolbar and table-owned action row."
47
48
  ],
48
49
  "responsiveBehavior": [
49
50
  "The start cluster, search, trailing Filter/Sort/Group, and table caption trailing actions stay on one line. Saved views hug their current trigger content up to the compact menu-width token rather than reserving that full width; long names truncate while the dirty dot stays visible. Filter, Sort, and Group triggers cap at the same token, and Sort and Group popups keep that compact menu-width floor. A standalone toolbar owns its horizontal scrolling; inside a table, the complete caption row is the single overflow owner so table-owned actions never remain pinned over scrolling toolbar controls.",
50
- "Inside a named ds-table container narrower than 900px, Search retains its standard maximum width and flexes smaller only when the complete row needs space. ds-table-filter and ds-table-sort omit their labels and chevrons, and Group controls explicitly opted into collapseLabel become icon-only; each exposes its label through a hover and keyboard-focus tooltip. Empty leading and trailing slot groups do not add spacing. Active filtering or grouping promotes the prefix icon to primary. Sort keeps resting secondary chrome. Table-owned Customize also becomes the icon-only Table menu button at that width with the same tooltip behavior.",
51
+ "Search consumes the row space left between fixed control groups and flexes smaller when the complete row needs space. Inside a named ds-table container narrower than 900px, ds-table-filter, ds-table-group, and ds-table-sort omit their labels and chevrons; each exposes its label through a hover and keyboard-focus tooltip. Empty leading and trailing slot groups do not add spacing. Active filtering promotes its prefix icon to primary. Group and Sort keep resting secondary chrome. Table-owned Customize also becomes the icon-only Table menu button at that width with the same tooltip behavior.",
51
52
  "The scrollbar is visually suppressed without removing touch, trackpad, mouse-wheel, or keyboard scrolling from the overflow container.",
52
53
  "Trailing controls remain grouped after leading controls; the toolbar does not collapse controls into an application-specific overflow menu."
53
54
  ],
54
55
  "frameworkCaveats": {
55
56
  "customElements": [
56
- "Project saved views through slot=start, search through slot=search, additional left-side middle controls through slot=leading, and ds-table-filter, Group, and ds-table-sort through slot=trailing. Set collapseLabel on the Group control and provide contextual accessible names when Table controls is insufficient."
57
+ "Project saved views through slot=start, search through slot=search, additional left-side middle controls through slot=leading, and ds-table-filter, ds-table-group, and ds-table-sort through slot=trailing. Provide contextual accessible names when Table controls is insufficient."
57
58
  ],
58
59
  "react": [
59
- "Set slot=start, slot=search, slot=leading, or slot=trailing on each child control. Use ds-table-filter for the standard Filter recipe, opt Group into collapseLabel, and keep values and event handlers in the application."
60
+ "Set slot=start, slot=search, slot=leading, or slot=trailing on each child control. Use ds-table-filter and ds-table-group for the standard Filter and Group recipes, and keep values and event handlers in the application."
60
61
  ],
61
62
  "angular": [
62
- "Set slot=start, slot=search, slot=leading, or slot=trailing on each child control, use DsTableFilter for the standard Filter recipe, opt Group into collapseLabel, and import the generated adapters."
63
+ "Set slot=start, slot=search, slot=leading, or slot=trailing on each child control, use DsTableFilter and DsTableGroup for the standard Filter and Group recipes, and import the generated adapters."
63
64
  ]
64
65
  },
65
66
  "references": [
@@ -147,7 +148,7 @@
147
148
  "files": [
148
149
  {
149
150
  "path": "src/wc/components/TableToolbar/TableToolbar.css",
150
- "content": ":host {\n --ds-control-press-active-scale: var(--dimension-scale-default);\n\n display: block;\n min-inline-size: 0;\n max-inline-size: 100%;\n}\n\n/* A table caption owns overflow for the complete application + table control row. */\n:host([slot='header']) {\n display: contents;\n max-inline-size: none;\n}\n\n.table-toolbar {\n box-sizing: border-box;\n display: flex;\n align-items: center;\n inline-size: 100%;\n min-inline-size: 0;\n gap: var(--dimension-space-100);\n overflow-x: auto;\n overflow-y: hidden;\n scrollbar-width: none;\n}\n\n:host([slot='header']) .table-toolbar {\n display: contents;\n}\n\n.table-toolbar::-webkit-scrollbar {\n display: none;\n}\n\n.table-toolbar__start,\n.table-toolbar__search,\n.table-toolbar__leading,\n.table-toolbar__trailing {\n display: flex;\n align-items: center;\n min-inline-size: 0;\n gap: var(--dimension-space-100);\n}\n\n.table-toolbar__start {\n flex: 0 0 auto;\n min-inline-size: 0;\n max-inline-size: var(--dimension-menu-width-xs);\n overflow: hidden;\n}\n\n.table-toolbar__rule {\n flex: 0 0 auto;\n}\n\n.table-toolbar__search {\n flex: 0 1 var(--dimension-panel-width-xs);\n max-inline-size: var(--dimension-panel-width-xs);\n contain: inline-size;\n}\n\n.table-toolbar__slot--empty {\n display: none;\n}\n\n.table-toolbar__leading {\n flex: 0 0 auto;\n}\n\n.table-toolbar__trailing {\n flex: 0 0 auto;\n margin-inline-start: auto;\n}\n\n.table-toolbar__trailing > *,\n.table-toolbar [slot='start'],\n.table-toolbar ds-table-filter,\n.table-toolbar ds-filter-menu,\n.table-toolbar ds-select,\n.table-toolbar ds-table-sort {\n min-inline-size: 0;\n max-inline-size: var(--dimension-menu-width-xs);\n --ds-choice-popup-min-inline-size: var(--dimension-menu-width-xs);\n}\n\n.table-toolbar__trailing > * {\n flex: 0 0 auto;\n}\n\n.table-toolbar__start slot,\n.table-toolbar__search slot,\n.table-toolbar__leading slot,\n.table-toolbar__trailing slot {\n display: contents;\n}\n",
151
+ "content": ":host {\n --ds-control-press-active-scale: var(--dimension-scale-default);\n\n display: block;\n min-inline-size: 0;\n max-inline-size: 100%;\n}\n\n/* A table caption owns overflow for the complete application + table control row. */\n:host([slot='header']) {\n display: contents;\n max-inline-size: none;\n}\n\n.table-toolbar {\n box-sizing: border-box;\n display: flex;\n align-items: center;\n inline-size: 100%;\n min-inline-size: 0;\n gap: var(--dimension-space-100);\n overflow-x: auto;\n overflow-y: hidden;\n scrollbar-width: none;\n}\n\n:host([slot='header']) .table-toolbar {\n display: contents;\n}\n\n.table-toolbar::-webkit-scrollbar {\n display: none;\n}\n\n.table-toolbar__start,\n.table-toolbar__search,\n.table-toolbar__leading,\n.table-toolbar__trailing {\n display: flex;\n align-items: center;\n min-inline-size: 0;\n gap: var(--dimension-space-100);\n}\n\n.table-toolbar__start {\n flex: 0 0 auto;\n min-inline-size: 0;\n max-inline-size: var(--dimension-menu-width-xs);\n overflow: hidden;\n}\n\n.table-toolbar__rule {\n flex: 0 0 auto;\n}\n\n.table-toolbar__search {\n flex: 1 1 0;\n max-inline-size: none;\n contain: inline-size;\n}\n\n.table-toolbar__slot--empty {\n display: none;\n}\n\n.table-toolbar__leading {\n flex: 0 0 auto;\n}\n\n.table-toolbar__trailing {\n flex: 0 0 auto;\n margin-inline-start: auto;\n}\n\n.table-toolbar__trailing > *,\n.table-toolbar [slot='start'],\n.table-toolbar ds-table-filter,\n.table-toolbar ds-filter-menu,\n.table-toolbar ds-select,\n.table-toolbar ds-table-group,\n.table-toolbar ds-table-sort {\n min-inline-size: 0;\n max-inline-size: var(--dimension-menu-width-xs);\n --ds-choice-popup-min-inline-size: var(--dimension-menu-width-xs);\n}\n\n.table-toolbar__trailing > * {\n flex: 0 0 auto;\n}\n\n.table-toolbar__start slot,\n.table-toolbar__search slot,\n.table-toolbar__leading slot,\n.table-toolbar__trailing slot {\n display: contents;\n}\n",
151
152
  "type": "registry:ui"
152
153
  },
153
154
  {