@ds-mo/ui 14.7.0 → 14.9.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 (112) hide show
  1. package/dist/.package-ready.json +1 -1
  2. package/dist/agent-patterns.json +1 -1
  3. package/dist/agent.json +273 -7
  4. package/dist/angular/ds-select.d.ts +7 -1
  5. package/dist/angular/ds-select.js +5 -2
  6. package/dist/angular/ds-table-saved-views.d.ts +49 -0
  7. package/dist/angular/ds-table-saved-views.js +57 -0
  8. package/dist/angular/ds-table-toolbar.d.ts +12 -0
  9. package/dist/angular/ds-table-toolbar.js +38 -0
  10. package/dist/angular/ds-table.d.ts +4 -1
  11. package/dist/angular/ds-table.js +7 -4
  12. package/dist/angular/index.d.ts +1 -1
  13. package/dist/angular/index.js +2 -0
  14. package/dist/angular/proxies.d.ts +2 -0
  15. package/dist/angular/proxies.js +2 -0
  16. package/dist/components/ds-bar-nav.js +1 -1
  17. package/dist/components/ds-bar-title.js +1 -1
  18. package/dist/components/ds-bar-workflow.js +1 -1
  19. package/dist/components/ds-bar-workflow.js.map +1 -1
  20. package/dist/components/ds-conversation-list-item.js +1 -1
  21. package/dist/components/ds-conversation-list-item.js.map +1 -1
  22. package/dist/components/ds-field.js +1 -1
  23. package/dist/components/ds-field.js.map +1 -1
  24. package/dist/components/ds-filter-menu.js +1 -1
  25. package/dist/components/ds-filter-menu.js.map +1 -1
  26. package/dist/components/ds-input.js +1 -1
  27. package/dist/components/ds-input.js.map +1 -1
  28. package/dist/components/ds-menu.js +1 -1
  29. package/dist/components/ds-mobile-header.js +1 -1
  30. package/dist/components/ds-mobile-section-switcher.js +1 -1
  31. package/dist/components/ds-modal.js +1 -1
  32. package/dist/components/ds-modal.js.map +1 -1
  33. package/dist/components/ds-pagination.js +1 -1
  34. package/dist/components/ds-panel-tool-search.js +1 -1
  35. package/dist/components/ds-select.js +1 -1
  36. package/dist/components/ds-shell-app.js +1 -1
  37. package/dist/components/ds-shell-tools.js +1 -1
  38. package/dist/components/ds-table-saved-views.d.ts +11 -0
  39. package/dist/components/ds-table-saved-views.js +2 -0
  40. package/dist/components/ds-table-saved-views.js.map +1 -0
  41. package/dist/components/ds-table-toolbar.d.ts +11 -0
  42. package/dist/components/ds-table-toolbar.js +2 -0
  43. package/dist/components/ds-table-toolbar.js.map +1 -0
  44. package/dist/components/ds-table.js +1 -1
  45. package/dist/components/ds-table.js.map +1 -1
  46. package/dist/components/ds-toast.js +1 -1
  47. package/dist/components/ds-typing-indicator.js +1 -1
  48. package/dist/components/{p-B5um-ps9.js → p-BCg_6ErW.js} +2 -2
  49. package/dist/components/p-BFSnIEsC.js +2 -0
  50. package/dist/components/p-BFSnIEsC.js.map +1 -0
  51. package/dist/components/p-BPnmiiIS.js +2 -0
  52. package/dist/components/p-BPnmiiIS.js.map +1 -0
  53. package/dist/components/p-BVmKcpyP.js +2 -0
  54. package/dist/components/p-BVmKcpyP.js.map +1 -0
  55. package/dist/components/p-BiTwXttN.js +2 -0
  56. package/dist/components/{p-BKWgofxz.js.map → p-BiTwXttN.js.map} +1 -1
  57. package/dist/components/{p-DQUxg6Si.js → p-BmWWU20X.js} +2 -2
  58. package/dist/components/{p-BAebDW3a.js → p-BuGGwWmt.js} +2 -2
  59. package/dist/components/{p-BmqfTAXM.js → p-Bv1CFHgD.js} +2 -2
  60. package/dist/components/{p--VFa9x3k.js → p-C-Zgq_Fx.js} +2 -2
  61. package/dist/components/p-C1q-tq3U.js +2 -0
  62. package/dist/components/p-C1q-tq3U.js.map +1 -0
  63. package/dist/components/p-C6DNhlES.js +2 -0
  64. package/dist/components/p-C6DNhlES.js.map +1 -0
  65. package/dist/components/{p-DOC4RaAy.js → p-CqbMoVDQ.js} +2 -2
  66. package/dist/components/{p-BAgjMUt3.js → p-Cxr0IQjC.js} +1 -1
  67. package/dist/components/p-Cxr0IQjC.js.map +1 -0
  68. package/dist/docs/components.json +844 -64
  69. package/dist/lib/utils/choice-list.d.ts +8 -1
  70. package/dist/mcp-data/registry/empty-state.json +1 -0
  71. package/dist/mcp-data/registry/registry.json +278 -6
  72. package/dist/mcp-data/registry/select.json +20 -3
  73. package/dist/mcp-data/registry/table-saved-views.json +383 -0
  74. package/dist/mcp-data/registry/table-toolbar.json +146 -0
  75. package/dist/mcp-data/registry/table.json +113 -8
  76. package/dist/react/components.d.ts +2 -0
  77. package/dist/react/components.js +2 -0
  78. package/dist/react/ds-select.d.ts +2 -1
  79. package/dist/react/ds-select.js +2 -1
  80. package/dist/react/ds-table-saved-views.d.ts +17 -0
  81. package/dist/react/ds-table-saved-views.js +19 -0
  82. package/dist/react/ds-table-toolbar.d.ts +9 -0
  83. package/dist/react/ds-table-toolbar.js +12 -0
  84. package/dist/react/ds-table.d.ts +2 -1
  85. package/dist/react/ds-table.js +2 -1
  86. package/dist/styles/table.css +49 -1
  87. package/dist/types/components/Select/Select.d.ts +7 -0
  88. package/dist/types/components/Select/index.d.ts +1 -1
  89. package/dist/types/components/Table/Table.d.ts +19 -1
  90. package/dist/types/components/Table/index.d.ts +1 -1
  91. package/dist/types/components/Table/table-data-mode-switcher.d.ts +9 -0
  92. package/dist/types/components/Table/table-types.d.ts +7 -0
  93. package/dist/types/components/TableSavedViews/TableSavedViews.d.ts +61 -0
  94. package/dist/types/components/TableSavedViews/index.d.ts +2 -0
  95. package/dist/types/components/TableSavedViews/table-saved-views-types.d.ts +24 -0
  96. package/dist/types/components/TableToolbar/TableToolbar.d.ts +6 -0
  97. package/dist/types/components.d.ts +232 -4
  98. package/dist/types/utils/choice-list-parts.d.ts +1 -0
  99. package/dist/types/utils/choice-list.d.ts +8 -1
  100. package/package.json +1 -1
  101. package/dist/components/p-BAgjMUt3.js.map +0 -1
  102. package/dist/components/p-BKWgofxz.js +0 -2
  103. package/dist/components/p-mLRtp5A4.js +0 -2
  104. package/dist/components/p-mLRtp5A4.js.map +0 -1
  105. package/dist/components/p-tR8INOIN.js +0 -2
  106. package/dist/components/p-tR8INOIN.js.map +0 -1
  107. /package/dist/components/{p-B5um-ps9.js.map → p-BCg_6ErW.js.map} +0 -0
  108. /package/dist/components/{p-DQUxg6Si.js.map → p-BmWWU20X.js.map} +0 -0
  109. /package/dist/components/{p-BAebDW3a.js.map → p-BuGGwWmt.js.map} +0 -0
  110. /package/dist/components/{p-BmqfTAXM.js.map → p-Bv1CFHgD.js.map} +0 -0
  111. /package/dist/components/{p--VFa9x3k.js.map → p-C-Zgq_Fx.js.map} +0 -0
  112. /package/dist/components/{p-DOC4RaAy.js.map → p-CqbMoVDQ.js.map} +0 -0
@@ -0,0 +1,383 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "table-saved-views",
4
+ "title": "TableSavedViews",
5
+ "description": "Controlled saved-views UI for table toolbars, including selection, dirty state, create, save, discard, rename, remove, validation, and focus behavior without owning table configuration or persistence.",
6
+ "type": "registry:ui",
7
+ "meta": {
8
+ "library": "@ds-mo/ui",
9
+ "distribution": "npm",
10
+ "storybook": "https://zainadeel.github.io/compomo/?path=/story/table-saved-views",
11
+ "source": "src/wc/components/TableSavedViews/TableSavedViews.tsx",
12
+ "intentStatus": "complete",
13
+ "intent": {
14
+ "audience": "general",
15
+ "status": "stable",
16
+ "summary": "Controlled saved-views UI for table toolbars, including selection, dirty state, create, save, discard, rename, remove, validation, and focus behavior without owning table configuration or persistence.",
17
+ "useWhen": [
18
+ "A table workspace lets people recall and manage named combinations of table controls.",
19
+ "Several products need the same saved-view interaction while retaining different view payloads and storage systems."
20
+ ],
21
+ "avoidWhen": [
22
+ "The choices do not represent restorable table configurations.",
23
+ "The application expects the component to serialize filters, grouping, sorting, columns, pagination, or product-specific state."
24
+ ],
25
+ "commonlyComposedWith": [
26
+ "component:ds-table-toolbar",
27
+ "component:ds-table",
28
+ "component:ds-select",
29
+ "component:ds-menu",
30
+ "component:ds-modal"
31
+ ],
32
+ "patterns": [
33
+ "pattern:menu-trigger"
34
+ ],
35
+ "accessibility": [
36
+ "The primary control retains combobox semantics and exposes a direct accessible name.",
37
+ "Each custom view exposes a separately named options trigger. The options menu supports keyboard navigation and returns focus through the shared Select and Menu behavior.",
38
+ "The naming dialog traps focus through Modal behavior, associates validation text with the input through Field, and restores focus to the saved-views control after closing."
39
+ ],
40
+ "states": [
41
+ "views, value, and dirty are controlled. Selection, create, save, discard, rename, and remove events communicate intent and never mutate application records.",
42
+ "The component owns only transient interaction state: the contextual menu, naming dialog, draft name, duplicate-name validation, and focus restoration.",
43
+ "The default view is a non-editable template. Application-owned custom views appear in a separate section with Rename and Remove actions. New view opens the naming dialog. When an editable selected view is dirty, that view row shows Save and negative Discard supporting actions while the footer remains dedicated to New view. The application decides which table state to capture, restore, and persist."
44
+ ],
45
+ "responsiveBehavior": [
46
+ "The control remains a single toolbar item and relies on Select, Menu, and Modal collision and viewport behavior.",
47
+ "Place it in the leading region of ds-table-toolbar so narrow table headers preserve the expected control order."
48
+ ],
49
+ "frameworkCaveats": {
50
+ "customElements": [
51
+ "Assign views through the JavaScript property and update value after handling dsViewChange."
52
+ ],
53
+ "react": [
54
+ "Keep view records and serialized table configuration in application state; update controlled props in response to the component events."
55
+ ],
56
+ "angular": [
57
+ "Import the generated DsTableSavedViews adapter and bind views, value, and dirty; handle event.detail for selection and mutations."
58
+ ]
59
+ },
60
+ "references": [
61
+ {
62
+ "label": "Storybook review states",
63
+ "path": "src/wc/components/TableSavedViews/TableSavedViews.stories.ts"
64
+ },
65
+ {
66
+ "label": "Toolbar composition owner",
67
+ "path": "src/wc/components/TableToolbar/TableToolbar.agent.json"
68
+ }
69
+ ]
70
+ },
71
+ "api": {
72
+ "props": {
73
+ "createLabel": {
74
+ "type": "string",
75
+ "resolvedType": "string",
76
+ "attribute": "create-label",
77
+ "default": "'New view'",
78
+ "required": false,
79
+ "mutable": false,
80
+ "description": "Footer action label that opens the create-view dialog."
81
+ },
82
+ "defaultViewId": {
83
+ "type": "string",
84
+ "resolvedType": "string",
85
+ "attribute": "default-view-id",
86
+ "default": "'__default__'",
87
+ "required": false,
88
+ "mutable": false,
89
+ "description": "ID used for the built-in default view."
90
+ },
91
+ "defaultViewLabel": {
92
+ "type": "string",
93
+ "resolvedType": "string",
94
+ "attribute": "default-view-label",
95
+ "default": "'Default'",
96
+ "required": false,
97
+ "mutable": false,
98
+ "description": "Label used for the built-in default view."
99
+ },
100
+ "dirty": {
101
+ "type": "boolean",
102
+ "resolvedType": "boolean",
103
+ "attribute": "dirty",
104
+ "default": "false",
105
+ "required": false,
106
+ "mutable": false,
107
+ "description": "Whether the current table state differs from the selected view."
108
+ },
109
+ "label": {
110
+ "type": "string",
111
+ "resolvedType": "string",
112
+ "attribute": "label",
113
+ "default": "'Saved views'",
114
+ "required": false,
115
+ "mutable": false,
116
+ "description": "Accessible name for the saved-views select."
117
+ },
118
+ "triggerLabel": {
119
+ "type": "string",
120
+ "resolvedType": "string",
121
+ "attribute": "trigger-label",
122
+ "default": "'Views'",
123
+ "required": false,
124
+ "mutable": false,
125
+ "description": "Trigger label shown while the default view is selected."
126
+ },
127
+ "value": {
128
+ "type": "string",
129
+ "resolvedType": "string",
130
+ "attribute": "value",
131
+ "default": "'__default__'",
132
+ "required": false,
133
+ "mutable": false,
134
+ "description": "ID of the controlled active view, including the default view ID."
135
+ },
136
+ "views": {
137
+ "type": "TableSavedView[]",
138
+ "resolvedType": "TableSavedView[]",
139
+ "default": "[]",
140
+ "required": false,
141
+ "mutable": false,
142
+ "description": "Application-owned custom saved views. Assign arrays through the JavaScript property."
143
+ }
144
+ },
145
+ "events": [
146
+ {
147
+ "name": "dsViewChange",
148
+ "detail": "TableSavedViewChangeDetail",
149
+ "bubbles": true,
150
+ "cancelable": true,
151
+ "composed": true,
152
+ "description": "Emitted when a view selection is requested."
153
+ },
154
+ {
155
+ "name": "dsViewCreate",
156
+ "detail": "TableSavedViewCreateDetail",
157
+ "bubbles": true,
158
+ "cancelable": true,
159
+ "composed": true,
160
+ "description": "Emitted after a valid create-view name is submitted."
161
+ },
162
+ {
163
+ "name": "dsViewDiscard",
164
+ "detail": "TableSavedViewDiscardDetail",
165
+ "bubbles": true,
166
+ "cancelable": true,
167
+ "composed": true,
168
+ "description": "Emitted when the active custom view's stored payload should be restored."
169
+ },
170
+ {
171
+ "name": "dsViewRemove",
172
+ "detail": "TableSavedViewRemoveDetail",
173
+ "bubbles": true,
174
+ "cancelable": true,
175
+ "composed": true,
176
+ "description": "Emitted when a custom view removal is requested."
177
+ },
178
+ {
179
+ "name": "dsViewRename",
180
+ "detail": "TableSavedViewRenameDetail",
181
+ "bubbles": true,
182
+ "cancelable": true,
183
+ "composed": true,
184
+ "description": "Emitted after a valid renamed view name is submitted."
185
+ },
186
+ {
187
+ "name": "dsViewSave",
188
+ "detail": "TableSavedViewSaveDetail",
189
+ "bubbles": true,
190
+ "cancelable": true,
191
+ "composed": true,
192
+ "description": "Emitted when changes to the active custom view should replace its stored payload."
193
+ }
194
+ ],
195
+ "methods": [],
196
+ "slots": []
197
+ },
198
+ "props": {
199
+ "createLabel": {
200
+ "type": "string",
201
+ "resolvedType": "string",
202
+ "attribute": "create-label",
203
+ "default": "'New view'",
204
+ "required": false,
205
+ "mutable": false,
206
+ "description": "Footer action label that opens the create-view dialog."
207
+ },
208
+ "defaultViewId": {
209
+ "type": "string",
210
+ "resolvedType": "string",
211
+ "attribute": "default-view-id",
212
+ "default": "'__default__'",
213
+ "required": false,
214
+ "mutable": false,
215
+ "description": "ID used for the built-in default view."
216
+ },
217
+ "defaultViewLabel": {
218
+ "type": "string",
219
+ "resolvedType": "string",
220
+ "attribute": "default-view-label",
221
+ "default": "'Default'",
222
+ "required": false,
223
+ "mutable": false,
224
+ "description": "Label used for the built-in default view."
225
+ },
226
+ "dirty": {
227
+ "type": "boolean",
228
+ "resolvedType": "boolean",
229
+ "attribute": "dirty",
230
+ "default": "false",
231
+ "required": false,
232
+ "mutable": false,
233
+ "description": "Whether the current table state differs from the selected view."
234
+ },
235
+ "label": {
236
+ "type": "string",
237
+ "resolvedType": "string",
238
+ "attribute": "label",
239
+ "default": "'Saved views'",
240
+ "required": false,
241
+ "mutable": false,
242
+ "description": "Accessible name for the saved-views select."
243
+ },
244
+ "triggerLabel": {
245
+ "type": "string",
246
+ "resolvedType": "string",
247
+ "attribute": "trigger-label",
248
+ "default": "'Views'",
249
+ "required": false,
250
+ "mutable": false,
251
+ "description": "Trigger label shown while the default view is selected."
252
+ },
253
+ "value": {
254
+ "type": "string",
255
+ "resolvedType": "string",
256
+ "attribute": "value",
257
+ "default": "'__default__'",
258
+ "required": false,
259
+ "mutable": false,
260
+ "description": "ID of the controlled active view, including the default view ID."
261
+ },
262
+ "views": {
263
+ "type": "TableSavedView[]",
264
+ "resolvedType": "TableSavedView[]",
265
+ "default": "[]",
266
+ "required": false,
267
+ "mutable": false,
268
+ "description": "Application-owned custom saved views. Assign arrays through the JavaScript property."
269
+ }
270
+ },
271
+ "events": [
272
+ {
273
+ "name": "dsViewChange",
274
+ "detail": "TableSavedViewChangeDetail",
275
+ "bubbles": true,
276
+ "cancelable": true,
277
+ "composed": true,
278
+ "description": "Emitted when a view selection is requested."
279
+ },
280
+ {
281
+ "name": "dsViewCreate",
282
+ "detail": "TableSavedViewCreateDetail",
283
+ "bubbles": true,
284
+ "cancelable": true,
285
+ "composed": true,
286
+ "description": "Emitted after a valid create-view name is submitted."
287
+ },
288
+ {
289
+ "name": "dsViewDiscard",
290
+ "detail": "TableSavedViewDiscardDetail",
291
+ "bubbles": true,
292
+ "cancelable": true,
293
+ "composed": true,
294
+ "description": "Emitted when the active custom view's stored payload should be restored."
295
+ },
296
+ {
297
+ "name": "dsViewRemove",
298
+ "detail": "TableSavedViewRemoveDetail",
299
+ "bubbles": true,
300
+ "cancelable": true,
301
+ "composed": true,
302
+ "description": "Emitted when a custom view removal is requested."
303
+ },
304
+ {
305
+ "name": "dsViewRename",
306
+ "detail": "TableSavedViewRenameDetail",
307
+ "bubbles": true,
308
+ "cancelable": true,
309
+ "composed": true,
310
+ "description": "Emitted after a valid renamed view name is submitted."
311
+ },
312
+ {
313
+ "name": "dsViewSave",
314
+ "detail": "TableSavedViewSaveDetail",
315
+ "bubbles": true,
316
+ "cancelable": true,
317
+ "composed": true,
318
+ "description": "Emitted when changes to the active custom view should replace its stored payload."
319
+ }
320
+ ],
321
+ "methods": [],
322
+ "slots": [],
323
+ "exports": {
324
+ "customElement": "ds-table-saved-views",
325
+ "react": "DsTableSavedViews",
326
+ "angular": "DsTableSavedViews"
327
+ },
328
+ "consumption": {
329
+ "install": "npm install @ds-mo/ui @ds-mo/tokens @ds-mo/icons",
330
+ "cssSetup": "import '@ds-mo/tokens';\nimport '@ds-mo/tokens/reset';\nimport '@ds-mo/tokens/globals';",
331
+ "customElements": {
332
+ "import": "import '@ds-mo/ui/dist/components/ds-table-saved-views.js';",
333
+ "example": "<ds-table-saved-views></ds-table-saved-views>"
334
+ },
335
+ "react": {
336
+ "import": "import { DsTableSavedViews } from '@ds-mo/ui/react';",
337
+ "example": "<DsTableSavedViews />"
338
+ },
339
+ "angular": {
340
+ "import": "import { DsTableSavedViews } from '@ds-mo/ui/angular/ds-table-saved-views';",
341
+ "example": "<ds-table-saved-views></ds-table-saved-views>"
342
+ },
343
+ "complexPropertyNote": "Assign these non-primitive values as JavaScript properties: views.",
344
+ "peerDependencies": {
345
+ "required": [
346
+ "@ds-mo/tokens ^6.5.0",
347
+ "@ds-mo/icons ^7.0.0"
348
+ ],
349
+ "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
350
+ }
351
+ }
352
+ },
353
+ "dependencies": [
354
+ "@ds-mo/ui",
355
+ "@ds-mo/tokens"
356
+ ],
357
+ "registryDependencies": [
358
+ "button-filled",
359
+ "button-unfilled",
360
+ "field",
361
+ "input",
362
+ "menu",
363
+ "modal",
364
+ "select"
365
+ ],
366
+ "files": [
367
+ {
368
+ "path": "src/wc/components/TableSavedViews/TableSavedViews.css",
369
+ "content": ":host {\n --ds-choice-popup-min-inline-size: var(--dimension-menu-width-sm);\n\n display: inline-block;\n min-inline-size: 0;\n}\n\nds-select {\n display: block;\n}\n\n.table-saved-views__dialog-actions {\n display: flex;\n align-items: center;\n justify-content: flex-start;\n gap: var(--dimension-space-100);\n}\n",
370
+ "type": "registry:ui"
371
+ },
372
+ {
373
+ "path": "src/wc/components/TableSavedViews/TableSavedViews.tsx",
374
+ "content": "import {\n Component,\n Element,\n Event,\n EventEmitter,\n h,\n Host,\n Prop,\n State,\n} from '@stencil/core';\nimport type { MenuItemData } from '../Menu/menu-types';\nimport type {\n SelectOptionActionDetail,\n SelectOptionSubtextActionDetail,\n SelectValue,\n} from '../Select/Select';\nimport type {\n TableSavedView,\n TableSavedViewChangeDetail,\n TableSavedViewCreateDetail,\n TableSavedViewDiscardDetail,\n TableSavedViewRemoveDetail,\n TableSavedViewRenameDetail,\n TableSavedViewSaveDetail,\n} from './table-saved-views-types';\n\ntype SavedViewDialogMode = 'create' | 'rename';\n\nlet tableSavedViewsId = 0;\n\n@Component({\n tag: 'ds-table-saved-views',\n styleUrl: 'TableSavedViews.css',\n scoped: true,\n})\nexport class TableSavedViews {\n @Element() el!: HTMLElement;\n\n /** Application-owned custom saved views. Assign arrays through the JavaScript property. */\n @Prop() views: TableSavedView[] = [];\n /** ID of the controlled active view, including the default view ID. */\n @Prop() value: string = '__default__';\n /** Whether the current table state differs from the selected view. */\n @Prop() dirty: boolean = false;\n /** ID used for the built-in default view. */\n @Prop() defaultViewId: string = '__default__';\n /** Label used for the built-in default view. */\n @Prop() defaultViewLabel: string = 'Default';\n /** Accessible name for the saved-views select. */\n @Prop() label: string = 'Saved views';\n /** Trigger label shown while the default view is selected. */\n @Prop() triggerLabel: string = 'Views';\n /** Footer action label that opens the create-view dialog. */\n @Prop() createLabel: string = 'New view';\n\n /** Emitted when a view selection is requested. */\n @Event() dsViewChange!: EventEmitter<TableSavedViewChangeDetail>;\n /** Emitted after a valid create-view name is submitted. */\n @Event() dsViewCreate!: EventEmitter<TableSavedViewCreateDetail>;\n /** Emitted after a valid renamed view name is submitted. */\n @Event() dsViewRename!: EventEmitter<TableSavedViewRenameDetail>;\n /** Emitted when a custom view removal is requested. */\n @Event() dsViewRemove!: EventEmitter<TableSavedViewRemoveDetail>;\n /** Emitted when changes to the active custom view should replace its stored payload. */\n @Event() dsViewSave!: EventEmitter<TableSavedViewSaveDetail>;\n /** Emitted when the active custom view's stored payload should be restored. */\n @Event() dsViewDiscard!: EventEmitter<TableSavedViewDiscardDetail>;\n\n @State() private actionViewId: string | null = null;\n @State() private actionAnchorId: string | undefined;\n @State() private actionMenuOpen = false;\n @State() private actionMenuInitialFocusVisible = false;\n @State() private dialogOpen = false;\n @State() private dialogMode: SavedViewDialogMode = 'create';\n @State() private dialogViewId: string | null = null;\n @State() private nameDraft = '';\n @State() private nameError = '';\n\n private readonly componentId = `ds-table-saved-views-${++tableSavedViewsId}`;\n private readonly selectId = `${this.componentId}-select`;\n private readonly actionMenuId = `${this.componentId}-actions`;\n private readonly fieldId = `${this.componentId}-name-field`;\n private readonly inputId = `${this.componentId}-name-input`;\n private readonly actionItems: MenuItemData[] = [\n { label: 'Rename', value: 'rename' },\n { label: 'Remove', value: 'remove', isDestructive: true },\n ];\n\n private get selectedCustomView(): TableSavedView | undefined {\n return this.views.find(view => view.id === this.value);\n }\n\n private get dialogView(): TableSavedView | undefined {\n return this.views.find(view => view.id === this.dialogViewId);\n }\n\n private get sections() {\n const custom = this.views.map(view => ({\n label: view.label,\n value: view.id,\n subtextActions:\n this.dirty && view.id === this.selectedCustomView?.id\n ? [\n { label: 'Save', value: 'save' },\n { label: 'Discard', value: 'discard', tone: 'negative' as const },\n ]\n : undefined,\n action: {\n label: `Options for ${view.label}`,\n controls: this.actionMenuId,\n expanded: this.actionViewId === view.id && this.actionMenuOpen,\n },\n }));\n const templates = {\n header: 'Templates',\n options: [{ label: this.defaultViewLabel, value: this.defaultViewId }],\n divider: custom.length > 0,\n };\n return custom.length > 0\n ? [templates, { header: 'Custom', options: custom }]\n : [templates];\n }\n\n private handleViewChange(value: SelectValue) {\n if (typeof value === 'string') this.dsViewChange.emit({ viewId: value });\n }\n\n private handleSubtextAction(detail: SelectOptionSubtextActionDetail) {\n const view = this.selectedCustomView;\n if (!view || !this.dirty || detail.value !== view.id) return;\n if (detail.actionValue === 'save') this.dsViewSave.emit({ viewId: view.id });\n if (detail.actionValue === 'discard') this.dsViewDiscard.emit({ viewId: view.id });\n }\n\n private handleOptionAction(detail: SelectOptionActionDetail) {\n if (!this.views.some(view => view.id === detail.value)) return;\n this.actionMenuInitialFocusVisible = detail.originalEvent.detail === 0;\n if (this.actionViewId === detail.value && this.actionMenuOpen) {\n this.actionMenuOpen = false;\n return;\n }\n this.actionViewId = detail.value;\n this.actionAnchorId = detail.anchorId;\n this.actionMenuOpen = true;\n }\n\n private handleActionSelect(event: CustomEvent<MenuItemData>) {\n const viewId = this.actionViewId;\n this.actionMenuOpen = false;\n if (!viewId) return;\n if (event.detail.value === 'rename') this.openDialog('rename', viewId);\n if (event.detail.value === 'remove') this.dsViewRemove.emit({ viewId });\n }\n\n private openDialog(mode: SavedViewDialogMode, viewId: string | null = null) {\n const view = mode === 'rename' ? this.views.find(item => item.id === viewId) : undefined;\n if (mode === 'rename' && !view) return;\n this.dialogMode = mode;\n this.dialogViewId = view?.id ?? null;\n this.nameDraft = view?.label ?? '';\n this.nameError = '';\n this.dialogOpen = true;\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n const input = this.el.querySelector<HTMLElement & { setFocus?: () => Promise<void> }>(\n `#${this.inputId}`,\n );\n void input?.setFocus?.();\n });\n });\n }\n\n private validateName(): string {\n const name = this.nameDraft.trim();\n if (!name) return 'View name is required.';\n const duplicate = this.views.some(\n view => view.id !== this.dialogViewId && view.label.trim().toLocaleLowerCase() === name.toLocaleLowerCase(),\n );\n if (duplicate || this.defaultViewLabel.trim().toLocaleLowerCase() === name.toLocaleLowerCase()) {\n return 'A view with this name already exists.';\n }\n return '';\n }\n\n private submitDialog() {\n const error = this.validateName();\n if (error) {\n this.nameError = error;\n return;\n }\n const name = this.nameDraft.trim();\n if (this.dialogMode === 'rename' && this.dialogView) {\n this.dsViewRename.emit({ viewId: this.dialogView.id, name });\n } else {\n this.dsViewCreate.emit({ name });\n }\n this.dialogOpen = false;\n }\n\n private finishDialogClose() {\n if (this.dialogOpen) return;\n this.dialogViewId = null;\n this.nameDraft = '';\n this.nameError = '';\n const select = this.el.querySelector<HTMLElement & { setFocus?: () => Promise<void> }>(\n `#${this.selectId}`,\n );\n void select?.setFocus?.();\n }\n\n render() {\n return (\n <Host>\n <ds-select\n id={this.selectId}\n size=\"md\"\n placeholder={this.label}\n aria-label={this.label}\n sections={this.sections}\n value={this.value}\n triggerLabel={this.selectedCustomView ? undefined : this.triggerLabel}\n triggerLabelPlaceholder={!this.selectedCustomView}\n dot={this.dirty}\n footerActionLabel={this.createLabel}\n activeFill={false}\n allowClear={false}\n onDsChange={(event: CustomEvent<SelectValue>) => this.handleViewChange(event.detail)}\n onDsFooterAction={() => this.openDialog('create')}\n onDsOptionSubtextAction={(event: CustomEvent<SelectOptionSubtextActionDetail>) =>\n this.handleSubtextAction(event.detail)\n }\n onDsOptionAction={(event: CustomEvent<SelectOptionActionDetail>) =>\n this.handleOptionAction(event.detail)\n }\n />\n\n <ds-menu\n id={this.actionMenuId}\n anchorId={this.actionAnchorId}\n menuLabel=\"Saved view options\"\n side=\"bottom\"\n align=\"end\"\n menuWidth=\"max-content\"\n minWidth=\"0\"\n items={this.actionItems}\n open={this.actionMenuOpen}\n initialFocusVisible={this.actionMenuInitialFocusVisible}\n onDsClose={() => (this.actionMenuOpen = false)}\n onDsAfterClose={() => {\n if (this.actionMenuOpen) return;\n this.actionViewId = null;\n this.actionAnchorId = undefined;\n }}\n onDsSelect={(event: CustomEvent<MenuItemData>) => this.handleActionSelect(event)}\n />\n\n <ds-modal\n open={this.dialogOpen}\n heading={this.dialogMode === 'rename' ? 'Rename view' : 'Save as new view'}\n modalWidth=\"sm\"\n onDsClose={() => (this.dialogOpen = false)}\n onDsAfterClose={() => this.finishDialogClose()}\n >\n <ds-field\n label=\"Name\"\n fieldId={this.fieldId}\n error={this.nameError.length > 0}\n errorMessage={this.nameError}\n >\n <ds-input\n id={this.inputId}\n size=\"md\"\n placeholder=\"Enter a unique name\"\n value={this.nameDraft}\n autoFocus={true}\n onDsChange={(event: CustomEvent<string>) => {\n this.nameDraft = event.detail;\n if (this.nameError) this.nameError = '';\n }}\n onKeyDown={(event: KeyboardEvent) => {\n if (event.key === 'Enter') this.submitDialog();\n }}\n />\n </ds-field>\n\n <div slot=\"footer\" class=\"table-saved-views__dialog-actions\">\n <ds-button-filled\n label={this.dialogMode === 'rename' ? 'Rename' : 'Save'}\n size=\"md\"\n onDsClick={() => this.submitDialog()}\n />\n <ds-button-unfilled\n label=\"Cancel\"\n size=\"md\"\n onDsClick={() => (this.dialogOpen = false)}\n />\n </div>\n </ds-modal>\n </Host>\n );\n }\n}\n",
375
+ "type": "registry:ui"
376
+ },
377
+ {
378
+ "path": "src/wc/components/TableSavedViews/table-saved-views-types.ts",
379
+ "content": "export interface TableSavedView {\n id: string;\n label: string;\n}\n\nexport interface TableSavedViewChangeDetail {\n viewId: string;\n}\n\nexport interface TableSavedViewCreateDetail {\n name: string;\n}\n\nexport interface TableSavedViewRenameDetail {\n viewId: string;\n name: string;\n}\n\nexport interface TableSavedViewRemoveDetail {\n viewId: string;\n}\n\nexport interface TableSavedViewSaveDetail {\n viewId: string;\n}\n\nexport interface TableSavedViewDiscardDetail {\n viewId: string;\n}\n",
380
+ "type": "registry:ui"
381
+ }
382
+ ]
383
+ }
@@ -0,0 +1,146 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "table-toolbar",
4
+ "title": "TableToolbar",
5
+ "description": "Data-agnostic companion layout for application-owned controls in a table header, with leading and trailing control groups and narrow-width horizontal overflow.",
6
+ "type": "registry:ui",
7
+ "meta": {
8
+ "library": "@ds-mo/ui",
9
+ "distribution": "npm",
10
+ "storybook": "https://zainadeel.github.io/compomo/?path=/story/table-toolbar",
11
+ "source": "src/wc/components/TableToolbar/TableToolbar.tsx",
12
+ "intentStatus": "complete",
13
+ "intent": {
14
+ "audience": "general",
15
+ "status": "stable",
16
+ "summary": "Data-agnostic companion layout for application-owned controls in a table header, with leading and trailing control groups and narrow-width horizontal overflow.",
17
+ "useWhen": [
18
+ "Several table pages repeat the same leading discovery controls and trailing result-shaping controls.",
19
+ "A ds-table header slot needs consistent control spacing, alignment, grouping, and narrow-width overflow without moving product behavior into the table."
20
+ ],
21
+ "avoidWhen": [
22
+ "The controls do not operate on a table or structured result set.",
23
+ "The application expects the toolbar to define filters, saved views, grouping choices, persistence, or data transformations.",
24
+ "Only one standalone control is present and needs no grouped toolbar semantics."
25
+ ],
26
+ "commonlyComposedWith": [
27
+ "component:ds-table",
28
+ "component:ds-table-saved-views",
29
+ "component:ds-input",
30
+ "component:ds-filter-menu",
31
+ "component:ds-button-unfilled"
32
+ ],
33
+ "patterns": [],
34
+ "accessibility": [
35
+ "The component exposes a named toolbar while each slotted control retains its own accessible name and native keyboard behavior.",
36
+ "Use the leading slot for discovery and context controls such as saved views and search. Use the trailing slot for result-shaping controls such as filters and grouping.",
37
+ "Tab order follows authored DOM order. The toolbar does not add roving focus because it may contain heterogeneous form and popup controls."
38
+ ],
39
+ "states": [
40
+ "The component owns layout only. Slotted controls, values, menus, loading, persistence, and consequences remain controlled by the application. Use ds-table-saved-views when saved-view interaction needs the shared CompoMo behavior while application storage remains external.",
41
+ "Either region may be empty. The populated region continues to occupy the available width without adding placeholder chrome."
42
+ ],
43
+ "responsiveBehavior": [
44
+ "The two control groups stay on one line and horizontally scroll when their complete controls no longer fit.",
45
+ "The scrollbar is visually suppressed without removing touch, trackpad, mouse-wheel, or keyboard scrolling from the overflow container.",
46
+ "Trailing controls remain grouped after leading controls; the toolbar does not collapse controls into an application-specific overflow menu."
47
+ ],
48
+ "frameworkCaveats": {
49
+ "customElements": [
50
+ "Project controls through slot=leading or slot=trailing and provide a label that names their table context when Table controls is insufficient."
51
+ ],
52
+ "react": [
53
+ "Set slot=leading or slot=trailing on each child control; keep values and event handlers in the application."
54
+ ],
55
+ "angular": [
56
+ "Set slot=leading or slot=trailing on each child control and import the generated DsTableToolbar adapter alongside those controls."
57
+ ]
58
+ },
59
+ "references": [
60
+ {
61
+ "label": "Storybook review states",
62
+ "path": "src/wc/components/TableToolbar/TableToolbar.stories.ts"
63
+ },
64
+ {
65
+ "label": "Table composition owner",
66
+ "path": "src/wc/components/Table/Table.agent.json"
67
+ }
68
+ ]
69
+ },
70
+ "api": {
71
+ "props": {
72
+ "label": {
73
+ "type": "string",
74
+ "resolvedType": "string",
75
+ "attribute": "label",
76
+ "default": "'Table controls'",
77
+ "required": false,
78
+ "mutable": false,
79
+ "description": "Accessible name for the grouped table controls."
80
+ }
81
+ },
82
+ "events": [],
83
+ "methods": [],
84
+ "slots": []
85
+ },
86
+ "props": {
87
+ "label": {
88
+ "type": "string",
89
+ "resolvedType": "string",
90
+ "attribute": "label",
91
+ "default": "'Table controls'",
92
+ "required": false,
93
+ "mutable": false,
94
+ "description": "Accessible name for the grouped table controls."
95
+ }
96
+ },
97
+ "events": [],
98
+ "methods": [],
99
+ "slots": [],
100
+ "exports": {
101
+ "customElement": "ds-table-toolbar",
102
+ "react": "DsTableToolbar",
103
+ "angular": "DsTableToolbar"
104
+ },
105
+ "consumption": {
106
+ "install": "npm install @ds-mo/ui @ds-mo/tokens @ds-mo/icons",
107
+ "cssSetup": "import '@ds-mo/tokens';\nimport '@ds-mo/tokens/reset';\nimport '@ds-mo/tokens/globals';",
108
+ "customElements": {
109
+ "import": "import '@ds-mo/ui/dist/components/ds-table-toolbar.js';",
110
+ "example": "<ds-table-toolbar></ds-table-toolbar>"
111
+ },
112
+ "react": {
113
+ "import": "import { DsTableToolbar } from '@ds-mo/ui/react';",
114
+ "example": "<DsTableToolbar />"
115
+ },
116
+ "angular": {
117
+ "import": "import { DsTableToolbar } from '@ds-mo/ui/angular/ds-table-toolbar';",
118
+ "example": "<ds-table-toolbar></ds-table-toolbar>"
119
+ },
120
+ "peerDependencies": {
121
+ "required": [
122
+ "@ds-mo/tokens ^6.5.0",
123
+ "@ds-mo/icons ^7.0.0"
124
+ ],
125
+ "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
126
+ }
127
+ }
128
+ },
129
+ "dependencies": [
130
+ "@ds-mo/ui",
131
+ "@ds-mo/tokens"
132
+ ],
133
+ "registryDependencies": [],
134
+ "files": [
135
+ {
136
+ "path": "src/wc/components/TableToolbar/TableToolbar.css",
137
+ "content": ":host {\n display: block;\n min-inline-size: 0;\n max-inline-size: 100%;\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.table-toolbar::-webkit-scrollbar {\n display: none;\n}\n\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__leading {\n flex: 1 0 auto;\n}\n\n.table-toolbar__trailing {\n flex: 0 0 auto;\n margin-inline-start: auto;\n}\n\n.table-toolbar__leading slot,\n.table-toolbar__trailing slot {\n display: contents;\n}\n",
138
+ "type": "registry:ui"
139
+ },
140
+ {
141
+ "path": "src/wc/components/TableToolbar/TableToolbar.tsx",
142
+ "content": "import { Component, h, Host, Prop } from '@stencil/core';\n\n@Component({\n tag: 'ds-table-toolbar',\n styleUrl: 'TableToolbar.css',\n scoped: true,\n})\nexport class TableToolbar {\n /** Accessible name for the grouped table controls. */\n @Prop() label: string = 'Table controls';\n\n render() {\n return (\n <Host>\n <div class=\"table-toolbar\" role=\"toolbar\" aria-label={this.label}>\n <div class=\"table-toolbar__leading\">\n <slot name=\"leading\" />\n </div>\n <div class=\"table-toolbar__trailing\">\n <slot name=\"trailing\" />\n </div>\n </div>\n </Host>\n );\n }\n}\n",
143
+ "type": "registry:ui"
144
+ }
145
+ ]
146
+ }