@ds-mo/ui 15.1.1 → 15.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.package-ready.json +1 -1
- package/dist/agent-patterns.json +4 -4
- package/dist/agent.json +711 -48
- package/dist/angular/ds-card-setting.d.ts +1 -1
- package/dist/angular/ds-card-setting.js +3 -3
- package/dist/angular/ds-input-time.d.ts +1 -1
- package/dist/angular/ds-input-time.js +3 -3
- package/dist/angular/ds-input.d.ts +5 -1
- package/dist/angular/ds-input.js +10 -4
- package/dist/angular/ds-map-cluster-marker.d.ts +18 -0
- package/dist/angular/ds-map-cluster-marker.js +43 -0
- package/dist/angular/ds-map-entity-marker.d.ts +18 -0
- package/dist/angular/ds-map-entity-marker.js +43 -0
- package/dist/angular/ds-radio-tile.d.ts +15 -0
- package/dist/angular/ds-radio-tile.js +43 -0
- package/dist/angular/ds-setting-row.d.ts +18 -0
- package/dist/angular/ds-setting-row.js +42 -0
- package/dist/angular/ds-slider.d.ts +1 -1
- package/dist/angular/ds-slider.js +3 -3
- package/dist/angular/ds-textarea.d.ts +1 -1
- package/dist/angular/ds-textarea.js +3 -3
- package/dist/angular/ds-time-picker.d.ts +1 -1
- package/dist/angular/ds-time-picker.js +3 -3
- package/dist/angular/index.d.ts +1 -1
- package/dist/angular/index.js +4 -0
- package/dist/angular/number-value-accessor.d.ts +9 -0
- package/dist/angular/number-value-accessor.js +39 -0
- package/dist/angular/proxies.d.ts +4 -0
- package/dist/angular/proxies.js +4 -0
- package/dist/angular/radio-value-accessor.d.ts +1 -1
- package/dist/angular/radio-value-accessor.js +2 -2
- package/dist/angular/select-value-accessor.d.ts +1 -1
- package/dist/angular/select-value-accessor.js +2 -2
- package/dist/angular/text-value-accessor.d.ts +1 -1
- package/dist/angular/text-value-accessor.js +2 -2
- package/dist/components/ds-agent-activity.js +1 -1
- package/dist/components/ds-agent-questionnaire.js +1 -1
- package/dist/components/ds-agent-response.js +1 -1
- package/dist/components/ds-agent-source-list.js +1 -1
- package/dist/components/ds-agent-tool-call.js +1 -1
- package/dist/components/ds-attachment-list.js +1 -1
- package/dist/components/ds-badge.js +1 -1
- package/dist/components/ds-banner.js +1 -1
- package/dist/components/ds-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-navigation.js +1 -1
- package/dist/components/ds-card-overview.js +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-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-section.js +1 -1
- package/dist/components/ds-conversation-list.js +1 -1
- package/dist/components/ds-data-customize.js +1 -1
- package/dist/components/ds-data-customize.js.map +1 -1
- package/dist/components/ds-data-filter.js +1 -1
- package/dist/components/ds-data-filter.js.map +1 -1
- package/dist/components/ds-data-group.js +1 -1
- package/dist/components/ds-data-preferences.js +1 -1
- package/dist/components/ds-data-preferences.js.map +1 -1
- package/dist/components/ds-data-saved-views.js +1 -1
- package/dist/components/ds-data-saved-views.js.map +1 -1
- package/dist/components/ds-data-search.js +1 -1
- package/dist/components/ds-data-search.js.map +1 -1
- package/dist/components/ds-data-sort.js +1 -1
- package/dist/components/ds-data-toolbar.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-input-date.js +1 -1
- package/dist/components/ds-input-date.js.map +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-map-cluster-marker.d.ts +11 -0
- package/dist/components/ds-map-cluster-marker.js +2 -0
- package/dist/components/ds-map-cluster-marker.js.map +1 -0
- package/dist/components/ds-map-entity-marker.d.ts +11 -0
- package/dist/components/ds-map-entity-marker.js +2 -0
- package/dist/components/ds-map-entity-marker.js.map +1 -0
- package/dist/components/ds-markdown.js +1 -1
- package/dist/components/ds-menu.js +1 -1
- package/dist/components/ds-message-actions.js +1 -1
- package/dist/components/ds-message-bubble.js +1 -1
- package/dist/components/ds-message-bubble.js.map +1 -1
- package/dist/components/ds-message-composer.js +1 -1
- package/dist/components/ds-message-scroller.js +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-tool-search.js.map +1 -1
- package/dist/components/ds-panel-tools.js +1 -1
- package/dist/components/ds-paper-texture.js +1 -1
- package/dist/components/ds-radio-tile.d.ts +11 -0
- package/dist/components/ds-radio-tile.js +2 -0
- package/dist/components/ds-radio-tile.js.map +1 -0
- 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-select.js +1 -1
- package/dist/components/ds-setting-row.d.ts +11 -0
- package/dist/components/ds-setting-row.js +2 -0
- package/dist/components/ds-setting-row.js.map +1 -0
- 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-slider.js.map +1 -1
- package/dist/components/ds-swatch-picker.js +1 -1
- package/dist/components/ds-switch.js +1 -1
- package/dist/components/ds-tab-group.js +1 -1
- package/dist/components/ds-table.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-textarea.js.map +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/{p-etifxiDc.js → p-3bsypMrY.js} +2 -2
- package/dist/components/{p-Bm7rqs3C.js → p-AM_ht6Im.js} +2 -2
- package/dist/components/{p-Bm7rqs3C.js.map → p-AM_ht6Im.js.map} +1 -1
- package/dist/components/{p-Bn8I7iw6.js → p-AmT4GlhM.js} +2 -2
- package/dist/components/{p-FIBCluwV.js → p-B-HOVYoH.js} +2 -2
- package/dist/components/{p-CmexIVnE.js → p-B7QEpaMe.js} +2 -2
- package/dist/components/{p-CUcUHwI3.js → p-B7bXN-vf.js} +2 -2
- package/dist/components/p-BHI4LcGK.js +2 -0
- package/dist/components/p-BHI4LcGK.js.map +1 -0
- package/dist/components/{p-BhJXmuRA.js → p-BSuMDPaV.js} +2 -2
- package/dist/components/{p-0FYD2bev.js → p-BU1B6NW8.js} +2 -2
- package/dist/components/{p-Do2FJd1y.js → p-BVwg5XVz.js} +2 -2
- package/dist/components/{p-CYzFfFcH.js → p-BXSHN1OK.js} +2 -2
- package/dist/components/{p-ljbw3UMZ.js → p-BYJtvRQm.js} +2 -2
- package/dist/components/{p-CzIpquvw.js → p-Bh1Fx0aW.js} +2 -2
- package/dist/components/{p-CiJ1WR11.js → p-Bm3KWMe4.js} +2 -2
- package/dist/components/{p-DVINzOoK.js → p-Bo7GxFBh.js} +2 -2
- package/dist/components/{p-8yBdCXCn.js → p-BsM37Prt.js} +2 -2
- package/dist/components/p-Bu4OmhKK.js +2 -0
- package/dist/components/{p-CdGXzRP8.js.map → p-Bu4OmhKK.js.map} +1 -1
- package/dist/components/{p-DpoTwJAR.js → p-BugKaVWc.js} +2 -2
- package/dist/components/p-C-c8SJ00.js +2 -0
- package/dist/components/p-C-c8SJ00.js.map +1 -0
- package/dist/components/{p-Bpd6YCo4.js → p-C3-dcROG.js} +2 -2
- package/dist/components/p-C4VeD49U.js +2 -0
- package/dist/components/{p-BKZdqJQB.js.map → p-C4VeD49U.js.map} +1 -1
- package/dist/components/{p-CNxvnQ6A.js → p-CCpJT31M.js} +2 -2
- package/dist/components/p-CCpJT31M.js.map +1 -0
- package/dist/components/{p-Dzndkdx5.js → p-CI7c9kp-.js} +2 -2
- package/dist/components/{p-CdgOLWX9.js → p-CJD0kLeV.js} +2 -2
- package/dist/components/{p-BeNa6tw5.js → p-C_gJD8l3.js} +2 -2
- package/dist/components/{p-DwbcNDJF.js → p-Cihjy4WY.js} +2 -2
- package/dist/components/{p-BgQMEHRa.js → p-Co5MiPNa.js} +2 -2
- package/dist/components/{p-CD4DZPy_.js → p-CrL9wxZg.js} +2 -2
- package/dist/components/{p-CGidq1iC.js → p-CuwopElg.js} +2 -2
- package/dist/components/p-CwUXroLs.js +2 -0
- package/dist/components/{p-etg-Uk1u.js.map → p-CwUXroLs.js.map} +1 -1
- package/dist/components/{p-Cqc2U1ZQ.js → p-CzQ_JF6A.js} +2 -2
- package/dist/components/{p-BciTtTwx.js → p-CztB8Pbq.js} +2 -2
- package/dist/components/{p-BciTtTwx.js.map → p-CztB8Pbq.js.map} +1 -1
- package/dist/components/{p-ZhP2tVmM.js → p-D0KD-Nr_.js} +2 -2
- package/dist/components/{p-DOAybCp0.js → p-DCLRAH5i.js} +2 -2
- package/dist/components/{p-Dn9QHL4O.js → p-DFkw6wrS.js} +2 -2
- package/dist/components/{p-D1vQGJzc.js → p-DHapI2sf.js} +2 -2
- package/dist/components/p-DQPzqcYR.js +2 -0
- package/dist/components/p-DQPzqcYR.js.map +1 -0
- package/dist/components/{p-DHICPS4n.js → p-DWmYCOKz.js} +2 -2
- package/dist/components/p-DX2RyZba.js +2 -0
- package/dist/components/{p-C-3YqeTb.js.map → p-DX2RyZba.js.map} +1 -1
- package/dist/components/{p-vfCu7XSc.js → p-DZ58Sg5a.js} +2 -2
- package/dist/components/{p-D0y7Sja6.js → p-Dcn1eDVt.js} +2 -2
- package/dist/components/{p-E5W27Fu_.js → p-DcrdyDRw.js} +2 -2
- package/dist/components/p-DhKvqh5G.js +2 -0
- package/dist/components/p-DhKvqh5G.js.map +1 -0
- package/dist/components/p-Fkkd3gMB.js +2 -0
- package/dist/components/{p-DEW7r0U9.js.map → p-Fkkd3gMB.js.map} +1 -1
- package/dist/components/{p-xdmJCPCg.js → p-Gb-vTuXr.js} +2 -2
- package/dist/components/p-TVkFIMXz.js +2 -0
- package/dist/components/p-TVkFIMXz.js.map +1 -0
- package/dist/components/p-cuZva5tl.js +2 -0
- package/dist/components/{p-Dw9sE6Iw.js.map → p-cuZva5tl.js.map} +1 -1
- package/dist/components/p-fYZK3zqM.js +2 -0
- package/dist/components/{p-C6xVxqOn.js.map → p-fYZK3zqM.js.map} +1 -1
- package/dist/components/{p-CiH4GG6r.js → p-iGNHXFZA.js} +2 -2
- package/dist/components/{p-5URekSny.js → p-lADW-TI8.js} +2 -2
- package/dist/components/{p-D922f6Tb.js → p-p_MwknM3.js} +2 -2
- package/dist/components/{p-Dfs98tpL.js → p-tJaC01aE.js} +2 -2
- package/dist/components/{p-B_jdoM7e.js → p-z8L-YMi3.js} +2 -2
- package/dist/docs/components.json +1909 -44
- package/dist/framework/angular.d.ts +1 -0
- package/dist/framework/angular.js +1 -0
- package/dist/lint/index.js +1 -1
- package/dist/mcp-data/registry/agent-activity.json +2 -2
- package/dist/mcp-data/registry/agent-questionnaire.json +2 -2
- package/dist/mcp-data/registry/agent-response.json +2 -2
- package/dist/mcp-data/registry/agent-source-list.json +2 -2
- package/dist/mcp-data/registry/agent-tool-call.json +2 -2
- package/dist/mcp-data/registry/attachment-list.json +2 -2
- package/dist/mcp-data/registry/avatar.json +2 -2
- package/dist/mcp-data/registry/badge.json +2 -2
- package/dist/mcp-data/registry/banner.json +2 -2
- package/dist/mcp-data/registry/bar-action.json +2 -2
- package/dist/mcp-data/registry/bar-nav.json +2 -2
- package/dist/mcp-data/registry/bar-page-title.json +2 -2
- package/dist/mcp-data/registry/bar-title.json +4 -4
- package/dist/mcp-data/registry/bar-workflow.json +2 -2
- package/dist/mcp-data/registry/breadcrumb.json +2 -2
- package/dist/mcp-data/registry/button-filled.json +2 -2
- package/dist/mcp-data/registry/button-inverted.json +2 -2
- package/dist/mcp-data/registry/button-unfilled.json +2 -2
- package/dist/mcp-data/registry/calendar.json +2 -2
- package/dist/mcp-data/registry/card-action-center.json +2 -2
- package/dist/mcp-data/registry/card-chart.json +2 -2
- package/dist/mcp-data/registry/card-navigation.json +2 -2
- package/dist/mcp-data/registry/card-overview.json +2 -2
- package/dist/mcp-data/registry/card-setting.json +36 -15
- package/dist/mcp-data/registry/card-settings-scope.json +2 -2
- package/dist/mcp-data/registry/chart-legend.json +2 -2
- package/dist/mcp-data/registry/chart.json +2 -2
- package/dist/mcp-data/registry/checkbox.json +2 -2
- package/dist/mcp-data/registry/chip.json +2 -2
- package/dist/mcp-data/registry/code-block.json +2 -2
- package/dist/mcp-data/registry/conversation-list-item.json +2 -2
- package/dist/mcp-data/registry/conversation-list-section.json +2 -2
- package/dist/mcp-data/registry/conversation-list.json +2 -2
- package/dist/mcp-data/registry/data-customize.json +3 -3
- package/dist/mcp-data/registry/data-filter.json +2 -2
- package/dist/mcp-data/registry/data-group.json +2 -2
- package/dist/mcp-data/registry/data-preferences.json +2 -2
- package/dist/mcp-data/registry/data-saved-views.json +2 -2
- package/dist/mcp-data/registry/data-search.json +2 -2
- package/dist/mcp-data/registry/data-sort.json +2 -2
- package/dist/mcp-data/registry/data-toolbar.json +2 -2
- package/dist/mcp-data/registry/divider.json +2 -2
- package/dist/mcp-data/registry/empty-state.json +27 -14
- package/dist/mcp-data/registry/field.json +6 -5
- package/dist/mcp-data/registry/filter-menu.json +2 -2
- package/dist/mcp-data/registry/icon.json +2 -2
- package/dist/mcp-data/registry/input-date.json +2 -2
- package/dist/mcp-data/registry/input-time.json +26 -7
- package/dist/mcp-data/registry/input.json +265 -15
- package/dist/mcp-data/registry/loader.json +2 -2
- package/dist/mcp-data/registry/map-cluster-marker.json +263 -0
- package/dist/mcp-data/registry/map-entity-marker.json +321 -0
- package/dist/mcp-data/registry/markdown.json +2 -2
- package/dist/mcp-data/registry/menu.json +2 -2
- package/dist/mcp-data/registry/message-actions.json +2 -2
- package/dist/mcp-data/registry/message-bubble.json +2 -2
- package/dist/mcp-data/registry/message-composer.json +2 -2
- package/dist/mcp-data/registry/message-scroller.json +2 -2
- package/dist/mcp-data/registry/message.json +2 -2
- package/dist/mcp-data/registry/mobile-bar-nav.json +2 -2
- package/dist/mcp-data/registry/mobile-header.json +2 -2
- package/dist/mcp-data/registry/mobile-section-switcher.json +2 -2
- package/dist/mcp-data/registry/mobile-sheet-nav.json +2 -2
- package/dist/mcp-data/registry/modal.json +2 -2
- package/dist/mcp-data/registry/pagination.json +2 -2
- package/dist/mcp-data/registry/panel-nav.json +2 -2
- package/dist/mcp-data/registry/panel-sub-nav.json +2 -2
- package/dist/mcp-data/registry/panel-tool-header.json +2 -2
- package/dist/mcp-data/registry/panel-tool-search.json +2 -2
- package/dist/mcp-data/registry/panel-tools.json +2 -2
- package/dist/mcp-data/registry/paper-texture.json +2 -2
- package/dist/mcp-data/registry/radio-tile.json +398 -0
- package/dist/mcp-data/registry/radio.json +2 -2
- package/dist/mcp-data/registry/registry.json +724 -49
- package/dist/mcp-data/registry/score.json +2 -2
- package/dist/mcp-data/registry/scroll-overlay.json +2 -2
- package/dist/mcp-data/registry/select.json +2 -2
- package/dist/mcp-data/registry/setting-row.json +213 -0
- package/dist/mcp-data/registry/shell-app.json +3 -3
- package/dist/mcp-data/registry/shell-page.json +5 -5
- package/dist/mcp-data/registry/shell-tools.json +2 -2
- package/dist/mcp-data/registry/skeleton.json +2 -2
- package/dist/mcp-data/registry/slider.json +32 -12
- package/dist/mcp-data/registry/swatch-picker.json +2 -2
- package/dist/mcp-data/registry/switch.json +2 -2
- package/dist/mcp-data/registry/tab-group.json +2 -2
- package/dist/mcp-data/registry/table.json +5 -5
- package/dist/mcp-data/registry/tag.json +2 -2
- package/dist/mcp-data/registry/text.json +2 -2
- package/dist/mcp-data/registry/textarea.json +72 -5
- package/dist/mcp-data/registry/time-picker.json +28 -9
- package/dist/mcp-data/registry/toast.json +2 -2
- package/dist/mcp-data/registry/tooltip-chart.json +2 -2
- package/dist/mcp-data/registry/tooltip.json +2 -2
- package/dist/mcp-data/registry/typing-indicator.json +2 -2
- package/dist/react/components.d.ts +4 -0
- package/dist/react/components.js +4 -0
- package/dist/react/ds-input.d.ts +2 -0
- package/dist/react/ds-input.js +2 -0
- package/dist/react/ds-map-cluster-marker.d.ts +12 -0
- package/dist/react/ds-map-cluster-marker.js +12 -0
- package/dist/react/ds-map-entity-marker.d.ts +12 -0
- package/dist/react/ds-map-entity-marker.js +12 -0
- package/dist/react/ds-radio-tile.d.ts +12 -0
- package/dist/react/ds-radio-tile.js +12 -0
- package/dist/react/ds-setting-row.d.ts +12 -0
- package/dist/react/ds-setting-row.js +12 -0
- package/dist/types/components/CardSetting/CardSetting.d.ts +5 -3
- package/dist/types/components/CardSetting/index.d.ts +1 -1
- package/dist/types/components/EmptyState/EmptyState.d.ts +3 -0
- package/dist/types/components/Field/Field.d.ts +4 -0
- package/dist/types/components/Input/Input.d.ts +50 -1
- package/dist/types/components/InputTime/InputTime.d.ts +3 -0
- package/dist/types/components/MapClusterMarker/MapClusterMarker.d.ts +19 -0
- package/dist/types/components/MapEntityMarker/MapEntityMarker.d.ts +26 -0
- package/dist/types/components/RadioTile/RadioTile.d.ts +40 -0
- package/dist/types/components/RadioTile/index.d.ts +2 -0
- package/dist/types/components/SettingRow/SettingRow.d.ts +18 -0
- package/dist/types/components/Slider/Slider.d.ts +8 -1
- package/dist/types/components/Slider/index.d.ts +1 -1
- package/dist/types/components/Textarea/Textarea.d.ts +12 -0
- package/dist/types/components/TimePicker/TimePicker.d.ts +4 -0
- package/dist/types/components.d.ts +627 -11
- package/dist/types/utils/text-constraints.d.ts +20 -0
- package/dist/vue/components.d.ts +4 -0
- package/dist/vue/components.js +4 -0
- package/dist/vue/ds-card-setting.js +1 -0
- package/dist/vue/ds-input-time.js +1 -0
- package/dist/vue/ds-input.js +17 -0
- package/dist/vue/ds-map-cluster-marker.d.ts +3 -0
- package/dist/vue/ds-map-cluster-marker.js +14 -0
- package/dist/vue/ds-map-entity-marker.d.ts +3 -0
- package/dist/vue/ds-map-entity-marker.js +17 -0
- package/dist/vue/ds-radio-tile.d.ts +3 -0
- package/dist/vue/ds-radio-tile.js +24 -0
- package/dist/vue/ds-setting-row.d.ts +3 -0
- package/dist/vue/ds-setting-row.js +14 -0
- package/dist/vue/ds-slider.d.ts +1 -1
- package/dist/vue/ds-slider.js +2 -1
- package/dist/vue/ds-textarea.js +4 -0
- package/dist/vue/ds-time-picker.js +1 -0
- package/package.json +5 -5
- package/dist/components/p-0qY_HDId.js +0 -2
- package/dist/components/p-0qY_HDId.js.map +0 -1
- package/dist/components/p-BKZdqJQB.js +0 -2
- package/dist/components/p-C-3YqeTb.js +0 -2
- package/dist/components/p-C6xVxqOn.js +0 -2
- package/dist/components/p-CNxvnQ6A.js.map +0 -1
- package/dist/components/p-CdGXzRP8.js +0 -2
- package/dist/components/p-DEW7r0U9.js +0 -2
- package/dist/components/p-DVOjjuXc.js +0 -2
- package/dist/components/p-DVOjjuXc.js.map +0 -1
- package/dist/components/p-Di5FH7eh.js +0 -2
- package/dist/components/p-Di5FH7eh.js.map +0 -1
- package/dist/components/p-Dw9sE6Iw.js +0 -2
- package/dist/components/p-etg-Uk1u.js +0 -2
- package/dist/components/p-xtGGIZmU.js +0 -2
- package/dist/components/p-xtGGIZmU.js.map +0 -1
- /package/dist/components/{p-etifxiDc.js.map → p-3bsypMrY.js.map} +0 -0
- /package/dist/components/{p-Bn8I7iw6.js.map → p-AmT4GlhM.js.map} +0 -0
- /package/dist/components/{p-FIBCluwV.js.map → p-B-HOVYoH.js.map} +0 -0
- /package/dist/components/{p-CmexIVnE.js.map → p-B7QEpaMe.js.map} +0 -0
- /package/dist/components/{p-CUcUHwI3.js.map → p-B7bXN-vf.js.map} +0 -0
- /package/dist/components/{p-BhJXmuRA.js.map → p-BSuMDPaV.js.map} +0 -0
- /package/dist/components/{p-0FYD2bev.js.map → p-BU1B6NW8.js.map} +0 -0
- /package/dist/components/{p-Do2FJd1y.js.map → p-BVwg5XVz.js.map} +0 -0
- /package/dist/components/{p-CYzFfFcH.js.map → p-BXSHN1OK.js.map} +0 -0
- /package/dist/components/{p-ljbw3UMZ.js.map → p-BYJtvRQm.js.map} +0 -0
- /package/dist/components/{p-CzIpquvw.js.map → p-Bh1Fx0aW.js.map} +0 -0
- /package/dist/components/{p-CiJ1WR11.js.map → p-Bm3KWMe4.js.map} +0 -0
- /package/dist/components/{p-DVINzOoK.js.map → p-Bo7GxFBh.js.map} +0 -0
- /package/dist/components/{p-8yBdCXCn.js.map → p-BsM37Prt.js.map} +0 -0
- /package/dist/components/{p-DpoTwJAR.js.map → p-BugKaVWc.js.map} +0 -0
- /package/dist/components/{p-Bpd6YCo4.js.map → p-C3-dcROG.js.map} +0 -0
- /package/dist/components/{p-Dzndkdx5.js.map → p-CI7c9kp-.js.map} +0 -0
- /package/dist/components/{p-CdgOLWX9.js.map → p-CJD0kLeV.js.map} +0 -0
- /package/dist/components/{p-BeNa6tw5.js.map → p-C_gJD8l3.js.map} +0 -0
- /package/dist/components/{p-DwbcNDJF.js.map → p-Cihjy4WY.js.map} +0 -0
- /package/dist/components/{p-BgQMEHRa.js.map → p-Co5MiPNa.js.map} +0 -0
- /package/dist/components/{p-CD4DZPy_.js.map → p-CrL9wxZg.js.map} +0 -0
- /package/dist/components/{p-CGidq1iC.js.map → p-CuwopElg.js.map} +0 -0
- /package/dist/components/{p-Cqc2U1ZQ.js.map → p-CzQ_JF6A.js.map} +0 -0
- /package/dist/components/{p-ZhP2tVmM.js.map → p-D0KD-Nr_.js.map} +0 -0
- /package/dist/components/{p-DOAybCp0.js.map → p-DCLRAH5i.js.map} +0 -0
- /package/dist/components/{p-Dn9QHL4O.js.map → p-DFkw6wrS.js.map} +0 -0
- /package/dist/components/{p-D1vQGJzc.js.map → p-DHapI2sf.js.map} +0 -0
- /package/dist/components/{p-DHICPS4n.js.map → p-DWmYCOKz.js.map} +0 -0
- /package/dist/components/{p-vfCu7XSc.js.map → p-DZ58Sg5a.js.map} +0 -0
- /package/dist/components/{p-D0y7Sja6.js.map → p-Dcn1eDVt.js.map} +0 -0
- /package/dist/components/{p-E5W27Fu_.js.map → p-DcrdyDRw.js.map} +0 -0
- /package/dist/components/{p-xdmJCPCg.js.map → p-Gb-vTuXr.js.map} +0 -0
- /package/dist/components/{p-CiH4GG6r.js.map → p-iGNHXFZA.js.map} +0 -0
- /package/dist/components/{p-5URekSny.js.map → p-lADW-TI8.js.map} +0 -0
- /package/dist/components/{p-D922f6Tb.js.map → p-p_MwknM3.js.map} +0 -0
- /package/dist/components/{p-Dfs98tpL.js.map → p-tJaC01aE.js.map} +0 -0
- /package/dist/components/{p-B_jdoM7e.js.map → p-z8L-YMi3.js.map} +0 -0
|
@@ -258,8 +258,8 @@
|
|
|
258
258
|
},
|
|
259
259
|
"peerDependencies": {
|
|
260
260
|
"required": [
|
|
261
|
-
"@ds-mo/tokens ^6.5.
|
|
262
|
-
"@ds-mo/icons ^8.1.
|
|
261
|
+
"@ds-mo/tokens ^6.5.2",
|
|
262
|
+
"@ds-mo/icons ^8.1.1"
|
|
263
263
|
],
|
|
264
264
|
"frameworks": "Custom Elements; React 18/19 wrappers; Vue 3 wrappers; Angular 19-22 standalone adapters."
|
|
265
265
|
}
|
|
@@ -166,8 +166,8 @@
|
|
|
166
166
|
},
|
|
167
167
|
"peerDependencies": {
|
|
168
168
|
"required": [
|
|
169
|
-
"@ds-mo/tokens ^6.5.
|
|
170
|
-
"@ds-mo/icons ^8.1.
|
|
169
|
+
"@ds-mo/tokens ^6.5.2",
|
|
170
|
+
"@ds-mo/icons ^8.1.1"
|
|
171
171
|
],
|
|
172
172
|
"frameworks": "Custom Elements; React 18/19 wrappers; Vue 3 wrappers; Angular 19-22 standalone adapters."
|
|
173
173
|
}
|
|
@@ -1089,8 +1089,8 @@
|
|
|
1089
1089
|
"complexPropertyNote": "Assign these non-primitive values as JavaScript properties: boundary, options, sections.",
|
|
1090
1090
|
"peerDependencies": {
|
|
1091
1091
|
"required": [
|
|
1092
|
-
"@ds-mo/tokens ^6.5.
|
|
1093
|
-
"@ds-mo/icons ^8.1.
|
|
1092
|
+
"@ds-mo/tokens ^6.5.2",
|
|
1093
|
+
"@ds-mo/icons ^8.1.1"
|
|
1094
1094
|
],
|
|
1095
1095
|
"frameworks": "Custom Elements; React 18/19 wrappers; Vue 3 wrappers; Angular 19-22 standalone adapters."
|
|
1096
1096
|
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "setting-row",
|
|
4
|
+
"title": "SettingRow",
|
|
5
|
+
"description": "Two-column immediate binary setting with descriptive copy and an accessible switch.",
|
|
6
|
+
"type": "registry:ui",
|
|
7
|
+
"meta": {
|
|
8
|
+
"library": "@ds-mo/ui",
|
|
9
|
+
"distribution": "npm",
|
|
10
|
+
"storybook": "https://zainadeel.github.io/compomo/?path=/story/setting-row",
|
|
11
|
+
"source": "src/wc/components/SettingRow/SettingRow.tsx",
|
|
12
|
+
"intentStatus": "complete",
|
|
13
|
+
"intent": {
|
|
14
|
+
"audience": "general",
|
|
15
|
+
"status": "experimental",
|
|
16
|
+
"summary": "Two-column immediate binary setting with descriptive copy and an accessible switch.",
|
|
17
|
+
"useWhen": [
|
|
18
|
+
"A settings list pairs a setting name and explanation with one immediate on/off control."
|
|
19
|
+
],
|
|
20
|
+
"avoidWhen": [
|
|
21
|
+
"The value needs a text input, multiple choices, or an explicit Save workflow."
|
|
22
|
+
],
|
|
23
|
+
"commonlyComposedWith": [
|
|
24
|
+
"component:ds-card-setting",
|
|
25
|
+
"component:ds-switch",
|
|
26
|
+
"component:ds-text"
|
|
27
|
+
],
|
|
28
|
+
"accessibility": [
|
|
29
|
+
"The visible label names the switch and the optional description supplies its accessible description.",
|
|
30
|
+
"Switch owns focus, keyboard activation, checked state, and disabled semantics. The row adds no second tab stop.",
|
|
31
|
+
"For a settings list, the owner supplies list semantics and marks each row as a list item."
|
|
32
|
+
],
|
|
33
|
+
"states": [
|
|
34
|
+
"The application accepts or rejects change requests by updating the controlled checked value and owns persistence and errors.",
|
|
35
|
+
"Name the enabled setting consistently; the label must not change when the switch is toggled."
|
|
36
|
+
],
|
|
37
|
+
"responsiveBehavior": [
|
|
38
|
+
"The copy wraps while the switch keeps its natural size in the trailing column, including at narrow widths."
|
|
39
|
+
],
|
|
40
|
+
"references": [
|
|
41
|
+
{
|
|
42
|
+
"label": "Usage states",
|
|
43
|
+
"path": "src/wc/components/SettingRow/SettingRow.stories.ts"
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
"styling": {
|
|
47
|
+
"rationale": "The row owns copy/control spacing; its parent owns the section surface and width.",
|
|
48
|
+
"protected": [
|
|
49
|
+
{
|
|
50
|
+
"properties": [
|
|
51
|
+
"padding",
|
|
52
|
+
"gap"
|
|
53
|
+
],
|
|
54
|
+
"reason": "Preserve the shared settings-row spacing recipe.",
|
|
55
|
+
"props": []
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
"customProperties": []
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"api": {
|
|
62
|
+
"props": {
|
|
63
|
+
"checked": {
|
|
64
|
+
"type": "boolean",
|
|
65
|
+
"resolvedType": "boolean",
|
|
66
|
+
"attribute": "checked",
|
|
67
|
+
"default": "false",
|
|
68
|
+
"required": false,
|
|
69
|
+
"mutable": false,
|
|
70
|
+
"description": "Controlled enabled state."
|
|
71
|
+
},
|
|
72
|
+
"description": {
|
|
73
|
+
"type": "string",
|
|
74
|
+
"resolvedType": "string | undefined",
|
|
75
|
+
"attribute": "description",
|
|
76
|
+
"required": false,
|
|
77
|
+
"mutable": false,
|
|
78
|
+
"description": "Supporting copy explaining the setting and its alternative."
|
|
79
|
+
},
|
|
80
|
+
"disabled": {
|
|
81
|
+
"type": "boolean",
|
|
82
|
+
"resolvedType": "boolean",
|
|
83
|
+
"attribute": "disabled",
|
|
84
|
+
"default": "false",
|
|
85
|
+
"required": false,
|
|
86
|
+
"mutable": false,
|
|
87
|
+
"description": "Prevent changes while the setting is unavailable."
|
|
88
|
+
},
|
|
89
|
+
"label": {
|
|
90
|
+
"type": "string",
|
|
91
|
+
"resolvedType": "string",
|
|
92
|
+
"attribute": "label",
|
|
93
|
+
"required": true,
|
|
94
|
+
"mutable": false,
|
|
95
|
+
"description": "Name of the enabled setting."
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
"events": [
|
|
99
|
+
{
|
|
100
|
+
"name": "dsChange",
|
|
101
|
+
"detail": "boolean",
|
|
102
|
+
"bubbles": true,
|
|
103
|
+
"cancelable": true,
|
|
104
|
+
"composed": true,
|
|
105
|
+
"description": "Requests the next value; the application owns acceptance and persistence."
|
|
106
|
+
}
|
|
107
|
+
],
|
|
108
|
+
"methods": [],
|
|
109
|
+
"slots": []
|
|
110
|
+
},
|
|
111
|
+
"props": {
|
|
112
|
+
"checked": {
|
|
113
|
+
"type": "boolean",
|
|
114
|
+
"resolvedType": "boolean",
|
|
115
|
+
"attribute": "checked",
|
|
116
|
+
"default": "false",
|
|
117
|
+
"required": false,
|
|
118
|
+
"mutable": false,
|
|
119
|
+
"description": "Controlled enabled state."
|
|
120
|
+
},
|
|
121
|
+
"description": {
|
|
122
|
+
"type": "string",
|
|
123
|
+
"resolvedType": "string | undefined",
|
|
124
|
+
"attribute": "description",
|
|
125
|
+
"required": false,
|
|
126
|
+
"mutable": false,
|
|
127
|
+
"description": "Supporting copy explaining the setting and its alternative."
|
|
128
|
+
},
|
|
129
|
+
"disabled": {
|
|
130
|
+
"type": "boolean",
|
|
131
|
+
"resolvedType": "boolean",
|
|
132
|
+
"attribute": "disabled",
|
|
133
|
+
"default": "false",
|
|
134
|
+
"required": false,
|
|
135
|
+
"mutable": false,
|
|
136
|
+
"description": "Prevent changes while the setting is unavailable."
|
|
137
|
+
},
|
|
138
|
+
"label": {
|
|
139
|
+
"type": "string",
|
|
140
|
+
"resolvedType": "string",
|
|
141
|
+
"attribute": "label",
|
|
142
|
+
"required": true,
|
|
143
|
+
"mutable": false,
|
|
144
|
+
"description": "Name of the enabled setting."
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
"events": [
|
|
148
|
+
{
|
|
149
|
+
"name": "dsChange",
|
|
150
|
+
"detail": "boolean",
|
|
151
|
+
"bubbles": true,
|
|
152
|
+
"cancelable": true,
|
|
153
|
+
"composed": true,
|
|
154
|
+
"description": "Requests the next value; the application owns acceptance and persistence."
|
|
155
|
+
}
|
|
156
|
+
],
|
|
157
|
+
"methods": [],
|
|
158
|
+
"slots": [],
|
|
159
|
+
"exports": {
|
|
160
|
+
"customElement": "ds-setting-row",
|
|
161
|
+
"react": "DsSettingRow",
|
|
162
|
+
"vue": "DsSettingRow",
|
|
163
|
+
"angular": "DsSettingRow"
|
|
164
|
+
},
|
|
165
|
+
"consumption": {
|
|
166
|
+
"install": "npm install @ds-mo/ui @ds-mo/tokens @ds-mo/icons",
|
|
167
|
+
"cssSetup": "import '@ds-mo/tokens';\nimport '@ds-mo/tokens/reset';\nimport '@ds-mo/tokens/globals';",
|
|
168
|
+
"customElements": {
|
|
169
|
+
"import": "import '@ds-mo/ui/dist/components/ds-setting-row.js';",
|
|
170
|
+
"example": "<ds-setting-row></ds-setting-row>"
|
|
171
|
+
},
|
|
172
|
+
"react": {
|
|
173
|
+
"import": "import { DsSettingRow } from '@ds-mo/ui/react';",
|
|
174
|
+
"example": "<DsSettingRow />"
|
|
175
|
+
},
|
|
176
|
+
"vue": {
|
|
177
|
+
"import": "import { DsSettingRow } from '@ds-mo/ui/vue';",
|
|
178
|
+
"example": "<DsSettingRow />"
|
|
179
|
+
},
|
|
180
|
+
"angular": {
|
|
181
|
+
"import": "import { DsSettingRow } from '@ds-mo/ui/angular/ds-setting-row';",
|
|
182
|
+
"example": "<ds-setting-row></ds-setting-row>"
|
|
183
|
+
},
|
|
184
|
+
"peerDependencies": {
|
|
185
|
+
"required": [
|
|
186
|
+
"@ds-mo/tokens ^6.5.2",
|
|
187
|
+
"@ds-mo/icons ^8.1.1"
|
|
188
|
+
],
|
|
189
|
+
"frameworks": "Custom Elements; React 18/19 wrappers; Vue 3 wrappers; Angular 19-22 standalone adapters."
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
"dependencies": [
|
|
194
|
+
"@ds-mo/ui",
|
|
195
|
+
"@ds-mo/tokens"
|
|
196
|
+
],
|
|
197
|
+
"registryDependencies": [
|
|
198
|
+
"switch",
|
|
199
|
+
"text"
|
|
200
|
+
],
|
|
201
|
+
"files": [
|
|
202
|
+
{
|
|
203
|
+
"path": "src/wc/components/SettingRow/SettingRow.css",
|
|
204
|
+
"content": ":host {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n align-items: start;\n box-sizing: border-box;\n min-width: 0;\n padding: var(--dimension-space-100) var(--dimension-space-200);\n gap: var(--dimension-space-200);\n}\n\n.setting-row__copy {\n display: flex;\n flex-direction: column;\n min-width: 0;\n gap: var(--dimension-space-050);\n overflow-wrap: anywhere;\n -webkit-user-select: none;\n user-select: none;\n}\n",
|
|
205
|
+
"type": "registry:ui"
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"path": "src/wc/components/SettingRow/SettingRow.tsx",
|
|
209
|
+
"content": "import { Component, Event, EventEmitter, h, Host, Prop } from '@stencil/core';\n\nlet settingRowSequence = 0;\n\n/** A labeled binary setting that applies immediately through its application owner. */\n@Component({\n tag: 'ds-setting-row',\n styleUrl: 'SettingRow.css',\n scoped: true,\n})\nexport class SettingRow {\n /** Name of the enabled setting. */\n @Prop() label!: string;\n /** Supporting copy explaining the setting and its alternative. */\n @Prop() description?: string;\n /** Controlled enabled state. */\n @Prop() checked = false;\n /** Prevent changes while the setting is unavailable. */\n @Prop() disabled = false;\n\n /** Requests the next value; the application owns acceptance and persistence. */\n @Event() dsChange!: EventEmitter<boolean>;\n\n private readonly rowId = `ds-setting-row-${++settingRowSequence}`;\n\n private handleChange = (event: CustomEvent<boolean>) => {\n event.stopPropagation();\n // Keep the composed switch controlled until the owner accepts this request.\n (event.target as HTMLDsSwitchElement).checked = this.checked;\n this.dsChange.emit(event.detail);\n };\n\n render() {\n return (\n <Host>\n <div class=\"setting-row__copy\">\n <ds-text\n textId={`${this.rowId}-label`}\n variant=\"text-body-medium\"\n color=\"primary\"\n emphasis\n >\n {this.label}\n </ds-text>\n {this.description && (\n <ds-text\n textId={`${this.rowId}-description`}\n variant=\"text-body-medium\"\n color=\"secondary\"\n >\n {this.description}\n </ds-text>\n )}\n </div>\n <ds-switch\n checked={this.checked}\n disabled={this.disabled}\n aria-labelledby={`${this.rowId}-label`}\n aria-describedby={this.description ? `${this.rowId}-description` : undefined}\n onDsChange={this.handleChange}\n />\n </Host>\n );\n }\n}\n",
|
|
210
|
+
"type": "registry:ui"
|
|
211
|
+
}
|
|
212
|
+
]
|
|
213
|
+
}
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"On desktop, PanelNav defaults expanded, permits user collapse or expansion, and respects the locally persisted desktop preference.",
|
|
88
88
|
"On tablet, ShellApp keeps PanelNav permanently collapsed and prevents user expansion without overwriting the desktop preference.",
|
|
89
89
|
"On desktop and tablet, panel section navigation replaces BarNav with BarPageTitle; collapsed PanelNav exposes child sections through its anchored flyout.",
|
|
90
|
-
"When panel section navigation moves BarPageTitle into the shell bar, routed content starts at the standard
|
|
90
|
+
"When panel section navigation moves BarPageTitle into the shell bar, routed content starts at the standard 16px desktop and tablet gutter and ShellPage reports no page-local sticky-header height.",
|
|
91
91
|
"On mobile, ShellApp hides and inerts desktop chrome, uses a solid primary stage, and composes MobileHeader, MobileSheetNav, ShellTools, and the persistent MobileBarNav around the same application-owned content.",
|
|
92
92
|
"The section-navigation preference also selects mobile navigation: bar preserves flat area selection and the header section picker; panel expands child routes inline in MobileSheetNav and combines the page title with any local subsection in one sheet trigger.",
|
|
93
93
|
"Opening MobileSheetNav temporarily overlays the active routed page or global tool; closing it without selecting an area restores that same destination and its nested state. The sheet exposes the router-derived current area only while the routed area owns the mobile stage, so a global tool never leaves an underlying route highlighted.",
|
|
@@ -690,8 +690,8 @@
|
|
|
690
690
|
"complexPropertyNote": "Assign these non-primitive values as JavaScript properties: navigation, pageChrome, paperTexture, tools.",
|
|
691
691
|
"peerDependencies": {
|
|
692
692
|
"required": [
|
|
693
|
-
"@ds-mo/tokens ^6.5.
|
|
694
|
-
"@ds-mo/icons ^8.1.
|
|
693
|
+
"@ds-mo/tokens ^6.5.2",
|
|
694
|
+
"@ds-mo/icons ^8.1.1"
|
|
695
695
|
],
|
|
696
696
|
"frameworks": "Custom Elements; React 18/19 wrappers; Vue 3 wrappers; Angular 19-22 standalone adapters."
|
|
697
697
|
}
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"A ShellPage-managed BarTitle remains concealed until headerCapacity is available and its rendered variant matches the resolved presentation, preventing a default-variant flash during route activation.",
|
|
74
74
|
"responsiveMode=mobile hides and inerts the desktop header, exposes mobile-header, and disables desktop scroll-compaction geometry.",
|
|
75
75
|
"Update headerCapacity synchronously with shell layout state so opening tools or crossing application breakpoints cannot briefly render the wrong presentation.",
|
|
76
|
-
"The default content inset is balanced on every side
|
|
76
|
+
"The default content inset is balanced at 16px on every side in desktop, tablet, and mobile modes.",
|
|
77
77
|
"When desktopHeaderPlacement=shell-bar, desktop and tablet ignore page-header-specific block-start seam overrides and expose --ds-shell-page-content-block-start-offset as the responsive gutter only; mobile keeps its MobileHeader geometry and existing seam.",
|
|
78
78
|
"contentInsetBlockStart can independently remove the top gutter for table and canvas pages that should meet the sticky header; exact roomy and compact block-start sizes support pages whose seam changes with header capacity.",
|
|
79
79
|
"The selected content surface persists unchanged across desktop, tablet, and mobile modes.",
|
|
@@ -332,8 +332,8 @@
|
|
|
332
332
|
},
|
|
333
333
|
"peerDependencies": {
|
|
334
334
|
"required": [
|
|
335
|
-
"@ds-mo/tokens ^6.5.
|
|
336
|
-
"@ds-mo/icons ^8.1.
|
|
335
|
+
"@ds-mo/tokens ^6.5.2",
|
|
336
|
+
"@ds-mo/icons ^8.1.1"
|
|
337
337
|
],
|
|
338
338
|
"frameworks": "Custom Elements; React 18/19 wrappers; Vue 3 wrappers; Angular 19-22 standalone adapters."
|
|
339
339
|
}
|
|
@@ -347,12 +347,12 @@
|
|
|
347
347
|
"files": [
|
|
348
348
|
{
|
|
349
349
|
"path": "src/wc/components/ShellPage/ShellPage.css",
|
|
350
|
-
"content": ":host {\n --_shell-page-content-background: var(--color-background-primary);\n --_shell-page-header-background: var(--color-background-primary);\n --_shell-page-responsive-content-block-start-inset: var(--dimension-space-
|
|
350
|
+
"content": ":host {\n --_shell-page-content-background: var(--color-background-primary);\n --_shell-page-header-background: var(--color-background-primary);\n --_shell-page-responsive-content-block-start-inset: var(--dimension-space-200);\n --_shell-page-content-block-start-inset: var(\n --ds-shell-page-content-block-start-inset,\n var(--_shell-page-responsive-content-block-start-inset)\n );\n --ds-shell-page-sticky-header-block-size: var(--dimension-size-600);\n --ds-shell-page-content-block-start-offset: calc(\n var(--ds-shell-page-sticky-header-block-size) + var(--_shell-page-content-block-start-inset)\n );\n display: block;\n position: relative;\n min-width: 0;\n min-height: 100%;\n box-sizing: border-box;\n background: var(--_shell-page-content-background);\n}\n\n:host(.shell-page-host--surface-secondary) {\n --_shell-page-content-background: var(--color-background-secondary);\n}\n\n:host(.shell-page-host--surface-secondary[responsive-mode='desktop']),\n:host(.shell-page-host--surface-secondary[responsive-mode='tablet']) {\n --_shell-page-header-background: var(--color-background-secondary);\n}\n\n:host([responsive-mode='tablet']),\n:host([responsive-mode='mobile']) {\n --_shell-page-responsive-content-block-start-inset: var(--dimension-space-200);\n}\n\n:host([responsive-mode='mobile']) {\n --ds-shell-page-sticky-header-block-size: calc(\n var(--dimension-size-500) + var(--dimension-space-200)\n );\n}\n\n:host(.shell-page-host--header-compact),\n:host(.shell-page-host--header-constrained) {\n --_shell-page-content-block-start-inset: var(\n --ds-shell-page-compact-content-block-start-inset,\n var(--_shell-page-responsive-content-block-start-inset)\n );\n}\n\n:host(.shell-page-host--inset-none),\n:host(.shell-page-host--block-start-inset-none) {\n --_shell-page-content-block-start-inset: 0px;\n}\n\n:host(.shell-page-host--desktop-header-shell-bar) {\n --ds-shell-page-sticky-header-block-size: 0px;\n}\n\n:host(.shell-page-host--desktop-header-shell-bar.shell-page-host--inset-default) {\n --_shell-page-content-block-start-inset: var(--_shell-page-responsive-content-block-start-inset);\n}\n\n.shell-page__scroll-sentinel {\n position: absolute;\n inset-block-start: var(--ds-shell-page-header-travel, 0);\n inset-inline-start: 0;\n width: var(--dimension-stroke-width-012);\n height: var(--dimension-stroke-width-012);\n pointer-events: none;\n}\n\n.shell-page__sticky-header {\n position: sticky;\n inset-block-start: var(--ds-shell-page-sticky-offset, 0);\n z-index: var(--dimension-z-index-overlay);\n min-width: 0;\n background: var(--_shell-page-header-background);\n overflow: visible;\n overflow-anchor: none;\n}\n\n.shell-page__desktop-header ::slotted(ds-bar-title),\n.shell-page__desktop-header ::slotted(ds-bar-workflow),\n.shell-page__mobile-header ::slotted(ds-mobile-header) {\n display: block;\n min-width: 0;\n}\n\n.shell-page__desktop-header[hidden],\n.shell-page__mobile-header[hidden] {\n display: none;\n}\n\n:host(.shell-page-host--mobile) .shell-page__scroll-sentinel,\n:host(.shell-page-host--mobile) .shell-page__flow-spacer {\n display: none;\n}\n\n.shell-page__flow-spacer {\n height: var(--ds-shell-page-flow-spacer, 0);\n background: var(--_shell-page-content-background);\n}\n\n.shell-page__content {\n min-width: 0;\n box-sizing: border-box;\n background: var(--_shell-page-content-background);\n}\n\n:host(.shell-page-host--inset-default) .shell-page__content {\n padding: var(--dimension-space-200);\n padding-block-start: var(--_shell-page-content-block-start-inset);\n}\n\n:host(.shell-page-host--inset-default[responsive-mode='tablet']) .shell-page__content,\n:host(.shell-page-host--inset-default[responsive-mode='mobile']) .shell-page__content {\n padding: var(--dimension-space-200);\n padding-block-start: var(--_shell-page-content-block-start-inset);\n}\n\n:host(.shell-page-host--inset-none) .shell-page__content {\n padding: 0;\n}\n",
|
|
351
351
|
"type": "registry:ui"
|
|
352
352
|
},
|
|
353
353
|
{
|
|
354
354
|
"path": "src/wc/components/ShellPage/ShellPage.tsx",
|
|
355
|
-
"content": "import { Component, Element, h, Host, Prop, State, Watch } from '@stencil/core';\nimport type { ShellResponsiveMode } from '../../shell/shell-responsive';\nimport { resolveCssLengthPx } from '../../utils/resolve-css-length-px';\nimport type { BarTitleVariant } from '../BarTitle/bar-title-types';\nimport { resolveShellPageHeaderVariant } from './shell-page-responsive';\nimport type {\n ShellPageCapacity,\n ShellPageContentInset,\n ShellPageContentSurface,\n ShellPageDesktopHeaderPlacement,\n ShellPageHeaderPresentation,\n} from './shell-page-types';\n\ntype BarTitleElement = HTMLElement & { variant: BarTitleVariant };\n\n@Component({\n tag: 'ds-shell-page',\n styleUrl: 'ShellPage.css',\n scoped: true,\n})\nexport class ShellPage {\n private static readonly MAX_HEADER_REVEAL_FRAMES = 12;\n private static readonly MAX_HEADER_GEOMETRY_FRAMES = 12;\n\n @Element() el!: HTMLElement;\n\n /** Automatic capacity/scroll behavior, or an explicit header variant override. */\n @Prop() headerPresentation: ShellPageHeaderPresentation = 'auto';\n\n /** Available page-header capacity supplied by the owning application shell. */\n @Prop() headerCapacity?: ShellPageCapacity;\n\n /** Desktop title chrome is rendered in this page or externally in the shell bar. */\n @Prop({ attribute: 'desktop-header-placement', reflect: true })\n desktopHeaderPlacement: ShellPageDesktopHeaderPlacement = 'page';\n\n /** Standard page gutters, or no inset for full-bleed page content. */\n @Prop() contentInset: ShellPageContentInset = 'default';\n\n /** Preserve the responsive side/end gutters while allowing content to meet the header. */\n @Prop() contentInsetBlockStart: ShellPageContentInset = 'default';\n\n /** Exact block-start content inset, overriding the responsive default when provided. */\n @Prop() contentInsetBlockStartSize?: string;\n\n /** Exact block-start content inset while the header is compact or constrained. */\n @Prop() compactContentInsetBlockStartSize?: string;\n\n /** Allow roomy automatic headers to compact as the page scrolls. */\n @Prop() scrollCompaction: boolean = true;\n\n /** Canvas surface painted around and beneath routed page content. */\n @Prop() contentSurface: ShellPageContentSurface = 'primary';\n\n /** Explicit shell breakpoint presentation. Mobile uses the dedicated mobile-header slot. */\n @Prop({ attribute: 'responsive-mode', reflect: true })\n responsiveMode: ShellResponsiveMode = 'desktop';\n\n @State() private pageTopVisible = true;\n\n private sentinelEl: HTMLElement | null = null;\n private spacerEl: HTMLElement | null = null;\n private stickyHeaderEl: HTMLElement | null = null;\n private headerEl: BarTitleElement | null = null;\n private expandedHeaderHeight = 0;\n private headerTravel = 0;\n private expandedGeometryFrozen = false;\n private pageHeaderResizeObserver: ResizeObserver | null = null;\n private headerResizeObserver: ResizeObserver | null = null;\n private headerMutationObserver: MutationObserver | null = null;\n private headerRevealFrame: number | null = null;\n private headerGeometryFrame: number | null = null;\n private reconnectFrame: number | null = null;\n private scrollRoot: HTMLElement | Window | null = null;\n private hasLoaded = false;\n\n @Watch('headerCapacity')\n @Watch('headerPresentation')\n @Watch('scrollCompaction')\n handleHeaderContractChange() {\n this.syncHeaderVariant(true);\n }\n\n @Watch('responsiveMode')\n handleResponsiveModeChange() {\n this.el.style.removeProperty('--ds-shell-page-sticky-header-block-size');\n this.observeHeader(\n this.responsiveMode === 'mobile' || this.desktopHeaderExternal ? null : this.findHeader()\n );\n this.syncPageHeaderBlockSize();\n }\n\n @Watch('desktopHeaderPlacement')\n handleDesktopHeaderPlacementChange() {\n this.observeHeader(this.desktopHeaderExternal ? null : this.findHeader());\n this.syncPageHeaderBlockSize();\n }\n\n componentDidLoad() {\n this.hasLoaded = true;\n this.connectRuntime();\n }\n\n componentDidRender() {\n this.syncHeaderVariant();\n this.syncPageHeaderBlockSize();\n }\n\n connectedCallback() {\n if (!this.hasLoaded) return;\n if (this.reconnectFrame !== null) cancelAnimationFrame(this.reconnectFrame);\n this.reconnectFrame = requestAnimationFrame(() => {\n this.reconnectFrame = null;\n if (this.el.isConnected) this.connectRuntime();\n });\n }\n\n disconnectedCallback() {\n if (this.reconnectFrame !== null) cancelAnimationFrame(this.reconnectFrame);\n this.reconnectFrame = null;\n this.headerResizeObserver?.disconnect();\n this.headerResizeObserver = null;\n this.pageHeaderResizeObserver?.disconnect();\n this.pageHeaderResizeObserver = null;\n this.headerMutationObserver?.disconnect();\n this.headerMutationObserver = null;\n this.cancelHeaderReveal();\n this.cancelHeaderGeometrySync();\n this.headerEl?.style.removeProperty('--ds-bar-title-divider-inset');\n this.scrollRoot?.removeEventListener('scroll', this.handleScroll);\n this.scrollRoot = null;\n }\n\n private connectRuntime() {\n this.observePageHeaderGeometry();\n this.observeHeader(this.responsiveMode === 'mobile' ? null : this.findHeader());\n this.connectScrollRoot();\n }\n\n private get effectiveVariant(): BarTitleVariant {\n if (this.desktopHeaderExternal) return 'compact';\n return resolveShellPageHeaderVariant(\n this.headerPresentation,\n this.headerCapacity ?? 'roomy',\n this.effectiveScrollCompaction ? this.pageTopVisible : true\n );\n }\n\n private get desktopHeaderExternal(): boolean {\n return this.responsiveMode !== 'mobile' && this.desktopHeaderPlacement === 'shell-bar';\n }\n\n private get effectiveScrollCompaction(): boolean {\n return this.scrollCompaction && !this.desktopHeaderExternal;\n }\n\n private get headerContractResolved(): boolean {\n return this.headerPresentation !== 'auto' || this.headerCapacity !== undefined;\n }\n\n private get isScrollCompacted(): boolean {\n return (\n this.effectiveScrollCompaction &&\n this.headerPresentation === 'auto' &&\n this.headerCapacity === 'roomy' &&\n !this.pageTopVisible\n );\n }\n\n private get contentInsetStyles(): { [name: string]: string } {\n const styles: { [name: string]: string } = {};\n const expanded = this.contentInsetBlockStartSize?.trim();\n const compact = this.compactContentInsetBlockStartSize?.trim();\n if (expanded) styles['--ds-shell-page-content-block-start-inset'] = expanded;\n if (compact) styles['--ds-shell-page-compact-content-block-start-inset'] = compact;\n return styles;\n }\n\n private composedParent(element: Element): Element | null {\n return (element as HTMLElement).assignedSlot?.parentElement ?? element.parentElement;\n }\n\n private findScrollRoot(): HTMLElement | Window {\n let ancestor = this.composedParent(this.el);\n while (ancestor && ancestor !== document.documentElement) {\n const overflow = getComputedStyle(ancestor).overflowY;\n if (overflow === 'auto' || overflow === 'scroll' || overflow === 'overlay') {\n return ancestor as HTMLElement;\n }\n ancestor = this.composedParent(ancestor);\n }\n return window;\n }\n\n private connectScrollRoot() {\n const next = this.findScrollRoot();\n if (next === this.scrollRoot) return;\n this.scrollRoot?.removeEventListener('scroll', this.handleScroll);\n this.scrollRoot = next;\n this.scrollRoot.addEventListener('scroll', this.handleScroll, { passive: true });\n this.handleScroll();\n }\n\n private get scrollRootResolved(): boolean {\n const shell = this.el.closest('ds-shell-app');\n if (!shell) return true;\n const expected = shell.querySelector<HTMLElement>('.shell-app__content');\n return expected !== null && this.scrollRoot === expected;\n }\n\n private handleScroll = () => {\n if (!this.sentinelEl || !this.scrollRoot) return;\n const rootTop =\n this.scrollRoot === window ? 0 : (this.scrollRoot as HTMLElement).getBoundingClientRect().top;\n this.syncHeaderDividerInset(rootTop);\n const nextPageTopVisible = this.sentinelEl.getBoundingClientRect().top >= rootTop;\n if (\n this.effectiveScrollCompaction &&\n !nextPageTopVisible &&\n this.pageTopVisible &&\n this.headerEl?.classList.contains(this.variantClass('expanded'))\n ) {\n this.captureExpandedHeaderGeometry(this.headerEl);\n this.expandedGeometryFrozen = true;\n }\n if (\n nextPageTopVisible &&\n this.expandedGeometryFrozen &&\n this.el.getBoundingClientRect().top >= rootTop\n ) {\n this.expandedGeometryFrozen = false;\n if (this.headerEl?.classList.contains(this.variantClass('expanded'))) {\n this.captureExpandedHeaderGeometry(this.headerEl);\n }\n }\n this.pageTopVisible = nextPageTopVisible;\n };\n\n private findHeader(): BarTitleElement | null {\n return this.el.querySelector<BarTitleElement>('ds-bar-title');\n }\n\n private get compactHeaderHeight(): number {\n return resolveCssLengthPx('--dimension-size-600', 0);\n }\n\n private syncHeaderDividerInset(rootTop?: number) {\n const header = this.headerEl;\n if (!header) return;\n\n const ownsScrollTransition =\n this.effectiveScrollCompaction &&\n this.headerPresentation === 'auto' &&\n this.headerCapacity === 'roomy';\n const expandedInset = resolveCssLengthPx('--dimension-space-400', 0);\n if (\n !ownsScrollTransition ||\n !this.sentinelEl ||\n !this.scrollRoot ||\n this.headerTravel <= 0 ||\n expandedInset <= 0\n ) {\n header.style.removeProperty('--ds-bar-title-divider-inset');\n return;\n }\n\n const scrollRootTop =\n rootTop ??\n (this.scrollRoot === window\n ? 0\n : (this.scrollRoot as HTMLElement).getBoundingClientRect().top);\n const remainingTravel = this.sentinelEl.getBoundingClientRect().top - scrollRootTop;\n const progress = Math.min(1, Math.max(0, 1 - remainingTravel / this.headerTravel));\n header.style.setProperty('--ds-bar-title-divider-inset', `${expandedInset * (1 - progress)}px`);\n }\n\n private setHeaderTravel(distance: number) {\n const next = Math.max(0, distance);\n if (Math.abs(next - this.headerTravel) < 0.5) return;\n this.headerTravel = next;\n this.el.style.setProperty('--ds-shell-page-header-travel', `${next}px`);\n this.handleScroll();\n }\n\n private measureHeaderTravel(header: BarTitleElement) {\n const anchor = header.querySelector<HTMLElement>('[data-shell-page-header-anchor]');\n const compactHeight = this.compactHeaderHeight;\n if (!anchor || compactHeight <= 0) return;\n\n const headerRect = header.getBoundingClientRect();\n const anchorRect = anchor.getBoundingClientRect();\n if (headerRect.height <= 0 || anchorRect.height <= 0) return;\n\n const expandedAnchorTop = anchorRect.top - headerRect.top;\n const compactAnchorTop = Math.max(0, (compactHeight - anchorRect.height) / 2);\n this.setHeaderTravel(expandedAnchorTop - compactAnchorTop);\n }\n\n private captureExpandedHeaderGeometry(header: BarTitleElement) {\n // Keep the largest observed expanded geometry for this header instance.\n // During a variant handoff Chromium can briefly report the compact box\n // before the child host class mutation is delivered; that transient must\n // not erase the flow reservation captured at the roomy page top.\n this.expandedHeaderHeight = Math.max(\n this.expandedHeaderHeight,\n header.getBoundingClientRect().height\n );\n this.measureHeaderTravel(header);\n }\n\n private setSpacerHeight(height: number) {\n if (!this.spacerEl) return;\n this.spacerEl.style.setProperty('--ds-shell-page-flow-spacer', `${Math.max(0, height)}px`);\n }\n\n private variantClass(variant: BarTitleVariant): string {\n return `bar-title-host--${variant}`;\n }\n\n private setStickyHeaderOffset(renderedVariant: BarTitleVariant) {\n this.stickyHeaderEl?.style.setProperty(\n '--ds-shell-page-sticky-offset',\n renderedVariant === 'expanded' &&\n this.effectiveScrollCompaction &&\n this.headerPresentation === 'auto' &&\n this.headerCapacity === 'roomy'\n ? `${-this.headerTravel}px`\n : '0px'\n );\n }\n\n private syncPageHeaderBlockSize() {\n if (this.desktopHeaderExternal) {\n this.el.style.setProperty('--ds-shell-page-sticky-header-block-size', '0px');\n return;\n }\n const renderedHeight = this.stickyHeaderEl?.getBoundingClientRect().height ?? 0;\n if (renderedHeight > 0) {\n this.el.style.setProperty('--ds-shell-page-sticky-header-block-size', `${renderedHeight}px`);\n } else {\n this.el.style.removeProperty('--ds-shell-page-sticky-header-block-size');\n }\n }\n\n private observePageHeaderGeometry() {\n this.pageHeaderResizeObserver?.disconnect();\n this.pageHeaderResizeObserver = null;\n this.syncPageHeaderBlockSize();\n\n if (!this.stickyHeaderEl || typeof ResizeObserver === 'undefined') return;\n this.pageHeaderResizeObserver = new ResizeObserver(() => {\n this.syncPageHeaderBlockSize();\n });\n this.pageHeaderResizeObserver.observe(this.stickyHeaderEl);\n }\n\n private syncRenderedHeaderGeometry(header: BarTitleElement) {\n const renderedVariant = this.effectiveVariant;\n if (!header.classList.contains(this.variantClass(renderedVariant))) return;\n\n this.syncPageHeaderBlockSize();\n\n // Update both flow-preservation pieces before reading layout. Otherwise a\n // reverse compact → expanded render can briefly contain the tall header and\n // the compact spacer together, giving browser scroll anchoring a transient\n // height change to compensate.\n this.setSpacerHeight(\n this.isScrollCompacted ? this.expandedHeaderHeight - this.compactHeaderHeight : 0\n );\n this.setStickyHeaderOffset(renderedVariant);\n\n if (renderedVariant === 'expanded' && !this.expandedGeometryFrozen) {\n this.captureExpandedHeaderGeometry(header);\n this.setStickyHeaderOffset(renderedVariant);\n }\n }\n\n private cancelHeaderGeometrySync() {\n if (this.headerGeometryFrame !== null) cancelAnimationFrame(this.headerGeometryFrame);\n this.headerGeometryFrame = null;\n }\n\n private scheduleHeaderGeometrySync(header: BarTitleElement) {\n this.cancelHeaderGeometrySync();\n let framesRemaining = ShellPage.MAX_HEADER_GEOMETRY_FRAMES;\n let framesElapsed = 0;\n const sync = () => {\n if (this.headerEl !== header) return;\n framesRemaining -= 1;\n framesElapsed += 1;\n // In WebKit a child's componentDidLoad can precede its assignment into\n // the parent's rendered slot. Revalidate after ShellApp has had a frame\n // to expose the actual content scroller.\n this.connectScrollRoot();\n this.syncRenderedHeaderGeometry(header);\n const geometryReady =\n this.effectiveVariant !== 'expanded' ||\n (this.expandedHeaderHeight > 0 && this.headerTravel > 0);\n if (\n (geometryReady && this.scrollRootResolved && framesElapsed >= 2) ||\n framesRemaining <= 0\n ) {\n this.headerGeometryFrame = null;\n return;\n }\n this.headerGeometryFrame = requestAnimationFrame(sync);\n };\n this.headerGeometryFrame = requestAnimationFrame(sync);\n }\n\n private cancelHeaderReveal() {\n if (this.headerRevealFrame !== null) cancelAnimationFrame(this.headerRevealFrame);\n this.headerRevealFrame = null;\n this.headerEl?.removeAttribute('data-shell-page-syncing');\n }\n\n private revealHeaderWhenSynced(header: BarTitleElement, variant: BarTitleVariant) {\n // Stencil normally re-renders within a frame or two of a prop change. Stop\n // polling at the cap, but fail closed: a stale variant must never paint.\n let framesRemaining = ShellPage.MAX_HEADER_REVEAL_FRAMES;\n const reveal = () => {\n if (this.headerEl !== header) {\n header.removeAttribute('data-shell-page-syncing');\n return;\n }\n framesRemaining -= 1;\n if (header.classList.contains(this.variantClass(variant))) {\n header.removeAttribute('data-shell-page-syncing');\n this.headerRevealFrame = null;\n return;\n }\n if (framesRemaining <= 0) {\n this.headerRevealFrame = null;\n return;\n }\n this.headerRevealFrame = requestAnimationFrame(reveal);\n };\n this.headerRevealFrame = requestAnimationFrame(reveal);\n }\n\n private syncHeaderVariant(concealUntilSynced = false) {\n const header = this.headerEl;\n if (!header) return;\n\n this.syncHeaderDividerInset();\n if (!this.headerContractResolved) {\n this.cancelHeaderReveal();\n header.setAttribute('data-shell-page-syncing', '');\n return;\n }\n\n const next = this.effectiveVariant;\n const renderedVariantIsStale = !header.classList.contains(this.variantClass(next));\n if (!renderedVariantIsStale) this.syncRenderedHeaderGeometry(header);\n if (renderedVariantIsStale && this.isScrollCompacted) {\n // Preserve the expanded flow before asking BarTitle to become compact.\n // This prevents the sentinel from moving back into view during the\n // parent/child render handoff and keeps the variant transition stable.\n this.setSpacerHeight(this.expandedHeaderHeight - this.compactHeaderHeight);\n this.setStickyHeaderOffset(next);\n }\n if (concealUntilSynced) {\n this.cancelHeaderReveal();\n header.setAttribute('data-shell-page-syncing', '');\n }\n if (header.variant !== next) {\n header.variant = next;\n // The child update and its MutationObserver callback can land in\n // different phases across engines. Poll the rendered class as the\n // authoritative handoff before committing sticky/spacer geometry.\n this.scheduleHeaderGeometrySync(header);\n }\n if (concealUntilSynced) {\n this.revealHeaderWhenSynced(header, next);\n }\n }\n\n private observeHeader(header: BarTitleElement | null) {\n this.cancelHeaderReveal();\n this.cancelHeaderGeometrySync();\n this.headerResizeObserver?.disconnect();\n this.headerResizeObserver = null;\n this.headerMutationObserver?.disconnect();\n this.headerMutationObserver = null;\n this.headerEl?.style.removeProperty('--ds-bar-title-divider-inset');\n this.headerEl = header;\n this.expandedHeaderHeight = 0;\n this.expandedGeometryFrozen = false;\n this.setHeaderTravel(0);\n this.setSpacerHeight(0);\n this.stickyHeaderEl?.style.setProperty('--ds-shell-page-sticky-offset', '0px');\n\n if (!header) return;\n if (header.classList.contains(this.variantClass('expanded'))) {\n this.captureExpandedHeaderGeometry(header);\n }\n if (typeof ResizeObserver !== 'undefined') {\n this.headerResizeObserver = new ResizeObserver(() => {\n this.syncRenderedHeaderGeometry(header);\n });\n this.headerResizeObserver.observe(header);\n }\n if (typeof MutationObserver !== 'undefined') {\n this.headerMutationObserver = new MutationObserver(() => {\n this.syncRenderedHeaderGeometry(header);\n });\n this.headerMutationObserver.observe(header, {\n attributes: true,\n attributeFilter: ['class'],\n childList: true,\n subtree: true,\n });\n }\n this.syncHeaderVariant(true);\n this.scheduleHeaderGeometrySync(header);\n }\n\n private handleHeaderSlotChange = (event: Event) => {\n if (this.responsiveMode === 'mobile') return;\n const slot = event.target as HTMLSlotElement;\n const header = slot\n .assignedElements()\n .find(element => element.tagName.toLowerCase() === 'ds-bar-title');\n this.observeHeader((header as BarTitleElement | undefined) ?? null);\n };\n\n render() {\n const mobile = this.responsiveMode === 'mobile';\n return (\n <Host\n role=\"main\"\n style={this.contentInsetStyles}\n class={{\n 'shell-page-host--inset-default': this.contentInset === 'default',\n 'shell-page-host--inset-none': this.contentInset === 'none',\n 'shell-page-host--block-start-inset-none': this.contentInsetBlockStart === 'none',\n 'shell-page-host--surface-primary': this.contentSurface === 'primary',\n 'shell-page-host--surface-secondary': this.contentSurface === 'secondary',\n 'shell-page-host--mobile': mobile,\n 'shell-page-host--desktop-header-shell-bar': this.desktopHeaderExternal,\n [`shell-page-host--header-${this.effectiveVariant}`]: true,\n }}\n >\n <div\n ref={el => {\n this.sentinelEl = el ?? null;\n }}\n class=\"shell-page__scroll-sentinel\"\n aria-hidden=\"true\"\n />\n <div\n ref={el => {\n this.stickyHeaderEl = el ?? null;\n }}\n class=\"shell-page__sticky-header\"\n >\n <div\n class=\"shell-page__desktop-header\"\n hidden={mobile}\n aria-hidden={mobile ? 'true' : undefined}\n inert={mobile ? true : undefined}\n >\n <slot name=\"header\" onSlotchange={this.handleHeaderSlotChange} />\n </div>\n <div\n class=\"shell-page__mobile-header\"\n hidden={!mobile}\n aria-hidden={!mobile ? 'true' : undefined}\n inert={!mobile ? true : undefined}\n >\n <slot name=\"mobile-header\" />\n </div>\n </div>\n <div\n ref={el => {\n this.spacerEl = el ?? null;\n }}\n class=\"shell-page__flow-spacer\"\n aria-hidden=\"true\"\n />\n <div class=\"shell-page__content\">\n <slot />\n </div>\n </Host>\n );\n }\n}\n",
|
|
355
|
+
"content": "import { Component, Element, h, Host, Prop, State, Watch } from '@stencil/core';\nimport type { ShellResponsiveMode } from '../../shell/shell-responsive';\nimport { resolveCssLengthPx } from '../../utils/resolve-css-length-px';\nimport type { BarTitleVariant } from '../BarTitle/bar-title-types';\nimport { resolveShellPageHeaderVariant } from './shell-page-responsive';\nimport type {\n ShellPageCapacity,\n ShellPageContentInset,\n ShellPageContentSurface,\n ShellPageDesktopHeaderPlacement,\n ShellPageHeaderPresentation,\n} from './shell-page-types';\n\ntype BarTitleElement = HTMLElement & { variant: BarTitleVariant };\n\n@Component({\n tag: 'ds-shell-page',\n styleUrl: 'ShellPage.css',\n scoped: true,\n})\nexport class ShellPage {\n private static readonly MAX_HEADER_REVEAL_FRAMES = 12;\n private static readonly MAX_HEADER_GEOMETRY_FRAMES = 12;\n\n @Element() el!: HTMLElement;\n\n /** Automatic capacity/scroll behavior, or an explicit header variant override. */\n @Prop() headerPresentation: ShellPageHeaderPresentation = 'auto';\n\n /** Available page-header capacity supplied by the owning application shell. */\n @Prop() headerCapacity?: ShellPageCapacity;\n\n /** Desktop title chrome is rendered in this page or externally in the shell bar. */\n @Prop({ attribute: 'desktop-header-placement', reflect: true })\n desktopHeaderPlacement: ShellPageDesktopHeaderPlacement = 'page';\n\n /** Standard page gutters, or no inset for full-bleed page content. */\n @Prop() contentInset: ShellPageContentInset = 'default';\n\n /** Preserve the responsive side/end gutters while allowing content to meet the header. */\n @Prop() contentInsetBlockStart: ShellPageContentInset = 'default';\n\n /** Exact block-start content inset, overriding the responsive default when provided. */\n @Prop() contentInsetBlockStartSize?: string;\n\n /** Exact block-start content inset while the header is compact or constrained. */\n @Prop() compactContentInsetBlockStartSize?: string;\n\n /** Allow roomy automatic headers to compact as the page scrolls. */\n @Prop() scrollCompaction: boolean = true;\n\n /** Canvas surface painted around and beneath routed page content. */\n @Prop() contentSurface: ShellPageContentSurface = 'primary';\n\n /** Explicit shell breakpoint presentation. Mobile uses the dedicated mobile-header slot. */\n @Prop({ attribute: 'responsive-mode', reflect: true })\n responsiveMode: ShellResponsiveMode = 'desktop';\n\n @State() private pageTopVisible = true;\n\n private sentinelEl: HTMLElement | null = null;\n private spacerEl: HTMLElement | null = null;\n private stickyHeaderEl: HTMLElement | null = null;\n private headerEl: BarTitleElement | null = null;\n private expandedHeaderHeight = 0;\n private headerTravel = 0;\n private expandedGeometryFrozen = false;\n private pageHeaderResizeObserver: ResizeObserver | null = null;\n private headerResizeObserver: ResizeObserver | null = null;\n private headerMutationObserver: MutationObserver | null = null;\n private headerRevealFrame: number | null = null;\n private headerGeometryFrame: number | null = null;\n private reconnectFrame: number | null = null;\n private scrollRoot: HTMLElement | Window | null = null;\n private hasLoaded = false;\n\n @Watch('headerCapacity')\n @Watch('headerPresentation')\n @Watch('scrollCompaction')\n handleHeaderContractChange() {\n this.syncHeaderVariant(true);\n }\n\n @Watch('responsiveMode')\n handleResponsiveModeChange() {\n this.el.style.removeProperty('--ds-shell-page-sticky-header-block-size');\n this.observeHeader(\n this.responsiveMode === 'mobile' || this.desktopHeaderExternal ? null : this.findHeader()\n );\n this.syncPageHeaderBlockSize();\n }\n\n @Watch('desktopHeaderPlacement')\n handleDesktopHeaderPlacementChange() {\n this.observeHeader(this.desktopHeaderExternal ? null : this.findHeader());\n this.syncPageHeaderBlockSize();\n }\n\n componentDidLoad() {\n this.hasLoaded = true;\n this.connectRuntime();\n }\n\n componentDidRender() {\n this.syncHeaderVariant();\n this.syncPageHeaderBlockSize();\n }\n\n connectedCallback() {\n if (!this.hasLoaded) return;\n if (this.reconnectFrame !== null) cancelAnimationFrame(this.reconnectFrame);\n this.reconnectFrame = requestAnimationFrame(() => {\n this.reconnectFrame = null;\n if (this.el.isConnected) this.connectRuntime();\n });\n }\n\n disconnectedCallback() {\n if (this.reconnectFrame !== null) cancelAnimationFrame(this.reconnectFrame);\n this.reconnectFrame = null;\n this.headerResizeObserver?.disconnect();\n this.headerResizeObserver = null;\n this.pageHeaderResizeObserver?.disconnect();\n this.pageHeaderResizeObserver = null;\n this.headerMutationObserver?.disconnect();\n this.headerMutationObserver = null;\n this.cancelHeaderReveal();\n this.cancelHeaderGeometrySync();\n this.headerEl?.style.removeProperty('--ds-bar-title-divider-inset');\n this.scrollRoot?.removeEventListener('scroll', this.handleScroll);\n this.scrollRoot = null;\n }\n\n private connectRuntime() {\n this.observePageHeaderGeometry();\n this.observeHeader(this.responsiveMode === 'mobile' ? null : this.findHeader());\n this.connectScrollRoot();\n }\n\n private get effectiveVariant(): BarTitleVariant {\n if (this.desktopHeaderExternal) return 'compact';\n return resolveShellPageHeaderVariant(\n this.headerPresentation,\n this.headerCapacity ?? 'roomy',\n this.effectiveScrollCompaction ? this.pageTopVisible : true\n );\n }\n\n private get desktopHeaderExternal(): boolean {\n return this.responsiveMode !== 'mobile' && this.desktopHeaderPlacement === 'shell-bar';\n }\n\n private get effectiveScrollCompaction(): boolean {\n return this.scrollCompaction && !this.desktopHeaderExternal;\n }\n\n private get headerContractResolved(): boolean {\n return this.headerPresentation !== 'auto' || this.headerCapacity !== undefined;\n }\n\n private get isScrollCompacted(): boolean {\n return (\n this.effectiveScrollCompaction &&\n this.headerPresentation === 'auto' &&\n this.headerCapacity === 'roomy' &&\n !this.pageTopVisible\n );\n }\n\n private get contentInsetStyles(): { [name: string]: string } {\n const styles: { [name: string]: string } = {};\n const expanded = this.contentInsetBlockStartSize?.trim();\n const compact = this.compactContentInsetBlockStartSize?.trim();\n if (expanded) styles['--ds-shell-page-content-block-start-inset'] = expanded;\n if (compact) styles['--ds-shell-page-compact-content-block-start-inset'] = compact;\n return styles;\n }\n\n private composedParent(element: Element): Element | null {\n return (element as HTMLElement).assignedSlot?.parentElement ?? element.parentElement;\n }\n\n private findScrollRoot(): HTMLElement | Window {\n let ancestor = this.composedParent(this.el);\n while (ancestor && ancestor !== document.documentElement) {\n const overflow = getComputedStyle(ancestor).overflowY;\n if (overflow === 'auto' || overflow === 'scroll' || overflow === 'overlay') {\n return ancestor as HTMLElement;\n }\n ancestor = this.composedParent(ancestor);\n }\n return window;\n }\n\n private connectScrollRoot() {\n const next = this.findScrollRoot();\n if (next === this.scrollRoot) return;\n this.scrollRoot?.removeEventListener('scroll', this.handleScroll);\n this.scrollRoot = next;\n this.scrollRoot.addEventListener('scroll', this.handleScroll, { passive: true });\n this.handleScroll();\n }\n\n private get scrollRootResolved(): boolean {\n const shell = this.el.closest('ds-shell-app');\n if (!shell) return true;\n const expected = shell.querySelector<HTMLElement>('.shell-app__content');\n return expected !== null && this.scrollRoot === expected;\n }\n\n private handleScroll = () => {\n if (!this.sentinelEl || !this.scrollRoot) return;\n const rootTop =\n this.scrollRoot === window ? 0 : (this.scrollRoot as HTMLElement).getBoundingClientRect().top;\n this.syncHeaderDividerInset(rootTop);\n const nextPageTopVisible = this.sentinelEl.getBoundingClientRect().top >= rootTop;\n if (\n this.effectiveScrollCompaction &&\n !nextPageTopVisible &&\n this.pageTopVisible &&\n this.headerEl?.classList.contains(this.variantClass('expanded'))\n ) {\n this.captureExpandedHeaderGeometry(this.headerEl);\n this.expandedGeometryFrozen = true;\n }\n if (\n nextPageTopVisible &&\n this.expandedGeometryFrozen &&\n this.el.getBoundingClientRect().top >= rootTop\n ) {\n this.expandedGeometryFrozen = false;\n if (this.headerEl?.classList.contains(this.variantClass('expanded'))) {\n this.captureExpandedHeaderGeometry(this.headerEl);\n }\n }\n this.pageTopVisible = nextPageTopVisible;\n };\n\n private findHeader(): BarTitleElement | null {\n return this.el.querySelector<BarTitleElement>('ds-bar-title');\n }\n\n private get compactHeaderHeight(): number {\n return resolveCssLengthPx('--dimension-size-600', 0);\n }\n\n private syncHeaderDividerInset(rootTop?: number) {\n const header = this.headerEl;\n if (!header) return;\n\n const ownsScrollTransition =\n this.effectiveScrollCompaction &&\n this.headerPresentation === 'auto' &&\n this.headerCapacity === 'roomy';\n const expandedInset = resolveCssLengthPx('--dimension-space-200', 0);\n if (\n !ownsScrollTransition ||\n !this.sentinelEl ||\n !this.scrollRoot ||\n this.headerTravel <= 0 ||\n expandedInset <= 0\n ) {\n header.style.removeProperty('--ds-bar-title-divider-inset');\n return;\n }\n\n const scrollRootTop =\n rootTop ??\n (this.scrollRoot === window\n ? 0\n : (this.scrollRoot as HTMLElement).getBoundingClientRect().top);\n const remainingTravel = this.sentinelEl.getBoundingClientRect().top - scrollRootTop;\n const progress = Math.min(1, Math.max(0, 1 - remainingTravel / this.headerTravel));\n header.style.setProperty('--ds-bar-title-divider-inset', `${expandedInset * (1 - progress)}px`);\n }\n\n private setHeaderTravel(distance: number) {\n const next = Math.max(0, distance);\n if (Math.abs(next - this.headerTravel) < 0.5) return;\n this.headerTravel = next;\n this.el.style.setProperty('--ds-shell-page-header-travel', `${next}px`);\n this.handleScroll();\n }\n\n private measureHeaderTravel(header: BarTitleElement) {\n const anchor = header.querySelector<HTMLElement>('[data-shell-page-header-anchor]');\n const compactHeight = this.compactHeaderHeight;\n if (!anchor || compactHeight <= 0) return;\n\n const headerRect = header.getBoundingClientRect();\n const anchorRect = anchor.getBoundingClientRect();\n if (headerRect.height <= 0 || anchorRect.height <= 0) return;\n\n const expandedAnchorTop = anchorRect.top - headerRect.top;\n const compactAnchorTop = Math.max(0, (compactHeight - anchorRect.height) / 2);\n this.setHeaderTravel(expandedAnchorTop - compactAnchorTop);\n }\n\n private captureExpandedHeaderGeometry(header: BarTitleElement) {\n // Keep the largest observed expanded geometry for this header instance.\n // During a variant handoff Chromium can briefly report the compact box\n // before the child host class mutation is delivered; that transient must\n // not erase the flow reservation captured at the roomy page top.\n this.expandedHeaderHeight = Math.max(\n this.expandedHeaderHeight,\n header.getBoundingClientRect().height\n );\n this.measureHeaderTravel(header);\n }\n\n private setSpacerHeight(height: number) {\n if (!this.spacerEl) return;\n this.spacerEl.style.setProperty('--ds-shell-page-flow-spacer', `${Math.max(0, height)}px`);\n }\n\n private variantClass(variant: BarTitleVariant): string {\n return `bar-title-host--${variant}`;\n }\n\n private setStickyHeaderOffset(renderedVariant: BarTitleVariant) {\n this.stickyHeaderEl?.style.setProperty(\n '--ds-shell-page-sticky-offset',\n renderedVariant === 'expanded' &&\n this.effectiveScrollCompaction &&\n this.headerPresentation === 'auto' &&\n this.headerCapacity === 'roomy'\n ? `${-this.headerTravel}px`\n : '0px'\n );\n }\n\n private syncPageHeaderBlockSize() {\n if (this.desktopHeaderExternal) {\n this.el.style.setProperty('--ds-shell-page-sticky-header-block-size', '0px');\n return;\n }\n const renderedHeight = this.stickyHeaderEl?.getBoundingClientRect().height ?? 0;\n if (renderedHeight > 0) {\n this.el.style.setProperty('--ds-shell-page-sticky-header-block-size', `${renderedHeight}px`);\n } else {\n this.el.style.removeProperty('--ds-shell-page-sticky-header-block-size');\n }\n }\n\n private observePageHeaderGeometry() {\n this.pageHeaderResizeObserver?.disconnect();\n this.pageHeaderResizeObserver = null;\n this.syncPageHeaderBlockSize();\n\n if (!this.stickyHeaderEl || typeof ResizeObserver === 'undefined') return;\n this.pageHeaderResizeObserver = new ResizeObserver(() => {\n this.syncPageHeaderBlockSize();\n });\n this.pageHeaderResizeObserver.observe(this.stickyHeaderEl);\n }\n\n private syncRenderedHeaderGeometry(header: BarTitleElement) {\n const renderedVariant = this.effectiveVariant;\n if (!header.classList.contains(this.variantClass(renderedVariant))) return;\n\n this.syncPageHeaderBlockSize();\n\n // Update both flow-preservation pieces before reading layout. Otherwise a\n // reverse compact → expanded render can briefly contain the tall header and\n // the compact spacer together, giving browser scroll anchoring a transient\n // height change to compensate.\n this.setSpacerHeight(\n this.isScrollCompacted ? this.expandedHeaderHeight - this.compactHeaderHeight : 0\n );\n this.setStickyHeaderOffset(renderedVariant);\n\n if (renderedVariant === 'expanded' && !this.expandedGeometryFrozen) {\n this.captureExpandedHeaderGeometry(header);\n this.setStickyHeaderOffset(renderedVariant);\n }\n }\n\n private cancelHeaderGeometrySync() {\n if (this.headerGeometryFrame !== null) cancelAnimationFrame(this.headerGeometryFrame);\n this.headerGeometryFrame = null;\n }\n\n private scheduleHeaderGeometrySync(header: BarTitleElement) {\n this.cancelHeaderGeometrySync();\n let framesRemaining = ShellPage.MAX_HEADER_GEOMETRY_FRAMES;\n let framesElapsed = 0;\n const sync = () => {\n if (this.headerEl !== header) return;\n framesRemaining -= 1;\n framesElapsed += 1;\n // In WebKit a child's componentDidLoad can precede its assignment into\n // the parent's rendered slot. Revalidate after ShellApp has had a frame\n // to expose the actual content scroller.\n this.connectScrollRoot();\n this.syncRenderedHeaderGeometry(header);\n const geometryReady =\n this.effectiveVariant !== 'expanded' ||\n (this.expandedHeaderHeight > 0 && this.headerTravel > 0);\n if (\n (geometryReady && this.scrollRootResolved && framesElapsed >= 2) ||\n framesRemaining <= 0\n ) {\n this.headerGeometryFrame = null;\n return;\n }\n this.headerGeometryFrame = requestAnimationFrame(sync);\n };\n this.headerGeometryFrame = requestAnimationFrame(sync);\n }\n\n private cancelHeaderReveal() {\n if (this.headerRevealFrame !== null) cancelAnimationFrame(this.headerRevealFrame);\n this.headerRevealFrame = null;\n this.headerEl?.removeAttribute('data-shell-page-syncing');\n }\n\n private revealHeaderWhenSynced(header: BarTitleElement, variant: BarTitleVariant) {\n // Stencil normally re-renders within a frame or two of a prop change. Stop\n // polling at the cap, but fail closed: a stale variant must never paint.\n let framesRemaining = ShellPage.MAX_HEADER_REVEAL_FRAMES;\n const reveal = () => {\n if (this.headerEl !== header) {\n header.removeAttribute('data-shell-page-syncing');\n return;\n }\n framesRemaining -= 1;\n if (header.classList.contains(this.variantClass(variant))) {\n header.removeAttribute('data-shell-page-syncing');\n this.headerRevealFrame = null;\n return;\n }\n if (framesRemaining <= 0) {\n this.headerRevealFrame = null;\n return;\n }\n this.headerRevealFrame = requestAnimationFrame(reveal);\n };\n this.headerRevealFrame = requestAnimationFrame(reveal);\n }\n\n private syncHeaderVariant(concealUntilSynced = false) {\n const header = this.headerEl;\n if (!header) return;\n\n this.syncHeaderDividerInset();\n if (!this.headerContractResolved) {\n this.cancelHeaderReveal();\n header.setAttribute('data-shell-page-syncing', '');\n return;\n }\n\n const next = this.effectiveVariant;\n const renderedVariantIsStale = !header.classList.contains(this.variantClass(next));\n if (!renderedVariantIsStale) this.syncRenderedHeaderGeometry(header);\n if (renderedVariantIsStale && this.isScrollCompacted) {\n // Preserve the expanded flow before asking BarTitle to become compact.\n // This prevents the sentinel from moving back into view during the\n // parent/child render handoff and keeps the variant transition stable.\n this.setSpacerHeight(this.expandedHeaderHeight - this.compactHeaderHeight);\n this.setStickyHeaderOffset(next);\n }\n if (concealUntilSynced) {\n this.cancelHeaderReveal();\n header.setAttribute('data-shell-page-syncing', '');\n }\n if (header.variant !== next) {\n header.variant = next;\n // The child update and its MutationObserver callback can land in\n // different phases across engines. Poll the rendered class as the\n // authoritative handoff before committing sticky/spacer geometry.\n this.scheduleHeaderGeometrySync(header);\n }\n if (concealUntilSynced) {\n this.revealHeaderWhenSynced(header, next);\n }\n }\n\n private observeHeader(header: BarTitleElement | null) {\n this.cancelHeaderReveal();\n this.cancelHeaderGeometrySync();\n this.headerResizeObserver?.disconnect();\n this.headerResizeObserver = null;\n this.headerMutationObserver?.disconnect();\n this.headerMutationObserver = null;\n this.headerEl?.style.removeProperty('--ds-bar-title-divider-inset');\n this.headerEl = header;\n this.expandedHeaderHeight = 0;\n this.expandedGeometryFrozen = false;\n this.setHeaderTravel(0);\n this.setSpacerHeight(0);\n this.stickyHeaderEl?.style.setProperty('--ds-shell-page-sticky-offset', '0px');\n\n if (!header) return;\n if (header.classList.contains(this.variantClass('expanded'))) {\n this.captureExpandedHeaderGeometry(header);\n }\n if (typeof ResizeObserver !== 'undefined') {\n this.headerResizeObserver = new ResizeObserver(() => {\n this.syncRenderedHeaderGeometry(header);\n });\n this.headerResizeObserver.observe(header);\n }\n if (typeof MutationObserver !== 'undefined') {\n this.headerMutationObserver = new MutationObserver(() => {\n this.syncRenderedHeaderGeometry(header);\n });\n this.headerMutationObserver.observe(header, {\n attributes: true,\n attributeFilter: ['class'],\n childList: true,\n subtree: true,\n });\n }\n this.syncHeaderVariant(true);\n this.scheduleHeaderGeometrySync(header);\n }\n\n private handleHeaderSlotChange = (event: Event) => {\n if (this.responsiveMode === 'mobile') return;\n const slot = event.target as HTMLSlotElement;\n const header = slot\n .assignedElements()\n .find(element => element.tagName.toLowerCase() === 'ds-bar-title');\n this.observeHeader((header as BarTitleElement | undefined) ?? null);\n };\n\n render() {\n const mobile = this.responsiveMode === 'mobile';\n return (\n <Host\n role=\"main\"\n style={this.contentInsetStyles}\n class={{\n 'shell-page-host--inset-default': this.contentInset === 'default',\n 'shell-page-host--inset-none': this.contentInset === 'none',\n 'shell-page-host--block-start-inset-none': this.contentInsetBlockStart === 'none',\n 'shell-page-host--surface-primary': this.contentSurface === 'primary',\n 'shell-page-host--surface-secondary': this.contentSurface === 'secondary',\n 'shell-page-host--mobile': mobile,\n 'shell-page-host--desktop-header-shell-bar': this.desktopHeaderExternal,\n [`shell-page-host--header-${this.effectiveVariant}`]: true,\n }}\n >\n <div\n ref={el => {\n this.sentinelEl = el ?? null;\n }}\n class=\"shell-page__scroll-sentinel\"\n aria-hidden=\"true\"\n />\n <div\n ref={el => {\n this.stickyHeaderEl = el ?? null;\n }}\n class=\"shell-page__sticky-header\"\n >\n <div\n class=\"shell-page__desktop-header\"\n hidden={mobile}\n aria-hidden={mobile ? 'true' : undefined}\n inert={mobile ? true : undefined}\n >\n <slot name=\"header\" onSlotchange={this.handleHeaderSlotChange} />\n </div>\n <div\n class=\"shell-page__mobile-header\"\n hidden={!mobile}\n aria-hidden={!mobile ? 'true' : undefined}\n inert={!mobile ? true : undefined}\n >\n <slot name=\"mobile-header\" />\n </div>\n </div>\n <div\n ref={el => {\n this.spacerEl = el ?? null;\n }}\n class=\"shell-page__flow-spacer\"\n aria-hidden=\"true\"\n />\n <div class=\"shell-page__content\">\n <slot />\n </div>\n </Host>\n );\n }\n}\n",
|
|
356
356
|
"type": "registry:ui"
|
|
357
357
|
},
|
|
358
358
|
{
|
|
@@ -457,8 +457,8 @@
|
|
|
457
457
|
"complexPropertyNote": "Assign these non-primitive values as JavaScript properties: accessories, headers, items.",
|
|
458
458
|
"peerDependencies": {
|
|
459
459
|
"required": [
|
|
460
|
-
"@ds-mo/tokens ^6.5.
|
|
461
|
-
"@ds-mo/icons ^8.1.
|
|
460
|
+
"@ds-mo/tokens ^6.5.2",
|
|
461
|
+
"@ds-mo/icons ^8.1.1"
|
|
462
462
|
],
|
|
463
463
|
"frameworks": "Custom Elements; React 18/19 wrappers; Vue 3 wrappers; Angular 19-22 standalone adapters."
|
|
464
464
|
}
|
|
@@ -300,8 +300,8 @@
|
|
|
300
300
|
},
|
|
301
301
|
"peerDependencies": {
|
|
302
302
|
"required": [
|
|
303
|
-
"@ds-mo/tokens ^6.5.
|
|
304
|
-
"@ds-mo/icons ^8.1.
|
|
303
|
+
"@ds-mo/tokens ^6.5.2",
|
|
304
|
+
"@ds-mo/icons ^8.1.1"
|
|
305
305
|
],
|
|
306
306
|
"frameworks": "Custom Elements; React 18/19 wrappers; Vue 3 wrappers; Angular 19-22 standalone adapters."
|
|
307
307
|
}
|