@ds-mo/ui 14.18.1 → 14.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.package-ready.json +1 -1
- package/dist/agent-patterns.json +1 -1
- package/dist/agent.json +16 -9
- package/dist/angular/ds-pagination.d.ts +1 -1
- package/dist/angular/ds-pagination.js +3 -3
- 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-avatar.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-conversation-list.js +1 -1
- package/dist/components/ds-conversation-list.js.map +1 -1
- package/dist/components/ds-divider.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-icon.js +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-bubble.js +1 -1
- package/dist/components/ds-message-bubble.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.js +1 -1
- package/dist/components/ds-table-saved-views.js +1 -1
- package/dist/components/ds-table-sort.js +1 -1
- package/dist/components/ds-table-sort.js.map +1 -1
- package/dist/components/ds-table-toolbar.js +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/p-0XTyQNRG.js +2 -0
- package/dist/components/{p-JPTkZ3Ji.js.map → p-0XTyQNRG.js.map} +1 -1
- package/dist/components/{p-DFQ9mcbK.js → p-1YHFQeI7.js} +1 -1
- package/dist/components/p-1YHFQeI7.js.map +1 -0
- package/dist/components/p-B-HsfQOK.js +2 -0
- package/dist/components/p-B-HsfQOK.js.map +1 -0
- package/dist/components/p-B4wTnsUL.js +2 -0
- package/dist/components/p-B4wTnsUL.js.map +1 -0
- package/dist/components/p-B6WQre5D.js +2 -0
- package/dist/components/p-B6WQre5D.js.map +1 -0
- package/dist/components/{p-B1JRCDHS.js → p-BHhmB9-4.js} +2 -2
- package/dist/components/{p-B1JRCDHS.js.map → p-BHhmB9-4.js.map} +1 -1
- package/dist/components/{p-CqQFowZd.js → p-BNq9fr9S.js} +2 -2
- package/dist/components/p-BNq9fr9S.js.map +1 -0
- package/dist/components/{p-BKp__CQX.js → p-BWwiCvep.js} +2 -2
- package/dist/components/p-BWwiCvep.js.map +1 -0
- package/dist/components/{p-BBG6Sswb.js → p-BYFS2qWb.js} +2 -2
- package/dist/components/{p-BBG6Sswb.js.map → p-BYFS2qWb.js.map} +1 -1
- package/dist/components/{p-uI6z53BE.js → p-Bb4KnRy0.js} +2 -2
- package/dist/components/{p-HB_cf_aR.js → p-BeA6Jv-3.js} +2 -2
- package/dist/components/{p-HB_cf_aR.js.map → p-BeA6Jv-3.js.map} +1 -1
- package/dist/components/{p-1Hu9jlju.js → p-Bf7PZJPT.js} +2 -2
- package/dist/components/{p-Bbb9A3NV.js → p-BjRD57w3.js} +2 -2
- package/dist/components/p-BjRD57w3.js.map +1 -0
- package/dist/components/{p-CKVHrOEp.js → p-BmlGeaY8.js} +2 -2
- package/dist/components/p-BmlGeaY8.js.map +1 -0
- package/dist/components/{p-CLVxgTme.js → p-BubgDQlb.js} +2 -2
- package/dist/components/p-BubgDQlb.js.map +1 -0
- package/dist/components/{p-BNkU9dcc.js → p-Bv6FSDPR.js} +2 -2
- package/dist/components/{p-BNkU9dcc.js.map → p-Bv6FSDPR.js.map} +1 -1
- package/dist/components/p-Bx1IHqJb.js +2 -0
- package/dist/components/p-Bx1IHqJb.js.map +1 -0
- package/dist/components/{p-D3fMaUJ3.js → p-Bx5BSd6F.js} +2 -2
- package/dist/components/p-Bx5BSd6F.js.map +1 -0
- package/dist/components/{p-B8q-6a03.js → p-C0qN3PSb.js} +1 -1
- package/dist/components/{p-B8q-6a03.js.map → p-C0qN3PSb.js.map} +1 -1
- package/dist/components/{p-H5T258T-.js → p-CFsLF-ts.js} +2 -2
- package/dist/components/p-CFsLF-ts.js.map +1 -0
- package/dist/components/p-CKtJ6FHM.js +2 -0
- package/dist/components/p-CKtJ6FHM.js.map +1 -0
- package/dist/components/p-CRSwNbPk.js +2 -0
- package/dist/components/p-CRSwNbPk.js.map +1 -0
- package/dist/components/{p-BFfLvMZd.js → p-Cea7rROU.js} +2 -2
- package/dist/components/{p-BFfLvMZd.js.map → p-Cea7rROU.js.map} +1 -1
- package/dist/components/{p-B0Byw6OX.js → p-CgGcgMjs.js} +1 -1
- package/dist/components/{p-B0Byw6OX.js.map → p-CgGcgMjs.js.map} +1 -1
- package/dist/components/{p-C-Y7gYgz.js → p-ChfVRPHw.js} +2 -2
- package/dist/components/p-ChfVRPHw.js.map +1 -0
- package/dist/components/{p-CJq3n5kA.js → p-Ck1hgD6g.js} +2 -2
- package/dist/components/p-Ck1hgD6g.js.map +1 -0
- package/dist/components/{p-BMc0DE59.js → p-CochmPyg.js} +1 -1
- package/dist/components/p-CochmPyg.js.map +1 -0
- package/dist/components/{p-iMuFgSoM.js → p-Cp5Hr7Vz.js} +2 -2
- package/dist/components/p-Cp5Hr7Vz.js.map +1 -0
- package/dist/components/p-CpUXubax.js +2 -0
- package/dist/components/{p-DtndC9f1.js.map → p-CpUXubax.js.map} +1 -1
- package/dist/components/p-Crz606uD.js +2 -0
- package/dist/components/p-Crz606uD.js.map +1 -0
- package/dist/components/p-Cxr0IQjC.js.map +1 -1
- package/dist/components/p-D-MWvpg6.js +2 -0
- package/dist/components/p-D-MWvpg6.js.map +1 -0
- package/dist/components/{p-Bu3dOjpW.js → p-D7v6Na7U.js} +2 -2
- package/dist/components/{p-Bu3dOjpW.js.map → p-D7v6Na7U.js.map} +1 -1
- package/dist/components/{p-C6DNhlES.js → p-DA5H_Iq_.js} +1 -1
- package/dist/components/p-DA5H_Iq_.js.map +1 -0
- package/dist/components/p-DBYCwq9L.js +2 -0
- package/dist/components/{p-BXN-e7vF.js.map → p-DBYCwq9L.js.map} +1 -1
- package/dist/components/{p-CY4YFAT3.js → p-DCbKsABt.js} +1 -1
- package/dist/components/p-DCbKsABt.js.map +1 -0
- package/dist/components/{p-C6R5Lft0.js → p-DFC0sRM-.js} +2 -2
- package/dist/components/p-DFC0sRM-.js.map +1 -0
- package/dist/components/{p-CsBICYmt.js → p-DGkn8FwV.js} +2 -2
- package/dist/components/p-DGkn8FwV.js.map +1 -0
- package/dist/components/p-DIqT1m2r.js +2 -0
- package/dist/components/p-DIqT1m2r.js.map +1 -0
- package/dist/components/p-DKt_8s-U.js +2 -0
- package/dist/components/p-DKt_8s-U.js.map +1 -0
- package/dist/components/p-DOUk4KoT.js.map +1 -1
- package/dist/components/p-DQBT1Jkf.js +2 -0
- package/dist/components/p-DQBT1Jkf.js.map +1 -0
- package/dist/components/p-DW5Ita-P.js +2 -0
- package/dist/components/{p-DUdOmSFR.js.map → p-DW5Ita-P.js.map} +1 -1
- package/dist/components/{p-DNpLDbWY.js → p-DZ58Sg5a.js} +1 -1
- package/dist/components/p-DZ58Sg5a.js.map +1 -0
- package/dist/components/{p-DDgeXGbB.js → p-DZv2g9Pe.js} +2 -2
- package/dist/components/p-DZv2g9Pe.js.map +1 -0
- package/dist/components/{p-BknXYAEc.js → p-DdH7b4JF.js} +2 -2
- package/dist/components/p-DdH7b4JF.js.map +1 -0
- package/dist/components/{p-DBue3ISk.js → p-Demfba7T.js} +2 -2
- package/dist/components/p-Demfba7T.js.map +1 -0
- package/dist/components/p-Djxw2FjZ.js +2 -0
- package/dist/components/{p-CwjVgUqy.js.map → p-Djxw2FjZ.js.map} +1 -1
- package/dist/components/p-DmWoFSGs.js +2 -0
- package/dist/components/p-DmWoFSGs.js.map +1 -0
- package/dist/components/p-Do8wbFff.js +2 -0
- package/dist/components/p-Do8wbFff.js.map +1 -0
- package/dist/components/p-Drnyq8z7.js +2 -0
- package/dist/components/p-Drnyq8z7.js.map +1 -0
- package/dist/components/p-DsgEY6mZ.js +2 -0
- package/dist/components/p-DsgEY6mZ.js.map +1 -0
- package/dist/components/p-Is1lFmwp.js +2 -0
- package/dist/components/p-Is1lFmwp.js.map +1 -0
- package/dist/components/{p-BUDAwJVV.js → p-caLOcQUK.js} +2 -2
- package/dist/components/p-caLOcQUK.js.map +1 -0
- package/dist/components/{p-D1eEgwcP.js → p-etg-Uk1u.js} +1 -1
- package/dist/components/{p-D1eEgwcP.js.map → p-etg-Uk1u.js.map} +1 -1
- package/dist/components/{p-C9gC4haA.js → p-fRDzpfi9.js} +2 -2
- package/dist/components/p-fRDzpfi9.js.map +1 -0
- package/dist/components/{p-C-vRkDyn.js → p-rCeztgVn.js} +1 -1
- package/dist/components/{p-C-vRkDyn.js.map → p-rCeztgVn.js.map} +1 -1
- package/dist/components/{p-B_jZIC9R.js → p-vrotWZxt.js} +2 -2
- package/dist/components/{p-B_jZIC9R.js.map → p-vrotWZxt.js.map} +1 -1
- package/dist/docs/components.json +35 -6
- package/dist/lib/shell/index.d.ts +2 -2
- package/dist/lib/shell/index.js +8 -6
- package/dist/lib/shell/index.js.map +2 -2
- package/dist/lib/toast/index.js +1 -4
- package/dist/lib/toast/index.js.map +2 -2
- package/dist/lib/utils/index.d.ts +1 -1
- package/dist/lib/utils/index.js +104 -22
- package/dist/lib/utils/index.js.map +2 -2
- package/dist/mcp/cli.js +70 -31
- package/dist/mcp-data/registry/agent-activity.json +1 -1
- package/dist/mcp-data/registry/agent-questionnaire.json +3 -3
- package/dist/mcp-data/registry/agent-source-list.json +2 -2
- package/dist/mcp-data/registry/agent-tool-call.json +2 -2
- package/dist/mcp-data/registry/attachment-list.json +1 -1
- package/dist/mcp-data/registry/badge.json +1 -1
- package/dist/mcp-data/registry/banner.json +2 -2
- package/dist/mcp-data/registry/bar-action.json +1 -1
- package/dist/mcp-data/registry/bar-nav.json +3 -3
- package/dist/mcp-data/registry/bar-workflow.json +1 -1
- package/dist/mcp-data/registry/breadcrumb.json +1 -1
- package/dist/mcp-data/registry/button-filled.json +1 -1
- package/dist/mcp-data/registry/button-unfilled.json +1 -1
- package/dist/mcp-data/registry/card-chart.json +2 -2
- package/dist/mcp-data/registry/card-overview.json +3 -3
- package/dist/mcp-data/registry/card-setting.json +1 -1
- package/dist/mcp-data/registry/chart-legend.json +1 -1
- package/dist/mcp-data/registry/chart.json +1 -1
- package/dist/mcp-data/registry/checkbox.json +1 -1
- package/dist/mcp-data/registry/chip.json +1 -1
- package/dist/mcp-data/registry/code-block.json +1 -1
- package/dist/mcp-data/registry/conversation-list-item.json +1 -1
- package/dist/mcp-data/registry/conversation-list.json +2 -2
- package/dist/mcp-data/registry/divider.json +1 -1
- package/dist/mcp-data/registry/empty-state.json +1 -1
- package/dist/mcp-data/registry/field.json +1 -1
- package/dist/mcp-data/registry/filter-menu.json +1 -1
- package/dist/mcp-data/registry/icon.json +2 -2
- package/dist/mcp-data/registry/input.json +2 -2
- package/dist/mcp-data/registry/loader.json +1 -1
- package/dist/mcp-data/registry/menu.json +3 -3
- package/dist/mcp-data/registry/message-bubble.json +1 -1
- package/dist/mcp-data/registry/message-composer.json +2 -2
- package/dist/mcp-data/registry/message-scroller.json +3 -3
- package/dist/mcp-data/registry/mobile-bar-nav.json +1 -1
- package/dist/mcp-data/registry/mobile-header.json +2 -2
- package/dist/mcp-data/registry/mobile-section-switcher.json +2 -2
- package/dist/mcp-data/registry/mobile-sheet-nav.json +1 -1
- package/dist/mcp-data/registry/modal.json +2 -2
- package/dist/mcp-data/registry/pagination.json +32 -11
- package/dist/mcp-data/registry/panel-nav.json +2 -2
- package/dist/mcp-data/registry/panel-sub-nav.json +1 -1
- package/dist/mcp-data/registry/panel-tool-header.json +1 -1
- package/dist/mcp-data/registry/panel-tool-search.json +1 -1
- package/dist/mcp-data/registry/panel-tools.json +2 -2
- package/dist/mcp-data/registry/radio.json +1 -1
- package/dist/mcp-data/registry/registry.json +15 -8
- package/dist/mcp-data/registry/select.json +1 -1
- package/dist/mcp-data/registry/shell-app.json +2 -2
- package/dist/mcp-data/registry/shell-page.json +1 -1
- package/dist/mcp-data/registry/shell-tools.json +1 -1
- package/dist/mcp-data/registry/skeleton.json +2 -2
- package/dist/mcp-data/registry/slider.json +2 -2
- package/dist/mcp-data/registry/swatch-picker.json +3 -3
- package/dist/mcp-data/registry/switch.json +2 -2
- package/dist/mcp-data/registry/tab-group.json +2 -2
- package/dist/mcp-data/registry/table-sort.json +1 -1
- package/dist/mcp-data/registry/table.json +18 -18
- package/dist/mcp-data/registry/tag.json +2 -2
- package/dist/mcp-data/registry/text.json +4 -4
- package/dist/mcp-data/registry/toast.json +2 -2
- package/dist/mcp-data/registry/tooltip-chart.json +2 -2
- package/dist/mcp-data/registry/tooltip.json +2 -2
- package/dist/styles/prose.css +42 -38
- package/dist/styles/table.css +39 -67
- package/dist/types/components/CardSetting/index.d.ts +1 -1
- package/dist/types/components/Pagination/Pagination.d.ts +2 -0
- package/dist/types/components/Pagination/pagination-types.d.ts +2 -0
- package/dist/types/components/TooltipChart/index.d.ts +1 -1
- package/dist/types/components.d.ts +11 -0
- package/dist/types/shell/index.d.ts +2 -2
- package/dist/types/utils/index.d.ts +1 -1
- package/dist/vue/ds-pagination.js +1 -0
- package/package.json +11 -1
- package/dist/components/p-B00PEE6t.js +0 -2
- package/dist/components/p-B00PEE6t.js.map +0 -1
- package/dist/components/p-BCOl1x0_.js +0 -2
- package/dist/components/p-BCOl1x0_.js.map +0 -1
- package/dist/components/p-BHVOiWz5.js +0 -2
- package/dist/components/p-BHVOiWz5.js.map +0 -1
- package/dist/components/p-BKp__CQX.js.map +0 -1
- package/dist/components/p-BMc0DE59.js.map +0 -1
- package/dist/components/p-BPG0r_mC.js +0 -2
- package/dist/components/p-BPG0r_mC.js.map +0 -1
- package/dist/components/p-BUDAwJVV.js.map +0 -1
- package/dist/components/p-BXN-e7vF.js +0 -2
- package/dist/components/p-Bbb9A3NV.js.map +0 -1
- package/dist/components/p-Bece5KH3.js +0 -2
- package/dist/components/p-Bece5KH3.js.map +0 -1
- package/dist/components/p-BhYS_Ump.js +0 -2
- package/dist/components/p-BhYS_Ump.js.map +0 -1
- package/dist/components/p-Bkh7scMQ.js +0 -2
- package/dist/components/p-Bkh7scMQ.js.map +0 -1
- package/dist/components/p-BknXYAEc.js.map +0 -1
- package/dist/components/p-C-Y7gYgz.js.map +0 -1
- package/dist/components/p-C6DNhlES.js.map +0 -1
- package/dist/components/p-C6R5Lft0.js.map +0 -1
- package/dist/components/p-C9gC4haA.js.map +0 -1
- package/dist/components/p-CAnnQL1F.js +0 -2
- package/dist/components/p-CAnnQL1F.js.map +0 -1
- package/dist/components/p-CE2IKOsy.js +0 -2
- package/dist/components/p-CE2IKOsy.js.map +0 -1
- package/dist/components/p-CJq3n5kA.js.map +0 -1
- package/dist/components/p-CKVHrOEp.js.map +0 -1
- package/dist/components/p-CLVxgTme.js.map +0 -1
- package/dist/components/p-CY4YFAT3.js.map +0 -1
- package/dist/components/p-Cg0kimXZ.js +0 -2
- package/dist/components/p-Cg0kimXZ.js.map +0 -1
- package/dist/components/p-CqQFowZd.js.map +0 -1
- package/dist/components/p-CsBICYmt.js.map +0 -1
- package/dist/components/p-CwjVgUqy.js +0 -2
- package/dist/components/p-D123Jvp-.js +0 -2
- package/dist/components/p-D123Jvp-.js.map +0 -1
- package/dist/components/p-D3fMaUJ3.js.map +0 -1
- package/dist/components/p-D7R4yE2C.js +0 -2
- package/dist/components/p-D7R4yE2C.js.map +0 -1
- package/dist/components/p-D7haeL5y.js +0 -2
- package/dist/components/p-D7haeL5y.js.map +0 -1
- package/dist/components/p-DAvh38E4.js +0 -2
- package/dist/components/p-DAvh38E4.js.map +0 -1
- package/dist/components/p-DBue3ISk.js.map +0 -1
- package/dist/components/p-DDgeXGbB.js.map +0 -1
- package/dist/components/p-DFQ9mcbK.js.map +0 -1
- package/dist/components/p-DNpLDbWY.js.map +0 -1
- package/dist/components/p-DUdOmSFR.js +0 -2
- package/dist/components/p-DtndC9f1.js +0 -2
- package/dist/components/p-H5T258T-.js.map +0 -1
- package/dist/components/p-JPTkZ3Ji.js +0 -2
- package/dist/components/p-LK-jpxwq.js +0 -2
- package/dist/components/p-LK-jpxwq.js.map +0 -1
- package/dist/components/p-ag6Io-kL.js +0 -2
- package/dist/components/p-ag6Io-kL.js.map +0 -1
- package/dist/components/p-iMuFgSoM.js.map +0 -1
- /package/dist/components/{p-uI6z53BE.js.map → p-Bb4KnRy0.js.map} +0 -0
- /package/dist/components/{p-1Hu9jlju.js.map → p-Bf7PZJPT.js.map} +0 -0
|
@@ -388,7 +388,7 @@
|
|
|
388
388
|
},
|
|
389
389
|
{
|
|
390
390
|
"path": "src/wc/components/Radio/Radio.tsx",
|
|
391
|
-
"content": "import {
|
|
391
|
+
"content": "import {\n AttachInternals,\n Component,\n Prop,\n State,\n Event,\n EventEmitter,\n Element,\n Listen,\n Method,\n Watch,\n h,\n Host,\n} from '@stencil/core';\nimport {\n CONTROL_SUPPORTING_TEXT_VARIANT,\n CONTROL_TEXT_VARIANT,\n DEFAULT_REQUIRED_MESSAGE,\n restoreStringFormState,\n setFormControlValue,\n setRequiredValidity,\n} from '../../utils';\n\nexport interface RadioOption {\n label: string;\n value: string;\n description?: string;\n isInactive?: boolean;\n}\n\nexport type RadioSize = 'lg' | 'md' | 'sm' | 'xs';\n\nlet radioIdCounter = 0;\n\n@Component({\n tag: 'ds-radio',\n styleUrl: 'Radio.css',\n scoped: true,\n formAssociated: true,\n})\nexport class Radio {\n @Element() el!: HTMLElement;\n @AttachInternals() internals!: ElementInternals;\n\n private readonly instanceId = ++radioIdCounter;\n\n /** Visible choices in this one-of-many set. */\n @Prop() options: RadioOption[] = [];\n /** Selected option value. */\n @Prop({ mutable: true }) value: string = '';\n /** Visual and placement density for every option. */\n @Prop() size: RadioSize = 'md';\n /** Native form field name. */\n @Prop({ reflect: true }) name: string | undefined;\n /** Associates the radio set with a form by id when rendered outside that form. */\n @Prop({ reflect: true }) form: string | undefined;\n /** Native disabled state for the complete set. */\n @Prop({ reflect: true }) disabled: boolean = false;\n /** Require one option to be selected for form validity. */\n @Prop({ reflect: true }) required: boolean = false;\n /** Validation message used when a required set has no selection. */\n @Prop() requiredMessage: string = DEFAULT_REQUIRED_MESSAGE;\n /** Layout direction for the option set. */\n @Prop() direction: 'vertical' | 'horizontal' = 'vertical';\n /** Design-system inactive state for the complete set. */\n @Prop() isInactive: boolean = false;\n /** Accessible name when visible group labeling is unavailable. */\n @Prop({ attribute: 'aria-label' }) ariaLabel: string | null = null;\n /** Id reference for a visible group label. */\n @Prop({ attribute: 'aria-labelledby' }) ariaLabelledby: string | undefined;\n\n /** Emitted after user selection with the selected option value. */\n @Event() dsChange!: EventEmitter<string>;\n\n private initialValue = '';\n @State() private formDisabled = false;\n\n componentWillLoad() {\n this.initialValue = this.value;\n this.syncFormValue();\n }\n\n @Watch('value')\n @Watch('disabled')\n @Watch('isInactive')\n @Watch('required')\n syncFormValue() {\n const inactive = this.isInactive || this.disabled || this.formDisabled;\n setFormControlValue(this.internals, this.value, { inactive });\n const missing = this.required && !inactive && this.value.length === 0;\n setRequiredValidity(this.internals, missing, this.requiredMessage);\n }\n\n formDisabledCallback(disabled: boolean) {\n this.formDisabled = disabled;\n this.syncFormValue();\n }\n\n formResetCallback() {\n this.value = this.initialValue;\n }\n\n formStateRestoreCallback(state: string | File | FormData | null) {\n this.value = restoreStringFormState(state);\n }\n\n /** Focus the selected option, or the first active option when nothing is selected. */\n @Method()\n async setFocus() {\n this.activeItems.find(item => item.tabIndex === 0)?.focus();\n }\n\n private get activeItems(): HTMLElement[] {\n return Array.from(\n this.el.querySelectorAll<HTMLElement>('[data-radio-item]:not([data-inactive])')\n );\n }\n\n @Listen('keydown')\n handleKeyDown(e: KeyboardEvent) {\n const items = this.activeItems;\n if (!items.length) return;\n\n const focused = items.find(item => item === document.activeElement);\n if (!focused) return;\n\n const idx = items.indexOf(focused);\n let next = -1;\n\n switch (e.key) {\n case 'ArrowDown':\n case 'ArrowRight':\n e.preventDefault();\n next = (idx + 1) % items.length;\n break;\n case 'ArrowUp':\n case 'ArrowLeft':\n e.preventDefault();\n next = (idx - 1 + items.length) % items.length;\n break;\n case 'Home':\n e.preventDefault();\n next = 0;\n break;\n case 'End':\n e.preventDefault();\n next = items.length - 1;\n break;\n }\n\n if (next >= 0) {\n items[next].focus();\n const nextValue = items[next].dataset['value'];\n if (nextValue && nextValue !== this.value) {\n this.value = nextValue;\n this.dsChange.emit(nextValue);\n }\n }\n }\n\n private selectItem(optValue: string) {\n if (this.isInactive || this.disabled || this.formDisabled) return;\n if (optValue !== this.value) {\n this.value = optValue;\n this.dsChange.emit(optValue);\n }\n }\n\n render() {\n const inactive = this.isInactive || this.disabled || this.formDisabled;\n const invalid = this.required && !inactive && this.value.length === 0;\n const selectedIdx = this.options.findIndex(option => option.value === this.value);\n const firstActiveIdx = this.options.findIndex(option => !inactive && !option.isInactive);\n const focusableIdx =\n selectedIdx >= 0 && !inactive && !this.options[selectedIdx]?.isInactive\n ? selectedIdx\n : firstActiveIdx;\n\n return (\n <Host\n role=\"radiogroup\"\n aria-label={this.ariaLabel}\n aria-labelledby={this.ariaLabelledby}\n aria-required={this.required ? 'true' : undefined}\n aria-invalid={invalid ? 'true' : undefined}\n class={{\n radio: true,\n 'radio--horizontal': this.direction === 'horizontal',\n [`radio--${this.size}`]: true,\n }}\n >\n {this.options.map((option, index) => {\n const isItemInactive = inactive || !!option.isInactive;\n const isChecked = option.value === this.value;\n const tabIdx = isItemInactive ? -1 : index === focusableIdx ? 0 : -1;\n const labelId = `ds-radio-${this.instanceId}-label-${index}`;\n const descriptionId = `ds-radio-${this.instanceId}-description-${index}`;\n\n return (\n <div\n key={option.value}\n role=\"radio\"\n aria-checked={String(isChecked)}\n aria-disabled={isItemInactive ? 'true' : undefined}\n aria-labelledby={labelId}\n aria-describedby={option.description ? descriptionId : undefined}\n tabIndex={tabIdx}\n data-radio-item\n data-value={option.value}\n data-inactive={isItemInactive || undefined}\n class={{\n radio__item: true,\n 'radio__item--described': Boolean(option.description),\n [`ds-control--${this.size}`]: true,\n 'ds-control-inactive': isItemInactive,\n 'ds-focus-ring-inset': !isItemInactive,\n 'ds-interaction-fill': !isItemInactive,\n }}\n onClick={() => !isItemInactive && this.selectItem(option.value)}\n onKeyDown={(e: KeyboardEvent) => {\n if ((e.key === ' ' || e.key === 'Enter') && !isItemInactive) {\n e.preventDefault();\n this.selectItem(option.value);\n }\n }}\n >\n <span class=\"radio__placement ds-interaction-fill__content\" aria-hidden=\"true\">\n <span class={{ radio__circle: true, 'radio__circle--checked': isChecked }}>\n {isChecked && <span class=\"radio__dot\" />}\n </span>\n </span>\n <span class=\"radio__copy ds-interaction-fill__content\">\n <ds-text\n class=\"radio__label\"\n as=\"span\"\n variant={CONTROL_TEXT_VARIANT[this.size]}\n textId={labelId}\n >\n {option.label}\n </ds-text>\n {option.description ? (\n <ds-text\n as=\"span\"\n variant={CONTROL_SUPPORTING_TEXT_VARIANT[this.size]}\n color=\"secondary\"\n textId={descriptionId}\n >\n {option.description}\n </ds-text>\n ) : null}\n </span>\n </div>\n );\n })}\n </Host>\n );\n }\n}\n",
|
|
392
392
|
"type": "registry:ui"
|
|
393
393
|
}
|
|
394
394
|
]
|
|
@@ -6066,7 +6066,7 @@
|
|
|
6066
6066
|
{
|
|
6067
6067
|
"name": "pagination",
|
|
6068
6068
|
"title": "Pagination",
|
|
6069
|
-
"description": "Controlled fixed or fitted page-size, compact range, and first/
|
|
6069
|
+
"description": "Controlled fixed or fitted page-size, compact range, adjacent-page navigation, and optional first/last controls for application-owned data windows.",
|
|
6070
6070
|
"type": "registry:ui",
|
|
6071
6071
|
"meta": {
|
|
6072
6072
|
"library": "@ds-mo/ui",
|
|
@@ -6077,14 +6077,15 @@
|
|
|
6077
6077
|
"intent": {
|
|
6078
6078
|
"audience": "general",
|
|
6079
6079
|
"status": "stable",
|
|
6080
|
-
"summary": "Controlled fixed or fitted page-size, compact range, and first/
|
|
6080
|
+
"summary": "Controlled fixed or fitted page-size, compact range, adjacent-page navigation, and optional first/last controls for application-owned data windows.",
|
|
6081
6081
|
"useWhen": [
|
|
6082
6082
|
"A result set is divided into discrete pages and the application owns fetching or slicing each page.",
|
|
6083
|
-
"A table or list needs a compact page-size selector, result range, and boundary-aware navigation."
|
|
6083
|
+
"A table or list needs a compact page-size selector, result range, and boundary-aware navigation.",
|
|
6084
|
+
"Direct first-page and last-page navigation is useful enough to opt into alongside the default Previous and Next controls."
|
|
6084
6085
|
],
|
|
6085
6086
|
"avoidWhen": [
|
|
6086
6087
|
"More results append to one continuous list; use the owning component's incremental-loading behavior instead.",
|
|
6087
|
-
"
|
|
6088
|
+
"The total result count is unknown; use an incremental-loading pattern instead."
|
|
6088
6089
|
],
|
|
6089
6090
|
"commonlyComposedWith": [
|
|
6090
6091
|
"component:ds-table",
|
|
@@ -6095,18 +6096,20 @@
|
|
|
6095
6096
|
"The component renders a named navigation region, gives its compact page-size noun a complete accessible label, and gives every icon-only boundary control an explicit page action name.",
|
|
6096
6097
|
"The visible page-size cluster states the localized noun, selected size, and current-page count of total, for example Rows: [25] 25 of 500. The page position uses compact ‘of’ phrasing, for example 1 of 20. A polite atomic status update retains the complete current range and ‘Page’ phrasing with the localized item noun.",
|
|
6097
6098
|
"Left and Right Arrow request the adjacent page while focus is within pagination navigation controls. The page-size combobox retains its own arrow-key behavior.",
|
|
6098
|
-
"Inactive boundary controls and every control during loading remain non-interactive while retaining their accessible names."
|
|
6099
|
+
"Inactive boundary controls and every control during loading remain non-interactive while retaining their accessible names.",
|
|
6100
|
+
"showFirstLastButtons adds explicitly named First page and Last page controls; Previous and Next remain present in either mode."
|
|
6099
6101
|
],
|
|
6100
6102
|
"states": [
|
|
6101
6103
|
"pageIndex, numeric effective pageSize, pageSizeMode, totalItems, and pageSizeOptions are controlled inputs. dsChange reports the complete next pagination state, previous index, size, and mode, and whether the request changed the page, page size, or fitted capacity; it never mutates controlled inputs.",
|
|
6102
6104
|
"Page-size changes request the first page. Out-of-range page indexes and invalid numeric inputs normalize only for presentation; the owner remains authoritative.",
|
|
6103
6105
|
"Zero results display 0–0 of 0 and Page 1 of 1. A partial final page caps the visible range at totalItems.",
|
|
6104
6106
|
"loading preserves the full pagination surface, applies aria-busy to the navigation region, and prevents page or page-size requests.",
|
|
6105
|
-
"fitToPage appends a Fit to page choice. fitPageSize supplies its effective whole-item capacity; the closed trigger says Fit while the range continues to expose the resulting count. fitToPageInactive keeps that choice visible but unavailable when fitting is not meaningful for the current data shape. Pagination does not measure layouts itself."
|
|
6107
|
+
"fitToPage appends a Fit to page choice. fitPageSize supplies its effective whole-item capacity; the closed trigger says Fit while the range continues to expose the resulting count. fitToPageInactive keeps that choice visible but unavailable when fitting is not meaningful for the current data shape. Pagination does not measure layouts itself.",
|
|
6108
|
+
"showFirstLastButtons defaults to false. Enable it when direct jumps to both result boundaries materially help the workflow."
|
|
6106
6109
|
],
|
|
6107
6110
|
"responsiveBehavior": [
|
|
6108
|
-
"The full-width controls remain on one line with noun, selected size, current-page count of total, a vertical divider, page position, and
|
|
6109
|
-
"Inside a ds-table container narrower than 900px, pagination hides the visible current-page count and first/last
|
|
6111
|
+
"The full-width controls remain on one line with noun, selected size, current-page count of total, a vertical divider, page position, Previous and Next, plus optional First and Last navigation.",
|
|
6112
|
+
"Inside a ds-table container narrower than 900px, pagination hides the visible current-page count and any opted-in first/last controls while retaining the page-size noun and selector plus Previous, page position, and Next. The complete current range remains available to assistive technology."
|
|
6110
6113
|
],
|
|
6111
6114
|
"frameworkCaveats": {
|
|
6112
6115
|
"customElements": [
|
|
@@ -6183,6 +6186,10 @@
|
|
|
6183
6186
|
"type": "number[]",
|
|
6184
6187
|
"required": false
|
|
6185
6188
|
},
|
|
6189
|
+
"showFirstLastButtons": {
|
|
6190
|
+
"type": "boolean",
|
|
6191
|
+
"required": false
|
|
6192
|
+
},
|
|
6186
6193
|
"totalItems": {
|
|
6187
6194
|
"type": "number",
|
|
6188
6195
|
"required": false
|
|
@@ -971,7 +971,7 @@
|
|
|
971
971
|
},
|
|
972
972
|
{
|
|
973
973
|
"path": "src/wc/components/Select/Select.tsx",
|
|
974
|
-
"content": "import {\n AttachInternals,\n Component,\n Element,\n Event,\n EventEmitter,\n h,\n Host,\n Method,\n Prop,\n State,\n type VNode,\n Watch,\n} from '@stencil/core';\nimport {\n controlWidthClass,\n CONTROL_SUPPORTING_TEXT_VARIANT,\n CONTROL_TEXT_VARIANT,\n DEFAULT_REQUIRED_MESSAGE,\n restoreStringArrayFormState,\n restoreStringFormState,\n setFormControlValue,\n setRepeatedFormControlValue,\n setRequiredValidity,\n type ControlWidth,\n} from '../../utils';\nimport {\n ChoiceActionFooter,\n ChoiceFooter,\n ChoiceOptionRow,\n ChoiceSearch,\n} from '../../utils/choice-list-parts';\nimport {\n choiceBackgroundClassMap,\n choiceListUsesIcons,\n choiceListUsesSubtext,\n enabledChoiceIndexes,\n filterChoiceSections,\n flattenChoiceSections,\n resolveChoiceSections,\n type ChoiceBackground,\n type ChoiceOption,\n type ChoiceSection,\n} from '../../utils/choice-list';\nimport { SelectController } from '../../utils/select-controller';\nimport { observeTableCaptionCompact } from '../../utils/table-caption-compact';\n\nexport type SelectOption = ChoiceOption;\nexport type SelectSection = ChoiceSection;\nexport type SelectBackground = ChoiceBackground;\nexport type SelectSize = 'lg' | 'md' | 'sm' | 'xs';\nexport type SelectWidth = ControlWidth;\nexport type SelectPopupAlign = 'start' | 'end';\nexport type SelectValue = string | string[];\nexport type SelectIndicator = 'down' | 'up-down';\n\nexport interface SelectOptionActionDetail {\n value: string;\n anchorId: string;\n originalEvent: MouseEvent;\n}\n\nexport interface SelectOptionSubtextActionDetail {\n value: string;\n actionValue: string;\n originalEvent: MouseEvent;\n}\n\nconst ICON_SIZE: Record<SelectSize, 'lg' | 'md' | 'sm' | 'xs'> = {\n lg: 'lg',\n md: 'md',\n sm: 'sm',\n xs: 'xs',\n};\n\nconst OPTION_ACTION_SIZE: Record<SelectSize, 'md' | 'sm' | 'xs'> = {\n lg: 'md',\n md: 'sm',\n sm: 'xs',\n xs: 'xs',\n};\n\nlet selectId = 0;\n\n@Component({\n tag: 'ds-select',\n styleUrl: 'Select.css',\n scoped: true,\n formAssociated: true,\n})\nexport class Select {\n @Element() el!: HTMLElement;\n @AttachInternals() internals!: ElementInternals;\n\n /** Flat choices. Assign arrays through the JavaScript property. */\n @Prop() options: SelectOption[] = [];\n /** Grouped choices; takes precedence over options. Assign through JavaScript. */\n @Prop() sections: SelectSection[] = [];\n /** Enable independent multi-value selection while keeping the popup open. */\n @Prop({ reflect: true }) multiple: boolean = false;\n /** Selected scalar or array value according to `multiple`. */\n @Prop({ mutable: true }) value: SelectValue = '';\n /** Controlled popup visibility. */\n @Prop({ mutable: true, reflect: true }) open: boolean = false;\n /** Native form field name. */\n @Prop({ reflect: true }) name: string | undefined;\n /** Native disabled state. */\n @Prop({ reflect: true }) disabled: boolean = false;\n /** Require one valid selected value. */\n @Prop({ reflect: true }) required: boolean = false;\n /** Validation message used when required is missing. */\n @Prop() requiredMessage: string = DEFAULT_REQUIRED_MESSAGE;\n /** Trigger text shown when no valid value is selected. */\n @Prop() placeholder: string = 'Select';\n /** Optional scalar-mode trigger text that does not change the selected option label. */\n @Prop() triggerLabel: string | undefined;\n /** Present triggerLabel with placeholder emphasis while preserving the selected value. */\n @Prop() triggerLabelPlaceholder: boolean = false;\n /** Show a supplemental notification dot beside the trigger label. */\n @Prop() dot: boolean = false;\n /** Control density. */\n @Prop() size: SelectSize = 'md';\n /** Width fit — hug content (default) or fill the parent. */\n @Prop() width: SelectWidth = 'hug';\n /** Align the popup's choice edge to the trigger start or end edge. */\n @Prop() popupAlign: SelectPopupAlign = 'start';\n /** Explicit collision owner; otherwise the nearest data-ds-overlay-boundary ancestor is used. */\n @Prop() boundary: HTMLElement | undefined;\n /** Shared inactive treatment; removes interaction and form submission. */\n @Prop() isInactive: boolean = false;\n /** Replace the prefix with a loader and disable option interaction. */\n @Prop() isLoading: boolean = false;\n /** Show the selected interaction fill when a valid value exists. */\n @Prop() activeFill: boolean = true;\n /**\n * Opt into table-caption icon-only chrome below 900px. The trigger omits its\n * visible label and chevron; keep an accessible name via aria-label.\n */\n @Prop({ reflect: true }) collapseLabel: boolean = false;\n /** Show the surface-aware inset border, including focused and invalid strokes. */\n @Prop() hasBorder: boolean = true;\n /** Optional trigger prefix icon name. */\n @Prop() icon: string | undefined;\n /** Trailing choice indicator. Use up-down for compact value steppers such as page size. */\n @Prop() indicator: SelectIndicator = 'down';\n /** Show the clear footer action when a value exists. */\n @Prop() allowClear: boolean = true;\n /** Localized clear action label. */\n @Prop() clearLabel: string = 'Clear';\n /** Localized noun displayed after the selected count in multiple mode. */\n @Prop() selectedLabel: string = 'selected';\n /** Optional text action shown in the popup footer instead of the clear action. */\n @Prop() footerActionLabel: string | undefined;\n /** Show immediate local filtering over option labels, subtext, and section headings. */\n @Prop() searchable: boolean = false;\n /** Localized search-field placeholder and accessible name. */\n @Prop() searchPlaceholder: string = 'Search';\n /** Localized empty-filter result text. */\n @Prop() noResultsText: string = 'No results found';\n /** Accessible loading status label. */\n @Prop() loadingLabel: string = 'Loading';\n /** Actual parent surface context; omit on primary and secondary surfaces. */\n @Prop() background: SelectBackground | undefined;\n /** Show invalid visual state. */\n @Prop() error: boolean = false;\n /** Error text rendered below the trigger when error is true. */\n @Prop() errorMessage: string | undefined;\n /** ID applied to the internal combobox trigger for external labels. */\n @Prop() inputId: string | undefined;\n /** Direct accessible name when no external label is available. */\n @Prop({ attribute: 'aria-label' }) ariaLabel: string | null = null;\n /** IDs of elements that label the combobox. */\n @Prop({ attribute: 'aria-labelledby' }) ariaLabelledby: string | undefined;\n /** Additional IDs that describe the combobox. */\n @Prop({ attribute: 'aria-describedby' }) ariaDescribedby: string | undefined;\n\n /** Emitted after selection or clearing with the next scalar or array value. */\n @Event() dsChange!: EventEmitter<SelectValue>;\n /** Emitted after the footer clear action. */\n @Event() dsClear!: EventEmitter<void>;\n /** Emitted whenever popup visibility changes. */\n @Event() dsOpenChange!: EventEmitter<boolean>;\n /** Emitted when the optional popup footer action is activated. */\n @Event() dsFooterAction!: EventEmitter<void>;\n /** Emitted when an option's contextual ellipsis action is activated. */\n @Event() dsOptionAction!: EventEmitter<SelectOptionActionDetail>;\n /** Emitted when an option's supporting text action is activated. */\n @Event() dsOptionSubtextAction!: EventEmitter<SelectOptionSubtextActionDetail>;\n\n @State() private activeIndex = -1;\n @State() private searchTerm = '';\n @State() private formDisabled = false;\n @State() private position = { x: 0, y: 0 };\n @State() private positionReady = false;\n @State() private focusRingVisible = false;\n @State() private compactFooterSummary = false;\n @State() private captionCompact = false;\n\n private readonly generatedId = `ds-select-${++selectId}`;\n private readonly listboxId = `${this.generatedId}-listbox`;\n private readonly errorId = `${this.generatedId}-error`;\n private initialValue: SelectValue = '';\n private readonly controller = this.createController();\n private footerContentElement?: HTMLDivElement;\n private footerSummaryMeasureElement?: HTMLElement;\n private footerClearElement?: HTMLButtonElement;\n private footerResizeObserver?: ResizeObserver;\n private observedFooterContentElement?: HTMLDivElement;\n private footerMeasurementFrame?: number;\n private captionCompactDisconnect: (() => void) | undefined;\n private hasLoaded = false;\n\n private createController() {\n // eslint-disable-next-line @typescript-eslint/no-this-alias -- adapter getters preserve reactive component ownership without inheritance.\n const owner = this;\n return new SelectController<SelectOption>({\n get host() {\n return owner.el;\n },\n generatedId: owner.generatedId,\n get options() {\n return owner.visibleOptions;\n },\n get searchable() {\n return owner.searchable;\n },\n get isLoading() {\n return owner.isLoading;\n },\n get isDisabled() {\n return owner.isDisabled;\n },\n get preferredIndex() {\n if (owner.multiple) return -1;\n return owner.visibleOptions.findIndex(\n option => option.value === owner.scalarValue && !option.isInactive\n );\n },\n get popupAlign() {\n return owner.popupAlign;\n },\n get boundary() {\n return owner.boundary;\n },\n get open() {\n return owner.open;\n },\n set open(value) {\n owner.open = value;\n },\n get activeIndex() {\n return owner.activeIndex;\n },\n set activeIndex(value) {\n owner.activeIndex = value;\n },\n get searchTerm() {\n return owner.searchTerm;\n },\n set searchTerm(value) {\n owner.searchTerm = value;\n },\n get focusRingVisible() {\n return owner.focusRingVisible;\n },\n set focusRingVisible(value) {\n owner.focusRingVisible = value;\n },\n get position() {\n return owner.position;\n },\n set position(value) {\n owner.position = value;\n },\n get positionReady() {\n return owner.positionReady;\n },\n set positionReady(value) {\n owner.positionReady = value;\n },\n selectOption: option => owner.selectOption(option),\n });\n }\n\n componentWillLoad() {\n this.value = this.normalizeValueForMode(this.value);\n this.initialValue = Array.isArray(this.value) ? [...this.value] : this.value;\n this.syncFormValue();\n }\n\n componentDidLoad() {\n this.hasLoaded = true;\n this.controller.connect();\n this.observeFooterContent();\n this.syncCaptionCompactObserver();\n }\n\n connectedCallback() {\n if (!this.hasLoaded) return;\n this.controller.connect();\n this.observeFooterContent();\n this.syncCaptionCompactObserver();\n }\n\n componentDidRender() {\n this.observeFooterContent();\n this.scheduleFooterSummaryMeasurement();\n }\n\n disconnectedCallback() {\n this.controller.disconnect();\n this.footerResizeObserver?.disconnect();\n this.observedFooterContentElement = undefined;\n if (this.footerMeasurementFrame !== undefined) {\n cancelAnimationFrame(this.footerMeasurementFrame);\n this.footerMeasurementFrame = undefined;\n }\n this.disconnectCaptionCompactObserver();\n }\n\n @Watch('collapseLabel')\n onCollapseLabelChange() {\n this.syncCaptionCompactObserver();\n }\n\n @Watch('value')\n @Watch('multiple')\n @Watch('disabled')\n @Watch('isInactive')\n @Watch('required')\n @Watch('name')\n @Watch('options')\n @Watch('sections')\n syncFormValue() {\n if (\n (this.multiple && !Array.isArray(this.value)) ||\n (!this.multiple && Array.isArray(this.value))\n ) {\n this.value = this.normalizeValueForMode(this.value);\n }\n const inactive = this.isDisabled;\n if (this.multiple) {\n const values = this.resolvedValues;\n setRepeatedFormControlValue(this.internals, this.name, values, { inactive });\n } else {\n const value = this.hasSelection ? this.scalarValue : '';\n setFormControlValue(this.internals, value, { inactive });\n }\n const missing = this.required && !inactive && !this.hasSelection;\n setRequiredValidity(this.internals, missing, this.requiredMessage);\n this.controller.optionsChanged();\n }\n\n @Watch('isLoading')\n onLoadingChange() {\n this.controller.loadingChanged();\n }\n\n @Watch('popupAlign')\n @Watch('boundary')\n onPopupAlignChange() {\n this.controller.positionChanged();\n }\n\n @Watch('open')\n onOpenChange(open: boolean) {\n this.dsOpenChange.emit(open);\n this.controller.openChanged(open);\n }\n\n @Watch('searchTerm')\n onSearchTermChange() {\n this.controller.searchChanged();\n }\n\n @Watch('activeIndex')\n onActiveIndexChange() {\n this.controller.activeIndexChanged();\n }\n\n formDisabledCallback(disabled: boolean) {\n this.formDisabled = disabled;\n this.syncFormValue();\n }\n\n formResetCallback() {\n this.value = this.normalizeValueForMode(this.initialValue);\n this.closePopup();\n }\n\n formStateRestoreCallback(state: string | File | FormData | null) {\n if (!this.multiple) {\n this.value = restoreStringFormState(state);\n return;\n }\n this.value = restoreStringArrayFormState(state);\n }\n\n @Method()\n async setFocus() {\n this.controller.setFocus();\n }\n\n private get isDisabled(): boolean {\n return this.disabled || this.isInactive || this.formDisabled;\n }\n\n private normalizeValueForMode(value: SelectValue): SelectValue {\n if (this.multiple) {\n return Array.isArray(value) ? [...value] : value ? [value] : [];\n }\n return Array.isArray(value) ? (value[0] ?? '') : value;\n }\n\n private get allSections(): SelectSection[] {\n return resolveChoiceSections(this.options, this.sections);\n }\n\n private get allOptions(): SelectOption[] {\n return flattenChoiceSections(this.allSections);\n }\n\n private get visibleSections(): SelectSection[] {\n return this.searchable\n ? filterChoiceSections(this.allSections, this.searchTerm)\n : this.allSections;\n }\n\n private get visibleOptions(): SelectOption[] {\n return flattenChoiceSections(this.visibleSections);\n }\n\n private get scalarValue(): string {\n return typeof this.value === 'string' ? this.value : '';\n }\n\n private get resolvedValues(): string[] {\n if (!this.multiple || !Array.isArray(this.value)) return [];\n const valid = new Set(this.allOptions.map(option => option.value));\n return [...new Set(this.value.filter(value => valid.has(value)))];\n }\n\n private get selectedOption(): SelectOption | undefined {\n if (this.multiple) return undefined;\n return this.allOptions.find(option => option.value === this.scalarValue);\n }\n\n private get hasSelection(): boolean {\n return this.multiple ? this.resolvedValues.length > 0 : Boolean(this.selectedOption);\n }\n\n private get activeOptionId(): string | undefined {\n return this.controller.activeOptionId;\n }\n\n private get popupRole(): 'listbox' | 'grid' {\n return this.allOptions.some(option => Boolean(option.action)) ? 'grid' : 'listbox';\n }\n\n private observeFooterContent() {\n const content =\n this.open && this.multiple && this.hasSelection ? this.footerContentElement : undefined;\n if (!content?.isConnected) {\n this.footerResizeObserver?.disconnect();\n this.observedFooterContentElement = undefined;\n return;\n }\n if (this.observedFooterContentElement === content) return;\n\n this.footerResizeObserver?.disconnect();\n if (typeof ResizeObserver !== 'undefined') {\n this.footerResizeObserver = new ResizeObserver(() => this.scheduleFooterSummaryMeasurement());\n this.footerResizeObserver.observe(content);\n }\n this.observedFooterContentElement = content;\n }\n\n private scheduleFooterSummaryMeasurement() {\n if (\n !this.open ||\n !this.multiple ||\n !this.hasSelection ||\n this.footerMeasurementFrame !== undefined\n ) {\n return;\n }\n this.footerMeasurementFrame = requestAnimationFrame(() => {\n this.footerMeasurementFrame = undefined;\n this.measureFooterSummary();\n });\n }\n\n private measureFooterSummary() {\n const content = this.footerContentElement;\n const summary = this.footerSummaryMeasureElement;\n const clear = this.footerClearElement;\n if (!content?.isConnected || !summary?.isConnected || !clear?.isConnected) return;\n\n const styles = getComputedStyle(content);\n const innerWidth =\n content.clientWidth -\n Number.parseFloat(styles.paddingInlineStart || '0') -\n Number.parseFloat(styles.paddingInlineEnd || '0');\n const requiredWidth =\n summary.getBoundingClientRect().width +\n clear.getBoundingClientRect().width +\n Number.parseFloat(styles.columnGap || '0');\n const nextCompact = requiredWidth > innerWidth + 0.5;\n if (nextCompact !== this.compactFooterSummary) {\n this.compactFooterSummary = nextCompact;\n }\n }\n\n private closePopup(restoreFocus = false) {\n this.controller.closePopup(restoreFocus);\n }\n\n private openPopup(focusVisible: boolean, edge?: 'first' | 'last') {\n this.controller.openPopup(focusVisible, edge);\n }\n\n private selectOption(option: SelectOption) {\n if (option.isInactive || this.isLoading) return;\n if (this.multiple) {\n const current = this.resolvedValues;\n const next = current.includes(option.value)\n ? current.filter(value => value !== option.value)\n : [...current, option.value];\n this.value = next;\n this.dsChange.emit([...next]);\n return;\n }\n this.value = option.value;\n this.dsChange.emit(option.value);\n this.controller.closePopup(true);\n }\n\n private clearSelection = (event: Event) => {\n event.preventDefault();\n event.stopPropagation();\n this.value = this.multiple ? [] : '';\n this.dsChange.emit(this.multiple ? [] : '');\n this.dsClear.emit();\n if (!this.multiple) {\n const enabled = enabledChoiceIndexes(this.visibleOptions);\n this.activeIndex = enabled[0] ?? -1;\n }\n this.controller.focusSearchOrTrigger();\n };\n\n private activateFooterAction = (event: MouseEvent) => {\n event.preventDefault();\n event.stopPropagation();\n this.dsFooterAction.emit();\n this.closePopup();\n };\n\n private readonly handleListKeyDown = this.controller.handleListKeyDown;\n private readonly handleTriggerKeyDown = this.controller.handleTriggerKeyDown;\n\n private renderOption(\n option: SelectOption,\n index: number,\n usesIcons: boolean,\n usesSubtext: boolean\n ) {\n const selected = this.multiple\n ? this.resolvedValues.includes(option.value)\n : option.value === this.scalarValue;\n const active = index === this.activeIndex;\n const actionId = `${this.generatedId}-option-action-${index}`;\n return (\n <ChoiceOptionRow\n size={this.size}\n id={`${this.generatedId}-option-${index}`}\n option={option}\n selected={selected}\n active={active}\n focusRingVisible={this.focusRingVisible}\n usesSubtext={usesSubtext}\n actionOpen={option.action?.expanded}\n popupRole={this.popupRole}\n leading={\n this.multiple ? (\n <span\n class=\"ds-choice-item__icon ds-control-icon-box ds-interaction-fill__content\"\n aria-hidden=\"true\"\n >\n <ds-checkbox\n class=\"select-option__checkbox\"\n label=\"\"\n size={this.size}\n checked={selected}\n presentation\n />\n </span>\n ) : usesIcons && option.icon ? (\n <span\n class=\"ds-choice-item__icon ds-control-icon-box ds-interaction-fill__content\"\n aria-hidden=\"true\"\n >\n <ds-icon name={option.icon} size={this.size} color=\"inherit\" />\n </span>\n ) : undefined\n }\n supporting={\n option.subtextActions?.length ? (\n <span class=\"ds-choice-item__subtext ds-control-label-box select-option__subtext-actions\">\n {option.subtextActions.map((action, actionIndex) => [\n actionIndex > 0 ? (\n <ds-text\n class=\"select-option__subtext-action-separator\"\n as=\"span\"\n variant={CONTROL_SUPPORTING_TEXT_VARIANT[this.size]}\n color=\"inherit\"\n aria-hidden=\"true\"\n >\n ·\n </ds-text>\n ) : null,\n <button\n type=\"button\"\n class={{\n 'select-option__subtext-action': true,\n 'select-option__subtext-action--negative': action.tone === 'negative',\n 'ds-text-action': true,\n 'ds-focus-ring': true,\n }}\n onKeyDown={(event: KeyboardEvent) => event.stopPropagation()}\n onClick={(event: MouseEvent) => {\n event.preventDefault();\n event.stopPropagation();\n this.dsOptionSubtextAction.emit({\n value: option.value,\n actionValue: action.value,\n originalEvent: event,\n });\n }}\n >\n <ds-text\n as=\"span\"\n variant={CONTROL_SUPPORTING_TEXT_VARIANT[this.size]}\n color=\"inherit\"\n >\n {action.label}\n </ds-text>\n </button>,\n ])}\n </span>\n ) : undefined\n }\n action={\n option.action ? (\n <ds-tooltip label={option.action.label} side=\"left\" size=\"sm\">\n <ds-button-unfilled\n id={actionId}\n variant=\"icon\"\n size={OPTION_ACTION_SIZE[this.size]}\n icon=\"Ellipses\"\n rounded\n hasBorder={false}\n activeFill={false}\n ariaLabel={option.action.label}\n haspopup=\"menu\"\n controls={option.action.controls}\n expanded={option.action.expanded}\n onKeyDown={event => {\n event.stopPropagation();\n if (event.key === 'Escape' || event.key === 'ArrowLeft') {\n event.preventDefault();\n this.controller.focusSearchOrTrigger();\n }\n }}\n onDsClick={(event: CustomEvent<MouseEvent>) => {\n event.stopPropagation();\n this.dsOptionAction.emit({\n value: option.value,\n anchorId: actionId,\n originalEvent: event.detail,\n });\n }}\n />\n </ds-tooltip>\n ) : undefined\n }\n onHover={() => {\n this.focusRingVisible = false;\n this.activeIndex = index;\n }}\n onSelect={() => this.selectOption(option)}\n />\n );\n }\n\n private get captionIconOnly(): boolean {\n return this.collapseLabel && this.captionCompact && Boolean(this.icon || this.isLoading);\n }\n\n private syncCaptionCompactObserver(): void {\n this.disconnectCaptionCompactObserver();\n if (!this.collapseLabel) {\n if (this.captionCompact) this.captionCompact = false;\n return;\n }\n this.captionCompactDisconnect = observeTableCaptionCompact(this.el, compact => {\n if (this.captionCompact !== compact) this.captionCompact = compact;\n });\n }\n\n private disconnectCaptionCompactObserver(): void {\n this.captionCompactDisconnect?.();\n this.captionCompactDisconnect = undefined;\n }\n\n private renderCaptionTooltip(trigger: VNode, label: string): VNode {\n if (!this.collapseLabel) return trigger;\n return (\n <ds-tooltip label={label} side=\"top\" size=\"sm\">\n {trigger}\n </ds-tooltip>\n );\n }\n\n render() {\n const inactive = this.isDisabled;\n const showPlaceholder = !this.hasSelection;\n const count = this.resolvedValues.length;\n const label = this.multiple\n ? `${this.placeholder}${count > 0 ? ` · ${count}` : ''}`\n : showPlaceholder\n ? this.placeholder\n : (this.triggerLabel ?? this.selectedOption?.label);\n const textVariant = CONTROL_TEXT_VARIANT[this.size];\n const iconSize = ICON_SIZE[this.size];\n const usesOptionIcons = !this.multiple && choiceListUsesIcons(this.allOptions);\n const usesOptionSubtext = choiceListUsesSubtext(this.allOptions);\n const showError = this.error && Boolean(this.errorMessage);\n const describedBy =\n [this.ariaDescribedby, showError ? this.errorId : undefined].filter(Boolean).join(' ') ||\n undefined;\n const popupStyle = {\n position: 'fixed',\n left: '0',\n top: '0',\n transform: `translate(${Math.round(this.position.x)}px, ${Math.round(this.position.y)}px)`,\n zIndex: 'var(--dimension-z-index-floating)',\n visibility: this.positionReady ? 'visible' : 'hidden',\n };\n let flatIndex = 0;\n\n return (\n <Host\n class={{\n 'select-host': true,\n 'ds-select-trigger-host': true,\n 'select-host--multiple': this.multiple,\n 'ds-field-stack': true,\n 'ds-control-inactive': inactive,\n [`ds-control--${this.size}`]: true,\n 'ds-table-caption-control': this.collapseLabel,\n 'ds-table-caption-control--compact': this.captionIconOnly,\n ...controlWidthClass(this.width),\n [`select-host--background-${this.background}`]: !!this.background,\n [`ds-select-trigger-host--background-${this.background}`]: !!this.background,\n }}\n >\n {this.renderCaptionTooltip(\n <button\n ref={element => {\n this.controller.setTriggerElement((element as HTMLButtonElement) ?? null);\n }}\n id={this.inputId ?? this.generatedId}\n type=\"button\"\n class={{\n trigger: true,\n 'ds-control-frame': true,\n 'ds-focus-ring-inset': true,\n 'ds-interaction-fill': true,\n 'ds-interaction-fill--selected': !inactive && this.activeFill && this.hasSelection,\n 'trigger--expanded': !inactive && this.open,\n 'trigger--bordered': this.hasBorder,\n 'trigger--placeholder': showPlaceholder && !this.multiple,\n 'trigger--has-value': this.hasSelection,\n 'trigger--label-placeholder':\n !this.multiple && Boolean(this.triggerLabel) && this.triggerLabelPlaceholder,\n 'wrapper--error': this.hasBorder && this.error,\n [`ds-control--${this.size}`]: true,\n ...choiceBackgroundClassMap(this.background),\n }}\n disabled={inactive}\n role=\"combobox\"\n aria-haspopup={this.popupRole}\n aria-expanded={String(this.open)}\n aria-controls={this.open ? this.listboxId : undefined}\n aria-activedescendant={this.open && !this.searchable ? this.activeOptionId : undefined}\n aria-label={this.ariaLabel ?? (this.captionIconOnly ? label : undefined)}\n aria-labelledby={this.ariaLabelledby}\n aria-describedby={describedBy}\n aria-invalid={this.error ? 'true' : undefined}\n aria-required={this.required || undefined}\n aria-busy={this.isLoading ? 'true' : undefined}\n onClick={() => (this.open ? this.closePopup() : this.openPopup(false))}\n onKeyDown={event => {\n const action = this.visibleOptions[this.activeIndex]?.action;\n if (\n this.open &&\n action &&\n (event.key === 'ArrowRight' || (event.key === 'F10' && event.shiftKey))\n ) {\n event.preventDefault();\n document\n .getElementById(`${this.generatedId}-option-action-${this.activeIndex}`)\n ?.querySelector<HTMLElement>('button')\n ?.focus();\n return;\n }\n this.handleTriggerKeyDown(event);\n }}\n >\n {(this.icon || this.isLoading) && (\n <span\n class=\"trigger__prefix ds-control-icon-box ds-interaction-fill__content\"\n aria-hidden=\"true\"\n >\n {this.isLoading ? (\n <ds-loader size={iconSize} color=\"inherit\" />\n ) : (\n <ds-icon name={this.icon} size={iconSize} color=\"inherit\" />\n )}\n </span>\n )}\n {this.captionIconOnly ? null : (\n <span\n class={{\n 'trigger__label-box': true,\n 'ds-interaction-fill__content': true,\n }}\n >\n <span\n class={{\n 'trigger__label-content': true,\n 'ds-control-label-box': true,\n 'ds-control-label-dot': this.dot,\n 'trigger__label-content--dot': this.dot,\n }}\n >\n <ds-text\n class=\"trigger__label\"\n as=\"span\"\n variant={textVariant}\n color=\"inherit\"\n lineTruncation={1}\n >\n {label}\n </ds-text>\n {this.dot && (\n <ds-badge\n class=\"trigger__dot ds-control-label-dot__badge\"\n variant=\"dot\"\n hasRing={false}\n label=\"\"\n aria-hidden=\"true\"\n />\n )}\n </span>\n </span>\n )}\n {this.captionIconOnly ? null : (\n <span\n class=\"trigger__chevron ds-control-icon-box ds-interaction-fill__content\"\n aria-hidden=\"true\"\n >\n <ds-icon\n name={this.indicator === 'up-down' ? 'ChevronUpDown' : 'ChevronDown'}\n size={iconSize}\n color=\"inherit\"\n />\n </span>\n )}\n </button>,\n this.captionIconOnly ? (this.ariaLabel?.trim() || this.placeholder) : ''\n )}\n\n {this.open && (\n <div\n popover=\"manual\"\n ref={element => {\n this.controller.setPopupElement((element as HTMLDivElement) ?? null);\n }}\n class=\"select-popup ds-choice-popup\"\n style={popupStyle}\n >\n {this.searchable && (\n <ChoiceSearch\n size={this.size}\n value={this.searchTerm}\n placeholder={this.searchPlaceholder}\n controls={this.listboxId}\n activeDescendant={this.activeOptionId}\n inputRef={this.controller.setSearchElement}\n clearLabel={this.clearLabel}\n onValueChange={value => {\n this.searchTerm = value;\n }}\n onKeyDown={event => this.handleListKeyDown(event)}\n />\n )}\n <div\n id={this.listboxId}\n class=\"ds-choice-list\"\n role={this.popupRole}\n aria-multiselectable={this.multiple ? 'true' : undefined}\n aria-label={this.ariaLabel ?? this.placeholder}\n aria-busy={this.isLoading ? 'true' : undefined}\n >\n {this.isLoading ? (\n <div\n class=\"ds-choice-loading ds-empty-region\"\n role={this.popupRole === 'grid' ? 'row' : 'option'}\n aria-selected=\"false\"\n aria-disabled=\"true\"\n aria-label={this.loadingLabel}\n aria-live=\"polite\"\n >\n {this.popupRole === 'grid' ? (\n <span role=\"gridcell\">\n <ds-loader size={this.size} color=\"inherit\" />\n </span>\n ) : (\n <ds-loader size={this.size} color=\"inherit\" />\n )}\n </div>\n ) : this.visibleOptions.length === 0 ? (\n <div\n class=\"ds-choice-empty ds-empty-region\"\n role={this.popupRole === 'grid' ? 'row' : 'option'}\n aria-selected=\"false\"\n aria-disabled=\"true\"\n aria-label={this.noResultsText}\n aria-live=\"polite\"\n >\n {this.popupRole === 'grid' ? (\n <span role=\"gridcell\">\n <ds-empty-state body={this.noResultsText} />\n </span>\n ) : (\n <ds-empty-state body={this.noResultsText} />\n )}\n </div>\n ) : (\n this.visibleSections.map((section, sectionIndex) => (\n <div\n class={{\n 'ds-choice-section': true,\n 'ds-chrome-column': true,\n 'ds-chrome-space--sm': true,\n 'ds-choice-section--divided': !!section.divider,\n 'ds-choice-section--headed-after-first':\n sectionIndex > 0 && Boolean(section.header),\n }}\n role={\n this.popupRole === 'grid' ? 'rowgroup' : section.header ? 'group' : undefined\n }\n aria-label={section.header}\n >\n {section.header && this.popupRole === 'grid' ? (\n <ds-text\n class={`ds-choice-section__header ds-control-section-heading ds-control--${this.size}`}\n as=\"span\"\n variant={CONTROL_SUPPORTING_TEXT_VARIANT[this.size]}\n emphasis\n color=\"primary\"\n role=\"row\"\n >\n <span class=\"ds-choice-section__header-label\" role=\"gridcell\">\n {section.header}\n </span>\n </ds-text>\n ) : section.header ? (\n <ds-text\n class={`ds-choice-section__header ds-control-section-heading ds-control--${this.size}`}\n as=\"span\"\n variant={CONTROL_SUPPORTING_TEXT_VARIANT[this.size]}\n emphasis\n color=\"primary\"\n aria-hidden=\"true\"\n >\n <span class=\"ds-choice-section__header-label\">{section.header}</span>\n </ds-text>\n ) : null}\n {section.options.map(option =>\n this.renderOption(option, flatIndex++, usesOptionIcons, usesOptionSubtext)\n )}\n </div>\n ))\n )}\n </div>\n {this.footerActionLabel && !this.isLoading ? (\n <ChoiceActionFooter\n size={this.size}\n label={this.footerActionLabel}\n onAction={this.activateFooterAction}\n />\n ) : this.allowClear && this.hasSelection && !this.isLoading ? (\n <ChoiceFooter\n size={this.size}\n summary={this.multiple ? `${count} ${this.selectedLabel}` : undefined}\n compactSummary={this.multiple ? String(count) : undefined}\n useCompactSummary={this.compactFooterSummary}\n contentRef={element => {\n this.footerContentElement = element ?? undefined;\n }}\n summaryMeasureRef={element => {\n this.footerSummaryMeasureElement = element ?? undefined;\n }}\n clearRef={element => {\n this.footerClearElement = element ?? undefined;\n }}\n clearLabel={this.clearLabel}\n onClear={this.clearSelection}\n />\n ) : null}\n </div>\n )}\n\n {showError && (\n <ds-text\n class=\"error-text\"\n as=\"div\"\n variant=\"text-body-small\"\n color=\"negative\"\n textId={this.errorId}\n role=\"alert\"\n >\n {this.errorMessage}\n </ds-text>\n )}\n </Host>\n );\n }\n}\n",
|
|
974
|
+
"content": "import {\n AttachInternals,\n Component,\n Element,\n Event,\n EventEmitter,\n h,\n Host,\n Method,\n Prop,\n State,\n type VNode,\n Watch,\n} from '@stencil/core';\nimport {\n controlWidthClass,\n CONTROL_SUPPORTING_TEXT_VARIANT,\n CONTROL_TEXT_VARIANT,\n DEFAULT_REQUIRED_MESSAGE,\n restoreStringArrayFormState,\n restoreStringFormState,\n setFormControlValue,\n setRepeatedFormControlValue,\n setRequiredValidity,\n type ControlWidth,\n} from '../../utils';\nimport {\n ChoiceActionFooter,\n ChoiceFooter,\n ChoiceOptionRow,\n ChoiceSearch,\n} from '../../utils/choice-list-parts';\nimport {\n choiceBackgroundClassMap,\n choiceListUsesIcons,\n choiceListUsesSubtext,\n enabledChoiceIndexes,\n filterChoiceSections,\n flattenChoiceSections,\n resolveChoiceSections,\n type ChoiceBackground,\n type ChoiceOption,\n type ChoiceSection,\n} from '../../utils/choice-list';\nimport { SelectController } from '../../utils/select-controller';\nimport { observeTableCaptionCompact } from '../../utils/table-caption-compact';\n\nexport type SelectOption = ChoiceOption;\nexport type SelectSection = ChoiceSection;\nexport type SelectBackground = ChoiceBackground;\nexport type SelectSize = 'lg' | 'md' | 'sm' | 'xs';\nexport type SelectWidth = ControlWidth;\nexport type SelectPopupAlign = 'start' | 'end';\nexport type SelectValue = string | string[];\nexport type SelectIndicator = 'down' | 'up-down';\n\nexport interface SelectOptionActionDetail {\n value: string;\n anchorId: string;\n originalEvent: MouseEvent;\n}\n\nexport interface SelectOptionSubtextActionDetail {\n value: string;\n actionValue: string;\n originalEvent: MouseEvent;\n}\n\nconst ICON_SIZE: Record<SelectSize, 'lg' | 'md' | 'sm' | 'xs'> = {\n lg: 'lg',\n md: 'md',\n sm: 'sm',\n xs: 'xs',\n};\n\nconst OPTION_ACTION_SIZE: Record<SelectSize, 'md' | 'sm' | 'xs'> = {\n lg: 'md',\n md: 'sm',\n sm: 'xs',\n xs: 'xs',\n};\n\nlet selectId = 0;\n\n@Component({\n tag: 'ds-select',\n styleUrl: 'Select.css',\n scoped: true,\n formAssociated: true,\n})\nexport class Select {\n @Element() el!: HTMLElement;\n @AttachInternals() internals!: ElementInternals;\n\n /** Flat choices. Assign arrays through the JavaScript property. */\n @Prop() options: SelectOption[] = [];\n /** Grouped choices; takes precedence over options. Assign through JavaScript. */\n @Prop() sections: SelectSection[] = [];\n /** Enable independent multi-value selection while keeping the popup open. */\n @Prop({ reflect: true }) multiple: boolean = false;\n /** Selected scalar or array value according to `multiple`. */\n @Prop({ mutable: true }) value: SelectValue = '';\n /** Controlled popup visibility. */\n @Prop({ mutable: true, reflect: true }) open: boolean = false;\n /** Native form field name. */\n @Prop({ reflect: true }) name: string | undefined;\n /** Native disabled state. */\n @Prop({ reflect: true }) disabled: boolean = false;\n /** Require one valid selected value. */\n @Prop({ reflect: true }) required: boolean = false;\n /** Validation message used when required is missing. */\n @Prop() requiredMessage: string = DEFAULT_REQUIRED_MESSAGE;\n /** Trigger text shown when no valid value is selected. */\n @Prop() placeholder: string = 'Select';\n /** Optional scalar-mode trigger text that does not change the selected option label. */\n @Prop() triggerLabel: string | undefined;\n /** Present triggerLabel with placeholder emphasis while preserving the selected value. */\n @Prop() triggerLabelPlaceholder: boolean = false;\n /** Show a supplemental notification dot beside the trigger label. */\n @Prop() dot: boolean = false;\n /** Control density. */\n @Prop() size: SelectSize = 'md';\n /** Width fit — hug content (default) or fill the parent. */\n @Prop() width: SelectWidth = 'hug';\n /** Align the popup's choice edge to the trigger start or end edge. */\n @Prop() popupAlign: SelectPopupAlign = 'start';\n /** Explicit collision owner; otherwise the nearest data-ds-overlay-boundary ancestor is used. */\n @Prop() boundary: HTMLElement | undefined;\n /** Shared inactive treatment; removes interaction and form submission. */\n @Prop() isInactive: boolean = false;\n /** Replace the prefix with a loader and disable option interaction. */\n @Prop() isLoading: boolean = false;\n /** Show the selected interaction fill when a valid value exists. */\n @Prop() activeFill: boolean = true;\n /**\n * Opt into table-caption icon-only chrome below 900px. The trigger omits its\n * visible label and chevron; keep an accessible name via aria-label.\n */\n @Prop({ reflect: true }) collapseLabel: boolean = false;\n /** Show the surface-aware inset border, including focused and invalid strokes. */\n @Prop() hasBorder: boolean = true;\n /** Optional trigger prefix icon name. */\n @Prop() icon: string | undefined;\n /** Trailing choice indicator. Use up-down for compact value steppers such as page size. */\n @Prop() indicator: SelectIndicator = 'down';\n /** Show the clear footer action when a value exists. */\n @Prop() allowClear: boolean = true;\n /** Localized clear action label. */\n @Prop() clearLabel: string = 'Clear';\n /** Localized noun displayed after the selected count in multiple mode. */\n @Prop() selectedLabel: string = 'selected';\n /** Optional text action shown in the popup footer instead of the clear action. */\n @Prop() footerActionLabel: string | undefined;\n /** Show immediate local filtering over option labels, subtext, and section headings. */\n @Prop() searchable: boolean = false;\n /** Localized search-field placeholder and accessible name. */\n @Prop() searchPlaceholder: string = 'Search';\n /** Localized empty-filter result text. */\n @Prop() noResultsText: string = 'No results found';\n /** Accessible loading status label. */\n @Prop() loadingLabel: string = 'Loading';\n /** Actual parent surface context; omit on primary and secondary surfaces. */\n @Prop() background: SelectBackground | undefined;\n /** Show invalid visual state. */\n @Prop() error: boolean = false;\n /** Error text rendered below the trigger when error is true. */\n @Prop() errorMessage: string | undefined;\n /** ID applied to the internal combobox trigger for external labels. */\n @Prop() inputId: string | undefined;\n /** Direct accessible name when no external label is available. */\n @Prop({ attribute: 'aria-label' }) ariaLabel: string | null = null;\n /** IDs of elements that label the combobox. */\n @Prop({ attribute: 'aria-labelledby' }) ariaLabelledby: string | undefined;\n /** Additional IDs that describe the combobox. */\n @Prop({ attribute: 'aria-describedby' }) ariaDescribedby: string | undefined;\n\n /** Emitted after selection or clearing with the next scalar or array value. */\n @Event() dsChange!: EventEmitter<SelectValue>;\n /** Emitted after the footer clear action. */\n @Event() dsClear!: EventEmitter<void>;\n /** Emitted whenever popup visibility changes. */\n @Event() dsOpenChange!: EventEmitter<boolean>;\n /** Emitted when the optional popup footer action is activated. */\n @Event() dsFooterAction!: EventEmitter<void>;\n /** Emitted when an option's contextual ellipsis action is activated. */\n @Event() dsOptionAction!: EventEmitter<SelectOptionActionDetail>;\n /** Emitted when an option's supporting text action is activated. */\n @Event() dsOptionSubtextAction!: EventEmitter<SelectOptionSubtextActionDetail>;\n\n @State() private activeIndex = -1;\n @State() private searchTerm = '';\n @State() private formDisabled = false;\n @State() private position = { x: 0, y: 0 };\n @State() private positionReady = false;\n @State() private focusRingVisible = false;\n @State() private compactFooterSummary = false;\n @State() private captionCompact = false;\n\n private readonly generatedId = `ds-select-${++selectId}`;\n private readonly listboxId = `${this.generatedId}-listbox`;\n private readonly errorId = `${this.generatedId}-error`;\n private initialValue: SelectValue = '';\n private readonly controller = this.createController();\n private footerContentElement?: HTMLDivElement;\n private footerSummaryMeasureElement?: HTMLElement;\n private footerClearElement?: HTMLButtonElement;\n private footerResizeObserver?: ResizeObserver;\n private observedFooterContentElement?: HTMLDivElement;\n private footerMeasurementFrame?: number;\n private captionCompactDisconnect: (() => void) | undefined;\n private hasLoaded = false;\n\n private createController() {\n // eslint-disable-next-line @typescript-eslint/no-this-alias -- adapter getters preserve reactive component ownership without inheritance.\n const owner = this;\n return new SelectController<SelectOption>({\n get host() {\n return owner.el;\n },\n generatedId: owner.generatedId,\n get options() {\n return owner.visibleOptions;\n },\n get searchable() {\n return owner.searchable;\n },\n get isLoading() {\n return owner.isLoading;\n },\n get isDisabled() {\n return owner.isDisabled;\n },\n get preferredIndex() {\n if (owner.multiple) return -1;\n return owner.visibleOptions.findIndex(\n option => option.value === owner.scalarValue && !option.isInactive\n );\n },\n get popupAlign() {\n return owner.popupAlign;\n },\n get boundary() {\n return owner.boundary;\n },\n get open() {\n return owner.open;\n },\n set open(value) {\n owner.open = value;\n },\n get activeIndex() {\n return owner.activeIndex;\n },\n set activeIndex(value) {\n owner.activeIndex = value;\n },\n get searchTerm() {\n return owner.searchTerm;\n },\n set searchTerm(value) {\n owner.searchTerm = value;\n },\n get focusRingVisible() {\n return owner.focusRingVisible;\n },\n set focusRingVisible(value) {\n owner.focusRingVisible = value;\n },\n get position() {\n return owner.position;\n },\n set position(value) {\n owner.position = value;\n },\n get positionReady() {\n return owner.positionReady;\n },\n set positionReady(value) {\n owner.positionReady = value;\n },\n selectOption: option => owner.selectOption(option),\n });\n }\n\n componentWillLoad() {\n this.value = this.normalizeValueForMode(this.value);\n this.initialValue = Array.isArray(this.value) ? [...this.value] : this.value;\n this.syncFormValue();\n }\n\n componentDidLoad() {\n this.hasLoaded = true;\n this.controller.connect();\n this.observeFooterContent();\n this.syncCaptionCompactObserver();\n }\n\n connectedCallback() {\n if (!this.hasLoaded) return;\n this.controller.connect();\n this.observeFooterContent();\n this.syncCaptionCompactObserver();\n }\n\n componentDidRender() {\n this.observeFooterContent();\n this.scheduleFooterSummaryMeasurement();\n }\n\n disconnectedCallback() {\n this.controller.disconnect();\n this.footerResizeObserver?.disconnect();\n this.observedFooterContentElement = undefined;\n if (this.footerMeasurementFrame !== undefined) {\n cancelAnimationFrame(this.footerMeasurementFrame);\n this.footerMeasurementFrame = undefined;\n }\n this.disconnectCaptionCompactObserver();\n }\n\n @Watch('collapseLabel')\n onCollapseLabelChange() {\n this.syncCaptionCompactObserver();\n }\n\n @Watch('value')\n @Watch('multiple')\n @Watch('disabled')\n @Watch('isInactive')\n @Watch('required')\n @Watch('name')\n @Watch('options')\n @Watch('sections')\n syncFormValue() {\n if (\n (this.multiple && !Array.isArray(this.value)) ||\n (!this.multiple && Array.isArray(this.value))\n ) {\n this.value = this.normalizeValueForMode(this.value);\n }\n const inactive = this.isDisabled;\n if (this.multiple) {\n const values = this.resolvedValues;\n setRepeatedFormControlValue(this.internals, this.name, values, { inactive });\n } else {\n const value = this.hasSelection ? this.scalarValue : '';\n setFormControlValue(this.internals, value, { inactive });\n }\n const missing = this.required && !inactive && !this.hasSelection;\n setRequiredValidity(this.internals, missing, this.requiredMessage);\n this.controller.optionsChanged();\n }\n\n @Watch('isLoading')\n onLoadingChange() {\n this.controller.loadingChanged();\n }\n\n @Watch('popupAlign')\n @Watch('boundary')\n onPopupAlignChange() {\n this.controller.positionChanged();\n }\n\n @Watch('open')\n onOpenChange(open: boolean) {\n this.dsOpenChange.emit(open);\n this.controller.openChanged(open);\n }\n\n @Watch('searchTerm')\n onSearchTermChange() {\n this.controller.searchChanged();\n }\n\n @Watch('activeIndex')\n onActiveIndexChange() {\n this.controller.activeIndexChanged();\n }\n\n formDisabledCallback(disabled: boolean) {\n this.formDisabled = disabled;\n this.syncFormValue();\n }\n\n formResetCallback() {\n this.value = this.normalizeValueForMode(this.initialValue);\n this.closePopup();\n }\n\n formStateRestoreCallback(state: string | File | FormData | null) {\n if (!this.multiple) {\n this.value = restoreStringFormState(state);\n return;\n }\n this.value = restoreStringArrayFormState(state);\n }\n\n @Method()\n async setFocus() {\n this.controller.setFocus();\n }\n\n private get isDisabled(): boolean {\n return this.disabled || this.isInactive || this.formDisabled;\n }\n\n private normalizeValueForMode(value: SelectValue): SelectValue {\n if (this.multiple) {\n return Array.isArray(value) ? [...value] : value ? [value] : [];\n }\n return Array.isArray(value) ? (value[0] ?? '') : value;\n }\n\n private get allSections(): SelectSection[] {\n return resolveChoiceSections(this.options, this.sections);\n }\n\n private get allOptions(): SelectOption[] {\n return flattenChoiceSections(this.allSections);\n }\n\n private get visibleSections(): SelectSection[] {\n return this.searchable\n ? filterChoiceSections(this.allSections, this.searchTerm)\n : this.allSections;\n }\n\n private get visibleOptions(): SelectOption[] {\n return flattenChoiceSections(this.visibleSections);\n }\n\n private get scalarValue(): string {\n return typeof this.value === 'string' ? this.value : '';\n }\n\n private get resolvedValues(): string[] {\n if (!this.multiple || !Array.isArray(this.value)) return [];\n const valid = new Set(this.allOptions.map(option => option.value));\n return [...new Set(this.value.filter(value => valid.has(value)))];\n }\n\n private get selectedOption(): SelectOption | undefined {\n if (this.multiple) return undefined;\n return this.allOptions.find(option => option.value === this.scalarValue);\n }\n\n private get hasSelection(): boolean {\n return this.multiple ? this.resolvedValues.length > 0 : Boolean(this.selectedOption);\n }\n\n private get activeOptionId(): string | undefined {\n return this.controller.activeOptionId;\n }\n\n private get popupRole(): 'listbox' | 'grid' {\n return this.allOptions.some(option => Boolean(option.action)) ? 'grid' : 'listbox';\n }\n\n private observeFooterContent() {\n const content =\n this.open && this.multiple && this.hasSelection ? this.footerContentElement : undefined;\n if (!content?.isConnected) {\n this.footerResizeObserver?.disconnect();\n this.observedFooterContentElement = undefined;\n return;\n }\n if (this.observedFooterContentElement === content) return;\n\n this.footerResizeObserver?.disconnect();\n if (typeof ResizeObserver !== 'undefined') {\n this.footerResizeObserver = new ResizeObserver(() => this.scheduleFooterSummaryMeasurement());\n this.footerResizeObserver.observe(content);\n }\n this.observedFooterContentElement = content;\n }\n\n private scheduleFooterSummaryMeasurement() {\n if (\n !this.open ||\n !this.multiple ||\n !this.hasSelection ||\n this.footerMeasurementFrame !== undefined\n ) {\n return;\n }\n this.footerMeasurementFrame = requestAnimationFrame(() => {\n this.footerMeasurementFrame = undefined;\n this.measureFooterSummary();\n });\n }\n\n private measureFooterSummary() {\n const content = this.footerContentElement;\n const summary = this.footerSummaryMeasureElement;\n const clear = this.footerClearElement;\n if (!content?.isConnected || !summary?.isConnected || !clear?.isConnected) return;\n\n const styles = getComputedStyle(content);\n const innerWidth =\n content.clientWidth -\n Number.parseFloat(styles.paddingInlineStart || '0') -\n Number.parseFloat(styles.paddingInlineEnd || '0');\n const requiredWidth =\n summary.getBoundingClientRect().width +\n clear.getBoundingClientRect().width +\n Number.parseFloat(styles.columnGap || '0');\n const nextCompact = requiredWidth > innerWidth + 0.5;\n if (nextCompact !== this.compactFooterSummary) {\n this.compactFooterSummary = nextCompact;\n }\n }\n\n private closePopup(restoreFocus = false) {\n this.controller.closePopup(restoreFocus);\n }\n\n private openPopup(focusVisible: boolean, edge?: 'first' | 'last') {\n this.controller.openPopup(focusVisible, edge);\n }\n\n private selectOption(option: SelectOption) {\n if (option.isInactive || this.isLoading) return;\n if (this.multiple) {\n const current = this.resolvedValues;\n const next = current.includes(option.value)\n ? current.filter(value => value !== option.value)\n : [...current, option.value];\n this.value = next;\n this.dsChange.emit([...next]);\n return;\n }\n this.value = option.value;\n this.dsChange.emit(option.value);\n this.controller.closePopup(true);\n }\n\n private clearSelection = (event: Event) => {\n event.preventDefault();\n event.stopPropagation();\n this.value = this.multiple ? [] : '';\n this.dsChange.emit(this.multiple ? [] : '');\n this.dsClear.emit();\n if (!this.multiple) {\n const enabled = enabledChoiceIndexes(this.visibleOptions);\n this.activeIndex = enabled[0] ?? -1;\n }\n this.controller.focusSearchOrTrigger();\n };\n\n private activateFooterAction = (event: MouseEvent) => {\n event.preventDefault();\n event.stopPropagation();\n this.dsFooterAction.emit();\n this.closePopup();\n };\n\n private readonly handleListKeyDown = this.controller.handleListKeyDown;\n private readonly handleTriggerKeyDown = this.controller.handleTriggerKeyDown;\n\n private renderOption(\n option: SelectOption,\n index: number,\n usesIcons: boolean,\n usesSubtext: boolean\n ) {\n const selected = this.multiple\n ? this.resolvedValues.includes(option.value)\n : option.value === this.scalarValue;\n const active = index === this.activeIndex;\n const actionId = `${this.generatedId}-option-action-${index}`;\n return (\n <ChoiceOptionRow\n size={this.size}\n id={`${this.generatedId}-option-${index}`}\n option={option}\n selected={selected}\n active={active}\n focusRingVisible={this.focusRingVisible}\n usesSubtext={usesSubtext}\n actionOpen={option.action?.expanded}\n popupRole={this.popupRole}\n leading={\n this.multiple ? (\n <span\n class=\"ds-choice-item__icon ds-control-icon-box ds-interaction-fill__content\"\n aria-hidden=\"true\"\n >\n <ds-checkbox\n class=\"select-option__checkbox\"\n label=\"\"\n size={this.size}\n checked={selected}\n presentation\n />\n </span>\n ) : usesIcons && option.icon ? (\n <span\n class=\"ds-choice-item__icon ds-control-icon-box ds-interaction-fill__content\"\n aria-hidden=\"true\"\n >\n <ds-icon name={option.icon} size={this.size} color=\"inherit\" />\n </span>\n ) : undefined\n }\n supporting={\n option.subtextActions?.length ? (\n <span class=\"ds-choice-item__subtext ds-control-label-box select-option__subtext-actions\">\n {option.subtextActions.map((action, actionIndex) => [\n actionIndex > 0 ? (\n <ds-text\n class=\"select-option__subtext-action-separator\"\n as=\"span\"\n variant={CONTROL_SUPPORTING_TEXT_VARIANT[this.size]}\n color=\"inherit\"\n aria-hidden=\"true\"\n >\n ·\n </ds-text>\n ) : null,\n <button\n type=\"button\"\n class={{\n 'select-option__subtext-action': true,\n 'select-option__subtext-action--negative': action.tone === 'negative',\n 'ds-text-action': true,\n 'ds-focus-ring': true,\n }}\n onKeyDown={(event: KeyboardEvent) => event.stopPropagation()}\n onClick={(event: MouseEvent) => {\n event.preventDefault();\n event.stopPropagation();\n this.dsOptionSubtextAction.emit({\n value: option.value,\n actionValue: action.value,\n originalEvent: event,\n });\n }}\n >\n <ds-text\n as=\"span\"\n variant={CONTROL_SUPPORTING_TEXT_VARIANT[this.size]}\n color=\"inherit\"\n >\n {action.label}\n </ds-text>\n </button>,\n ])}\n </span>\n ) : undefined\n }\n action={\n option.action ? (\n <ds-tooltip label={option.action.label} side=\"left\" size=\"sm\">\n <ds-button-unfilled\n id={actionId}\n variant=\"icon\"\n size={OPTION_ACTION_SIZE[this.size]}\n icon=\"Ellipses\"\n rounded\n hasBorder={false}\n activeFill={false}\n ariaLabel={option.action.label}\n haspopup=\"menu\"\n controls={option.action.controls}\n expanded={option.action.expanded}\n onKeyDown={event => {\n event.stopPropagation();\n if (event.key === 'Escape' || event.key === 'ArrowLeft') {\n event.preventDefault();\n this.controller.focusSearchOrTrigger();\n }\n }}\n onDsClick={(event: CustomEvent<MouseEvent>) => {\n event.stopPropagation();\n this.dsOptionAction.emit({\n value: option.value,\n anchorId: actionId,\n originalEvent: event.detail,\n });\n }}\n />\n </ds-tooltip>\n ) : undefined\n }\n onHover={() => {\n this.focusRingVisible = false;\n this.activeIndex = index;\n }}\n onSelect={() => this.selectOption(option)}\n />\n );\n }\n\n private get captionIconOnly(): boolean {\n return this.collapseLabel && this.captionCompact && Boolean(this.icon || this.isLoading);\n }\n\n private syncCaptionCompactObserver(): void {\n this.disconnectCaptionCompactObserver();\n if (!this.collapseLabel) {\n if (this.captionCompact) this.captionCompact = false;\n return;\n }\n this.captionCompactDisconnect = observeTableCaptionCompact(this.el, compact => {\n if (this.captionCompact !== compact) this.captionCompact = compact;\n });\n }\n\n private disconnectCaptionCompactObserver(): void {\n this.captionCompactDisconnect?.();\n this.captionCompactDisconnect = undefined;\n }\n\n private renderCaptionTooltip(trigger: VNode, label: string): VNode {\n if (!this.collapseLabel) return trigger;\n return (\n <ds-tooltip label={label} side=\"top\" size=\"sm\">\n {trigger}\n </ds-tooltip>\n );\n }\n\n render() {\n const inactive = this.isDisabled;\n const showPlaceholder = !this.hasSelection;\n const count = this.resolvedValues.length;\n const label = this.multiple\n ? `${this.placeholder}${count > 0 ? ` · ${count}` : ''}`\n : showPlaceholder\n ? this.placeholder\n : (this.triggerLabel ?? this.selectedOption?.label);\n const textVariant = CONTROL_TEXT_VARIANT[this.size];\n const iconSize = ICON_SIZE[this.size];\n const usesOptionIcons = !this.multiple && choiceListUsesIcons(this.allOptions);\n const usesOptionSubtext = choiceListUsesSubtext(this.allOptions);\n const showError = this.error && Boolean(this.errorMessage);\n const describedBy =\n [this.ariaDescribedby, showError ? this.errorId : undefined].filter(Boolean).join(' ') ||\n undefined;\n const popupStyle = {\n position: 'fixed',\n left: '0',\n top: '0',\n transform: `translate(${Math.round(this.position.x)}px, ${Math.round(this.position.y)}px)`,\n zIndex: 'var(--dimension-z-index-floating)',\n visibility: this.positionReady ? 'visible' : 'hidden',\n };\n let flatIndex = 0;\n\n return (\n <Host\n class={{\n 'select-host': true,\n 'ds-select-trigger-host': true,\n 'select-host--multiple': this.multiple,\n 'ds-field-stack': true,\n 'ds-control-inactive': inactive,\n [`ds-control--${this.size}`]: true,\n 'ds-table-caption-control': this.collapseLabel,\n 'ds-table-caption-control--compact': this.captionIconOnly,\n ...controlWidthClass(this.width),\n [`select-host--background-${this.background}`]: !!this.background,\n [`ds-select-trigger-host--background-${this.background}`]: !!this.background,\n }}\n >\n {this.renderCaptionTooltip(\n <button\n ref={element => {\n this.controller.setTriggerElement((element as HTMLButtonElement) ?? null);\n }}\n id={this.inputId ?? this.generatedId}\n type=\"button\"\n class={{\n trigger: true,\n 'ds-control-frame': true,\n 'ds-focus-ring-inset': true,\n 'ds-interaction-fill': true,\n 'ds-interaction-fill--selected': !inactive && this.activeFill && this.hasSelection,\n 'trigger--expanded': !inactive && this.open,\n 'trigger--bordered': this.hasBorder,\n 'trigger--placeholder': showPlaceholder && !this.multiple,\n 'trigger--has-value': this.hasSelection,\n 'trigger--label-placeholder':\n !this.multiple && Boolean(this.triggerLabel) && this.triggerLabelPlaceholder,\n 'wrapper--error': this.hasBorder && this.error,\n [`ds-control--${this.size}`]: true,\n ...choiceBackgroundClassMap(this.background),\n }}\n disabled={inactive}\n role=\"combobox\"\n aria-haspopup={this.popupRole}\n aria-expanded={String(this.open)}\n aria-controls={this.open ? this.listboxId : undefined}\n aria-activedescendant={this.open && !this.searchable ? this.activeOptionId : undefined}\n aria-label={this.ariaLabel ?? (this.captionIconOnly ? label : undefined)}\n aria-labelledby={this.ariaLabelledby}\n aria-describedby={describedBy}\n aria-invalid={this.error ? 'true' : undefined}\n aria-required={this.required || undefined}\n aria-busy={this.isLoading ? 'true' : undefined}\n onClick={() => (this.open ? this.closePopup() : this.openPopup(false))}\n onKeyDown={event => {\n const action = this.visibleOptions[this.activeIndex]?.action;\n if (\n this.open &&\n action &&\n (event.key === 'ArrowRight' || (event.key === 'F10' && event.shiftKey))\n ) {\n event.preventDefault();\n document\n .getElementById(`${this.generatedId}-option-action-${this.activeIndex}`)\n ?.querySelector<HTMLElement>('button')\n ?.focus();\n return;\n }\n this.handleTriggerKeyDown(event);\n }}\n >\n {(this.icon || this.isLoading) && (\n <span\n class=\"trigger__prefix ds-control-icon-box ds-interaction-fill__content\"\n aria-hidden=\"true\"\n >\n {this.isLoading ? (\n <ds-loader size={iconSize} color=\"inherit\" />\n ) : (\n <ds-icon name={this.icon} size={iconSize} color=\"inherit\" />\n )}\n </span>\n )}\n {this.captionIconOnly ? null : (\n <span\n class={{\n 'trigger__label-box': true,\n 'ds-interaction-fill__content': true,\n }}\n >\n <span\n class={{\n 'trigger__label-content': true,\n 'ds-control-label-box': true,\n 'ds-control-label-dot': this.dot,\n 'trigger__label-content--dot': this.dot,\n }}\n >\n <ds-text\n class=\"trigger__label\"\n as=\"span\"\n variant={textVariant}\n color=\"inherit\"\n lineTruncation={1}\n >\n {label}\n </ds-text>\n {this.dot && (\n <ds-badge\n class=\"trigger__dot ds-control-label-dot__badge\"\n variant=\"dot\"\n hasRing={false}\n label=\"\"\n aria-hidden=\"true\"\n />\n )}\n </span>\n </span>\n )}\n {this.captionIconOnly ? null : (\n <span\n class=\"trigger__chevron ds-control-icon-box ds-interaction-fill__content\"\n aria-hidden=\"true\"\n >\n <ds-icon\n name={this.indicator === 'up-down' ? 'ChevronUpDown' : 'ChevronDown'}\n size={iconSize}\n color=\"inherit\"\n />\n </span>\n )}\n </button>,\n this.captionIconOnly ? this.ariaLabel?.trim() || this.placeholder : ''\n )}\n\n {this.open && (\n <div\n popover=\"manual\"\n ref={element => {\n this.controller.setPopupElement((element as HTMLDivElement) ?? null);\n }}\n class=\"select-popup ds-choice-popup\"\n style={popupStyle}\n >\n {this.searchable && (\n <ChoiceSearch\n size={this.size}\n value={this.searchTerm}\n placeholder={this.searchPlaceholder}\n controls={this.listboxId}\n activeDescendant={this.activeOptionId}\n inputRef={this.controller.setSearchElement}\n clearLabel={this.clearLabel}\n onValueChange={value => {\n this.searchTerm = value;\n }}\n onKeyDown={event => this.handleListKeyDown(event)}\n />\n )}\n <div\n id={this.listboxId}\n class=\"ds-choice-list\"\n role={this.popupRole}\n aria-multiselectable={this.multiple ? 'true' : undefined}\n aria-label={this.ariaLabel ?? this.placeholder}\n aria-busy={this.isLoading ? 'true' : undefined}\n >\n {this.isLoading ? (\n <div\n class=\"ds-choice-loading ds-empty-region\"\n role={this.popupRole === 'grid' ? 'row' : 'option'}\n aria-selected=\"false\"\n aria-disabled=\"true\"\n aria-label={this.loadingLabel}\n aria-live=\"polite\"\n >\n {this.popupRole === 'grid' ? (\n <span role=\"gridcell\">\n <ds-loader size={this.size} color=\"inherit\" />\n </span>\n ) : (\n <ds-loader size={this.size} color=\"inherit\" />\n )}\n </div>\n ) : this.visibleOptions.length === 0 ? (\n <div\n class=\"ds-choice-empty ds-empty-region\"\n role={this.popupRole === 'grid' ? 'row' : 'option'}\n aria-selected=\"false\"\n aria-disabled=\"true\"\n aria-label={this.noResultsText}\n aria-live=\"polite\"\n >\n {this.popupRole === 'grid' ? (\n <span role=\"gridcell\">\n <ds-empty-state body={this.noResultsText} />\n </span>\n ) : (\n <ds-empty-state body={this.noResultsText} />\n )}\n </div>\n ) : (\n this.visibleSections.map((section, sectionIndex) => (\n <div\n class={{\n 'ds-choice-section': true,\n 'ds-chrome-column': true,\n 'ds-chrome-space--sm': true,\n 'ds-choice-section--divided': !!section.divider,\n 'ds-choice-section--headed-after-first':\n sectionIndex > 0 && Boolean(section.header),\n }}\n role={\n this.popupRole === 'grid' ? 'rowgroup' : section.header ? 'group' : undefined\n }\n aria-label={section.header}\n >\n {section.header && this.popupRole === 'grid' ? (\n <ds-text\n class={`ds-choice-section__header ds-control-section-heading ds-control--${this.size}`}\n as=\"span\"\n variant={CONTROL_SUPPORTING_TEXT_VARIANT[this.size]}\n emphasis\n color=\"primary\"\n role=\"row\"\n >\n <span class=\"ds-choice-section__header-label\" role=\"gridcell\">\n {section.header}\n </span>\n </ds-text>\n ) : section.header ? (\n <ds-text\n class={`ds-choice-section__header ds-control-section-heading ds-control--${this.size}`}\n as=\"span\"\n variant={CONTROL_SUPPORTING_TEXT_VARIANT[this.size]}\n emphasis\n color=\"primary\"\n aria-hidden=\"true\"\n >\n <span class=\"ds-choice-section__header-label\">{section.header}</span>\n </ds-text>\n ) : null}\n {section.options.map(option =>\n this.renderOption(option, flatIndex++, usesOptionIcons, usesOptionSubtext)\n )}\n </div>\n ))\n )}\n </div>\n {this.footerActionLabel && !this.isLoading ? (\n <ChoiceActionFooter\n size={this.size}\n label={this.footerActionLabel}\n onAction={this.activateFooterAction}\n />\n ) : this.allowClear && this.hasSelection && !this.isLoading ? (\n <ChoiceFooter\n size={this.size}\n summary={this.multiple ? `${count} ${this.selectedLabel}` : undefined}\n compactSummary={this.multiple ? String(count) : undefined}\n useCompactSummary={this.compactFooterSummary}\n contentRef={element => {\n this.footerContentElement = element ?? undefined;\n }}\n summaryMeasureRef={element => {\n this.footerSummaryMeasureElement = element ?? undefined;\n }}\n clearRef={element => {\n this.footerClearElement = element ?? undefined;\n }}\n clearLabel={this.clearLabel}\n onClear={this.clearSelection}\n />\n ) : null}\n </div>\n )}\n\n {showError && (\n <ds-text\n class=\"error-text\"\n as=\"div\"\n variant=\"text-body-small\"\n color=\"negative\"\n textId={this.errorId}\n role=\"alert\"\n >\n {this.errorMessage}\n </ds-text>\n )}\n </Host>\n );\n }\n}\n",
|
|
975
975
|
"type": "registry:ui"
|
|
976
976
|
}
|
|
977
977
|
]
|