@ds-mo/ui 14.15.1 → 14.17.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.
- package/dist/.package-ready.json +1 -1
- package/dist/agent-patterns.json +3 -3
- package/dist/agent.json +317 -24
- package/dist/angular/ds-button-unfilled.d.ts +1 -1
- package/dist/angular/ds-button-unfilled.js +3 -3
- package/dist/angular/ds-filter-menu.d.ts +1 -1
- package/dist/angular/ds-filter-menu.js +3 -3
- package/dist/angular/ds-select.d.ts +1 -1
- package/dist/angular/ds-select.js +3 -3
- package/dist/angular/ds-table-filter.d.ts +44 -0
- package/dist/angular/ds-table-filter.js +57 -0
- package/dist/angular/ds-table-sort.d.ts +16 -0
- package/dist/angular/ds-table-sort.js +43 -0
- package/dist/angular/index.d.ts +1 -1
- package/dist/angular/index.js +2 -0
- package/dist/angular/proxies.d.ts +2 -0
- package/dist/angular/proxies.js +2 -0
- package/dist/components/ds-agent-activity.js +1 -1
- package/dist/components/ds-agent-questionnaire.js +1 -1
- package/dist/components/ds-agent-response.js +1 -1
- package/dist/components/ds-agent-source-list.js +1 -1
- package/dist/components/ds-agent-tool-call.js +1 -1
- package/dist/components/ds-attachment-list.js +1 -1
- package/dist/components/ds-badge.js +1 -1
- package/dist/components/ds-banner.js +1 -1
- package/dist/components/ds-banner.js.map +1 -1
- package/dist/components/ds-bar-action.js +1 -1
- package/dist/components/ds-bar-action.js.map +1 -1
- package/dist/components/ds-bar-nav.js +1 -1
- package/dist/components/ds-bar-title.js +1 -1
- package/dist/components/ds-bar-workflow.js +1 -1
- package/dist/components/ds-bar-workflow.js.map +1 -1
- package/dist/components/ds-breadcrumb.js +1 -1
- package/dist/components/ds-button-filled.js +1 -1
- package/dist/components/ds-button-unfilled.js +1 -1
- package/dist/components/ds-card-chart.js +1 -1
- package/dist/components/ds-card-chart.js.map +1 -1
- package/dist/components/ds-card-overview.js +1 -1
- package/dist/components/ds-card-overview.js.map +1 -1
- package/dist/components/ds-card-setting.js +1 -1
- package/dist/components/ds-card-setting.js.map +1 -1
- package/dist/components/ds-chart-legend.js +1 -1
- package/dist/components/ds-chart-legend.js.map +1 -1
- package/dist/components/ds-chart.js +1 -1
- package/dist/components/ds-chart.js.map +1 -1
- package/dist/components/ds-checkbox.js +1 -1
- package/dist/components/ds-chip.js +1 -1
- package/dist/components/ds-chip.js.map +1 -1
- package/dist/components/ds-code-block.js +1 -1
- package/dist/components/ds-conversation-list-item.js +1 -1
- package/dist/components/ds-conversation-list-item.js.map +1 -1
- package/dist/components/ds-conversation-list-section.js +1 -1
- package/dist/components/ds-empty-state.js +1 -1
- package/dist/components/ds-field.js +1 -1
- package/dist/components/ds-filter-menu.js +1 -1
- package/dist/components/ds-filter-menu.js.map +1 -1
- package/dist/components/ds-input.js +1 -1
- package/dist/components/ds-loader.js +1 -1
- package/dist/components/ds-markdown.js +1 -1
- package/dist/components/ds-menu.js +1 -1
- package/dist/components/ds-message-actions.js +1 -1
- package/dist/components/ds-message-actions.js.map +1 -1
- package/dist/components/ds-message-composer.js +1 -1
- package/dist/components/ds-message-composer.js.map +1 -1
- package/dist/components/ds-message-scroller.js +1 -1
- package/dist/components/ds-message-scroller.js.map +1 -1
- package/dist/components/ds-message.js +1 -1
- package/dist/components/ds-mobile-bar-nav.js +1 -1
- package/dist/components/ds-mobile-header.js +1 -1
- package/dist/components/ds-mobile-section-switcher.js +1 -1
- package/dist/components/ds-mobile-sheet-nav.js +1 -1
- package/dist/components/ds-modal.js +1 -1
- package/dist/components/ds-pagination.js +1 -1
- package/dist/components/ds-panel-nav.js +1 -1
- package/dist/components/ds-panel-sub-nav.js +1 -1
- package/dist/components/ds-panel-sub-nav.js.map +1 -1
- package/dist/components/ds-panel-tool-header.js +1 -1
- package/dist/components/ds-panel-tool-search.js +1 -1
- package/dist/components/ds-panel-tool-search.js.map +1 -1
- package/dist/components/ds-panel-tools.js +1 -1
- package/dist/components/ds-paper-texture.js +1 -1
- package/dist/components/ds-radio.js +1 -1
- package/dist/components/ds-scroll-overlay.js +1 -1
- package/dist/components/ds-scroll-overlay.js.map +1 -1
- package/dist/components/ds-select.js +1 -1
- package/dist/components/ds-shell-app.js +1 -1
- package/dist/components/ds-shell-app.js.map +1 -1
- package/dist/components/ds-shell-page.js +1 -1
- package/dist/components/ds-shell-tools.js +1 -1
- package/dist/components/ds-skeleton.js +1 -1
- package/dist/components/ds-slider.js +1 -1
- package/dist/components/ds-slider.js.map +1 -1
- package/dist/components/ds-swatch-picker.js +1 -1
- package/dist/components/ds-switch.js +1 -1
- package/dist/components/ds-tab-group.js +1 -1
- package/dist/components/ds-table-filter.d.ts +11 -0
- package/dist/components/ds-table-filter.js +2 -0
- package/dist/components/ds-table-filter.js.map +1 -0
- package/dist/components/ds-table-saved-views.js +1 -1
- package/dist/components/ds-table-saved-views.js.map +1 -1
- package/dist/components/ds-table-sort.d.ts +11 -0
- package/dist/components/ds-table-sort.js +2 -0
- package/dist/components/ds-table-sort.js.map +1 -0
- package/dist/components/ds-table-toolbar.js +1 -1
- package/dist/components/ds-table-toolbar.js.map +1 -1
- package/dist/components/ds-table.js +1 -1
- package/dist/components/ds-table.js.map +1 -1
- package/dist/components/ds-tag.js +1 -1
- package/dist/components/ds-text.js +1 -1
- package/dist/components/ds-toast.js +1 -1
- package/dist/components/ds-toast.js.map +1 -1
- package/dist/components/ds-tooltip-chart.js +1 -1
- package/dist/components/ds-tooltip.js +1 -1
- package/dist/components/ds-typing-indicator.js +1 -1
- package/dist/components/index.js +1 -1
- package/dist/components/index.js.map +1 -1
- package/dist/components/p-23u3lIr1.js +2 -0
- package/dist/components/{p-DLkNcCY0.js.map → p-23u3lIr1.js.map} +1 -1
- package/dist/components/{p-C1q-tq3U.js → p-7tZx4ukz.js} +2 -2
- package/dist/components/{p-DfyfrfHL.js → p-Asi9h3pa.js} +2 -2
- package/dist/components/{p-DfyfrfHL.js.map → p-Asi9h3pa.js.map} +1 -1
- package/dist/components/{p-C5TrNd31.js → p-B0PEg5RF.js} +2 -2
- package/dist/components/{p-C5TrNd31.js.map → p-B0PEg5RF.js.map} +1 -1
- package/dist/components/p-BFSLT0yc.js +2 -0
- package/dist/components/p-BFSLT0yc.js.map +1 -0
- package/dist/components/{p-DZFilC--.js → p-BGXqFq2C.js} +2 -2
- package/dist/components/{p-Rfpd5mWO.js → p-BHDkJmrC.js} +2 -2
- package/dist/components/{p-Rfpd5mWO.js.map → p-BHDkJmrC.js.map} +1 -1
- package/dist/components/{p-Cyw9odJX.js → p-BHz78C8h.js} +2 -2
- package/dist/components/{p-Cyw9odJX.js.map → p-BHz78C8h.js.map} +1 -1
- package/dist/components/{p-Dcnd5ZX5.js → p-BKp__CQX.js} +2 -2
- package/dist/components/{p-V2ywPfsR.js → p-BVlfQ8xZ.js} +2 -2
- package/dist/components/{p-V2ywPfsR.js.map → p-BVlfQ8xZ.js.map} +1 -1
- package/dist/components/p-BdnQv7Zl.js +2 -0
- package/dist/components/p-BdnQv7Zl.js.map +1 -0
- package/dist/components/{p-Dsn1RCS5.js → p-Be1sY58w.js} +2 -2
- package/dist/components/{p-Dsn1RCS5.js.map → p-Be1sY58w.js.map} +1 -1
- package/dist/components/p-Bh67XbfB.js +2 -0
- package/dist/components/{p-BzLnSwG9.js.map → p-Bh67XbfB.js.map} +1 -1
- package/dist/components/{p-Btxwp4lJ.js → p-BhYWdbEp.js} +2 -2
- package/dist/components/{p-Btxwp4lJ.js.map → p-BhYWdbEp.js.map} +1 -1
- package/dist/components/{p-D1Nqlrrl.js → p-Bn8NryHK.js} +2 -2
- package/dist/components/{p-D1Nqlrrl.js.map → p-Bn8NryHK.js.map} +1 -1
- package/dist/components/{p-B80NfEqc.js → p-C0X2zKbB.js} +2 -2
- package/dist/components/{p-Ct-m0I6U.js → p-C4TgWJi9.js} +2 -2
- package/dist/components/p-CKmfuK7k.js +2 -0
- package/dist/components/p-CKmfuK7k.js.map +1 -0
- package/dist/components/{p-DTMxwYQj.js → p-CMFvaY05.js} +2 -2
- package/dist/components/{p-PzI11fs8.js → p-CzcWZiH-.js} +2 -2
- package/dist/components/{p-PzI11fs8.js.map → p-CzcWZiH-.js.map} +1 -1
- package/dist/components/{p-BagXfn1G.js → p-D1YhYulL.js} +2 -2
- package/dist/components/{p-BzObtSlo.js → p-D2z2DgtX.js} +2 -2
- package/dist/components/{p-BzObtSlo.js.map → p-D2z2DgtX.js.map} +1 -1
- package/dist/components/{p-BXPQmp7q.js → p-D4K9CE8a.js} +2 -2
- package/dist/components/{p-DcPVSNDr.js → p-D8Qd6gkf.js} +2 -2
- package/dist/components/{p-DcPVSNDr.js.map → p-D8Qd6gkf.js.map} +1 -1
- package/dist/components/{p-BHjw0x3L.js → p-DAqSoyd4.js} +2 -2
- package/dist/components/{p-BHjw0x3L.js.map → p-DAqSoyd4.js.map} +1 -1
- package/dist/components/p-DEjh3klB.js +2 -0
- package/dist/components/{p-CKJzHclc.js.map → p-DEjh3klB.js.map} +1 -1
- package/dist/components/{p-NdahPypA.js → p-DFQ9mcbK.js} +2 -2
- package/dist/components/{p-Ck_VRQYi.js → p-DHuyj-9J.js} +2 -2
- package/dist/components/{p-Ck_VRQYi.js.map → p-DHuyj-9J.js.map} +1 -1
- package/dist/components/p-DIEK54fV.js +2 -0
- package/dist/components/{p-URdgCwEs.js.map → p-DIEK54fV.js.map} +1 -1
- package/dist/components/{p-Bf_D3crn.js → p-DNJTN_s0.js} +2 -2
- package/dist/components/p-DOqnJVos.js +2 -0
- package/dist/components/{p-CWDmHPJC.js.map → p-DOqnJVos.js.map} +1 -1
- package/dist/components/{p-DL_5VENG.js → p-DQqOzDEq.js} +2 -2
- package/dist/components/{p-D-kWb8cF.js → p-DSIOINBK.js} +2 -2
- package/dist/components/{p-C5eosu67.js → p-DZ55e3j9.js} +2 -2
- package/dist/components/{p-C5eosu67.js.map → p-DZ55e3j9.js.map} +1 -1
- package/dist/components/{p-QdI17bl-.js → p-Dg9qiMDa.js} +2 -2
- package/dist/components/p-DsJW5W3W.js +2 -0
- package/dist/components/{p-C2n8A8NA.js.map → p-DsJW5W3W.js.map} +1 -1
- package/dist/components/p-JPTkZ3Ji.js +2 -0
- package/dist/components/p-JPTkZ3Ji.js.map +1 -0
- package/dist/components/p-NzGRAPcF.js +2 -0
- package/dist/components/p-NzGRAPcF.js.map +1 -0
- package/dist/components/{p-B7Hf86NF.js → p-PD1Vfg0t.js} +2 -2
- package/dist/components/{p-B7Hf86NF.js.map → p-PD1Vfg0t.js.map} +1 -1
- package/dist/components/{p-BxqBiVbH.js → p-U8JIZzI2.js} +2 -2
- package/dist/components/{p-BxqBiVbH.js.map → p-U8JIZzI2.js.map} +1 -1
- package/dist/components/{p-CKY989vF.js → p-ZZzC52Ea.js} +2 -2
- package/dist/components/{p-CKY989vF.js.map → p-ZZzC52Ea.js.map} +1 -1
- package/dist/components/p-__lNIImO.js +2 -0
- package/dist/components/{p-CnWC0QvG.js.map → p-__lNIImO.js.map} +1 -1
- package/dist/components/{p-vWK5faWZ.js → p-a6qP60zb.js} +2 -2
- package/dist/components/{p-CjgnFGd6.js → p-rrFMAZkj.js} +2 -2
- package/dist/components/{p-CjgnFGd6.js.map → p-rrFMAZkj.js.map} +1 -1
- package/dist/components/p-ruPTIV6t.js +2 -0
- package/dist/components/{p-M9DPfXbP.js.map → p-ruPTIV6t.js.map} +1 -1
- package/dist/components/{p-jMCRaxQW.js → p-y4jhIkRc.js} +2 -2
- package/dist/components/{p-jMCRaxQW.js.map → p-y4jhIkRc.js.map} +1 -1
- package/dist/docs/components.json +813 -76
- package/dist/mcp-data/registry/button-unfilled.json +26 -7
- package/dist/mcp-data/registry/filter-menu.json +21 -2
- package/dist/mcp-data/registry/loader.json +2 -1
- package/dist/mcp-data/registry/registry.json +322 -23
- package/dist/mcp-data/registry/select.json +22 -3
- package/dist/mcp-data/registry/table-filter.json +399 -0
- package/dist/mcp-data/registry/table-saved-views.json +4 -4
- package/dist/mcp-data/registry/table-sort.json +229 -0
- package/dist/mcp-data/registry/table-toolbar.json +21 -14
- package/dist/mcp-data/registry/table.json +11 -8
- package/dist/react/components.d.ts +2 -0
- package/dist/react/components.js +2 -0
- package/dist/react/ds-table-filter.d.ts +17 -0
- package/dist/react/ds-table-filter.js +19 -0
- package/dist/react/ds-table-sort.d.ts +12 -0
- package/dist/react/ds-table-sort.js +12 -0
- package/dist/types/components/ButtonUnfilled/ButtonUnfilled.d.ts +18 -1
- package/dist/types/components/FilterMenu/FilterMenu.d.ts +13 -0
- package/dist/types/components/Select/Select.d.ts +13 -0
- package/dist/types/components/Table/Table.d.ts +4 -0
- package/dist/types/components/Table/table-column-customizer.d.ts +2 -0
- package/dist/types/components/TableFilter/TableFilter.d.ts +37 -0
- package/dist/types/components/TableSort/TableSort.d.ts +24 -0
- package/dist/types/components/TableSort/table-sort-menu.d.ts +15 -0
- package/dist/types/components.d.ts +254 -2
- package/dist/types/utils/table-caption-compact.d.ts +6 -0
- package/dist/vue/components.d.ts +2 -0
- package/dist/vue/components.js +2 -0
- package/dist/vue/ds-button-unfilled.js +1 -0
- package/dist/vue/ds-filter-menu.js +1 -0
- package/dist/vue/ds-select.js +1 -0
- package/dist/vue/ds-table-filter.d.ts +3 -0
- package/dist/vue/ds-table-filter.js +29 -0
- package/dist/vue/ds-table-sort.d.ts +3 -0
- package/dist/vue/ds-table-sort.js +13 -0
- package/package.json +1 -1
- package/dist/components/p-BzLnSwG9.js +0 -2
- package/dist/components/p-C2n8A8NA.js +0 -2
- package/dist/components/p-C7rzVotG.js +0 -2
- package/dist/components/p-C7rzVotG.js.map +0 -1
- package/dist/components/p-CKJzHclc.js +0 -2
- package/dist/components/p-CWDmHPJC.js +0 -2
- package/dist/components/p-Ca-I2UnB.js +0 -2
- package/dist/components/p-Ca-I2UnB.js.map +0 -1
- package/dist/components/p-CnWC0QvG.js +0 -2
- package/dist/components/p-CooNucm0.js +0 -2
- package/dist/components/p-CooNucm0.js.map +0 -1
- package/dist/components/p-DLkNcCY0.js +0 -2
- package/dist/components/p-M9DPfXbP.js +0 -2
- package/dist/components/p-URdgCwEs.js +0 -2
- /package/dist/components/{p-C1q-tq3U.js.map → p-7tZx4ukz.js.map} +0 -0
- /package/dist/components/{p-DZFilC--.js.map → p-BGXqFq2C.js.map} +0 -0
- /package/dist/components/{p-Dcnd5ZX5.js.map → p-BKp__CQX.js.map} +0 -0
- /package/dist/components/{p-B80NfEqc.js.map → p-C0X2zKbB.js.map} +0 -0
- /package/dist/components/{p-Ct-m0I6U.js.map → p-C4TgWJi9.js.map} +0 -0
- /package/dist/components/{p-DTMxwYQj.js.map → p-CMFvaY05.js.map} +0 -0
- /package/dist/components/{p-BagXfn1G.js.map → p-D1YhYulL.js.map} +0 -0
- /package/dist/components/{p-BXPQmp7q.js.map → p-D4K9CE8a.js.map} +0 -0
- /package/dist/components/{p-NdahPypA.js.map → p-DFQ9mcbK.js.map} +0 -0
- /package/dist/components/{p-Bf_D3crn.js.map → p-DNJTN_s0.js.map} +0 -0
- /package/dist/components/{p-DL_5VENG.js.map → p-DQqOzDEq.js.map} +0 -0
- /package/dist/components/{p-D-kWb8cF.js.map → p-DSIOINBK.js.map} +0 -0
- /package/dist/components/{p-QdI17bl-.js.map → p-Dg9qiMDa.js.map} +0 -0
- /package/dist/components/{p-vWK5faWZ.js.map → p-a6qP60zb.js.map} +0 -0
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "table-filter",
|
|
4
|
+
"title": "TableFilter",
|
|
5
|
+
"description": "Standard table-toolbar Filter control that owns the shared trigger, filter menu UI, accessible defaults, and compact-caption behavior while the application controls data and values.",
|
|
6
|
+
"type": "registry:ui",
|
|
7
|
+
"meta": {
|
|
8
|
+
"library": "@ds-mo/ui",
|
|
9
|
+
"distribution": "npm",
|
|
10
|
+
"storybook": "https://zainadeel.github.io/compomo/?path=/story/table-filter",
|
|
11
|
+
"source": "src/wc/components/TableFilter/TableFilter.tsx",
|
|
12
|
+
"intentStatus": "complete",
|
|
13
|
+
"intent": {
|
|
14
|
+
"audience": "general",
|
|
15
|
+
"status": "stable",
|
|
16
|
+
"summary": "Standard table-toolbar Filter control that owns the shared trigger, filter menu UI, accessible defaults, and compact-caption behavior while the application controls data and values.",
|
|
17
|
+
"useWhen": [
|
|
18
|
+
"A table toolbar should expose the standard singular Filter control and shared filter-menu interaction.",
|
|
19
|
+
"A product supplies filter definitions and controlled values but should not recreate the toolbar trigger, selected count, category navigation, or responsive treatment."
|
|
20
|
+
],
|
|
21
|
+
"avoidWhen": [
|
|
22
|
+
"Filtering is unrelated to a table or structured result set; use ds-filter-menu directly.",
|
|
23
|
+
"The application expects the component to filter, fetch, persist, or otherwise transform rows.",
|
|
24
|
+
"A filter requires a product-specific editor that the shared filter category kinds cannot represent."
|
|
25
|
+
],
|
|
26
|
+
"alternatives": [
|
|
27
|
+
{
|
|
28
|
+
"when": "A non-table surface needs the generic filter interaction or custom trigger recipe.",
|
|
29
|
+
"component": "component:ds-filter-menu",
|
|
30
|
+
"reason": "FilterMenu exposes generic trigger geometry and popup positioning rather than the fixed table-toolbar recipe."
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"commonlyComposedWith": [
|
|
34
|
+
"component:ds-table",
|
|
35
|
+
"component:ds-table-toolbar",
|
|
36
|
+
"component:ds-table-saved-views",
|
|
37
|
+
"component:ds-table-sort",
|
|
38
|
+
"component:ds-filter-menu"
|
|
39
|
+
],
|
|
40
|
+
"patterns": [
|
|
41
|
+
"pattern:menu-trigger"
|
|
42
|
+
],
|
|
43
|
+
"accessibility": [
|
|
44
|
+
"The trigger is an icon-label combobox named Filter table unless a product aria-label supplies the table context. Compact caption widths preserve that accessible name after the visible label and chevron are omitted.",
|
|
45
|
+
"The popup retains ds-filter-menu category navigation, option semantics, focus entry, dismissal, and focus return.",
|
|
46
|
+
"Localize label, menuLabel, categoriesLabel, and clearLabel together so visible and accessible wording remains consistent."
|
|
47
|
+
],
|
|
48
|
+
"states": [
|
|
49
|
+
"filters, values, and activeFilterId are controlled. dsChange, dsClear, and dsActiveFilterChange report intent without changing those inputs; the application applies query consequences and returns replacement values.",
|
|
50
|
+
"The visible trigger label defaults to singular Filter and appends the selected criterion count. The trigger uses resting secondary chrome at typical widths; active criteria promote only the visible label, without redefining generic button active semantics.",
|
|
51
|
+
"Popup visibility follows the same mutable open and dsOpenChange contract as ds-filter-menu."
|
|
52
|
+
],
|
|
53
|
+
"responsiveBehavior": [
|
|
54
|
+
"Place the control in the trailing region of ds-table-toolbar.",
|
|
55
|
+
"Inside a named ds-table container narrower than 900px, the trigger becomes icon-only and active criteria promote the Filters icon to primary."
|
|
56
|
+
],
|
|
57
|
+
"frameworkCaveats": {
|
|
58
|
+
"customElements": [
|
|
59
|
+
"Assign filters, values, and activeFilterId as JavaScript properties. Replace controlled values in response to dsChange and dsClear."
|
|
60
|
+
],
|
|
61
|
+
"react": [
|
|
62
|
+
"Keep filters and values in application state and replace values from the typed dsChange and dsClear events."
|
|
63
|
+
],
|
|
64
|
+
"angular": [
|
|
65
|
+
"Import the generated DsTableFilter adapter, bind filters and values, and handle dsChange, dsClear, and dsActiveFilterChange."
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
"references": [
|
|
69
|
+
{
|
|
70
|
+
"label": "Storybook review states",
|
|
71
|
+
"path": "src/wc/components/TableFilter/TableFilter.stories.ts"
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"label": "Toolbar composition owner",
|
|
75
|
+
"path": "src/wc/components/TableToolbar/TableToolbar.agent.json"
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
},
|
|
79
|
+
"api": {
|
|
80
|
+
"props": {
|
|
81
|
+
"activeFilterId": {
|
|
82
|
+
"type": "string | undefined",
|
|
83
|
+
"resolvedType": "string | undefined",
|
|
84
|
+
"attribute": "active-filter-id",
|
|
85
|
+
"required": false,
|
|
86
|
+
"mutable": false,
|
|
87
|
+
"description": "Controlled category shown in the option pane."
|
|
88
|
+
},
|
|
89
|
+
"ariaLabel": {
|
|
90
|
+
"type": "string | null",
|
|
91
|
+
"resolvedType": "null | string",
|
|
92
|
+
"attribute": "aria-label",
|
|
93
|
+
"default": "null",
|
|
94
|
+
"required": false,
|
|
95
|
+
"mutable": false,
|
|
96
|
+
"description": "Accessible name for the trigger. Defaults to Filter table."
|
|
97
|
+
},
|
|
98
|
+
"categoriesLabel": {
|
|
99
|
+
"type": "string",
|
|
100
|
+
"resolvedType": "string",
|
|
101
|
+
"attribute": "categories-label",
|
|
102
|
+
"default": "'Filter categories'",
|
|
103
|
+
"required": false,
|
|
104
|
+
"mutable": false,
|
|
105
|
+
"description": "Accessible name for the category tab list."
|
|
106
|
+
},
|
|
107
|
+
"clearLabel": {
|
|
108
|
+
"type": "string",
|
|
109
|
+
"resolvedType": "string",
|
|
110
|
+
"attribute": "clear-label",
|
|
111
|
+
"default": "'Clear'",
|
|
112
|
+
"required": false,
|
|
113
|
+
"mutable": false,
|
|
114
|
+
"description": "Footer action and date-clear accessible label."
|
|
115
|
+
},
|
|
116
|
+
"filters": {
|
|
117
|
+
"type": "FilterMenuFilter[]",
|
|
118
|
+
"resolvedType": "FilterMenuFilter[]",
|
|
119
|
+
"default": "[]",
|
|
120
|
+
"required": false,
|
|
121
|
+
"mutable": false,
|
|
122
|
+
"description": "Product-owned filter categories and option definitions."
|
|
123
|
+
},
|
|
124
|
+
"label": {
|
|
125
|
+
"type": "string",
|
|
126
|
+
"resolvedType": "string",
|
|
127
|
+
"attribute": "label",
|
|
128
|
+
"default": "'Filter'",
|
|
129
|
+
"required": false,
|
|
130
|
+
"mutable": false,
|
|
131
|
+
"description": "Visible trigger label."
|
|
132
|
+
},
|
|
133
|
+
"menuLabel": {
|
|
134
|
+
"type": "string | null",
|
|
135
|
+
"resolvedType": "null | string",
|
|
136
|
+
"attribute": "menu-label",
|
|
137
|
+
"default": "null",
|
|
138
|
+
"required": false,
|
|
139
|
+
"mutable": false,
|
|
140
|
+
"description": "Accessible name for the non-modal filter dialog. Defaults to the trigger name."
|
|
141
|
+
},
|
|
142
|
+
"open": {
|
|
143
|
+
"type": "boolean",
|
|
144
|
+
"resolvedType": "boolean",
|
|
145
|
+
"attribute": "open",
|
|
146
|
+
"default": "false",
|
|
147
|
+
"required": false,
|
|
148
|
+
"mutable": true,
|
|
149
|
+
"description": "Controlled popup visibility."
|
|
150
|
+
},
|
|
151
|
+
"values": {
|
|
152
|
+
"type": "FilterMenuValues",
|
|
153
|
+
"resolvedType": "{ [x: string]: FilterMenuValue | undefined; }",
|
|
154
|
+
"default": "{}",
|
|
155
|
+
"required": false,
|
|
156
|
+
"mutable": false,
|
|
157
|
+
"description": "Controlled values keyed by filter id."
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
"events": [
|
|
161
|
+
{
|
|
162
|
+
"name": "dsActiveFilterChange",
|
|
163
|
+
"detail": "string",
|
|
164
|
+
"bubbles": true,
|
|
165
|
+
"cancelable": true,
|
|
166
|
+
"composed": true,
|
|
167
|
+
"description": "Requests a controlled active-category replacement."
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"name": "dsAfterClose",
|
|
171
|
+
"detail": "void",
|
|
172
|
+
"bubbles": true,
|
|
173
|
+
"cancelable": true,
|
|
174
|
+
"composed": true,
|
|
175
|
+
"description": "Emitted after exit motion and rendered popup removal complete."
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"name": "dsChange",
|
|
179
|
+
"detail": "FilterMenuChangeDetail",
|
|
180
|
+
"bubbles": true,
|
|
181
|
+
"cancelable": true,
|
|
182
|
+
"composed": true,
|
|
183
|
+
"description": "Requests a controlled value replacement without closing the popup."
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"name": "dsClear",
|
|
187
|
+
"detail": "void",
|
|
188
|
+
"bubbles": true,
|
|
189
|
+
"cancelable": true,
|
|
190
|
+
"composed": true,
|
|
191
|
+
"description": "Requests that the consumer clear every filter value."
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
"name": "dsClose",
|
|
195
|
+
"detail": "void",
|
|
196
|
+
"bubbles": true,
|
|
197
|
+
"cancelable": true,
|
|
198
|
+
"composed": true,
|
|
199
|
+
"description": "Requests that the controlled popup close."
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"name": "dsOpenChange",
|
|
203
|
+
"detail": "boolean",
|
|
204
|
+
"bubbles": true,
|
|
205
|
+
"cancelable": true,
|
|
206
|
+
"composed": true,
|
|
207
|
+
"description": "Emitted whenever trigger activation changes popup visibility."
|
|
208
|
+
}
|
|
209
|
+
],
|
|
210
|
+
"methods": [],
|
|
211
|
+
"slots": []
|
|
212
|
+
},
|
|
213
|
+
"props": {
|
|
214
|
+
"activeFilterId": {
|
|
215
|
+
"type": "string | undefined",
|
|
216
|
+
"resolvedType": "string | undefined",
|
|
217
|
+
"attribute": "active-filter-id",
|
|
218
|
+
"required": false,
|
|
219
|
+
"mutable": false,
|
|
220
|
+
"description": "Controlled category shown in the option pane."
|
|
221
|
+
},
|
|
222
|
+
"ariaLabel": {
|
|
223
|
+
"type": "string | null",
|
|
224
|
+
"resolvedType": "null | string",
|
|
225
|
+
"attribute": "aria-label",
|
|
226
|
+
"default": "null",
|
|
227
|
+
"required": false,
|
|
228
|
+
"mutable": false,
|
|
229
|
+
"description": "Accessible name for the trigger. Defaults to Filter table."
|
|
230
|
+
},
|
|
231
|
+
"categoriesLabel": {
|
|
232
|
+
"type": "string",
|
|
233
|
+
"resolvedType": "string",
|
|
234
|
+
"attribute": "categories-label",
|
|
235
|
+
"default": "'Filter categories'",
|
|
236
|
+
"required": false,
|
|
237
|
+
"mutable": false,
|
|
238
|
+
"description": "Accessible name for the category tab list."
|
|
239
|
+
},
|
|
240
|
+
"clearLabel": {
|
|
241
|
+
"type": "string",
|
|
242
|
+
"resolvedType": "string",
|
|
243
|
+
"attribute": "clear-label",
|
|
244
|
+
"default": "'Clear'",
|
|
245
|
+
"required": false,
|
|
246
|
+
"mutable": false,
|
|
247
|
+
"description": "Footer action and date-clear accessible label."
|
|
248
|
+
},
|
|
249
|
+
"filters": {
|
|
250
|
+
"type": "FilterMenuFilter[]",
|
|
251
|
+
"resolvedType": "FilterMenuFilter[]",
|
|
252
|
+
"default": "[]",
|
|
253
|
+
"required": false,
|
|
254
|
+
"mutable": false,
|
|
255
|
+
"description": "Product-owned filter categories and option definitions."
|
|
256
|
+
},
|
|
257
|
+
"label": {
|
|
258
|
+
"type": "string",
|
|
259
|
+
"resolvedType": "string",
|
|
260
|
+
"attribute": "label",
|
|
261
|
+
"default": "'Filter'",
|
|
262
|
+
"required": false,
|
|
263
|
+
"mutable": false,
|
|
264
|
+
"description": "Visible trigger label."
|
|
265
|
+
},
|
|
266
|
+
"menuLabel": {
|
|
267
|
+
"type": "string | null",
|
|
268
|
+
"resolvedType": "null | string",
|
|
269
|
+
"attribute": "menu-label",
|
|
270
|
+
"default": "null",
|
|
271
|
+
"required": false,
|
|
272
|
+
"mutable": false,
|
|
273
|
+
"description": "Accessible name for the non-modal filter dialog. Defaults to the trigger name."
|
|
274
|
+
},
|
|
275
|
+
"open": {
|
|
276
|
+
"type": "boolean",
|
|
277
|
+
"resolvedType": "boolean",
|
|
278
|
+
"attribute": "open",
|
|
279
|
+
"default": "false",
|
|
280
|
+
"required": false,
|
|
281
|
+
"mutable": true,
|
|
282
|
+
"description": "Controlled popup visibility."
|
|
283
|
+
},
|
|
284
|
+
"values": {
|
|
285
|
+
"type": "FilterMenuValues",
|
|
286
|
+
"resolvedType": "{ [x: string]: FilterMenuValue | undefined; }",
|
|
287
|
+
"default": "{}",
|
|
288
|
+
"required": false,
|
|
289
|
+
"mutable": false,
|
|
290
|
+
"description": "Controlled values keyed by filter id."
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
"events": [
|
|
294
|
+
{
|
|
295
|
+
"name": "dsActiveFilterChange",
|
|
296
|
+
"detail": "string",
|
|
297
|
+
"bubbles": true,
|
|
298
|
+
"cancelable": true,
|
|
299
|
+
"composed": true,
|
|
300
|
+
"description": "Requests a controlled active-category replacement."
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
"name": "dsAfterClose",
|
|
304
|
+
"detail": "void",
|
|
305
|
+
"bubbles": true,
|
|
306
|
+
"cancelable": true,
|
|
307
|
+
"composed": true,
|
|
308
|
+
"description": "Emitted after exit motion and rendered popup removal complete."
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
"name": "dsChange",
|
|
312
|
+
"detail": "FilterMenuChangeDetail",
|
|
313
|
+
"bubbles": true,
|
|
314
|
+
"cancelable": true,
|
|
315
|
+
"composed": true,
|
|
316
|
+
"description": "Requests a controlled value replacement without closing the popup."
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
"name": "dsClear",
|
|
320
|
+
"detail": "void",
|
|
321
|
+
"bubbles": true,
|
|
322
|
+
"cancelable": true,
|
|
323
|
+
"composed": true,
|
|
324
|
+
"description": "Requests that the consumer clear every filter value."
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
"name": "dsClose",
|
|
328
|
+
"detail": "void",
|
|
329
|
+
"bubbles": true,
|
|
330
|
+
"cancelable": true,
|
|
331
|
+
"composed": true,
|
|
332
|
+
"description": "Requests that the controlled popup close."
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
"name": "dsOpenChange",
|
|
336
|
+
"detail": "boolean",
|
|
337
|
+
"bubbles": true,
|
|
338
|
+
"cancelable": true,
|
|
339
|
+
"composed": true,
|
|
340
|
+
"description": "Emitted whenever trigger activation changes popup visibility."
|
|
341
|
+
}
|
|
342
|
+
],
|
|
343
|
+
"methods": [],
|
|
344
|
+
"slots": [],
|
|
345
|
+
"exports": {
|
|
346
|
+
"customElement": "ds-table-filter",
|
|
347
|
+
"react": "DsTableFilter",
|
|
348
|
+
"vue": "DsTableFilter",
|
|
349
|
+
"angular": "DsTableFilter"
|
|
350
|
+
},
|
|
351
|
+
"consumption": {
|
|
352
|
+
"install": "npm install @ds-mo/ui @ds-mo/tokens @ds-mo/icons",
|
|
353
|
+
"cssSetup": "import '@ds-mo/tokens';\nimport '@ds-mo/tokens/reset';\nimport '@ds-mo/tokens/globals';",
|
|
354
|
+
"customElements": {
|
|
355
|
+
"import": "import '@ds-mo/ui/dist/components/ds-table-filter.js';",
|
|
356
|
+
"example": "<ds-table-filter></ds-table-filter>"
|
|
357
|
+
},
|
|
358
|
+
"react": {
|
|
359
|
+
"import": "import { DsTableFilter } from '@ds-mo/ui/react';",
|
|
360
|
+
"example": "<DsTableFilter />"
|
|
361
|
+
},
|
|
362
|
+
"vue": {
|
|
363
|
+
"import": "import { DsTableFilter } from '@ds-mo/ui/vue';",
|
|
364
|
+
"example": "<DsTableFilter />"
|
|
365
|
+
},
|
|
366
|
+
"angular": {
|
|
367
|
+
"import": "import { DsTableFilter } from '@ds-mo/ui/angular/ds-table-filter';",
|
|
368
|
+
"example": "<ds-table-filter></ds-table-filter>"
|
|
369
|
+
},
|
|
370
|
+
"complexPropertyNote": "Assign these non-primitive values as JavaScript properties: filters, values.",
|
|
371
|
+
"peerDependencies": {
|
|
372
|
+
"required": [
|
|
373
|
+
"@ds-mo/tokens ^6.5.0",
|
|
374
|
+
"@ds-mo/icons ^7.0.0"
|
|
375
|
+
],
|
|
376
|
+
"frameworks": "Custom Elements; React 18/19 wrappers; Vue 3 wrappers; Angular 19-22 standalone adapters."
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
"dependencies": [
|
|
381
|
+
"@ds-mo/ui",
|
|
382
|
+
"@ds-mo/tokens"
|
|
383
|
+
],
|
|
384
|
+
"registryDependencies": [
|
|
385
|
+
"filter-menu"
|
|
386
|
+
],
|
|
387
|
+
"files": [
|
|
388
|
+
{
|
|
389
|
+
"path": "src/wc/components/TableFilter/TableFilter.css",
|
|
390
|
+
"content": ":host {\n display: inline-flex;\n min-inline-size: 0;\n max-inline-size: var(--dimension-menu-width-xs);\n}\n\nds-filter-menu {\n min-inline-size: 0;\n max-inline-size: 100%;\n --ds-choice-popup-min-inline-size: var(--dimension-menu-width-xs);\n}\n",
|
|
391
|
+
"type": "registry:ui"
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
"path": "src/wc/components/TableFilter/TableFilter.tsx",
|
|
395
|
+
"content": "import { Component, Event, EventEmitter, h, Host, Prop } from '@stencil/core';\nimport type {\n FilterMenuChangeDetail,\n FilterMenuFilter,\n FilterMenuValues,\n} from '../FilterMenu/FilterMenu';\n\n@Component({\n tag: 'ds-table-filter',\n styleUrl: 'TableFilter.css',\n scoped: true,\n})\nexport class TableFilter {\n /** Controlled popup visibility. */\n @Prop({ mutable: true }) open: boolean = false;\n /** Product-owned filter categories and option definitions. */\n @Prop() filters: FilterMenuFilter[] = [];\n /** Controlled values keyed by filter id. */\n @Prop() values: FilterMenuValues = {};\n /** Controlled category shown in the option pane. */\n @Prop() activeFilterId: string | undefined;\n /** Visible trigger label. */\n @Prop() label: string = 'Filter';\n /** Accessible name for the trigger. Defaults to Filter table. */\n @Prop({ attribute: 'aria-label' }) ariaLabel: string | null = null;\n /** Accessible name for the non-modal filter dialog. Defaults to the trigger name. */\n @Prop() menuLabel: string | null = null;\n /** Accessible name for the category tab list. */\n @Prop() categoriesLabel: string = 'Filter categories';\n /** Footer action and date-clear accessible label. */\n @Prop() clearLabel: string = 'Clear';\n\n /** Requests a controlled value replacement without closing the popup. */\n @Event() dsChange!: EventEmitter<FilterMenuChangeDetail>;\n /** Requests that the consumer clear every filter value. */\n @Event() dsClear!: EventEmitter<void>;\n /** Requests a controlled active-category replacement. */\n @Event() dsActiveFilterChange!: EventEmitter<string>;\n /** Requests that the controlled popup close. */\n @Event() dsClose!: EventEmitter<void>;\n /** Emitted whenever trigger activation changes popup visibility. */\n @Event() dsOpenChange!: EventEmitter<boolean>;\n /** Emitted after exit motion and rendered popup removal complete. */\n @Event() dsAfterClose!: EventEmitter<void>;\n\n render() {\n const name = this.ariaLabel?.trim() || 'Filter table';\n const menuName = this.menuLabel?.trim() || name;\n\n return (\n <Host>\n <ds-filter-menu\n open={this.open}\n triggerLabel={this.label}\n icon=\"Filters\"\n size=\"md\"\n width=\"hug\"\n hasBorder={true}\n activeFill={false}\n collapseLabel={true}\n aria-label={name}\n filters={this.filters}\n values={this.values}\n activeFilterId={this.activeFilterId}\n menuLabel={menuName}\n categoriesLabel={this.categoriesLabel}\n clearLabel={this.clearLabel}\n onDsChange={event => this.forward(event, this.dsChange)}\n onDsClear={event => this.forward(event, this.dsClear)}\n onDsActiveFilterChange={event => this.forward(event, this.dsActiveFilterChange)}\n onDsClose={event => this.forward(event, this.dsClose)}\n onDsOpenChange={event => {\n event.stopPropagation();\n this.open = event.detail;\n this.dsOpenChange.emit(event.detail);\n }}\n onDsAfterClose={event => this.forward(event, this.dsAfterClose)}\n />\n </Host>\n );\n }\n\n private forward<T>(event: CustomEvent<T>, emitter: EventEmitter<T>): void {\n event.stopPropagation();\n emitter.emit(event.detail);\n }\n}\n",
|
|
396
|
+
"type": "registry:ui"
|
|
397
|
+
}
|
|
398
|
+
]
|
|
399
|
+
}
|
|
@@ -43,8 +43,8 @@
|
|
|
43
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
44
|
],
|
|
45
45
|
"responsiveBehavior": [
|
|
46
|
-
"The control remains a single toolbar item and relies on Select, Menu, and Modal collision and viewport behavior. Its choice popup keeps
|
|
47
|
-
"Place it in the
|
|
46
|
+
"The control remains a single toolbar item and relies on Select, Menu, and Modal collision and viewport behavior. Its trigger hugs short view names and is capped at the compact menu-width token so long names truncate while the dirty dot stays visible. Its choice popup keeps that same token as the minimum width.",
|
|
47
|
+
"Place it in the start region of ds-table-toolbar so the middle region can keep left-side and right-side controls on one spanning cluster."
|
|
48
48
|
],
|
|
49
49
|
"frameworkCaveats": {
|
|
50
50
|
"customElements": [
|
|
@@ -371,12 +371,12 @@
|
|
|
371
371
|
"files": [
|
|
372
372
|
{
|
|
373
373
|
"path": "src/wc/components/TableSavedViews/TableSavedViews.css",
|
|
374
|
-
"content": ":host {\n --ds-choice-popup-min-inline-size: var(--dimension-menu-width-xs);\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",
|
|
374
|
+
"content": ":host {\n --ds-choice-popup-min-inline-size: var(--dimension-menu-width-xs);\n\n display: inline-block;\n min-inline-size: 0;\n max-inline-size: var(--dimension-menu-width-xs);\n}\n\nds-select {\n display: block;\n min-inline-size: 0;\n max-inline-size: 100%;\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",
|
|
375
375
|
"type": "registry:ui"
|
|
376
376
|
},
|
|
377
377
|
{
|
|
378
378
|
"path": "src/wc/components/TableSavedViews/TableSavedViews.tsx",
|
|
379
|
-
"content": "import { Component, Element, Event, EventEmitter, h, Host, Prop, State } 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 custom view. Ignored for the default 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 ? [templates, { header: 'Custom', options: custom }] : [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 =>\n view.id !== this.dialogViewId &&\n view.label.trim().toLocaleLowerCase() === name.toLocaleLowerCase()\n );\n if (\n duplicate ||\n this.defaultViewLabel.trim().toLocaleLowerCase() === name.toLocaleLowerCase()\n ) {\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 && Boolean(this.selectedCustomView)}\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",
|
|
379
|
+
"content": "import { Component, Element, Event, EventEmitter, h, Host, Prop, State } 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 custom view. Ignored for the default 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 ? [templates, { header: 'Custom', options: custom }] : [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 =>\n view.id !== this.dialogViewId &&\n view.label.trim().toLocaleLowerCase() === name.toLocaleLowerCase()\n );\n if (\n duplicate ||\n this.defaultViewLabel.trim().toLocaleLowerCase() === name.toLocaleLowerCase()\n ) {\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 width=\"fill\"\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 && Boolean(this.selectedCustomView)}\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",
|
|
380
380
|
"type": "registry:ui"
|
|
381
381
|
},
|
|
382
382
|
{
|