@ds-mo/ui 14.36.0 → 15.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +6 -0
- package/README.md +7 -0
- package/dist/.package-ready.json +1 -1
- package/dist/agent-patterns.json +1 -1
- package/dist/agent.json +7485 -5096
- package/dist/angular/ds-conversation-list.d.ts +1 -1
- package/dist/angular/ds-conversation-list.js +3 -4
- package/dist/angular/ds-data-customize.d.ts +18 -0
- package/dist/angular/ds-data-customize.js +46 -0
- package/dist/angular/ds-data-filter.d.ts +50 -0
- package/dist/angular/{ds-table-filter.js → ds-data-filter.js} +10 -10
- package/dist/angular/ds-data-group.d.ts +29 -0
- package/dist/angular/{ds-table-group.js → ds-data-group.js} +10 -10
- package/dist/angular/ds-data-preferences.d.ts +39 -0
- package/dist/angular/{ds-table-preferences.js → ds-data-preferences.js} +15 -15
- package/dist/angular/ds-data-saved-views.d.ts +49 -0
- package/dist/angular/{ds-table-saved-views.js → ds-data-saved-views.js} +10 -10
- package/dist/angular/ds-data-search.d.ts +20 -0
- package/dist/angular/{ds-table-search.js → ds-data-search.js} +12 -12
- package/dist/angular/ds-data-sort.d.ts +16 -0
- package/dist/angular/{ds-table-sort.js → ds-data-sort.js} +12 -12
- package/dist/angular/ds-data-toolbar.d.ts +12 -0
- package/dist/angular/{ds-table-toolbar.js → ds-data-toolbar.js} +10 -10
- package/dist/angular/ds-table.d.ts +7 -7
- package/dist/angular/ds-table.js +6 -6
- package/dist/angular/index.d.ts +1 -1
- package/dist/angular/index.js +8 -7
- package/dist/angular/proxies.d.ts +8 -7
- package/dist/angular/proxies.js +8 -7
- 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-nav.js +1 -1
- package/dist/components/ds-bar-page-title.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-breadcrumb.js +1 -1
- package/dist/components/ds-button-filled.js +1 -1
- package/dist/components/ds-button-inverted.js +1 -1
- package/dist/components/ds-button-unfilled.js +1 -1
- package/dist/components/ds-calendar.js +1 -1
- package/dist/components/ds-card-action-center.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-navigation.js +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-card-settings-scope.js +1 -1
- package/dist/components/ds-chart-legend.js +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-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-data-customize.d.ts +11 -0
- package/dist/components/ds-data-customize.js +2 -0
- package/dist/components/ds-data-customize.js.map +1 -0
- package/dist/components/{ds-table-group.d.ts → ds-data-filter.d.ts} +4 -4
- package/dist/components/ds-data-filter.js +2 -0
- package/dist/components/ds-data-filter.js.map +1 -0
- package/dist/components/{ds-table-sort.d.ts → ds-data-group.d.ts} +4 -4
- package/dist/components/ds-data-group.js +2 -0
- package/dist/components/{ds-table-saved-views.d.ts → ds-data-preferences.d.ts} +4 -4
- package/dist/components/ds-data-preferences.js +2 -0
- package/dist/components/ds-data-preferences.js.map +1 -0
- package/dist/components/ds-data-saved-views.d.ts +11 -0
- package/dist/components/ds-data-saved-views.js +2 -0
- package/dist/components/ds-data-saved-views.js.map +1 -0
- package/dist/components/{ds-table-search.d.ts → ds-data-search.d.ts} +4 -4
- package/dist/components/ds-data-search.js +2 -0
- package/dist/components/ds-data-search.js.map +1 -0
- package/dist/components/ds-data-sort.d.ts +11 -0
- package/dist/components/ds-data-sort.js +2 -0
- package/dist/components/ds-data-sort.js.map +1 -0
- package/dist/components/{ds-table-filter.d.ts → ds-data-toolbar.d.ts} +4 -4
- package/dist/components/ds-data-toolbar.js +2 -0
- package/dist/components/ds-data-toolbar.js.map +1 -0
- 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-date.js +1 -1
- package/dist/components/ds-input-time.js +1 -1
- package/dist/components/ds-input-time.js.map +1 -1
- package/dist/components/ds-input.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-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-tool-header.js +1 -1
- package/dist/components/ds-panel-tool-search.js +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-score.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-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-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.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-textarea.js +1 -1
- package/dist/components/ds-time-picker.js +1 -1
- package/dist/components/ds-toast.js +1 -1
- package/dist/components/ds-tooltip-chart.js +1 -1
- package/dist/components/ds-tooltip.js +1 -1
- package/dist/components/ds-typing-indicator.js +1 -1
- package/dist/components/index.d.ts +5 -5
- package/dist/components/{p-D8rpKod8.js → p-0FYD2bev.js} +2 -2
- package/dist/components/p-0qY_HDId.js +2 -0
- package/dist/components/{p-C0CE2v5D.js.map → p-0qY_HDId.js.map} +1 -1
- package/dist/components/{p-DK9Ex3aY.js → p-5URekSny.js} +2 -2
- package/dist/components/{p-Ds51RgCi.js → p-7V1AhetH.js} +2 -2
- package/dist/components/{p-C2JnoaWI.js → p-8yBdCXCn.js} +2 -2
- package/dist/components/{p-CQIh2yRP.js → p-BKZdqJQB.js} +2 -2
- package/dist/components/p-BKZdqJQB.js.map +1 -0
- package/dist/components/p-BYrBU_U9.js +2 -0
- package/dist/components/p-BYrBU_U9.js.map +1 -0
- package/dist/components/{p-ClJhuG5Y.js → p-B_jdoM7e.js} +2 -2
- package/dist/components/{p-ClJhuG5Y.js.map → p-B_jdoM7e.js.map} +1 -1
- package/dist/components/{p-su6y4hLK.js → p-BciTtTwx.js} +2 -2
- package/dist/components/{p-DgG9W9N4.js → p-BeNa6tw5.js} +2 -2
- package/dist/components/{p-DgG9W9N4.js.map → p-BeNa6tw5.js.map} +1 -1
- package/dist/components/p-BgQMEHRa.js +2 -0
- package/dist/components/{p-D7yBIPB6.js.map → p-BgQMEHRa.js.map} +1 -1
- package/dist/components/{p-CQ5RwY2C.js → p-BhJXmuRA.js} +2 -2
- package/dist/components/{p-neWTRxmf.js → p-Bm7rqs3C.js} +2 -2
- package/dist/components/{p-BV5T1hzN.js → p-Bn8I7iw6.js} +2 -2
- package/dist/components/{p-PmcUwTQ_.js → p-Bpd6YCo4.js} +2 -2
- package/dist/components/p-C-3YqeTb.js +2 -0
- package/dist/components/{p-TjXGRPik.js.map → p-C-3YqeTb.js.map} +1 -1
- package/dist/components/{p-B27_chI7.js → p-C6xVxqOn.js} +2 -2
- package/dist/components/{p-C4m8y9ki.js → p-CD4DZPy_.js} +2 -2
- package/dist/components/{p-CuwopElg.js → p-CGidq1iC.js} +2 -2
- package/dist/components/{p-CWog26I_.js → p-CNxvnQ6A.js} +2 -2
- package/dist/components/{p-2GVGhrh0.js → p-CUcUHwI3.js} +2 -2
- package/dist/components/{p-CbSUyv5B.js → p-CYzFfFcH.js} +2 -2
- package/dist/components/{p-8oRkhvjy.js → p-CdGXzRP8.js} +2 -2
- package/dist/components/p-CdgOLWX9.js +2 -0
- package/dist/components/p-CdgOLWX9.js.map +1 -0
- package/dist/components/{p-Jaqdfw4E.js → p-CiH4GG6r.js} +2 -2
- package/dist/components/{p-Cl9phSf0.js → p-CiJ1WR11.js} +2 -2
- package/dist/components/{p-Cus1Sigh.js → p-CmexIVnE.js} +2 -2
- package/dist/components/{p-Dryt3COt.js → p-Cqc2U1ZQ.js} +2 -2
- package/dist/components/{p-Dryt3COt.js.map → p-Cqc2U1ZQ.js.map} +1 -1
- package/dist/components/{p-DLXXGgmf.js → p-CzIpquvw.js} +2 -2
- package/dist/components/{p-tNQ0YulU.js → p-D0y7Sja6.js} +2 -2
- package/dist/components/{p-x1wf38q3.js → p-D1vQGJzc.js} +2 -2
- package/dist/components/{p-x1wf38q3.js.map → p-D1vQGJzc.js.map} +1 -1
- package/dist/components/{p-ji09W9el.js → p-D922f6Tb.js} +2 -2
- package/dist/components/{p-DtkLtKY0.js → p-DEW7r0U9.js} +2 -2
- package/dist/components/p-DEW7r0U9.js.map +1 -0
- package/dist/components/{p-CaNCGKEJ.js → p-DHICPS4n.js} +2 -2
- package/dist/components/{p-Cga6Ex0E.js → p-DOAybCp0.js} +2 -2
- package/dist/components/{p-CWPT3aOB.js → p-DVINzOoK.js} +2 -2
- package/dist/components/{p-CWPT3aOB.js.map → p-DVINzOoK.js.map} +1 -1
- package/dist/components/{p-7O-2D29W.js → p-DVOjjuXc.js} +2 -2
- package/dist/components/{p-CMQUuGvl.js → p-Dfs98tpL.js} +2 -2
- package/dist/components/{p-CMQUuGvl.js.map → p-Dfs98tpL.js.map} +1 -1
- package/dist/components/{p-DkikNSSO.js → p-Di5FH7eh.js} +2 -2
- package/dist/components/{p-DkikNSSO.js.map → p-Di5FH7eh.js.map} +1 -1
- package/dist/components/p-DjkUFlDW.js +2 -0
- package/dist/components/p-DjkUFlDW.js.map +1 -0
- package/dist/components/{p-DVd1TIxi.js → p-Dn9QHL4O.js} +2 -2
- package/dist/components/{p-C-dHtQMi.js → p-Do2FJd1y.js} +2 -2
- package/dist/components/{p-BWfzaSc3.js → p-DpoTwJAR.js} +2 -2
- package/dist/components/p-Dw9sE6Iw.js +2 -0
- package/dist/components/p-Dw9sE6Iw.js.map +1 -0
- package/dist/components/{p-BgLI9JoT.js → p-DwbcNDJF.js} +2 -2
- package/dist/components/p-Dzndkdx5.js +2 -0
- package/dist/components/{p-BOhpMUwO.js.map → p-Dzndkdx5.js.map} +1 -1
- package/dist/components/{p-DvzI2A_M.js → p-E5W27Fu_.js} +2 -2
- package/dist/components/{p-DkfM55x8.js → p-FIBCluwV.js} +2 -2
- package/dist/components/{p-DBwa1OXP.js → p-ZhP2tVmM.js} +2 -2
- package/dist/components/p-ZhP2tVmM.js.map +1 -0
- package/dist/components/p-aD5cw_m2.js +2 -0
- package/dist/components/p-aD5cw_m2.js.map +1 -0
- package/dist/components/{p-J5x-QtlD.js → p-etifxiDc.js} +2 -2
- package/dist/components/p-etifxiDc.js.map +1 -0
- package/dist/components/p-kDZ0sEII.js +2 -0
- package/dist/components/p-kDZ0sEII.js.map +1 -0
- package/dist/components/{p-rYePOPbJ.js → p-ljbw3UMZ.js} +2 -2
- package/dist/components/{p-DZ58Sg5a.js → p-vfCu7XSc.js} +2 -2
- package/dist/components/{p-Ce4zy8q1.js → p-xdmJCPCg.js} +2 -2
- package/dist/components/{p-CDJ2Ntcq.js → p-xtGGIZmU.js} +2 -2
- package/dist/docs/components.json +13115 -12615
- package/dist/lib/components/Icon/flag-icon-catalog.d.ts +2 -0
- package/dist/lib/components/Icon/icon-cache.d.ts +36 -4
- package/dist/lib/components/Icon/map-icon-catalog.d.ts +2 -0
- package/dist/lib/shell/index.js +2 -2
- package/dist/lib/shell/index.js.map +1 -1
- package/dist/lib/utils/index.js +68 -5
- package/dist/lib/utils/index.js.map +3 -3
- package/dist/lint/STYLELINT-LICENSE +20 -0
- package/dist/lint/THIRD-PARTY-NOTICES +553 -0
- package/dist/lint/index.d.ts +7 -0
- package/dist/lint/index.js +51385 -0
- package/dist/lint/types.d.ts +13 -0
- package/dist/mcp-data/registry/agent-activity.json +8 -3
- package/dist/mcp-data/registry/agent-questionnaire.json +8 -3
- package/dist/mcp-data/registry/agent-response.json +13 -2
- package/dist/mcp-data/registry/agent-source-list.json +7 -2
- package/dist/mcp-data/registry/agent-tool-call.json +8 -3
- package/dist/mcp-data/registry/attachment-list.json +7 -2
- package/dist/mcp-data/registry/avatar.json +22 -2
- package/dist/mcp-data/registry/badge.json +31 -3
- package/dist/mcp-data/registry/banner.json +29 -3
- package/dist/mcp-data/registry/bar-action.json +25 -2
- package/dist/mcp-data/registry/bar-nav.json +7 -2
- package/dist/mcp-data/registry/bar-page-title.json +25 -2
- package/dist/mcp-data/registry/bar-title.json +27 -2
- package/dist/mcp-data/registry/bar-workflow.json +25 -2
- package/dist/mcp-data/registry/breadcrumb.json +7 -2
- package/dist/mcp-data/registry/button-filled.json +69 -2
- package/dist/mcp-data/registry/button-inverted.json +65 -2
- package/dist/mcp-data/registry/button-unfilled.json +67 -2
- package/dist/mcp-data/registry/calendar.json +15 -2
- package/dist/mcp-data/registry/card-action-center.json +25 -2
- package/dist/mcp-data/registry/card-chart.json +38 -3
- package/dist/mcp-data/registry/card-navigation.json +37 -2
- package/dist/mcp-data/registry/card-overview.json +28 -3
- package/dist/mcp-data/registry/card-setting.json +36 -3
- package/dist/mcp-data/registry/card-settings-scope.json +25 -2
- package/dist/mcp-data/registry/chart-legend.json +7 -2
- package/dist/mcp-data/registry/chart.json +8 -3
- package/dist/mcp-data/registry/checkbox.json +39 -3
- package/dist/mcp-data/registry/chip.json +51 -2
- package/dist/mcp-data/registry/code-block.json +31 -2
- package/dist/mcp-data/registry/conversation-list-item.json +26 -3
- package/dist/mcp-data/registry/conversation-list-section.json +15 -2
- package/dist/mcp-data/registry/conversation-list.json +16 -33
- package/dist/mcp-data/registry/data-customize.json +399 -0
- package/dist/mcp-data/registry/{table-filter.json → data-filter.json} +59 -34
- package/dist/mcp-data/registry/data-group.json +383 -0
- package/dist/mcp-data/registry/data-preferences.json +591 -0
- package/dist/mcp-data/registry/data-saved-views.json +458 -0
- package/dist/mcp-data/registry/data-search.json +392 -0
- package/dist/mcp-data/registry/data-sort.json +279 -0
- package/dist/mcp-data/registry/data-toolbar.json +194 -0
- package/dist/mcp-data/registry/divider.json +7 -2
- package/dist/mcp-data/registry/empty-state.json +7 -2
- package/dist/mcp-data/registry/field.json +15 -2
- package/dist/mcp-data/registry/filter-menu.json +51 -4
- package/dist/mcp-data/registry/icon.json +34 -8
- package/dist/mcp-data/registry/input-date.json +49 -2
- package/dist/mcp-data/registry/input-time.json +49 -2
- package/dist/mcp-data/registry/input.json +50 -3
- package/dist/mcp-data/registry/loader.json +22 -2
- package/dist/mcp-data/registry/markdown.json +18 -2
- package/dist/mcp-data/registry/menu.json +29 -4
- package/dist/mcp-data/registry/message-actions.json +7 -2
- package/dist/mcp-data/registry/message-bubble.json +27 -2
- package/dist/mcp-data/registry/message-composer.json +26 -3
- package/dist/mcp-data/registry/message-scroller.json +13 -2
- package/dist/mcp-data/registry/message.json +7 -2
- package/dist/mcp-data/registry/mobile-bar-nav.json +26 -3
- package/dist/mcp-data/registry/mobile-header.json +27 -2
- package/dist/mcp-data/registry/mobile-section-switcher.json +27 -2
- package/dist/mcp-data/registry/mobile-sheet-nav.json +28 -3
- package/dist/mcp-data/registry/modal.json +25 -2
- package/dist/mcp-data/registry/pagination.json +7 -2
- package/dist/mcp-data/registry/panel-nav.json +29 -3
- package/dist/mcp-data/registry/panel-sub-nav.json +26 -1
- package/dist/mcp-data/registry/panel-tool-header.json +25 -2
- package/dist/mcp-data/registry/panel-tool-search.json +25 -2
- package/dist/mcp-data/registry/panel-tools.json +28 -4
- package/dist/mcp-data/registry/paper-texture.json +7 -2
- package/dist/mcp-data/registry/radio.json +36 -2
- package/dist/mcp-data/registry/registry.json +7465 -5073
- package/dist/mcp-data/registry/score.json +39 -2
- package/dist/mcp-data/registry/scroll-overlay.json +7 -2
- package/dist/mcp-data/registry/select.json +53 -2
- package/dist/mcp-data/registry/shell-app.json +27 -2
- package/dist/mcp-data/registry/shell-page.json +7 -2
- package/dist/mcp-data/registry/shell-tools.json +26 -2
- package/dist/mcp-data/registry/skeleton.json +42 -2
- package/dist/mcp-data/registry/slider.json +43 -2
- package/dist/mcp-data/registry/swatch-picker.json +25 -2
- package/dist/mcp-data/registry/switch.json +38 -2
- package/dist/mcp-data/registry/tab-group.json +50 -2
- package/dist/mcp-data/registry/table.json +173 -72
- package/dist/mcp-data/registry/tag.json +53 -2
- package/dist/mcp-data/registry/text.json +23 -2
- package/dist/mcp-data/registry/textarea.json +39 -2
- package/dist/mcp-data/registry/time-picker.json +15 -2
- package/dist/mcp-data/registry/toast.json +25 -2
- package/dist/mcp-data/registry/tooltip-chart.json +25 -2
- package/dist/mcp-data/registry/tooltip.json +27 -2
- package/dist/mcp-data/registry/typing-indicator.json +7 -2
- package/dist/react/components.d.ts +8 -7
- package/dist/react/components.js +8 -7
- package/dist/react/ds-data-customize.d.ts +13 -0
- package/dist/react/ds-data-customize.js +15 -0
- package/dist/react/ds-data-filter.d.ts +18 -0
- package/dist/react/{ds-table-filter.js → ds-data-filter.js} +5 -5
- package/dist/react/ds-data-group.d.ts +14 -0
- package/dist/react/{ds-table-group.js → ds-data-group.js} +5 -5
- package/dist/react/ds-data-preferences.d.ts +21 -0
- package/dist/react/{ds-table-preferences.js → ds-data-preferences.js} +6 -6
- package/dist/react/ds-data-saved-views.d.ts +17 -0
- package/dist/react/{ds-table-saved-views.js → ds-data-saved-views.js} +5 -5
- package/dist/react/ds-data-search.d.ts +14 -0
- package/dist/react/{ds-table-search.js → ds-data-search.js} +5 -5
- package/dist/react/ds-data-sort.d.ts +12 -0
- package/dist/react/ds-data-sort.js +12 -0
- package/dist/react/{ds-table-toolbar.d.ts → ds-data-toolbar.d.ts} +3 -3
- package/dist/react/ds-data-toolbar.js +12 -0
- package/dist/react/ds-table.d.ts +3 -3
- package/dist/react/ds-table.js +1 -1
- package/dist/styles/table.css +11 -11
- package/dist/types/components/ConversationList/ConversationList.d.ts +0 -7
- package/dist/types/components/DataCustomize/DataCustomize.d.ts +67 -0
- package/dist/types/components/{TableFilter/TableFilter.d.ts → DataFilter/DataFilter.d.ts} +2 -2
- package/dist/types/components/{TableGroup/TableGroup.d.ts → DataGroup/DataGroup.d.ts} +8 -8
- package/dist/types/components/{TablePreferences/TablePreferences.d.ts → DataPreferences/DataPreferences.d.ts} +29 -13
- package/dist/types/components/{TableSavedViews/TableSavedViews.d.ts → DataSavedViews/DataSavedViews.d.ts} +10 -10
- package/dist/types/components/DataSavedViews/data-saved-views-types.d.ts +24 -0
- package/dist/types/components/DataSavedViews/index.d.ts +2 -0
- package/dist/types/components/{TableSearch/TableSearch.d.ts → DataSearch/DataSearch.d.ts} +6 -6
- package/dist/types/components/{TableSearch/table-search-model.d.ts → DataSearch/data-search-model.d.ts} +6 -6
- package/dist/types/components/{TableSearch/table-search-types.d.ts → DataSearch/data-search-types.d.ts} +3 -3
- package/dist/types/components/{TableSort/TableSort.d.ts → DataSort/DataSort.d.ts} +7 -6
- package/dist/types/components/DataSort/data-sort-menu.d.ts +15 -0
- package/dist/types/components/{TableToolbar/TableToolbar.d.ts → DataToolbar/DataToolbar.d.ts} +3 -3
- package/dist/types/components/Icon/icon-cache.d.ts +36 -4
- package/dist/types/components/Icon/map-icon-catalog.d.ts +3 -0
- package/dist/types/components/PanelTools/PanelTools.d.ts +1 -1
- package/dist/types/components/Table/Table.d.ts +9 -9
- package/dist/types/components/Table/index.d.ts +1 -1
- package/dist/types/components/Table/table-action-menu.d.ts +1 -1
- package/dist/types/components/Table/table-body-renderer.d.ts +2 -2
- package/dist/types/components/Table/table-column-customizer.d.ts +9 -9
- package/dist/types/components/Table/table-group-load-controller.d.ts +2 -2
- package/dist/types/components/Table/table-model.d.ts +7 -7
- package/dist/types/components/Table/table-render-model.d.ts +3 -3
- package/dist/types/components/Table/table-types.d.ts +17 -36
- package/dist/types/components/Table/table-virtual-model.d.ts +2 -2
- package/dist/types/components.d.ts +3837 -3640
- package/dist/types/utils/data-field.d.ts +38 -0
- package/dist/types/utils/list-customizer.d.ts +38 -0
- package/dist/types/utils/table-caption-compact.d.ts +1 -1
- package/dist/vue/components.d.ts +8 -7
- package/dist/vue/components.js +8 -7
- package/dist/vue/ds-conversation-list.js +1 -3
- package/dist/vue/ds-data-customize.d.ts +3 -0
- package/dist/vue/ds-data-customize.js +23 -0
- package/dist/vue/{ds-table-group.d.ts → ds-data-filter.d.ts} +1 -1
- package/dist/vue/{ds-table-filter.js → ds-data-filter.js} +2 -2
- package/dist/vue/{ds-table-sort.d.ts → ds-data-group.d.ts} +1 -1
- package/dist/vue/{ds-table-group.js → ds-data-group.js} +2 -2
- package/dist/vue/{ds-table-saved-views.d.ts → ds-data-preferences.d.ts} +1 -1
- package/dist/vue/{ds-table-preferences.js → ds-data-preferences.js} +10 -7
- package/dist/vue/ds-data-saved-views.d.ts +3 -0
- package/dist/vue/{ds-table-saved-views.js → ds-data-saved-views.js} +2 -2
- package/dist/vue/{ds-table-filter.d.ts → ds-data-search.d.ts} +1 -1
- package/dist/vue/{ds-table-search.js → ds-data-search.js} +3 -3
- package/dist/vue/{ds-table-search.d.ts → ds-data-sort.d.ts} +1 -1
- package/dist/vue/{ds-table-sort.js → ds-data-sort.js} +3 -3
- package/dist/vue/ds-data-toolbar.d.ts +3 -0
- package/dist/vue/ds-data-toolbar.js +9 -0
- package/dist/vue/ds-table.js +4 -4
- package/package.json +34 -9
- package/dist/angular/ds-table-filter.d.ts +0 -50
- package/dist/angular/ds-table-group.d.ts +0 -29
- package/dist/angular/ds-table-preferences.d.ts +0 -39
- package/dist/angular/ds-table-saved-views.d.ts +0 -49
- package/dist/angular/ds-table-search.d.ts +0 -20
- package/dist/angular/ds-table-sort.d.ts +0 -16
- package/dist/angular/ds-table-toolbar.d.ts +0 -12
- package/dist/components/ds-table-filter.js +0 -2
- package/dist/components/ds-table-filter.js.map +0 -1
- package/dist/components/ds-table-group.js +0 -2
- package/dist/components/ds-table-preferences.d.ts +0 -11
- package/dist/components/ds-table-preferences.js +0 -2
- package/dist/components/ds-table-preferences.js.map +0 -1
- package/dist/components/ds-table-saved-views.js +0 -2
- package/dist/components/ds-table-saved-views.js.map +0 -1
- package/dist/components/ds-table-search.js +0 -2
- package/dist/components/ds-table-search.js.map +0 -1
- package/dist/components/ds-table-sort.js +0 -2
- package/dist/components/ds-table-sort.js.map +0 -1
- package/dist/components/ds-table-toolbar.d.ts +0 -11
- package/dist/components/ds-table-toolbar.js +0 -2
- package/dist/components/ds-table-toolbar.js.map +0 -1
- package/dist/components/p-BOhpMUwO.js +0 -2
- package/dist/components/p-C0CE2v5D.js +0 -2
- package/dist/components/p-CKarsuR2.js +0 -2
- package/dist/components/p-CKarsuR2.js.map +0 -1
- package/dist/components/p-CQIh2yRP.js.map +0 -1
- package/dist/components/p-Cea7rROU.js +0 -2
- package/dist/components/p-Cea7rROU.js.map +0 -1
- package/dist/components/p-D7yBIPB6.js +0 -2
- package/dist/components/p-DBwa1OXP.js.map +0 -1
- package/dist/components/p-DtkLtKY0.js.map +0 -1
- package/dist/components/p-J5x-QtlD.js.map +0 -1
- package/dist/components/p-KBrC9IG4.js +0 -2
- package/dist/components/p-KBrC9IG4.js.map +0 -1
- package/dist/components/p-MQ36mEgn.js +0 -2
- package/dist/components/p-MQ36mEgn.js.map +0 -1
- package/dist/components/p-TjXGRPik.js +0 -2
- package/dist/components/p-bM3LH3kc.js +0 -2
- package/dist/components/p-bM3LH3kc.js.map +0 -1
- package/dist/mcp-data/registry/table-group.json +0 -358
- package/dist/mcp-data/registry/table-preferences.json +0 -511
- package/dist/mcp-data/registry/table-saved-views.json +0 -433
- package/dist/mcp-data/registry/table-search.json +0 -366
- package/dist/mcp-data/registry/table-sort.json +0 -253
- package/dist/mcp-data/registry/table-toolbar.json +0 -180
- package/dist/react/ds-table-filter.d.ts +0 -18
- package/dist/react/ds-table-group.d.ts +0 -14
- package/dist/react/ds-table-preferences.d.ts +0 -21
- package/dist/react/ds-table-saved-views.d.ts +0 -17
- package/dist/react/ds-table-search.d.ts +0 -14
- package/dist/react/ds-table-sort.d.ts +0 -12
- package/dist/react/ds-table-sort.js +0 -12
- package/dist/react/ds-table-toolbar.js +0 -12
- package/dist/types/components/TableSavedViews/index.d.ts +0 -2
- package/dist/types/components/TableSavedViews/table-saved-views-types.d.ts +0 -24
- package/dist/types/components/TableSort/table-sort-menu.d.ts +0 -15
- package/dist/vue/ds-table-preferences.d.ts +0 -3
- package/dist/vue/ds-table-toolbar.d.ts +0 -3
- package/dist/vue/ds-table-toolbar.js +0 -9
- /package/dist/components/{ds-table-group.js.map → ds-data-group.js.map} +0 -0
- /package/dist/components/{p-D8rpKod8.js.map → p-0FYD2bev.js.map} +0 -0
- /package/dist/components/{p-DK9Ex3aY.js.map → p-5URekSny.js.map} +0 -0
- /package/dist/components/{p-Ds51RgCi.js.map → p-7V1AhetH.js.map} +0 -0
- /package/dist/components/{p-C2JnoaWI.js.map → p-8yBdCXCn.js.map} +0 -0
- /package/dist/components/{p-su6y4hLK.js.map → p-BciTtTwx.js.map} +0 -0
- /package/dist/components/{p-CQ5RwY2C.js.map → p-BhJXmuRA.js.map} +0 -0
- /package/dist/components/{p-neWTRxmf.js.map → p-Bm7rqs3C.js.map} +0 -0
- /package/dist/components/{p-BV5T1hzN.js.map → p-Bn8I7iw6.js.map} +0 -0
- /package/dist/components/{p-PmcUwTQ_.js.map → p-Bpd6YCo4.js.map} +0 -0
- /package/dist/components/{p-B27_chI7.js.map → p-C6xVxqOn.js.map} +0 -0
- /package/dist/components/{p-C4m8y9ki.js.map → p-CD4DZPy_.js.map} +0 -0
- /package/dist/components/{p-CuwopElg.js.map → p-CGidq1iC.js.map} +0 -0
- /package/dist/components/{p-CWog26I_.js.map → p-CNxvnQ6A.js.map} +0 -0
- /package/dist/components/{p-2GVGhrh0.js.map → p-CUcUHwI3.js.map} +0 -0
- /package/dist/components/{p-CbSUyv5B.js.map → p-CYzFfFcH.js.map} +0 -0
- /package/dist/components/{p-8oRkhvjy.js.map → p-CdGXzRP8.js.map} +0 -0
- /package/dist/components/{p-Jaqdfw4E.js.map → p-CiH4GG6r.js.map} +0 -0
- /package/dist/components/{p-Cl9phSf0.js.map → p-CiJ1WR11.js.map} +0 -0
- /package/dist/components/{p-Cus1Sigh.js.map → p-CmexIVnE.js.map} +0 -0
- /package/dist/components/{p-DLXXGgmf.js.map → p-CzIpquvw.js.map} +0 -0
- /package/dist/components/{p-tNQ0YulU.js.map → p-D0y7Sja6.js.map} +0 -0
- /package/dist/components/{p-ji09W9el.js.map → p-D922f6Tb.js.map} +0 -0
- /package/dist/components/{p-CaNCGKEJ.js.map → p-DHICPS4n.js.map} +0 -0
- /package/dist/components/{p-Cga6Ex0E.js.map → p-DOAybCp0.js.map} +0 -0
- /package/dist/components/{p-7O-2D29W.js.map → p-DVOjjuXc.js.map} +0 -0
- /package/dist/components/{p-DVd1TIxi.js.map → p-Dn9QHL4O.js.map} +0 -0
- /package/dist/components/{p-C-dHtQMi.js.map → p-Do2FJd1y.js.map} +0 -0
- /package/dist/components/{p-BWfzaSc3.js.map → p-DpoTwJAR.js.map} +0 -0
- /package/dist/components/{p-BgLI9JoT.js.map → p-DwbcNDJF.js.map} +0 -0
- /package/dist/components/{p-DvzI2A_M.js.map → p-E5W27Fu_.js.map} +0 -0
- /package/dist/components/{p-DkfM55x8.js.map → p-FIBCluwV.js.map} +0 -0
- /package/dist/components/{p-rYePOPbJ.js.map → p-ljbw3UMZ.js.map} +0 -0
- /package/dist/components/{p-DZ58Sg5a.js.map → p-vfCu7XSc.js.map} +0 -0
- /package/dist/components/{p-Ce4zy8q1.js.map → p-xdmJCPCg.js.map} +0 -0
- /package/dist/components/{p-CDJ2Ntcq.js.map → p-xtGGIZmU.js.map} +0 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Options for the opt-in CompoMo ESLint flat configuration. */
|
|
2
|
+
export interface LintOptions {
|
|
3
|
+
/** Consumer policy is the default; authoring preserves library exceptions. */
|
|
4
|
+
mode?: 'authoring' | 'consumer';
|
|
5
|
+
/** Promote consumer findings to errors without enabling additional rules. */
|
|
6
|
+
strict?: boolean;
|
|
7
|
+
/** Defaults to all CSS files. An empty array disables CSS checks. */
|
|
8
|
+
cssFiles?: string[];
|
|
9
|
+
/** Defaults to JS/JSX/TS/TSX. An empty array enables CSS-only adoption. */
|
|
10
|
+
jsxFiles?: string[];
|
|
11
|
+
/** Ignore patterns local to these presets, not the application's other rules. */
|
|
12
|
+
ignores?: string[];
|
|
13
|
+
}
|
|
@@ -43,7 +43,12 @@
|
|
|
43
43
|
"label": "Storybook examples",
|
|
44
44
|
"path": "src/wc/components/MessageScroller/MessageScroller.stories.ts"
|
|
45
45
|
}
|
|
46
|
-
]
|
|
46
|
+
],
|
|
47
|
+
"styling": {
|
|
48
|
+
"rationale": "Disclosure composition owns its rows; the host remains an application layout box.",
|
|
49
|
+
"protected": [],
|
|
50
|
+
"customProperties": []
|
|
51
|
+
}
|
|
47
52
|
},
|
|
48
53
|
"api": {
|
|
49
54
|
"props": {
|
|
@@ -132,7 +137,7 @@
|
|
|
132
137
|
"peerDependencies": {
|
|
133
138
|
"required": [
|
|
134
139
|
"@ds-mo/tokens ^6.5.1",
|
|
135
|
-
"@ds-mo/icons ^
|
|
140
|
+
"@ds-mo/icons ^8.1.0"
|
|
136
141
|
],
|
|
137
142
|
"frameworks": "Custom Elements; React 18/19 wrappers; Vue 3 wrappers; Angular 19-22 standalone adapters."
|
|
138
143
|
}
|
|
@@ -156,7 +161,7 @@
|
|
|
156
161
|
},
|
|
157
162
|
{
|
|
158
163
|
"path": "src/wc/components/AgentActivity/AgentActivity.tsx",
|
|
159
|
-
"content": "import { Component, Prop, State, Watch, h, Host } from '@stencil/core';\nimport type { AgentActivityItem } from '../conversation-types';\n\n@Component({\n tag: 'ds-agent-activity',\n styleUrl: 'AgentActivity.css',\n scoped: true,\n})\nexport class AgentActivity {\n @Prop() items: AgentActivityItem[] = [];\n @Prop() heading: string = 'Activity';\n @Prop() open: boolean = false;\n\n @State() private expanded: boolean = false;\n\n componentWillLoad() {\n this.expanded = this.open;\n }\n\n @Watch('open')\n handleOpenChange(open: boolean) {\n this.expanded = open;\n }\n\n private get currentItem(): AgentActivityItem | undefined {\n return (\n this.items.find(item => item.state === 'active') ??\n this.items.find(item => item.state === 'error') ??\n [...this.items].reverse().find(item => item.state === 'complete') ??\n this.items.find(item => item.state === 'pending')\n );\n }\n\n private handleToggle = (event: Event) => {\n this.expanded = (event.currentTarget as HTMLDetailsElement).open;\n };\n\n private stateLabel(item: AgentActivityItem): string {\n if (item.state === 'active') return 'In progress';\n if (item.state === 'complete') return 'Complete';\n if (item.state === 'error') return 'Error';\n return 'Pending';\n }\n\n render() {\n return (\n <Host>\n <details class=\"agent-activity\" open={this.open} onToggle={this.handleToggle}>\n <summary>\n <ds-icon name=\"ChevronRight\" size=\"xs\" color=\"inherit\" />\n <ds-text\n class=\"agent-activity__summary-label\"\n variant=\"text-body-small\"\n shimmer={!this.expanded && this.currentItem?.state === 'active'}\n >\n {this.expanded ? this.heading : (this.currentItem?.label ?? this.heading)}\n </ds-text>\n </summary>\n <ol>\n {this.items.map(item => (\n <li class={`agent-activity__item agent-activity__item--${item.state}`}>\n <span class=\"agent-activity__marker\" aria-hidden=\"true\">\n {item.state === 'active' ? (\n <ds-loader size=\"xs\" color=\"inherit\" />\n ) : item.state === 'pending' ? (\n <span class=\"agent-activity__pending-dot\" />\n ) : (\n <ds-icon\n name={item.state === 'error' ? '
|
|
164
|
+
"content": "import { Component, Prop, State, Watch, h, Host } from '@stencil/core';\nimport type { AgentActivityItem } from '../conversation-types';\n\n@Component({\n tag: 'ds-agent-activity',\n styleUrl: 'AgentActivity.css',\n scoped: true,\n})\nexport class AgentActivity {\n @Prop() items: AgentActivityItem[] = [];\n @Prop() heading: string = 'Activity';\n @Prop() open: boolean = false;\n\n @State() private expanded: boolean = false;\n\n componentWillLoad() {\n this.expanded = this.open;\n }\n\n @Watch('open')\n handleOpenChange(open: boolean) {\n this.expanded = open;\n }\n\n private get currentItem(): AgentActivityItem | undefined {\n return (\n this.items.find(item => item.state === 'active') ??\n this.items.find(item => item.state === 'error') ??\n [...this.items].reverse().find(item => item.state === 'complete') ??\n this.items.find(item => item.state === 'pending')\n );\n }\n\n private handleToggle = (event: Event) => {\n this.expanded = (event.currentTarget as HTMLDetailsElement).open;\n };\n\n private stateLabel(item: AgentActivityItem): string {\n if (item.state === 'active') return 'In progress';\n if (item.state === 'complete') return 'Complete';\n if (item.state === 'error') return 'Error';\n return 'Pending';\n }\n\n render() {\n return (\n <Host>\n <details class=\"agent-activity\" open={this.open} onToggle={this.handleToggle}>\n <summary>\n <ds-icon name=\"ChevronRight\" size=\"xs\" color=\"inherit\" />\n <ds-text\n class=\"agent-activity__summary-label\"\n variant=\"text-body-small\"\n shimmer={!this.expanded && this.currentItem?.state === 'active'}\n >\n {this.expanded ? this.heading : (this.currentItem?.label ?? this.heading)}\n </ds-text>\n </summary>\n <ol>\n {this.items.map(item => (\n <li class={`agent-activity__item agent-activity__item--${item.state}`}>\n <span class=\"agent-activity__marker\" aria-hidden=\"true\">\n {item.state === 'active' ? (\n <ds-loader size=\"xs\" color=\"inherit\" />\n ) : item.state === 'pending' ? (\n <span class=\"agent-activity__pending-dot\" />\n ) : (\n <ds-icon\n name={item.state === 'error' ? 'ExclamationTriangle' : 'Check'}\n size=\"xs\"\n color=\"inherit\"\n />\n )}\n </span>\n <span>\n <ds-text variant=\"text-body-small\" shimmer={item.state === 'active'}>\n {item.label}\n </ds-text>\n {item.detail ? (\n <ds-text variant=\"text-caption\" color=\"secondary\">\n {item.detail}\n </ds-text>\n ) : null}\n <span class=\"agent-activity__state ds-visually-hidden\">\n {this.stateLabel(item)}\n </span>\n </span>\n </li>\n ))}\n </ol>\n </details>\n </Host>\n );\n }\n}\n",
|
|
160
165
|
"type": "registry:ui"
|
|
161
166
|
}
|
|
162
167
|
]
|
|
@@ -56,7 +56,12 @@
|
|
|
56
56
|
"label": "Agent chat composition",
|
|
57
57
|
"path": "agent/patterns/agent-chat/pattern.agent.json"
|
|
58
58
|
}
|
|
59
|
-
]
|
|
59
|
+
],
|
|
60
|
+
"styling": {
|
|
61
|
+
"rationale": "Question and action components own their recipes; the questionnaire host remains a responsive layout box.",
|
|
62
|
+
"protected": [],
|
|
63
|
+
"customProperties": []
|
|
64
|
+
}
|
|
60
65
|
},
|
|
61
66
|
"api": {
|
|
62
67
|
"props": {
|
|
@@ -237,7 +242,7 @@
|
|
|
237
242
|
"peerDependencies": {
|
|
238
243
|
"required": [
|
|
239
244
|
"@ds-mo/tokens ^6.5.1",
|
|
240
|
-
"@ds-mo/icons ^
|
|
245
|
+
"@ds-mo/icons ^8.1.0"
|
|
241
246
|
],
|
|
242
247
|
"frameworks": "Custom Elements; React 18/19 wrappers; Vue 3 wrappers; Angular 19-22 standalone adapters."
|
|
243
248
|
}
|
|
@@ -266,7 +271,7 @@
|
|
|
266
271
|
},
|
|
267
272
|
{
|
|
268
273
|
"path": "src/wc/components/AgentQuestionnaire/AgentQuestionnaire.tsx",
|
|
269
|
-
"content": "import {\n Component,\n Element,\n Event,\n EventEmitter,\n h,\n Host,\n Method,\n Prop,\n State,\n Watch,\n} from '@stencil/core';\nimport type {\n AgentQuestion,\n AgentQuestionAnswer,\n AgentQuestionnaireAnswerEventDetail,\n AgentQuestionnaireCancelEventDetail,\n AgentQuestionnaireLabels,\n AgentQuestionnaireStatus,\n} from '../conversation-types';\nimport {\n createQuestionnaireDrafts,\n formatQuestionAnswer,\n normalizeQuestionAnswer,\n validateQuestionDraft,\n} from './agent-questionnaire-model';\nimport type { AgentQuestionDraft, AgentQuestionDrafts } from './agent-questionnaire-model';\n\nconst DEFAULT_LABELS: AgentQuestionnaireLabels = {\n progress: 'Question {current} of {total}',\n previous: 'Previous',\n next: 'Next',\n answer: 'Answer',\n skip: 'Skip',\n cancel: 'Cancel',\n other: 'Other',\n otherPlaceholder: 'Enter another answer',\n preparing: 'Preparing questions…',\n};\n\nlet questionnaireId = 0;\n\n@Component({\n tag: 'ds-agent-questionnaire',\n styleUrl: 'AgentQuestionnaire.css',\n scoped: true,\n})\nexport class AgentQuestionnaire {\n @Element() el!: HTMLElement;\n\n @Prop() requestId!: string;\n @Prop() questions!: AgentQuestion[];\n @Prop() answers: AgentQuestionAnswer[] = [];\n @Prop() status: AgentQuestionnaireStatus = 'ready';\n @Prop() errorMessage?: string;\n @Prop() allowCancel: boolean = false;\n @Prop() labels?: Partial<AgentQuestionnaireLabels>;\n\n @Event() dsAnswer!: EventEmitter<AgentQuestionnaireAnswerEventDetail>;\n @Event() dsCancel!: EventEmitter<AgentQuestionnaireCancelEventDetail>;\n\n @State() private currentStep = 0;\n @State() private drafts: AgentQuestionDrafts = {};\n @State() private validation: Record<string, string> = {};\n\n private readonly instanceId = ++questionnaireId;\n private readonly otherValue = `__ds-agent-questionnaire-other-${this.instanceId}`;\n private answerPending = false;\n private resetScheduled = false;\n private draftRequestId?: string;\n private answersSnapshot: AgentQuestionAnswer[] = [];\n private waitingForRequestAnswers = false;\n\n componentWillLoad() {\n this.draftRequestId = this.requestId;\n this.answersSnapshot = this.answers;\n this.resetDraft();\n }\n\n @Watch('requestId')\n @Watch('questions')\n @Watch('answers')\n handleQuestionnaireInputChange() {\n this.scheduleDraftReset();\n }\n\n @Watch('status')\n handleStatusChange(status: AgentQuestionnaireStatus) {\n if (status !== 'submitting') this.answerPending = false;\n }\n\n @Method()\n async setFocus(): Promise<void> {\n this.focusCurrentControl();\n }\n\n private get copy(): AgentQuestionnaireLabels {\n return { ...DEFAULT_LABELS, ...this.labels };\n }\n\n private get disabled(): boolean {\n return this.status === 'submitting';\n }\n\n private resetDraft(answers = this.answers) {\n this.currentStep = 0;\n this.drafts = createQuestionnaireDrafts(this.questions ?? [], answers ?? []);\n this.validation = {};\n this.answerPending = false;\n }\n\n private scheduleDraftReset() {\n if (this.resetScheduled) return;\n this.resetScheduled = true;\n queueMicrotask(() => {\n this.resetScheduled = false;\n const requestChanged = this.requestId !== this.draftRequestId;\n const answersChanged = this.answers !== this.answersSnapshot;\n if (requestChanged && !answersChanged) {\n this.waitingForRequestAnswers = true;\n } else if (answersChanged) {\n this.waitingForRequestAnswers = false;\n }\n this.resetDraft(this.waitingForRequestAnswers ? [] : this.answers);\n this.draftRequestId = this.requestId;\n this.answersSnapshot = this.answers;\n });\n }\n\n private focusCurrentControl(preferInvalid = false) {\n const focus = () => {\n const invalidControl = preferInvalid\n ? this.el.querySelector<HTMLElement>(\n '[data-question-control][aria-invalid=\"true\"]:not([disabled])'\n )\n : null;\n const control =\n invalidControl ??\n this.el.querySelector<HTMLElement>(\n '[data-question-control]:not([disabled]):not([is-inactive])'\n );\n const action = this.el.querySelector<HTMLElement>(\n '[data-question-action]:not([is-inactive]):not([is-loading])'\n );\n const target = (control ?? action) as\n | (HTMLElement & { setFocus?: () => Promise<void> })\n | null;\n if (target?.setFocus) void target.setFocus();\n else target?.focus();\n };\n focus();\n requestAnimationFrame(focus);\n setTimeout(focus, 0);\n }\n\n private updateDraft(questionId: string, update: Partial<AgentQuestionDraft>) {\n const previous = this.drafts[questionId] ?? {\n value: '',\n otherSelected: false,\n otherText: '',\n skipped: false,\n };\n this.drafts = {\n ...this.drafts,\n [questionId]: { ...previous, ...update, skipped: false },\n };\n if (this.validation[questionId]) {\n const { [questionId]: _removed, ...rest } = this.validation;\n this.validation = rest;\n }\n }\n\n private validate(question: AgentQuestion): boolean {\n const message = validateQuestionDraft(question, this.drafts[question.id]);\n if (!message) return true;\n this.validation = { ...this.validation, [question.id]: message };\n this.focusCurrentControl(true);\n return false;\n }\n\n private move(step: number) {\n this.currentStep = Math.min(Math.max(step, 0), this.questions.length - 1);\n this.focusCurrentControl();\n }\n\n private next(question: AgentQuestion) {\n if (!this.validate(question)) return;\n this.move(this.currentStep + 1);\n }\n\n private skip(question: AgentQuestion) {\n if (question.required) return;\n this.drafts = {\n ...this.drafts,\n [question.id]: {\n ...(this.drafts[question.id] ?? {\n value: '',\n otherSelected: false,\n otherText: '',\n }),\n skipped: true,\n },\n };\n if (this.currentStep < this.questions.length - 1) this.move(this.currentStep + 1);\n else this.submit();\n }\n\n private submit() {\n if (this.disabled || this.answerPending) return;\n const invalidIndex = this.questions.findIndex(question => !this.validateWithoutFocus(question));\n if (invalidIndex >= 0) {\n this.currentStep = invalidIndex;\n this.focusCurrentControl(true);\n return;\n }\n\n this.answerPending = true;\n this.dsAnswer.emit({\n requestId: this.requestId,\n answers: this.questions.map(question =>\n normalizeQuestionAnswer(question, this.drafts[question.id])\n ),\n });\n }\n\n private validateWithoutFocus(question: AgentQuestion): boolean {\n const message = validateQuestionDraft(question, this.drafts[question.id]);\n if (!message) return true;\n this.validation = { ...this.validation, [question.id]: message };\n return false;\n }\n\n private progressLabel(): string {\n return this.copy.progress\n .split('{current}')\n .join(String(this.currentStep + 1))\n .split('{total}')\n .join(String(this.questions.length));\n }\n\n private selectSingle(question: AgentQuestion, value: string) {\n const otherSelected = value === this.otherValue;\n this.updateDraft(question.id, {\n value: otherSelected ? '' : value,\n otherSelected,\n });\n if (otherSelected) {\n requestAnimationFrame(() =>\n this.el.querySelector<HTMLInputElement>('[data-question-other-input]')?.focus()\n );\n }\n }\n\n private renderSingleChoice(question: AgentQuestion, promptId: string, errorId: string) {\n const draft = this.drafts[question.id];\n const value = draft.otherSelected\n ? this.otherValue\n : typeof draft.value === 'string'\n ? draft.value\n : '';\n const options = [\n ...(question.choices ?? []),\n ...(question.allowOther ? [{ value: this.otherValue, label: this.copy.other }] : []),\n ];\n return (\n <div class=\"questionnaire__single-choice\">\n <ds-radio\n data-question-control\n size=\"lg\"\n options={options}\n value={value}\n disabled={this.disabled}\n form={`ds-agent-questionnaire-detached-${this.instanceId}`}\n ariaLabelledby={promptId}\n aria-describedby={this.validation[question.id] ? errorId : undefined}\n onDsChange={(event: CustomEvent<string>) => this.selectSingle(question, event.detail)}\n />\n {draft.otherSelected ? this.renderOtherInput(question, errorId) : null}\n </div>\n );\n }\n\n private renderMultipleChoice(question: AgentQuestion, errorId: string) {\n const draft = this.drafts[question.id];\n const values = Array.isArray(draft.value) ? draft.value : [];\n const detachedForm = `ds-agent-questionnaire-detached-${this.instanceId}`;\n return (\n <div class=\"questionnaire__multiple-choice\">\n {question.choices?.map(choice => (\n <ds-checkbox\n key={choice.value}\n data-question-control\n size=\"lg\"\n label={choice.label}\n description={choice.description}\n value={choice.value}\n checked={values.includes(choice.value)}\n disabled={this.disabled}\n form={detachedForm}\n onDsChange={(event: CustomEvent<boolean>) => {\n this.updateDraft(question.id, {\n value: event.detail\n ? [...values, choice.value]\n : values.filter(value => value !== choice.value),\n });\n }}\n />\n ))}\n {question.allowOther ? (\n <div class=\"questionnaire__other-choice\">\n <ds-checkbox\n data-question-control\n size=\"lg\"\n label={this.copy.other}\n checked={draft.otherSelected}\n disabled={this.disabled}\n form={detachedForm}\n onDsChange={(event: CustomEvent<boolean>) => {\n this.updateDraft(question.id, { otherSelected: event.detail });\n if (event.detail) {\n requestAnimationFrame(() =>\n this.el.querySelector<HTMLInputElement>('[data-question-other-input]')?.focus()\n );\n }\n }}\n />\n {draft.otherSelected ? this.renderOtherInput(question, errorId) : null}\n </div>\n ) : null}\n </div>\n );\n }\n\n private renderOtherInput(question: AgentQuestion, errorId: string) {\n const draft = this.drafts[question.id];\n return (\n <input\n class=\"questionnaire__other-input\"\n data-question-control\n data-question-other-input\n type=\"text\"\n form={`ds-agent-questionnaire-detached-${this.instanceId}`}\n value={draft.otherText}\n placeholder={this.copy.otherPlaceholder}\n aria-label={this.copy.other}\n aria-invalid={this.validation[question.id] ? 'true' : undefined}\n aria-describedby={this.validation[question.id] ? errorId : undefined}\n disabled={this.disabled}\n onInput={(event: Event) =>\n this.updateDraft(question.id, {\n otherText: (event.target as HTMLInputElement).value,\n })\n }\n />\n );\n }\n\n private renderQuestion(question: AgentQuestion) {\n const draft = this.drafts[question.id];\n const errorId = `ds-agent-questionnaire-${this.instanceId}-${this.currentStep}-error`;\n const promptId = `ds-agent-questionnaire-${this.instanceId}-${this.currentStep}-prompt`;\n return (\n <div class=\"questionnaire__body\">\n <div class=\"questionnaire__prompt\">\n <ds-text as=\"h2\" variant=\"text-title-small\" textId={promptId}>\n {question.question}\n </ds-text>\n {question.description ? (\n <ds-text variant=\"text-body-small\" color=\"secondary\">\n {question.description}\n </ds-text>\n ) : null}\n </div>\n {question.type === 'text' ? (\n <textarea\n class=\"questionnaire__text-input\"\n data-question-control\n value={typeof draft.value === 'string' ? draft.value : ''}\n form={`ds-agent-questionnaire-detached-${this.instanceId}`}\n placeholder={question.placeholder}\n aria-label={question.question}\n aria-invalid={this.validation[question.id] ? 'true' : undefined}\n aria-describedby={this.validation[question.id] ? errorId : undefined}\n disabled={this.disabled}\n rows={4}\n onInput={(event: Event) =>\n this.updateDraft(question.id, {\n value: (event.target as HTMLTextAreaElement).value,\n })\n }\n />\n ) : (\n <fieldset\n class=\"questionnaire__choices\"\n aria-describedby={this.validation[question.id] ? errorId : undefined}\n >\n <legend class=\"ds-visually-hidden\">{question.question}</legend>\n {question.type === 'single'\n ? this.renderSingleChoice(question, promptId, errorId)\n : this.renderMultipleChoice(question, errorId)}\n </fieldset>\n )}\n {this.validation[question.id] ? (\n <div id={errorId} class=\"questionnaire__error\" role=\"alert\">\n <ds-icon name=\"ErrorTriangle\" size=\"xs\" color=\"inherit\" />\n <ds-text variant=\"text-body-small\" color=\"negative\">\n {this.validation[question.id]}\n </ds-text>\n </div>\n ) : null}\n </div>\n );\n }\n\n private renderAnswered() {\n return (\n <div class=\"questionnaire questionnaire--answered\">\n <ol class=\"questionnaire__summary\">\n {this.questions.map(question => (\n <li>\n <ds-text variant=\"text-body-small\" emphasis>\n {question.question}\n </ds-text>\n <ds-text variant=\"text-body-small\" color=\"secondary\">\n {formatQuestionAnswer(\n question,\n this.answers.find(answer => answer.questionId === question.id)\n )}\n </ds-text>\n </li>\n ))}\n </ol>\n </div>\n );\n }\n\n render() {\n if (this.status === 'preparing') {\n return (\n <Host>\n <div class=\"questionnaire questionnaire--preparing\" role=\"status\">\n <ds-loader size=\"sm\" />\n <ds-text variant=\"text-body-medium\">{this.copy.preparing}</ds-text>\n </div>\n </Host>\n );\n }\n if (!this.questions?.length) return null;\n if (this.status === 'answered') return <Host>{this.renderAnswered()}</Host>;\n\n const question = this.questions[this.currentStep];\n const last = this.currentStep === this.questions.length - 1;\n const canSkip = Boolean(question.skippable && !question.required);\n return (\n <Host>\n <section\n class={{\n questionnaire: true,\n 'questionnaire--submitting': this.disabled,\n 'questionnaire--error': this.status === 'error',\n }}\n aria-busy={this.disabled ? 'true' : undefined}\n aria-label={question.question}\n >\n <header class=\"questionnaire__header\">\n <ds-text variant=\"text-caption\" color=\"secondary\">\n {this.progressLabel()}\n </ds-text>\n {this.allowCancel ? (\n <ds-tooltip label={this.copy.cancel} side=\"top\" size=\"sm\">\n <ds-button-unfilled\n data-question-action\n variant=\"icon\"\n icon=\"Cross\"\n size=\"sm\"\n aria-label={this.copy.cancel}\n hasBorder={false}\n isInactive={this.disabled}\n onDsClick={() => this.dsCancel.emit({ requestId: this.requestId })}\n />\n </ds-tooltip>\n ) : null}\n </header>\n {this.renderQuestion(question)}\n {this.status === 'error' && this.errorMessage ? (\n <div class=\"questionnaire__submission-error\" role=\"alert\">\n <ds-icon name=\"ErrorTriangle\" size=\"xs\" color=\"inherit\" />\n <ds-text variant=\"text-body-small\" color=\"negative\">\n {this.errorMessage}\n </ds-text>\n </div>\n ) : null}\n <footer class=\"questionnaire__actions\">\n <div class=\"questionnaire__actions-leading\">\n {this.currentStep > 0 ? (\n <div class=\"questionnaire__action-item\">\n <ds-button-unfilled\n data-question-action\n label={this.copy.previous}\n size=\"sm\"\n width=\"fill\"\n isInactive={this.disabled}\n onDsClick={() => this.move(this.currentStep - 1)}\n />\n </div>\n ) : null}\n {canSkip ? (\n <div class=\"questionnaire__action-item\">\n <ds-button-unfilled\n data-question-action\n label={this.copy.skip}\n size=\"sm\"\n width=\"fill\"\n hasBorder={false}\n isInactive={this.disabled}\n onDsClick={() => this.skip(question)}\n />\n </div>\n ) : null}\n </div>\n <div class=\"questionnaire__action-item questionnaire__action-item--primary\">\n <ds-button-filled\n data-question-action\n label={last ? this.copy.answer : this.copy.next}\n size=\"sm\"\n width=\"fill\"\n isLoading={this.disabled}\n onDsClick={() => (last ? this.submit() : this.next(question))}\n />\n </div>\n </footer>\n </section>\n </Host>\n );\n }\n}\n",
|
|
274
|
+
"content": "import {\n Component,\n Element,\n Event,\n EventEmitter,\n h,\n Host,\n Method,\n Prop,\n State,\n Watch,\n} from '@stencil/core';\nimport type {\n AgentQuestion,\n AgentQuestionAnswer,\n AgentQuestionnaireAnswerEventDetail,\n AgentQuestionnaireCancelEventDetail,\n AgentQuestionnaireLabels,\n AgentQuestionnaireStatus,\n} from '../conversation-types';\nimport {\n createQuestionnaireDrafts,\n formatQuestionAnswer,\n normalizeQuestionAnswer,\n validateQuestionDraft,\n} from './agent-questionnaire-model';\nimport type { AgentQuestionDraft, AgentQuestionDrafts } from './agent-questionnaire-model';\n\nconst DEFAULT_LABELS: AgentQuestionnaireLabels = {\n progress: 'Question {current} of {total}',\n previous: 'Previous',\n next: 'Next',\n answer: 'Answer',\n skip: 'Skip',\n cancel: 'Cancel',\n other: 'Other',\n otherPlaceholder: 'Enter another answer',\n preparing: 'Preparing questions…',\n};\n\nlet questionnaireId = 0;\n\n@Component({\n tag: 'ds-agent-questionnaire',\n styleUrl: 'AgentQuestionnaire.css',\n scoped: true,\n})\nexport class AgentQuestionnaire {\n @Element() el!: HTMLElement;\n\n @Prop() requestId!: string;\n @Prop() questions!: AgentQuestion[];\n @Prop() answers: AgentQuestionAnswer[] = [];\n @Prop() status: AgentQuestionnaireStatus = 'ready';\n @Prop() errorMessage?: string;\n @Prop() allowCancel: boolean = false;\n @Prop() labels?: Partial<AgentQuestionnaireLabels>;\n\n @Event() dsAnswer!: EventEmitter<AgentQuestionnaireAnswerEventDetail>;\n @Event() dsCancel!: EventEmitter<AgentQuestionnaireCancelEventDetail>;\n\n @State() private currentStep = 0;\n @State() private drafts: AgentQuestionDrafts = {};\n @State() private validation: Record<string, string> = {};\n\n private readonly instanceId = ++questionnaireId;\n private readonly otherValue = `__ds-agent-questionnaire-other-${this.instanceId}`;\n private answerPending = false;\n private resetScheduled = false;\n private draftRequestId?: string;\n private answersSnapshot: AgentQuestionAnswer[] = [];\n private waitingForRequestAnswers = false;\n\n componentWillLoad() {\n this.draftRequestId = this.requestId;\n this.answersSnapshot = this.answers;\n this.resetDraft();\n }\n\n @Watch('requestId')\n @Watch('questions')\n @Watch('answers')\n handleQuestionnaireInputChange() {\n this.scheduleDraftReset();\n }\n\n @Watch('status')\n handleStatusChange(status: AgentQuestionnaireStatus) {\n if (status !== 'submitting') this.answerPending = false;\n }\n\n @Method()\n async setFocus(): Promise<void> {\n this.focusCurrentControl();\n }\n\n private get copy(): AgentQuestionnaireLabels {\n return { ...DEFAULT_LABELS, ...this.labels };\n }\n\n private get disabled(): boolean {\n return this.status === 'submitting';\n }\n\n private resetDraft(answers = this.answers) {\n this.currentStep = 0;\n this.drafts = createQuestionnaireDrafts(this.questions ?? [], answers ?? []);\n this.validation = {};\n this.answerPending = false;\n }\n\n private scheduleDraftReset() {\n if (this.resetScheduled) return;\n this.resetScheduled = true;\n queueMicrotask(() => {\n this.resetScheduled = false;\n const requestChanged = this.requestId !== this.draftRequestId;\n const answersChanged = this.answers !== this.answersSnapshot;\n if (requestChanged && !answersChanged) {\n this.waitingForRequestAnswers = true;\n } else if (answersChanged) {\n this.waitingForRequestAnswers = false;\n }\n this.resetDraft(this.waitingForRequestAnswers ? [] : this.answers);\n this.draftRequestId = this.requestId;\n this.answersSnapshot = this.answers;\n });\n }\n\n private focusCurrentControl(preferInvalid = false) {\n const focus = () => {\n const invalidControl = preferInvalid\n ? this.el.querySelector<HTMLElement>(\n '[data-question-control][aria-invalid=\"true\"]:not([disabled])'\n )\n : null;\n const control =\n invalidControl ??\n this.el.querySelector<HTMLElement>(\n '[data-question-control]:not([disabled]):not([is-inactive])'\n );\n const action = this.el.querySelector<HTMLElement>(\n '[data-question-action]:not([is-inactive]):not([is-loading])'\n );\n const target = (control ?? action) as\n | (HTMLElement & { setFocus?: () => Promise<void> })\n | null;\n if (target?.setFocus) void target.setFocus();\n else target?.focus();\n };\n focus();\n requestAnimationFrame(focus);\n setTimeout(focus, 0);\n }\n\n private updateDraft(questionId: string, update: Partial<AgentQuestionDraft>) {\n const previous = this.drafts[questionId] ?? {\n value: '',\n otherSelected: false,\n otherText: '',\n skipped: false,\n };\n this.drafts = {\n ...this.drafts,\n [questionId]: { ...previous, ...update, skipped: false },\n };\n if (this.validation[questionId]) {\n const { [questionId]: _removed, ...rest } = this.validation;\n this.validation = rest;\n }\n }\n\n private validate(question: AgentQuestion): boolean {\n const message = validateQuestionDraft(question, this.drafts[question.id]);\n if (!message) return true;\n this.validation = { ...this.validation, [question.id]: message };\n this.focusCurrentControl(true);\n return false;\n }\n\n private move(step: number) {\n this.currentStep = Math.min(Math.max(step, 0), this.questions.length - 1);\n this.focusCurrentControl();\n }\n\n private next(question: AgentQuestion) {\n if (!this.validate(question)) return;\n this.move(this.currentStep + 1);\n }\n\n private skip(question: AgentQuestion) {\n if (question.required) return;\n this.drafts = {\n ...this.drafts,\n [question.id]: {\n ...(this.drafts[question.id] ?? {\n value: '',\n otherSelected: false,\n otherText: '',\n }),\n skipped: true,\n },\n };\n if (this.currentStep < this.questions.length - 1) this.move(this.currentStep + 1);\n else this.submit();\n }\n\n private submit() {\n if (this.disabled || this.answerPending) return;\n const invalidIndex = this.questions.findIndex(question => !this.validateWithoutFocus(question));\n if (invalidIndex >= 0) {\n this.currentStep = invalidIndex;\n this.focusCurrentControl(true);\n return;\n }\n\n this.answerPending = true;\n this.dsAnswer.emit({\n requestId: this.requestId,\n answers: this.questions.map(question =>\n normalizeQuestionAnswer(question, this.drafts[question.id])\n ),\n });\n }\n\n private validateWithoutFocus(question: AgentQuestion): boolean {\n const message = validateQuestionDraft(question, this.drafts[question.id]);\n if (!message) return true;\n this.validation = { ...this.validation, [question.id]: message };\n return false;\n }\n\n private progressLabel(): string {\n return this.copy.progress\n .split('{current}')\n .join(String(this.currentStep + 1))\n .split('{total}')\n .join(String(this.questions.length));\n }\n\n private selectSingle(question: AgentQuestion, value: string) {\n const otherSelected = value === this.otherValue;\n this.updateDraft(question.id, {\n value: otherSelected ? '' : value,\n otherSelected,\n });\n if (otherSelected) {\n requestAnimationFrame(() =>\n this.el.querySelector<HTMLInputElement>('[data-question-other-input]')?.focus()\n );\n }\n }\n\n private renderSingleChoice(question: AgentQuestion, promptId: string, errorId: string) {\n const draft = this.drafts[question.id];\n const value = draft.otherSelected\n ? this.otherValue\n : typeof draft.value === 'string'\n ? draft.value\n : '';\n const options = [\n ...(question.choices ?? []),\n ...(question.allowOther ? [{ value: this.otherValue, label: this.copy.other }] : []),\n ];\n return (\n <div class=\"questionnaire__single-choice\">\n <ds-radio\n data-question-control\n size=\"lg\"\n options={options}\n value={value}\n disabled={this.disabled}\n form={`ds-agent-questionnaire-detached-${this.instanceId}`}\n ariaLabelledby={promptId}\n aria-describedby={this.validation[question.id] ? errorId : undefined}\n onDsChange={(event: CustomEvent<string>) => this.selectSingle(question, event.detail)}\n />\n {draft.otherSelected ? this.renderOtherInput(question, errorId) : null}\n </div>\n );\n }\n\n private renderMultipleChoice(question: AgentQuestion, errorId: string) {\n const draft = this.drafts[question.id];\n const values = Array.isArray(draft.value) ? draft.value : [];\n const detachedForm = `ds-agent-questionnaire-detached-${this.instanceId}`;\n return (\n <div class=\"questionnaire__multiple-choice\">\n {question.choices?.map(choice => (\n <ds-checkbox\n key={choice.value}\n data-question-control\n size=\"lg\"\n label={choice.label}\n description={choice.description}\n value={choice.value}\n checked={values.includes(choice.value)}\n disabled={this.disabled}\n form={detachedForm}\n onDsChange={(event: CustomEvent<boolean>) => {\n this.updateDraft(question.id, {\n value: event.detail\n ? [...values, choice.value]\n : values.filter(value => value !== choice.value),\n });\n }}\n />\n ))}\n {question.allowOther ? (\n <div class=\"questionnaire__other-choice\">\n <ds-checkbox\n data-question-control\n size=\"lg\"\n label={this.copy.other}\n checked={draft.otherSelected}\n disabled={this.disabled}\n form={detachedForm}\n onDsChange={(event: CustomEvent<boolean>) => {\n this.updateDraft(question.id, { otherSelected: event.detail });\n if (event.detail) {\n requestAnimationFrame(() =>\n this.el.querySelector<HTMLInputElement>('[data-question-other-input]')?.focus()\n );\n }\n }}\n />\n {draft.otherSelected ? this.renderOtherInput(question, errorId) : null}\n </div>\n ) : null}\n </div>\n );\n }\n\n private renderOtherInput(question: AgentQuestion, errorId: string) {\n const draft = this.drafts[question.id];\n return (\n <input\n class=\"questionnaire__other-input\"\n data-question-control\n data-question-other-input\n type=\"text\"\n form={`ds-agent-questionnaire-detached-${this.instanceId}`}\n value={draft.otherText}\n placeholder={this.copy.otherPlaceholder}\n aria-label={this.copy.other}\n aria-invalid={this.validation[question.id] ? 'true' : undefined}\n aria-describedby={this.validation[question.id] ? errorId : undefined}\n disabled={this.disabled}\n onInput={(event: Event) =>\n this.updateDraft(question.id, {\n otherText: (event.target as HTMLInputElement).value,\n })\n }\n />\n );\n }\n\n private renderQuestion(question: AgentQuestion) {\n const draft = this.drafts[question.id];\n const errorId = `ds-agent-questionnaire-${this.instanceId}-${this.currentStep}-error`;\n const promptId = `ds-agent-questionnaire-${this.instanceId}-${this.currentStep}-prompt`;\n return (\n <div class=\"questionnaire__body\">\n <div class=\"questionnaire__prompt\">\n <ds-text as=\"h2\" variant=\"text-title-small\" textId={promptId}>\n {question.question}\n </ds-text>\n {question.description ? (\n <ds-text variant=\"text-body-small\" color=\"secondary\">\n {question.description}\n </ds-text>\n ) : null}\n </div>\n {question.type === 'text' ? (\n <textarea\n class=\"questionnaire__text-input\"\n data-question-control\n value={typeof draft.value === 'string' ? draft.value : ''}\n form={`ds-agent-questionnaire-detached-${this.instanceId}`}\n placeholder={question.placeholder}\n aria-label={question.question}\n aria-invalid={this.validation[question.id] ? 'true' : undefined}\n aria-describedby={this.validation[question.id] ? errorId : undefined}\n disabled={this.disabled}\n rows={4}\n onInput={(event: Event) =>\n this.updateDraft(question.id, {\n value: (event.target as HTMLTextAreaElement).value,\n })\n }\n />\n ) : (\n <fieldset\n class=\"questionnaire__choices\"\n aria-describedby={this.validation[question.id] ? errorId : undefined}\n >\n <legend class=\"ds-visually-hidden\">{question.question}</legend>\n {question.type === 'single'\n ? this.renderSingleChoice(question, promptId, errorId)\n : this.renderMultipleChoice(question, errorId)}\n </fieldset>\n )}\n {this.validation[question.id] ? (\n <div id={errorId} class=\"questionnaire__error\" role=\"alert\">\n <ds-icon name=\"ExclamationTriangle\" size=\"xs\" color=\"inherit\" />\n <ds-text variant=\"text-body-small\" color=\"negative\">\n {this.validation[question.id]}\n </ds-text>\n </div>\n ) : null}\n </div>\n );\n }\n\n private renderAnswered() {\n return (\n <div class=\"questionnaire questionnaire--answered\">\n <ol class=\"questionnaire__summary\">\n {this.questions.map(question => (\n <li>\n <ds-text variant=\"text-body-small\" emphasis>\n {question.question}\n </ds-text>\n <ds-text variant=\"text-body-small\" color=\"secondary\">\n {formatQuestionAnswer(\n question,\n this.answers.find(answer => answer.questionId === question.id)\n )}\n </ds-text>\n </li>\n ))}\n </ol>\n </div>\n );\n }\n\n render() {\n if (this.status === 'preparing') {\n return (\n <Host>\n <div class=\"questionnaire questionnaire--preparing\" role=\"status\">\n <ds-loader size=\"sm\" />\n <ds-text variant=\"text-body-medium\">{this.copy.preparing}</ds-text>\n </div>\n </Host>\n );\n }\n if (!this.questions?.length) return null;\n if (this.status === 'answered') return <Host>{this.renderAnswered()}</Host>;\n\n const question = this.questions[this.currentStep];\n const last = this.currentStep === this.questions.length - 1;\n const canSkip = Boolean(question.skippable && !question.required);\n return (\n <Host>\n <section\n class={{\n questionnaire: true,\n 'questionnaire--submitting': this.disabled,\n 'questionnaire--error': this.status === 'error',\n }}\n aria-busy={this.disabled ? 'true' : undefined}\n aria-label={question.question}\n >\n <header class=\"questionnaire__header\">\n <ds-text variant=\"text-caption\" color=\"secondary\">\n {this.progressLabel()}\n </ds-text>\n {this.allowCancel ? (\n <ds-tooltip label={this.copy.cancel} side=\"top\" size=\"sm\">\n <ds-button-unfilled\n data-question-action\n variant=\"icon\"\n icon=\"Cross\"\n size=\"sm\"\n aria-label={this.copy.cancel}\n hasBorder={false}\n isInactive={this.disabled}\n onDsClick={() => this.dsCancel.emit({ requestId: this.requestId })}\n />\n </ds-tooltip>\n ) : null}\n </header>\n {this.renderQuestion(question)}\n {this.status === 'error' && this.errorMessage ? (\n <div class=\"questionnaire__submission-error\" role=\"alert\">\n <ds-icon name=\"ExclamationTriangle\" size=\"xs\" color=\"inherit\" />\n <ds-text variant=\"text-body-small\" color=\"negative\">\n {this.errorMessage}\n </ds-text>\n </div>\n ) : null}\n <footer class=\"questionnaire__actions\">\n <div class=\"questionnaire__actions-leading\">\n {this.currentStep > 0 ? (\n <div class=\"questionnaire__action-item\">\n <ds-button-unfilled\n data-question-action\n label={this.copy.previous}\n size=\"sm\"\n width=\"fill\"\n isInactive={this.disabled}\n onDsClick={() => this.move(this.currentStep - 1)}\n />\n </div>\n ) : null}\n {canSkip ? (\n <div class=\"questionnaire__action-item\">\n <ds-button-unfilled\n data-question-action\n label={this.copy.skip}\n size=\"sm\"\n width=\"fill\"\n hasBorder={false}\n isInactive={this.disabled}\n onDsClick={() => this.skip(question)}\n />\n </div>\n ) : null}\n </div>\n <div class=\"questionnaire__action-item questionnaire__action-item--primary\">\n <ds-button-filled\n data-question-action\n label={last ? this.copy.answer : this.copy.next}\n size=\"sm\"\n width=\"fill\"\n isLoading={this.disabled}\n onDsClick={() => (last ? this.submit() : this.next(question))}\n />\n </div>\n </footer>\n </section>\n </Host>\n );\n }\n}\n",
|
|
270
275
|
"type": "registry:ui"
|
|
271
276
|
},
|
|
272
277
|
{
|
|
@@ -56,7 +56,18 @@
|
|
|
56
56
|
"label": "Prose distribution decision",
|
|
57
57
|
"path": "docs/prose-foundation.md"
|
|
58
58
|
}
|
|
59
|
-
]
|
|
59
|
+
],
|
|
60
|
+
"styling": {
|
|
61
|
+
"rationale": "Structured response parts own their recipes; the host permits application layout and the public prose measure.",
|
|
62
|
+
"protected": [],
|
|
63
|
+
"customProperties": [
|
|
64
|
+
{
|
|
65
|
+
"name": "--ds-prose-text-max-inline-size",
|
|
66
|
+
"source": "src/wc/styles/prose.css",
|
|
67
|
+
"reason": "Application-owned readable prose measure."
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
}
|
|
60
71
|
},
|
|
61
72
|
"api": {
|
|
62
73
|
"props": {
|
|
@@ -229,7 +240,7 @@
|
|
|
229
240
|
"peerDependencies": {
|
|
230
241
|
"required": [
|
|
231
242
|
"@ds-mo/tokens ^6.5.1",
|
|
232
|
-
"@ds-mo/icons ^
|
|
243
|
+
"@ds-mo/icons ^8.1.0"
|
|
233
244
|
],
|
|
234
245
|
"frameworks": "Custom Elements; React 18/19 wrappers; Vue 3 wrappers; Angular 19-22 standalone adapters."
|
|
235
246
|
}
|
|
@@ -43,7 +43,12 @@
|
|
|
43
43
|
"label": "Source safety and disclosure stories",
|
|
44
44
|
"path": "src/wc/components/AgentSourceList/AgentSourceList.stories.ts"
|
|
45
45
|
}
|
|
46
|
-
]
|
|
46
|
+
],
|
|
47
|
+
"styling": {
|
|
48
|
+
"rationale": "Source rows own their recipes; the disclosure host remains an application layout box.",
|
|
49
|
+
"protected": [],
|
|
50
|
+
"customProperties": []
|
|
51
|
+
}
|
|
47
52
|
},
|
|
48
53
|
"api": {
|
|
49
54
|
"props": {
|
|
@@ -148,7 +153,7 @@
|
|
|
148
153
|
"peerDependencies": {
|
|
149
154
|
"required": [
|
|
150
155
|
"@ds-mo/tokens ^6.5.1",
|
|
151
|
-
"@ds-mo/icons ^
|
|
156
|
+
"@ds-mo/icons ^8.1.0"
|
|
152
157
|
],
|
|
153
158
|
"frameworks": "Custom Elements; React 18/19 wrappers; Vue 3 wrappers; Angular 19-22 standalone adapters."
|
|
154
159
|
}
|
|
@@ -53,7 +53,12 @@
|
|
|
53
53
|
"label": "Font ownership and loading",
|
|
54
54
|
"path": "docs/font-ownership.md"
|
|
55
55
|
}
|
|
56
|
-
]
|
|
56
|
+
],
|
|
57
|
+
"styling": {
|
|
58
|
+
"rationale": "Slotted tool summaries and results retain application-owned styling and layout.",
|
|
59
|
+
"protected": [],
|
|
60
|
+
"customProperties": []
|
|
61
|
+
}
|
|
57
62
|
},
|
|
58
63
|
"api": {
|
|
59
64
|
"props": {
|
|
@@ -228,7 +233,7 @@
|
|
|
228
233
|
"peerDependencies": {
|
|
229
234
|
"required": [
|
|
230
235
|
"@ds-mo/tokens ^6.5.1",
|
|
231
|
-
"@ds-mo/icons ^
|
|
236
|
+
"@ds-mo/icons ^8.1.0"
|
|
232
237
|
],
|
|
233
238
|
"frameworks": "Custom Elements; React 18/19 wrappers; Vue 3 wrappers; Angular 19-22 standalone adapters."
|
|
234
239
|
}
|
|
@@ -252,7 +257,7 @@
|
|
|
252
257
|
},
|
|
253
258
|
{
|
|
254
259
|
"path": "src/wc/components/AgentToolCall/AgentToolCall.tsx",
|
|
255
|
-
"content": "import { Component, Element, Event, EventEmitter, h, Host, Prop, State } from '@stencil/core';\nimport type { AgentToolState } from '../conversation-types';\n\nconst STATUS_LABELS: Record<AgentToolState, string> = {\n preparing: 'Preparing',\n queued: 'Queued',\n running: 'Running',\n 'waiting-for-user': 'Waiting for your response',\n success: 'Completed',\n error: 'Failed',\n denied: 'Permission denied',\n canceled: 'Canceled',\n};\n\n@Component({ tag: 'ds-agent-tool-call', styleUrl: 'AgentToolCall.css', scoped: true })\nexport class AgentToolCall {\n @Element() el!: HTMLElement;\n\n @Prop() name: string = '';\n @Prop() label: string = '';\n @Prop() state: AgentToolState = 'queued';\n @Prop() statusLabel?: string;\n @Prop() input?: unknown;\n @Prop() output?: unknown;\n @Prop() error?: string;\n @Prop() open: boolean = false;\n\n @Event() dsOpenChange!: EventEmitter<{ open: boolean }>;\n\n @State() private hasSummary = false;\n @State() private hasResult = false;\n @State() private hasPlainTextResult = false;\n @State() private hasDetails = false;\n\n private slotObserver?: MutationObserver;\n\n componentWillLoad() {\n this.syncSlots();\n }\n\n componentDidLoad() {\n this.syncSlots();\n this.slotObserver = new MutationObserver(() => this.syncSlots());\n this.slotObserver.observe(this.el, {\n childList: true,\n subtree: false,\n attributes: true,\n attributeFilter: ['slot'],\n });\n }\n\n disconnectedCallback() {\n this.slotObserver?.disconnect();\n }\n\n private syncSlots() {\n this.hasSummary = Boolean(this.el.querySelector('[slot=\"summary\"]'));\n const result = this.el.querySelector<HTMLElement>('[slot=\"result\"]');\n this.hasResult = Boolean(result);\n this.hasPlainTextResult = Boolean(\n result && result.childElementCount === 0 && result.textContent?.trim()\n );\n this.hasDetails = Boolean(this.el.querySelector('[slot=\"details\"]'));\n }\n\n private serialize(value: unknown) {\n if (value === undefined) return '';\n if (typeof value === 'string') return value;\n try {\n return JSON.stringify(value, null, 2);\n } catch {\n return String(value);\n }\n }\n\n private get active(): boolean {\n return ['preparing', 'queued', 'running'].includes(this.state);\n }\n\n private get expandable(): boolean {\n return (\n this.hasDetails ||\n this.input !== undefined ||\n this.output !== undefined ||\n Boolean(this.error)\n );\n }\n\n private renderStatusIcon() {\n if (this.active) return <ds-loader size=\"xs\" color=\"inherit\" />;\n if (this.state === 'success') {\n return <ds-icon name=\"Check\" size=\"xs\" color=\"inherit\" />;\n }\n if (this.state === 'waiting-for-user') {\n return <ds-icon name=\"Clock\" size=\"xs\" color=\"inherit\" />;\n }\n return <ds-icon name=\"
|
|
260
|
+
"content": "import { Component, Element, Event, EventEmitter, h, Host, Prop, State } from '@stencil/core';\nimport type { AgentToolState } from '../conversation-types';\n\nconst STATUS_LABELS: Record<AgentToolState, string> = {\n preparing: 'Preparing',\n queued: 'Queued',\n running: 'Running',\n 'waiting-for-user': 'Waiting for your response',\n success: 'Completed',\n error: 'Failed',\n denied: 'Permission denied',\n canceled: 'Canceled',\n};\n\n@Component({ tag: 'ds-agent-tool-call', styleUrl: 'AgentToolCall.css', scoped: true })\nexport class AgentToolCall {\n @Element() el!: HTMLElement;\n\n @Prop() name: string = '';\n @Prop() label: string = '';\n @Prop() state: AgentToolState = 'queued';\n @Prop() statusLabel?: string;\n @Prop() input?: unknown;\n @Prop() output?: unknown;\n @Prop() error?: string;\n @Prop() open: boolean = false;\n\n @Event() dsOpenChange!: EventEmitter<{ open: boolean }>;\n\n @State() private hasSummary = false;\n @State() private hasResult = false;\n @State() private hasPlainTextResult = false;\n @State() private hasDetails = false;\n\n private slotObserver?: MutationObserver;\n\n componentWillLoad() {\n this.syncSlots();\n }\n\n componentDidLoad() {\n this.syncSlots();\n this.slotObserver = new MutationObserver(() => this.syncSlots());\n this.slotObserver.observe(this.el, {\n childList: true,\n subtree: false,\n attributes: true,\n attributeFilter: ['slot'],\n });\n }\n\n disconnectedCallback() {\n this.slotObserver?.disconnect();\n }\n\n private syncSlots() {\n this.hasSummary = Boolean(this.el.querySelector('[slot=\"summary\"]'));\n const result = this.el.querySelector<HTMLElement>('[slot=\"result\"]');\n this.hasResult = Boolean(result);\n this.hasPlainTextResult = Boolean(\n result && result.childElementCount === 0 && result.textContent?.trim()\n );\n this.hasDetails = Boolean(this.el.querySelector('[slot=\"details\"]'));\n }\n\n private serialize(value: unknown) {\n if (value === undefined) return '';\n if (typeof value === 'string') return value;\n try {\n return JSON.stringify(value, null, 2);\n } catch {\n return String(value);\n }\n }\n\n private get active(): boolean {\n return ['preparing', 'queued', 'running'].includes(this.state);\n }\n\n private get expandable(): boolean {\n return (\n this.hasDetails ||\n this.input !== undefined ||\n this.output !== undefined ||\n Boolean(this.error)\n );\n }\n\n private renderStatusIcon() {\n if (this.active) return <ds-loader size=\"xs\" color=\"inherit\" />;\n if (this.state === 'success') {\n return <ds-icon name=\"Check\" size=\"xs\" color=\"inherit\" />;\n }\n if (this.state === 'waiting-for-user') {\n return <ds-icon name=\"Clock\" size=\"xs\" color=\"inherit\" />;\n }\n return <ds-icon name=\"ExclamationTriangle\" size=\"xs\" color=\"inherit\" />;\n }\n\n private renderSummary(expandable: boolean) {\n return (\n <span class=\"agent-tool__summary-content\">\n <span class=\"agent-tool__status-icon\" aria-hidden=\"true\">\n {this.renderStatusIcon()}\n </span>\n <span class=\"agent-tool__title\">\n {this.hasSummary ? (\n <slot name=\"summary\" />\n ) : (\n <ds-text as=\"span\" variant=\"text-body-small\" emphasis>\n {this.label || this.name}\n </ds-text>\n )}\n <ds-text\n class=\"agent-tool__state-label\"\n as=\"span\"\n variant=\"text-caption\"\n color={['error', 'denied'].includes(this.state) ? 'negative' : 'secondary'}\n >\n {this.statusLabel ?? STATUS_LABELS[this.state]}\n </ds-text>\n </span>\n {expandable ? (\n <ds-icon class=\"agent-tool__chevron\" name=\"ChevronRight\" size=\"xs\" color=\"inherit\" />\n ) : null}\n </span>\n );\n }\n\n private renderGenericDetails() {\n return (\n <div class=\"agent-tool__diagnostics\">\n {this.input !== undefined ? (\n <section>\n <ds-text variant=\"text-caption\" emphasis>\n Input\n </ds-text>\n <pre>{this.serialize(this.input)}</pre>\n </section>\n ) : null}\n {this.output !== undefined ? (\n <section>\n <ds-text variant=\"text-caption\" emphasis>\n Output\n </ds-text>\n <pre>{this.serialize(this.output)}</pre>\n </section>\n ) : null}\n {this.error ? (\n <section>\n <ds-text variant=\"text-caption\" emphasis>\n Error\n </ds-text>\n <pre>{this.error}</pre>\n </section>\n ) : null}\n </div>\n );\n }\n\n private handleToggle = (event: Event) => {\n this.dsOpenChange.emit({ open: (event.currentTarget as HTMLDetailsElement).open });\n };\n\n render() {\n const expandable = this.expandable;\n return (\n <Host>\n <div class={`agent-tool agent-tool--${this.state}`}>\n {expandable ? (\n <details open={this.open} onToggle={this.handleToggle}>\n <summary>{this.renderSummary(true)}</summary>\n <div class=\"agent-tool__details\">\n {this.hasDetails ? <slot name=\"details\" /> : this.renderGenericDetails()}\n </div>\n </details>\n ) : (\n <div class=\"agent-tool__row\">{this.renderSummary(false)}</div>\n )}\n {this.hasResult ? (\n <div\n class={{\n 'agent-tool__result': true,\n 'agent-tool__result--plain-text': this.hasPlainTextResult,\n }}\n >\n <slot name=\"result\" />\n </div>\n ) : null}\n </div>\n </Host>\n );\n }\n}\n",
|
|
256
261
|
"type": "registry:ui"
|
|
257
262
|
}
|
|
258
263
|
]
|
|
@@ -39,7 +39,12 @@
|
|
|
39
39
|
"label": "Storybook examples",
|
|
40
40
|
"path": "src/wc/components/MessageScroller/MessageScroller.stories.ts"
|
|
41
41
|
}
|
|
42
|
-
]
|
|
42
|
+
],
|
|
43
|
+
"styling": {
|
|
44
|
+
"rationale": "Attachment items own their recipes; the list host permits application layout.",
|
|
45
|
+
"protected": [],
|
|
46
|
+
"customProperties": []
|
|
47
|
+
}
|
|
43
48
|
},
|
|
44
49
|
"api": {
|
|
45
50
|
"props": {
|
|
@@ -112,7 +117,7 @@
|
|
|
112
117
|
"peerDependencies": {
|
|
113
118
|
"required": [
|
|
114
119
|
"@ds-mo/tokens ^6.5.1",
|
|
115
|
-
"@ds-mo/icons ^
|
|
120
|
+
"@ds-mo/icons ^8.1.0"
|
|
116
121
|
],
|
|
117
122
|
"frameworks": "Custom Elements; React 18/19 wrappers; Vue 3 wrappers; Angular 19-22 standalone adapters."
|
|
118
123
|
}
|
|
@@ -37,7 +37,27 @@
|
|
|
37
37
|
"label": "Storybook examples",
|
|
38
38
|
"path": "src/wc/components/Avatar/Avatar.stories.ts"
|
|
39
39
|
}
|
|
40
|
-
]
|
|
40
|
+
],
|
|
41
|
+
"styling": {
|
|
42
|
+
"rationale": "The component owns the protected recipes below; unspecified host properties and application layout remain customizable.",
|
|
43
|
+
"protected": [
|
|
44
|
+
{
|
|
45
|
+
"properties": [
|
|
46
|
+
"width",
|
|
47
|
+
"height",
|
|
48
|
+
"min-width",
|
|
49
|
+
"min-height",
|
|
50
|
+
"max-width",
|
|
51
|
+
"max-height"
|
|
52
|
+
],
|
|
53
|
+
"reason": "The primitive owns its size recipe; select size and position it through the parent layout.",
|
|
54
|
+
"props": [
|
|
55
|
+
"size"
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
"customProperties": []
|
|
60
|
+
}
|
|
41
61
|
},
|
|
42
62
|
"api": {
|
|
43
63
|
"props": {
|
|
@@ -149,7 +169,7 @@
|
|
|
149
169
|
"peerDependencies": {
|
|
150
170
|
"required": [
|
|
151
171
|
"@ds-mo/tokens ^6.5.1",
|
|
152
|
-
"@ds-mo/icons ^
|
|
172
|
+
"@ds-mo/icons ^8.1.0"
|
|
153
173
|
],
|
|
154
174
|
"frameworks": "Custom Elements; React 18/19 wrappers; Vue 3 wrappers; Angular 19-22 standalone adapters."
|
|
155
175
|
}
|
|
@@ -84,7 +84,35 @@
|
|
|
84
84
|
"label": "Storybook examples",
|
|
85
85
|
"path": "src/wc/components/Badge/Badge.stories.ts"
|
|
86
86
|
}
|
|
87
|
-
]
|
|
87
|
+
],
|
|
88
|
+
"styling": {
|
|
89
|
+
"rationale": "The component owns the protected recipes below; unspecified host properties and application layout remain customizable.",
|
|
90
|
+
"protected": [
|
|
91
|
+
{
|
|
92
|
+
"properties": [
|
|
93
|
+
"background",
|
|
94
|
+
"border",
|
|
95
|
+
"box-shadow",
|
|
96
|
+
"color"
|
|
97
|
+
],
|
|
98
|
+
"reason": "The component owns its surface and interaction treatment; use its semantic presentation props or compose an application-owned surrounding surface.",
|
|
99
|
+
"props": [
|
|
100
|
+
"background",
|
|
101
|
+
"surface",
|
|
102
|
+
"variant",
|
|
103
|
+
"hasRing"
|
|
104
|
+
]
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"properties": [
|
|
108
|
+
"border-radius"
|
|
109
|
+
],
|
|
110
|
+
"reason": "The component owns its surface radius; retain the shared recipe and customize an outer wrapper when needed.",
|
|
111
|
+
"props": []
|
|
112
|
+
}
|
|
113
|
+
],
|
|
114
|
+
"customProperties": []
|
|
115
|
+
}
|
|
88
116
|
},
|
|
89
117
|
"api": {
|
|
90
118
|
"props": {
|
|
@@ -264,7 +292,7 @@
|
|
|
264
292
|
"peerDependencies": {
|
|
265
293
|
"required": [
|
|
266
294
|
"@ds-mo/tokens ^6.5.1",
|
|
267
|
-
"@ds-mo/icons ^
|
|
295
|
+
"@ds-mo/icons ^8.1.0"
|
|
268
296
|
],
|
|
269
297
|
"frameworks": "Custom Elements; React 18/19 wrappers; Vue 3 wrappers; Angular 19-22 standalone adapters."
|
|
270
298
|
}
|
|
@@ -280,7 +308,7 @@
|
|
|
280
308
|
"files": [
|
|
281
309
|
{
|
|
282
310
|
"path": "src/wc/components/Badge/Badge.css",
|
|
283
|
-
"content": "@import '../../utils/visually-hidden.css';\n@import '../../utils/forced-colors.css';\n\n:host {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n container-name: ds-badge-gradient;\n pointer-events: none;\n vertical-align: middle;\n\n --_badge-bg: var(--color-background-bold-brand);\n --_badge-fg: var(--color-foreground-on-bold-background-primary);\n --_badge-ring: var(--color-background-secondary);\n --_badge-ring-width: var(--dimension-stroke-width-018);\n --_badge-gradient-context: var(--ds-shell-gradient-context, flat);\n}\n\n:host(.badge--force-gradient-background) {\n --_badge-gradient-context: gradient;\n}\n\n:host(.badge--force-flat-background) {\n --_badge-gradient-context: flat;\n}\n\n.badge__mark {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n box-sizing: border-box;\n color: var(--_badge-fg);\n background: var(--_badge-bg);\n box-shadow: 0 0 0 var(--_badge-ring-width) var(--_badge-ring);\n white-space: nowrap;\n}\n\n:host(.badge--no-ring) {\n --_badge-ring-width: 0;\n}\n\n:host(.badge--dot) .badge__mark {\n width: var(--dimension-size-075);\n height: var(--dimension-size-075);\n border-radius: var(--dimension-radius-half);\n}\n\n:host(.badge--counter) .badge__mark {\n width: auto;\n min-width: var(--dimension-size-150);\n height: var(--dimension-size-150);\n padding: 0 var(--dimension-space-025);\n border-radius: var(--dimension-radius-half);\n}\n\n/* Shell gradient ring — base fill + wash donut (option 3). */\n@container ds-badge-gradient style(--_badge-gradient-context: gradient) {\n /* Match the scoped base rule's specificity; Stencil does not yet scope style-query selectors. */\n .badge__mark.badge__mark {\n position: relative;\n z-index: 0;\n box-shadow: none;\n isolation: isolate;\n }\n\n .badge__mark::before,\n .badge__mark::after {\n content: '';\n position: absolute;\n inset: calc(-1 * var(--_badge-ring-width));\n border-radius: inherit;\n box-sizing: border-box;\n padding: var(--_badge-ring-width);\n pointer-events: none;\n /*
|
|
311
|
+
"content": "@import '../../utils/visually-hidden.css';\n@import '../../utils/forced-colors.css';\n\n:host {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n container-name: ds-badge-gradient;\n pointer-events: none;\n vertical-align: middle;\n\n --_badge-bg: var(--color-background-bold-brand);\n --_badge-fg: var(--color-foreground-on-bold-background-primary);\n --_badge-ring: var(--color-background-secondary);\n --_badge-ring-width: var(--dimension-stroke-width-018);\n --_badge-gradient-context: var(--ds-shell-gradient-context, flat);\n}\n\n:host(.badge--force-gradient-background) {\n --_badge-gradient-context: gradient;\n}\n\n:host(.badge--force-flat-background) {\n --_badge-gradient-context: flat;\n}\n\n.badge__mark {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n box-sizing: border-box;\n color: var(--_badge-fg);\n background: var(--_badge-bg);\n box-shadow: 0 0 0 var(--_badge-ring-width) var(--_badge-ring);\n white-space: nowrap;\n}\n\n:host(.badge--no-ring) {\n --_badge-ring-width: 0;\n}\n\n:host(.badge--dot) .badge__mark {\n width: var(--dimension-size-075);\n height: var(--dimension-size-075);\n border-radius: var(--dimension-radius-half);\n}\n\n:host(.badge--counter) .badge__mark {\n width: auto;\n min-width: var(--dimension-size-150);\n height: var(--dimension-size-150);\n padding: 0 var(--dimension-space-025);\n border-radius: var(--dimension-radius-half);\n}\n\n/* Shell gradient ring — base fill + wash donut (option 3). */\n@container ds-badge-gradient style(--_badge-gradient-context: gradient) {\n /* Match the scoped base rule's specificity; Stencil does not yet scope style-query selectors. */\n .badge__mark.badge__mark {\n position: relative;\n z-index: 0;\n box-shadow: none;\n isolation: isolate;\n }\n\n .badge__mark::before,\n .badge__mark::after {\n content: '';\n position: absolute;\n inset: calc(-1 * var(--_badge-ring-width));\n border-radius: inherit;\n box-sizing: border-box;\n padding: var(--_badge-ring-width);\n pointer-events: none;\n /* eslint-disable compomo/color-no-hex -- mask luminance stops for ring cutout, not theme colors */\n -webkit-mask:\n linear-gradient(#fff 0 0) content-box,\n linear-gradient(#fff 0 0);\n -webkit-mask-composite: xor;\n mask:\n linear-gradient(#fff 0 0) content-box,\n linear-gradient(#fff 0 0);\n /* eslint-enable compomo/color-no-hex */\n mask-composite: exclude;\n }\n\n .badge__mark::before {\n z-index: -2;\n background-color: var(--_badge-ring);\n }\n\n .badge__mark::after {\n z-index: -1;\n background-image: var(--ds-shell-gradient-image, none);\n background-size: var(--ds-shell-gradient-size, auto);\n background-position: 0 0;\n background-repeat: no-repeat;\n background-attachment: fixed;\n opacity: var(--ds-shell-gradient-opacity, 1);\n }\n}\n\n@media (forced-colors: active) {\n .badge__mark {\n color: var(--ds-forced-color-on-selected);\n background: var(--ds-forced-color-selected);\n box-shadow: none;\n outline: var(--_badge-ring-width) solid var(--ds-forced-color-surface);\n }\n\n .badge__mark::before,\n .badge__mark::after {\n display: none;\n }\n}\n",
|
|
284
312
|
"type": "registry:ui"
|
|
285
313
|
},
|
|
286
314
|
{
|
|
@@ -91,7 +91,33 @@
|
|
|
91
91
|
"label": "Application shell contract",
|
|
92
92
|
"path": "agent/patterns/application-shell/pattern.agent.json"
|
|
93
93
|
}
|
|
94
|
-
]
|
|
94
|
+
],
|
|
95
|
+
"styling": {
|
|
96
|
+
"rationale": "The component owns the protected recipes below; unspecified host properties and application layout remain customizable.",
|
|
97
|
+
"protected": [
|
|
98
|
+
{
|
|
99
|
+
"properties": [
|
|
100
|
+
"background",
|
|
101
|
+
"border",
|
|
102
|
+
"box-shadow",
|
|
103
|
+
"color"
|
|
104
|
+
],
|
|
105
|
+
"reason": "The component owns its surface and interaction treatment; use its semantic presentation props or compose an application-owned surrounding surface.",
|
|
106
|
+
"props": [
|
|
107
|
+
"intent",
|
|
108
|
+
"contrast"
|
|
109
|
+
]
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"properties": [
|
|
113
|
+
"border-radius"
|
|
114
|
+
],
|
|
115
|
+
"reason": "The component owns its surface radius; retain the shared recipe and customize an outer wrapper when needed.",
|
|
116
|
+
"props": []
|
|
117
|
+
}
|
|
118
|
+
],
|
|
119
|
+
"customProperties": []
|
|
120
|
+
}
|
|
95
121
|
},
|
|
96
122
|
"api": {
|
|
97
123
|
"props": {
|
|
@@ -309,7 +335,7 @@
|
|
|
309
335
|
"peerDependencies": {
|
|
310
336
|
"required": [
|
|
311
337
|
"@ds-mo/tokens ^6.5.1",
|
|
312
|
-
"@ds-mo/icons ^
|
|
338
|
+
"@ds-mo/icons ^8.1.0"
|
|
313
339
|
],
|
|
314
340
|
"frameworks": "Custom Elements; React 18/19 wrappers; Vue 3 wrappers; Angular 19-22 standalone adapters."
|
|
315
341
|
}
|
|
@@ -332,7 +358,7 @@
|
|
|
332
358
|
},
|
|
333
359
|
{
|
|
334
360
|
"path": "src/wc/components/Banner/Banner.tsx",
|
|
335
|
-
"content": "import {\n Component,\n Element,\n Event,\n EventEmitter,\n h,\n Host,\n Prop,\n State,\n Watch,\n} from '@stencil/core';\nimport { resolveMotionTimeMs, TOKEN_DEFAULTS } from '../../utils';\n\nexport type BannerIntent = 'neutral' | 'brand' | 'positive' | 'warning' | 'caution' | 'negative';\n\nexport type BannerContrast = 'faint' | 'medium' | 'strong' | 'bold';\nexport type BannerAnnouncement = 'none' | 'polite' | 'assertive';\nexport type BannerOrientation = 'horizontal' | 'vertical';\n\ntype BannerPhase = 'closed' | 'open' | 'closing';\n\n@Component({\n tag: 'ds-banner',\n styleUrl: 'Banner.css',\n scoped: true,\n})\nexport class Banner {\n @Element() el!: HTMLElement;\n\n /** Required explanatory copy. */\n @Prop() description!: string;\n /** Optional visual heading rendered before the description. */\n @Prop() heading: string = '';\n /** Semantic color family. */\n @Prop({ reflect: true }) intent: BannerIntent = 'neutral';\n /** Surface fill weight. */\n @Prop({ reflect: true }) contrast: BannerContrast = 'faint';\n /** Copy and action-lane arrangement, selected by the application. */\n @Prop({ reflect: true }) orientation: BannerOrientation = 'horizontal';\n /** Controlled visibility. Keep the element mounted until dsAfterClose when unmounting. */\n @Prop({ reflect: true }) open: boolean = true;\n /** Localized accessible label for the close control. */\n @Prop() dismissLabel: string = 'Dismiss banner';\n /** Live-region urgency, independent of visual intent. */\n @Prop() announcement: BannerAnnouncement = 'none';\n\n /** Emitted when the close control requests dismissal. The application controls open. */\n @Event() dsClose!: EventEmitter<MouseEvent>;\n /** Emitted once after controlled exit motion and layout collapse complete. */\n @Event() dsAfterClose!: EventEmitter<void>;\n\n @State() private rendered = false;\n @State() private phase: BannerPhase = 'closed';\n @State() private hasActions = false;\n\n private loaded = false;\n private transitionTimer: ReturnType<typeof setTimeout> | null = null;\n private closePending = false;\n\n componentWillLoad() {\n this.rendered = this.open;\n this.phase = this.open ? 'open' : 'closed';\n }\n\n componentDidLoad() {\n this.loaded = true;\n this.updateActionsPresence();\n }\n\n disconnectedCallback() {\n this.clearTransitionWork();\n }\n\n @Watch('open')\n onOpenChange(isOpen: boolean) {\n if (!this.loaded) return;\n if (isOpen) this.startOpen();\n else this.startClose();\n }\n\n private get closeMotionMs(): number {\n return resolveMotionTimeMs(TOKEN_DEFAULTS.motionShort3, TOKEN_DEFAULTS.animationDurationShort3);\n }\n\n private clearTransitionWork() {\n if (this.transitionTimer) clearTimeout(this.transitionTimer);\n this.transitionTimer = null;\n }\n\n private startOpen() {\n this.clearTransitionWork();\n this.closePending = false;\n this.rendered = true;\n this.phase = 'open';\n }\n\n private startClose() {\n this.clearTransitionWork();\n if (!this.rendered || this.phase === 'closed') {\n this.rendered = false;\n return;\n }\n\n this.closePending = true;\n this.phase = 'closing';\n const duration = this.closeMotionMs;\n if (duration <= 0) {\n this.finishClose();\n return;\n }\n this.transitionTimer = setTimeout(() => {\n this.transitionTimer = null;\n this.finishClose();\n }, duration);\n }\n\n private finishClose() {\n if (this.open || !this.closePending) return;\n this.closePending = false;\n this.rendered = false;\n this.phase = 'closed';\n this.dsAfterClose.emit();\n }\n\n private updateActionsPresence(slot?: HTMLSlotElement) {\n const actions = slot\n ? slot.assignedElements().filter(element => element.tagName === 'DS-BUTTON-UNFILLED')\n : Array.from(this.el.querySelectorAll('ds-button-unfilled[slot=\"actions\"]'));\n this.hasActions = actions.length > 0;\n }\n\n private requestClose(event: CustomEvent<MouseEvent>) {\n if (!this.open || this.phase === 'closing') return;\n this.dsClose.emit(event.detail);\n }\n\n private get role(): 'status' | 'alert' | undefined {\n if (this.announcement === 'polite') return 'status';\n if (this.announcement === 'assertive') return 'alert';\n return undefined;\n }\n\n private get ariaLive(): 'polite' | 'assertive' | undefined {\n return this.announcement === 'none' ? undefined : this.announcement;\n }\n\n render() {\n const closing = this.phase === 'closing';\n const hostClass = {\n banner: true,\n 'banner--open': this.phase === 'open',\n 'banner--closing': closing,\n 'banner--hidden': !this.rendered,\n 'banner--has-actions': this.hasActions,\n [`banner--orientation-${this.orientation}`]: true,\n [`banner--intent-${this.intent}`]: true,\n [`banner--contrast-${this.contrast}`]: true,\n };\n\n return (\n <Host class={hostClass}>\n <div class=\"banner-browser-edge\" aria-hidden=\"true\" />\n <div class=\"banner-overflow\">\n <section\n class=\"banner-surface ds-chrome-header ds-chrome-header--wrapping\"\n role={this.role}\n aria-live={this.ariaLive}\n aria-atomic={this.announcement === 'none' ? undefined : 'true'}\n aria-hidden={closing ? 'true' : undefined}\n inert={closing ? true : undefined}\n data-phase={this.phase}\n >\n {/* eslint-disable-next-line
|
|
361
|
+
"content": "import {\n Component,\n Element,\n Event,\n EventEmitter,\n h,\n Host,\n Prop,\n State,\n Watch,\n} from '@stencil/core';\nimport { resolveMotionTimeMs, TOKEN_DEFAULTS } from '../../utils';\n\nexport type BannerIntent = 'neutral' | 'brand' | 'positive' | 'warning' | 'caution' | 'negative';\n\nexport type BannerContrast = 'faint' | 'medium' | 'strong' | 'bold';\nexport type BannerAnnouncement = 'none' | 'polite' | 'assertive';\nexport type BannerOrientation = 'horizontal' | 'vertical';\n\ntype BannerPhase = 'closed' | 'open' | 'closing';\n\n@Component({\n tag: 'ds-banner',\n styleUrl: 'Banner.css',\n scoped: true,\n})\nexport class Banner {\n @Element() el!: HTMLElement;\n\n /** Required explanatory copy. */\n @Prop() description!: string;\n /** Optional visual heading rendered before the description. */\n @Prop() heading: string = '';\n /** Semantic color family. */\n @Prop({ reflect: true }) intent: BannerIntent = 'neutral';\n /** Surface fill weight. */\n @Prop({ reflect: true }) contrast: BannerContrast = 'faint';\n /** Copy and action-lane arrangement, selected by the application. */\n @Prop({ reflect: true }) orientation: BannerOrientation = 'horizontal';\n /** Controlled visibility. Keep the element mounted until dsAfterClose when unmounting. */\n @Prop({ reflect: true }) open: boolean = true;\n /** Localized accessible label for the close control. */\n @Prop() dismissLabel: string = 'Dismiss banner';\n /** Live-region urgency, independent of visual intent. */\n @Prop() announcement: BannerAnnouncement = 'none';\n\n /** Emitted when the close control requests dismissal. The application controls open. */\n @Event() dsClose!: EventEmitter<MouseEvent>;\n /** Emitted once after controlled exit motion and layout collapse complete. */\n @Event() dsAfterClose!: EventEmitter<void>;\n\n @State() private rendered = false;\n @State() private phase: BannerPhase = 'closed';\n @State() private hasActions = false;\n\n private loaded = false;\n private transitionTimer: ReturnType<typeof setTimeout> | null = null;\n private closePending = false;\n\n componentWillLoad() {\n this.rendered = this.open;\n this.phase = this.open ? 'open' : 'closed';\n }\n\n componentDidLoad() {\n this.loaded = true;\n this.updateActionsPresence();\n }\n\n disconnectedCallback() {\n this.clearTransitionWork();\n }\n\n @Watch('open')\n onOpenChange(isOpen: boolean) {\n if (!this.loaded) return;\n if (isOpen) this.startOpen();\n else this.startClose();\n }\n\n private get closeMotionMs(): number {\n return resolveMotionTimeMs(TOKEN_DEFAULTS.motionShort3, TOKEN_DEFAULTS.animationDurationShort3);\n }\n\n private clearTransitionWork() {\n if (this.transitionTimer) clearTimeout(this.transitionTimer);\n this.transitionTimer = null;\n }\n\n private startOpen() {\n this.clearTransitionWork();\n this.closePending = false;\n this.rendered = true;\n this.phase = 'open';\n }\n\n private startClose() {\n this.clearTransitionWork();\n if (!this.rendered || this.phase === 'closed') {\n this.rendered = false;\n return;\n }\n\n this.closePending = true;\n this.phase = 'closing';\n const duration = this.closeMotionMs;\n if (duration <= 0) {\n this.finishClose();\n return;\n }\n this.transitionTimer = setTimeout(() => {\n this.transitionTimer = null;\n this.finishClose();\n }, duration);\n }\n\n private finishClose() {\n if (this.open || !this.closePending) return;\n this.closePending = false;\n this.rendered = false;\n this.phase = 'closed';\n this.dsAfterClose.emit();\n }\n\n private updateActionsPresence(slot?: HTMLSlotElement) {\n const actions = slot\n ? slot.assignedElements().filter(element => element.tagName === 'DS-BUTTON-UNFILLED')\n : Array.from(this.el.querySelectorAll('ds-button-unfilled[slot=\"actions\"]'));\n this.hasActions = actions.length > 0;\n }\n\n private requestClose(event: CustomEvent<MouseEvent>) {\n if (!this.open || this.phase === 'closing') return;\n this.dsClose.emit(event.detail);\n }\n\n private get role(): 'status' | 'alert' | undefined {\n if (this.announcement === 'polite') return 'status';\n if (this.announcement === 'assertive') return 'alert';\n return undefined;\n }\n\n private get ariaLive(): 'polite' | 'assertive' | undefined {\n return this.announcement === 'none' ? undefined : this.announcement;\n }\n\n render() {\n const closing = this.phase === 'closing';\n const hostClass = {\n banner: true,\n 'banner--open': this.phase === 'open',\n 'banner--closing': closing,\n 'banner--hidden': !this.rendered,\n 'banner--has-actions': this.hasActions,\n [`banner--orientation-${this.orientation}`]: true,\n [`banner--intent-${this.intent}`]: true,\n [`banner--contrast-${this.contrast}`]: true,\n };\n\n return (\n <Host class={hostClass}>\n <div class=\"banner-browser-edge\" aria-hidden=\"true\" />\n <div class=\"banner-overflow\">\n <section\n class=\"banner-surface ds-chrome-header ds-chrome-header--wrapping\"\n role={this.role}\n aria-live={this.ariaLive}\n aria-atomic={this.announcement === 'none' ? undefined : 'true'}\n aria-hidden={closing ? 'true' : undefined}\n inert={closing ? true : undefined}\n data-phase={this.phase}\n >\n {/* eslint-disable-next-line compomo/prefer-direct-ds-text -- The header-copy lane owns the 6px container inset separately from the inline text flow's 2px inset. */}\n <div class=\"banner-copy ds-chrome-header__copy ds-chrome-header__copy--wrapping ds-control--md\">\n <ds-text class=\"banner-copy-flow\" as=\"div\" variant=\"text-body-medium\" color=\"inherit\">\n {this.heading && (\n <ds-text\n class=\"banner-heading\"\n as=\"span\"\n variant=\"text-title-small\"\n color=\"inherit\"\n >\n {this.heading}\n </ds-text>\n )}\n <span class=\"banner-description\">{this.description}</span>\n </ds-text>\n </div>\n <div class=\"banner-trailing ds-chrome-header__trailing\">\n <div class={{ 'banner-actions': true, 'banner-actions--empty': !this.hasActions }}>\n <slot\n name=\"actions\"\n onSlotchange={(event: Event) =>\n this.updateActionsPresence(event.currentTarget as HTMLSlotElement)\n }\n />\n </div>\n <div class=\"banner-dismiss\">\n <ds-tooltip label={this.dismissLabel} side=\"bottom\" size=\"sm\">\n <ds-button-unfilled\n class=\"banner-close\"\n variant=\"icon\"\n icon=\"Cross\"\n size=\"md\"\n type=\"button\"\n background={this.contrast}\n aria-label={this.dismissLabel}\n hasBorder={false}\n activeFill={false}\n onDsClick={(event: CustomEvent<MouseEvent>) => this.requestClose(event)}\n />\n </ds-tooltip>\n </div>\n </div>\n </section>\n </div>\n </Host>\n );\n }\n}\n",
|
|
336
362
|
"type": "registry:ui"
|
|
337
363
|
}
|
|
338
364
|
]
|
|
@@ -82,7 +82,30 @@
|
|
|
82
82
|
"label": "Rendered behavior coverage",
|
|
83
83
|
"path": "tests/e2e/bar-action.spec.ts"
|
|
84
84
|
}
|
|
85
|
-
]
|
|
85
|
+
],
|
|
86
|
+
"styling": {
|
|
87
|
+
"rationale": "The component owns the protected recipes below; unspecified host properties and application layout remain customizable.",
|
|
88
|
+
"protected": [
|
|
89
|
+
{
|
|
90
|
+
"properties": [
|
|
91
|
+
"background",
|
|
92
|
+
"border",
|
|
93
|
+
"box-shadow",
|
|
94
|
+
"color"
|
|
95
|
+
],
|
|
96
|
+
"reason": "The component owns its surface and interaction treatment; use its semantic presentation props or compose an application-owned surrounding surface.",
|
|
97
|
+
"props": []
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"properties": [
|
|
101
|
+
"border-radius"
|
|
102
|
+
],
|
|
103
|
+
"reason": "The component owns its surface radius; retain the shared recipe and customize an outer wrapper when needed.",
|
|
104
|
+
"props": []
|
|
105
|
+
}
|
|
106
|
+
],
|
|
107
|
+
"customProperties": []
|
|
108
|
+
}
|
|
86
109
|
},
|
|
87
110
|
"api": {
|
|
88
111
|
"props": {
|
|
@@ -214,7 +237,7 @@
|
|
|
214
237
|
"peerDependencies": {
|
|
215
238
|
"required": [
|
|
216
239
|
"@ds-mo/tokens ^6.5.1",
|
|
217
|
-
"@ds-mo/icons ^
|
|
240
|
+
"@ds-mo/icons ^8.1.0"
|
|
218
241
|
],
|
|
219
242
|
"frameworks": "Custom Elements; React 18/19 wrappers; Vue 3 wrappers; Angular 19-22 standalone adapters."
|
|
220
243
|
}
|
|
@@ -106,7 +106,12 @@
|
|
|
106
106
|
"label": "Surface ownership and trigger state",
|
|
107
107
|
"path": "src/docs/SurfaceOwnership.mdx"
|
|
108
108
|
}
|
|
109
|
-
]
|
|
109
|
+
],
|
|
110
|
+
"styling": {
|
|
111
|
+
"rationale": "The host participates in shell-owned layout; internal tabs and chrome own their recipes.",
|
|
112
|
+
"protected": [],
|
|
113
|
+
"customProperties": []
|
|
114
|
+
}
|
|
110
115
|
},
|
|
111
116
|
"api": {
|
|
112
117
|
"props": {
|
|
@@ -287,7 +292,7 @@
|
|
|
287
292
|
"peerDependencies": {
|
|
288
293
|
"required": [
|
|
289
294
|
"@ds-mo/tokens ^6.5.1",
|
|
290
|
-
"@ds-mo/icons ^
|
|
295
|
+
"@ds-mo/icons ^8.1.0"
|
|
291
296
|
],
|
|
292
297
|
"frameworks": "Custom Elements; React 18/19 wrappers; Vue 3 wrappers; Angular 19-22 standalone adapters."
|
|
293
298
|
}
|
|
@@ -104,7 +104,30 @@
|
|
|
104
104
|
"label": "Surface ownership and trigger state",
|
|
105
105
|
"path": "src/docs/SurfaceOwnership.mdx"
|
|
106
106
|
}
|
|
107
|
-
]
|
|
107
|
+
],
|
|
108
|
+
"styling": {
|
|
109
|
+
"rationale": "The component owns the protected recipes below; unspecified host properties and application layout remain customizable.",
|
|
110
|
+
"protected": [
|
|
111
|
+
{
|
|
112
|
+
"properties": [
|
|
113
|
+
"background",
|
|
114
|
+
"border",
|
|
115
|
+
"box-shadow",
|
|
116
|
+
"color"
|
|
117
|
+
],
|
|
118
|
+
"reason": "The component owns its surface and interaction treatment; use its semantic presentation props or compose an application-owned surrounding surface.",
|
|
119
|
+
"props": []
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"properties": [
|
|
123
|
+
"border-radius"
|
|
124
|
+
],
|
|
125
|
+
"reason": "The component owns its surface radius; retain the shared recipe and customize an outer wrapper when needed.",
|
|
126
|
+
"props": []
|
|
127
|
+
}
|
|
128
|
+
],
|
|
129
|
+
"customProperties": []
|
|
130
|
+
}
|
|
108
131
|
},
|
|
109
132
|
"api": {
|
|
110
133
|
"props": {
|
|
@@ -399,7 +422,7 @@
|
|
|
399
422
|
"peerDependencies": {
|
|
400
423
|
"required": [
|
|
401
424
|
"@ds-mo/tokens ^6.5.1",
|
|
402
|
-
"@ds-mo/icons ^
|
|
425
|
+
"@ds-mo/icons ^8.1.0"
|
|
403
426
|
],
|
|
404
427
|
"frameworks": "Custom Elements; React 18/19 wrappers; Vue 3 wrappers; Angular 19-22 standalone adapters."
|
|
405
428
|
}
|