@ds-mo/ui 16.1.3 → 16.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 +1 -1
- package/dist/agent.json +574 -3
- package/dist/angular/ds-disclosure.d.ts +18 -0
- package/dist/angular/ds-disclosure.js +42 -0
- package/dist/angular/ds-drawer.d.ts +40 -0
- package/dist/angular/ds-drawer.js +54 -0
- package/dist/angular/ds-popover.d.ts +24 -0
- package/dist/angular/ds-popover.js +45 -0
- package/dist/angular/index.d.ts +1 -1
- package/dist/angular/index.js +3 -0
- package/dist/angular/proxies.d.ts +3 -0
- package/dist/angular/proxies.js +3 -0
- package/dist/components/ds-agent-activity.js +1 -1
- package/dist/components/ds-agent-questionnaire.js +1 -1
- package/dist/components/ds-agent-response.js +1 -1
- package/dist/components/ds-agent-source-list.js +1 -1
- package/dist/components/ds-agent-tool-call.js +1 -1
- package/dist/components/ds-attachment-list.js +1 -1
- package/dist/components/ds-badge.js +1 -1
- package/dist/components/ds-banner.js +1 -1
- package/dist/components/ds-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-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-group.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-filter.js +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-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-disclosure.d.ts +11 -0
- package/dist/components/ds-disclosure.js +2 -0
- package/dist/components/ds-disclosure.js.map +1 -0
- package/dist/components/ds-divider.js +1 -1
- package/dist/components/ds-drawer.d.ts +11 -0
- package/dist/components/ds-drawer.js +2 -0
- package/dist/components/ds-drawer.js.map +1 -0
- 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-inline-banner-settings.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.js +1 -1
- package/dist/components/ds-map-cluster-marker.js +1 -1
- package/dist/components/ds-map-entity-marker.js +1 -1
- package/dist/components/ds-map-marker.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-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-navigation-tab-group.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-popover.d.ts +11 -0
- package/dist/components/ds-popover.js +2 -0
- package/dist/components/ds-popover.js.map +1 -0
- package/dist/components/ds-radio-tile.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-select-toggle.js +1 -1
- package/dist/components/ds-select.js +1 -1
- package/dist/components/ds-setting-row-checkbox.js +1 -1
- package/dist/components/ds-setting-row-radio.js +1 -1
- package/dist/components/ds-setting-row-radio.js.map +1 -1
- package/dist/components/ds-setting-row-toggle.js +1 -1
- package/dist/components/ds-shell-app.js +1 -1
- package/dist/components/ds-shell-app.js.map +1 -1
- package/dist/components/ds-shell-page.js +1 -1
- package/dist/components/ds-shell-tools.js +1 -1
- package/dist/components/ds-skeleton.js +1 -1
- package/dist/components/ds-slider.js +1 -1
- package/dist/components/ds-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-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-toast.js.map +1 -1
- package/dist/components/ds-tooltip-chart.js +1 -1
- package/dist/components/ds-tooltip.js +1 -1
- package/dist/components/ds-typing-indicator.js +1 -1
- package/dist/components/{p-D-Lo5uuI.js → p-3_LHRdqq.js} +2 -2
- package/dist/components/{p-Cc8p57pS.js → p-9EjkmESv.js} +2 -2
- package/dist/components/{p-CQnjpuMX.js → p-9PpepIfc.js} +2 -2
- package/dist/components/{p-C860ilDn.js → p-A47UHyiC.js} +2 -2
- package/dist/components/p-BCzvtlR2.js +2 -0
- package/dist/components/{p-D3FrQk7o.js.map → p-BCzvtlR2.js.map} +1 -1
- package/dist/components/{p-ClOvX8cD.js → p-BEj9uENH.js} +2 -2
- package/dist/components/{p-CtomYv1z.js → p-BJmAAyRk.js} +2 -2
- package/dist/components/{p-Bt1Ybvz-.js → p-BQ6p3LFP.js} +2 -2
- package/dist/components/{p-DiNEjFpy.js → p-BbndBzzZ.js} +2 -2
- package/dist/components/{p-hFe35P2l.js → p-Bg1UmSZn.js} +2 -2
- package/dist/components/{p-ClhfXMaD.js → p-Bn-cskPo.js} +2 -2
- package/dist/components/p-Bz9dI7nI.js +2 -0
- package/dist/components/{p-Dc_zSOKq.js.map → p-Bz9dI7nI.js.map} +1 -1
- package/dist/components/{p-D538svhW.js → p-C0-sabnr.js} +2 -2
- package/dist/components/{p-DQ-wfTOw.js → p-C11CnHDl.js} +2 -2
- package/dist/components/{p-xszHlNkG.js → p-C68nt4dl.js} +2 -2
- package/dist/components/p-C8UThLHd.js +2 -0
- package/dist/components/{p-CsGkdf4W.js.map → p-C8UThLHd.js.map} +1 -1
- package/dist/components/p-C8WKo0sf.js +2 -0
- package/dist/components/{p-D8iNLSCa.js.map → p-C8WKo0sf.js.map} +1 -1
- package/dist/components/p-CAYqnjyC.js +2 -0
- package/dist/components/{p-oBBzQpg_.js.map → p-CAYqnjyC.js.map} +1 -1
- package/dist/components/{p-G6r6yYg8.js → p-CBezgJ3r.js} +2 -2
- package/dist/components/p-CF6Px2b2.js +2 -0
- package/dist/components/{p-omfGVBEt.js.map → p-CF6Px2b2.js.map} +1 -1
- package/dist/components/{p-CMZAEySh.js → p-CVSE2Ool.js} +2 -2
- package/dist/components/{p-KTaiJztz.js → p-CVuSm1Sh.js} +2 -2
- package/dist/components/{p-BNvZM4jP.js → p-CZVoCdYE.js} +2 -2
- package/dist/components/{p-BYo1-6b5.js → p-CeACJYcU.js} +2 -2
- package/dist/components/{p-lnCWfvuA.js → p-Cjw63BmX.js} +2 -2
- package/dist/components/{p-N-2CZze7.js → p-CkUF45d_.js} +2 -2
- package/dist/components/{p-CbyZ7BDJ.js → p-ClRd39mR.js} +2 -2
- package/dist/components/{p-Nx52go85.js → p-Cn6Ud58k.js} +2 -2
- package/dist/components/{p-BFgJoeVV.js → p-D3r_5Fw-.js} +2 -2
- package/dist/components/p-DCqDQWVK.js +2 -0
- package/dist/components/{p-BuvVFyAA.js.map → p-DCqDQWVK.js.map} +1 -1
- package/dist/components/{p-CXrOGz7X.js → p-DIXpyBWB.js} +2 -2
- package/dist/components/{p-1lWK_LMY.js → p-DJtohrKV.js} +2 -2
- package/dist/components/{p-D5-hqLPK.js → p-DKL7n7NM.js} +2 -2
- package/dist/components/{p-DYI6UlOP.js → p-D_CfgH1L.js} +2 -2
- package/dist/components/{p-P2-eHQJU.js → p-D_N38YjY.js} +2 -2
- package/dist/components/p-Dalq5voO.js +2 -0
- package/dist/components/{p-ClgJZFkb.js.map → p-Dalq5voO.js.map} +1 -1
- package/dist/components/{p-DMPn_dt7.js → p-DcwgXLe-.js} +2 -2
- package/dist/components/{p-BckLHaey.js → p-DpA8UeSy.js} +2 -2
- package/dist/components/{p-hom6svhE.js → p-DtNCj4p0.js} +2 -2
- package/dist/components/{p-pkJ3X3s7.js → p-Dwga8azB.js} +2 -2
- package/dist/components/{p-CGtq7TnC.js → p-LFHrICnV.js} +2 -2
- package/dist/components/p-LQGcTdiv.js +2 -0
- package/dist/components/{p-BI-XuQKW.js.map → p-LQGcTdiv.js.map} +1 -1
- package/dist/components/{p-BoWpIl_m.js → p-N8tEOYt8.js} +2 -2
- package/dist/components/{p-CbJx9swS.js → p-QU-CyuBt.js} +2 -2
- package/dist/components/p-QYI9nW7a.js +2 -0
- package/dist/components/p-QYI9nW7a.js.map +1 -0
- package/dist/components/{p-DcvPY30Q.js → p-X-GPE9bP.js} +2 -2
- package/dist/components/{p-CnsuVR2e.js → p-cOkFnnZV.js} +2 -2
- package/dist/components/{p-BctjNfzt.js → p-c_qWiZ6V.js} +2 -2
- package/dist/components/{p-M-Sxsv7V.js → p-dVdTdZdu.js} +2 -2
- package/dist/components/{p-CE_S4oQ_.js → p-hHtKJMVu.js} +2 -2
- package/dist/components/{p-6r60E0i4.js → p-snYhfM1q.js} +2 -2
- package/dist/components/p-xHv_RGJh.js +2 -0
- package/dist/components/p-xHv_RGJh.js.map +1 -0
- package/dist/components/{p-cvVUXaen.js → p-zGd26CTX.js} +2 -2
- package/dist/docs/components.json +1291 -1
- package/dist/lint/index.js +1 -1
- package/dist/mcp-data/registry/disclosure.json +282 -0
- package/dist/mcp-data/registry/drawer.json +567 -0
- package/dist/mcp-data/registry/popover.json +385 -0
- package/dist/mcp-data/registry/registry.json +582 -2
- package/dist/mcp-data/registry/shell-app.json +6 -4
- package/dist/react/components.d.ts +3 -0
- package/dist/react/components.js +3 -0
- package/dist/react/ds-disclosure.d.ts +12 -0
- package/dist/react/ds-disclosure.js +12 -0
- package/dist/react/ds-drawer.d.ts +16 -0
- package/dist/react/ds-drawer.js +18 -0
- package/dist/react/ds-popover.d.ts +13 -0
- package/dist/react/ds-popover.js +15 -0
- package/dist/types/components/Disclosure/Disclosure.d.ts +22 -0
- package/dist/types/components/Drawer/Drawer.d.ts +123 -0
- package/dist/types/components/Popover/Popover.d.ts +64 -0
- package/dist/types/components.d.ts +423 -0
- package/dist/vue/components.d.ts +3 -0
- package/dist/vue/components.js +3 -0
- package/dist/vue/ds-disclosure.d.ts +3 -0
- package/dist/vue/ds-disclosure.js +15 -0
- package/dist/vue/ds-drawer.d.ts +3 -0
- package/dist/vue/ds-drawer.js +34 -0
- package/dist/vue/ds-popover.d.ts +3 -0
- package/dist/vue/ds-popover.js +21 -0
- package/package.json +1 -1
- package/dist/components/p-BI-XuQKW.js +0 -2
- package/dist/components/p-BuvVFyAA.js +0 -2
- package/dist/components/p-ClgJZFkb.js +0 -2
- package/dist/components/p-CsGkdf4W.js +0 -2
- package/dist/components/p-D3FrQk7o.js +0 -2
- package/dist/components/p-D8iNLSCa.js +0 -2
- package/dist/components/p-DTlNtCX1.js +0 -2
- package/dist/components/p-DTlNtCX1.js.map +0 -1
- package/dist/components/p-Dc_zSOKq.js +0 -2
- package/dist/components/p-oBBzQpg_.js +0 -2
- package/dist/components/p-omfGVBEt.js +0 -2
- /package/dist/components/{p-D-Lo5uuI.js.map → p-3_LHRdqq.js.map} +0 -0
- /package/dist/components/{p-Cc8p57pS.js.map → p-9EjkmESv.js.map} +0 -0
- /package/dist/components/{p-CQnjpuMX.js.map → p-9PpepIfc.js.map} +0 -0
- /package/dist/components/{p-C860ilDn.js.map → p-A47UHyiC.js.map} +0 -0
- /package/dist/components/{p-ClOvX8cD.js.map → p-BEj9uENH.js.map} +0 -0
- /package/dist/components/{p-CtomYv1z.js.map → p-BJmAAyRk.js.map} +0 -0
- /package/dist/components/{p-Bt1Ybvz-.js.map → p-BQ6p3LFP.js.map} +0 -0
- /package/dist/components/{p-DiNEjFpy.js.map → p-BbndBzzZ.js.map} +0 -0
- /package/dist/components/{p-hFe35P2l.js.map → p-Bg1UmSZn.js.map} +0 -0
- /package/dist/components/{p-ClhfXMaD.js.map → p-Bn-cskPo.js.map} +0 -0
- /package/dist/components/{p-D538svhW.js.map → p-C0-sabnr.js.map} +0 -0
- /package/dist/components/{p-DQ-wfTOw.js.map → p-C11CnHDl.js.map} +0 -0
- /package/dist/components/{p-xszHlNkG.js.map → p-C68nt4dl.js.map} +0 -0
- /package/dist/components/{p-G6r6yYg8.js.map → p-CBezgJ3r.js.map} +0 -0
- /package/dist/components/{p-CMZAEySh.js.map → p-CVSE2Ool.js.map} +0 -0
- /package/dist/components/{p-KTaiJztz.js.map → p-CVuSm1Sh.js.map} +0 -0
- /package/dist/components/{p-BNvZM4jP.js.map → p-CZVoCdYE.js.map} +0 -0
- /package/dist/components/{p-BYo1-6b5.js.map → p-CeACJYcU.js.map} +0 -0
- /package/dist/components/{p-lnCWfvuA.js.map → p-Cjw63BmX.js.map} +0 -0
- /package/dist/components/{p-N-2CZze7.js.map → p-CkUF45d_.js.map} +0 -0
- /package/dist/components/{p-CbyZ7BDJ.js.map → p-ClRd39mR.js.map} +0 -0
- /package/dist/components/{p-Nx52go85.js.map → p-Cn6Ud58k.js.map} +0 -0
- /package/dist/components/{p-BFgJoeVV.js.map → p-D3r_5Fw-.js.map} +0 -0
- /package/dist/components/{p-CXrOGz7X.js.map → p-DIXpyBWB.js.map} +0 -0
- /package/dist/components/{p-1lWK_LMY.js.map → p-DJtohrKV.js.map} +0 -0
- /package/dist/components/{p-D5-hqLPK.js.map → p-DKL7n7NM.js.map} +0 -0
- /package/dist/components/{p-DYI6UlOP.js.map → p-D_CfgH1L.js.map} +0 -0
- /package/dist/components/{p-P2-eHQJU.js.map → p-D_N38YjY.js.map} +0 -0
- /package/dist/components/{p-DMPn_dt7.js.map → p-DcwgXLe-.js.map} +0 -0
- /package/dist/components/{p-BckLHaey.js.map → p-DpA8UeSy.js.map} +0 -0
- /package/dist/components/{p-hom6svhE.js.map → p-DtNCj4p0.js.map} +0 -0
- /package/dist/components/{p-pkJ3X3s7.js.map → p-Dwga8azB.js.map} +0 -0
- /package/dist/components/{p-CGtq7TnC.js.map → p-LFHrICnV.js.map} +0 -0
- /package/dist/components/{p-BoWpIl_m.js.map → p-N8tEOYt8.js.map} +0 -0
- /package/dist/components/{p-CbJx9swS.js.map → p-QU-CyuBt.js.map} +0 -0
- /package/dist/components/{p-DcvPY30Q.js.map → p-X-GPE9bP.js.map} +0 -0
- /package/dist/components/{p-CnsuVR2e.js.map → p-cOkFnnZV.js.map} +0 -0
- /package/dist/components/{p-BctjNfzt.js.map → p-c_qWiZ6V.js.map} +0 -0
- /package/dist/components/{p-M-Sxsv7V.js.map → p-dVdTdZdu.js.map} +0 -0
- /package/dist/components/{p-CE_S4oQ_.js.map → p-hHtKJMVu.js.map} +0 -0
- /package/dist/components/{p-6r60E0i4.js.map → p-snYhfM1q.js.map} +0 -0
- /package/dist/components/{p-cvVUXaen.js.map → p-zGd26CTX.js.map} +0 -0
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "drawer",
|
|
4
|
+
"title": "Drawer",
|
|
5
|
+
"description": "Edge-attached details panel beside the current page with a generic header, a composable content slot, and a footer for record actions and previous/next paging; non-modal by default with an optional modal presentation and a Back action for multi-depth content.",
|
|
6
|
+
"type": "registry:ui",
|
|
7
|
+
"meta": {
|
|
8
|
+
"library": "@ds-mo/ui",
|
|
9
|
+
"distribution": "npm",
|
|
10
|
+
"storybook": "https://zainadeel.github.io/compomo/?path=/story/drawer",
|
|
11
|
+
"source": "src/wc/components/Drawer/Drawer.tsx",
|
|
12
|
+
"intentStatus": "complete",
|
|
13
|
+
"intent": {
|
|
14
|
+
"audience": "general",
|
|
15
|
+
"status": "stable",
|
|
16
|
+
"summary": "Edge-attached details panel beside the current page with a generic header, a composable content slot, and a footer for record actions and previous/next paging; non-modal by default with an optional modal presentation and a Back action for multi-depth content.",
|
|
17
|
+
"useWhen": [
|
|
18
|
+
"The user inspects the details of an item selected from a list, table, or map while the page stays visible and interactive.",
|
|
19
|
+
"The user steps through records in order from the details view; enable navigation for previous and next paging.",
|
|
20
|
+
"Details drill into a related record, such as vehicle to driver; set backLabel and swap content in place.",
|
|
21
|
+
"A contained task such as a multi-field edit needs more room than a Modal but not a new page; set modal."
|
|
22
|
+
],
|
|
23
|
+
"avoidWhen": [
|
|
24
|
+
"The user must confirm a single focused decision; Modal is more direct.",
|
|
25
|
+
"The content is a short contextual control anchored to a trigger.",
|
|
26
|
+
"The panel is persistent application navigation or a shell tool panel.",
|
|
27
|
+
"The workflow is long enough to need its own URL, history, or deep links; offer an open-full-page header action instead.",
|
|
28
|
+
"A second drawer would be stacked over an existing one; only one non-modal drawer is open per document, so swap content in place and use backLabel."
|
|
29
|
+
],
|
|
30
|
+
"alternatives": [
|
|
31
|
+
{
|
|
32
|
+
"when": "The user confirms or cancels one focused decision.",
|
|
33
|
+
"component": "component:ds-modal",
|
|
34
|
+
"reason": "Modal centers a short blocking decision without moving the user's attention to a page edge."
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"when": "A trigger reveals short interactive content anchored to it.",
|
|
38
|
+
"component": "component:ds-popover",
|
|
39
|
+
"reason": "Popover keeps the content beside its trigger and dismisses on outside activation."
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"when": "Supplementary tools such as search, inbox, or agents live beside every page.",
|
|
43
|
+
"component": "component:ds-shell-tools",
|
|
44
|
+
"reason": "ShellTools owns the persistent application tool panel and its responsive layout."
|
|
45
|
+
}
|
|
46
|
+
],
|
|
47
|
+
"commonlyComposedWith": [
|
|
48
|
+
"component:ds-shell-app",
|
|
49
|
+
"component:ds-button-filled",
|
|
50
|
+
"component:ds-button-unfilled",
|
|
51
|
+
"component:ds-tooltip",
|
|
52
|
+
"component:ds-text",
|
|
53
|
+
"component:ds-divider"
|
|
54
|
+
],
|
|
55
|
+
"accessibility": [
|
|
56
|
+
"The heading is the dialog's accessible name. Keep it a generic label for what the panel shows, such as Event details or Vehicle details; record identity belongs in the content.",
|
|
57
|
+
"Connect explanatory content through aria-describedby when the task needs context beyond the heading.",
|
|
58
|
+
"Opening moves focus to the leading header action: Back when backLabel is set, otherwise Close. Changing backLabel while focus is in the drawer moves focus to the new leading action.",
|
|
59
|
+
"A modal drawer makes the page inert, traps Tab, and shows a shade. A non-modal drawer leaves the page interactive, lets Tab leave the panel, and ignores outside clicks.",
|
|
60
|
+
"Close, Escape, and shade activation emit dsClose with close-button, escape, or backdrop before dismissal. A non-modal drawer handles Escape at the document unless another handler prevented it or a modal dialog is open.",
|
|
61
|
+
"With navigation enabled, ArrowLeft and ArrowRight page records from anywhere in the document, respecting writing direction, except inside text fields, selects, sliders, menus, listboxes, tablists, comboboxes, and editable content. Set navigationShortcutsBlocked while the page needs those keys.",
|
|
62
|
+
"Focus return after close follows the presentation. A modal drawer returns focus to its trigger and reports restoreFocus false. A non-modal drawer moves no focus itself; dsAfterClose reports restoreFocus true unless another drawer replaced it, and the owner then focuses the record now shown, such as the current row or map marker, since paging may have moved past the original trigger.",
|
|
63
|
+
"Header actions and footer navigation are icon buttons with tooltips; give header-actions their own accessible names and keep them to one or two."
|
|
64
|
+
],
|
|
65
|
+
"states": [
|
|
66
|
+
"The owner controls open, heading, content, and paging availability; the component mutates open to false on dismissal and emits dsAfterClose after exit motion.",
|
|
67
|
+
"Only one non-modal drawer is open per document. Opening another closes the previous one with reason replaced and restoreFocus false.",
|
|
68
|
+
"The header holds optional Back, the heading, optional header-actions such as open full page, a divider when actions exist, and Close. Back and Close appear together at depth.",
|
|
69
|
+
"The default slot is composable content with a 16px inset; bodyInset none removes it for content that owns its edges.",
|
|
70
|
+
"The footer holds slot footer actions at the start and, with navigation, previous and next at the end. It is hidden when both are empty.",
|
|
71
|
+
"previousAvailable and nextAvailable make the paging actions inactive at list edges; the owner advances the selection on dsPrevious and dsNext.",
|
|
72
|
+
"modal is read on open; changing it while open re-presents the same surface in the new mode.",
|
|
73
|
+
"dsClose, dsAfterClose, dsBack, dsPrevious, and dsNext from slotted components, such as a footer ds-menu, stop at the drawer boundary, so owner listeners on the drawer only receive the drawer's own events; listen on the nested component directly."
|
|
74
|
+
],
|
|
75
|
+
"responsiveBehavior": [
|
|
76
|
+
"Rendered anywhere inside ShellApp page content, a non-modal drawer covers the page content lane only, below the shell bar and beside the navigation and tool panels, and stays in place while the page scrolls; outside a shell it attaches to the viewport edge.",
|
|
77
|
+
"Modal drawers always present in the top layer against the viewport edge.",
|
|
78
|
+
"Width comes from drawerWidth, defaulting to the small panel width, and is clamped to the available lane.",
|
|
79
|
+
"Below 768px the panel spans the full width of its lane; header and footer stay fixed while content scrolls."
|
|
80
|
+
],
|
|
81
|
+
"frameworkCaveats": {
|
|
82
|
+
"customElements": [
|
|
83
|
+
"Set open, modal, previousAvailable, and nextAvailable as JavaScript properties when controlling the drawer.",
|
|
84
|
+
"Render a page details drawer in the page's own markup next to the list it describes; assign footer actions with slot=\"footer\" and icon actions with slot=\"header-actions\"."
|
|
85
|
+
],
|
|
86
|
+
"react": [
|
|
87
|
+
"Pass open, modal, previousAvailable, and nextAvailable to DsDrawer; handle onDsClose, onDsPrevious, onDsNext, onDsBack, and onDsAfterClose.",
|
|
88
|
+
"Render the drawer inside the routed page component; assign footer and header-actions slots on its children."
|
|
89
|
+
],
|
|
90
|
+
"angular": [
|
|
91
|
+
"Bind [open], [modal], [previousAvailable], and [nextAvailable]; handle (dsClose), (dsPrevious), (dsNext), (dsBack), and (dsAfterClose) with $event.detail.",
|
|
92
|
+
"Render the drawer in the routed page template; assign footer and header-actions slots on its children."
|
|
93
|
+
]
|
|
94
|
+
},
|
|
95
|
+
"references": [
|
|
96
|
+
{
|
|
97
|
+
"label": "Storybook event details, standalone, multi-depth, modal, and flush examples",
|
|
98
|
+
"path": "src/wc/components/Drawer/Drawer.stories.ts"
|
|
99
|
+
}
|
|
100
|
+
],
|
|
101
|
+
"styling": {
|
|
102
|
+
"rationale": "The component owns its panel surface, edge elevation, motion, and fixed header and footer chrome so every details drawer shares one rhythm; the content slot is application-owned.",
|
|
103
|
+
"protected": [
|
|
104
|
+
{
|
|
105
|
+
"properties": [
|
|
106
|
+
"background",
|
|
107
|
+
"border",
|
|
108
|
+
"box-shadow",
|
|
109
|
+
"color"
|
|
110
|
+
],
|
|
111
|
+
"reason": "The component owns its surface and edge elevation; compose an application-owned surface inside the content slot when needed.",
|
|
112
|
+
"props": []
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"properties": [
|
|
116
|
+
"width"
|
|
117
|
+
],
|
|
118
|
+
"reason": "Width comes from panel-width tokens through drawerWidth and is clamped to the lane.",
|
|
119
|
+
"props": [
|
|
120
|
+
"drawerWidth"
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
],
|
|
124
|
+
"customProperties": []
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
"api": {
|
|
128
|
+
"props": {
|
|
129
|
+
"ariaDescribedby": {
|
|
130
|
+
"type": "string | undefined",
|
|
131
|
+
"resolvedType": "string | undefined",
|
|
132
|
+
"attribute": "aria-describedby",
|
|
133
|
+
"required": false,
|
|
134
|
+
"mutable": false,
|
|
135
|
+
"description": "Optional id reference for explanatory content in the body."
|
|
136
|
+
},
|
|
137
|
+
"backLabel": {
|
|
138
|
+
"type": "string | undefined",
|
|
139
|
+
"resolvedType": "string | undefined",
|
|
140
|
+
"attribute": "back-label",
|
|
141
|
+
"required": false,
|
|
142
|
+
"mutable": false,
|
|
143
|
+
"description": "When set, shows a leading Back action for multi-depth content and emits dsBack."
|
|
144
|
+
},
|
|
145
|
+
"bodyInset": {
|
|
146
|
+
"type": "DrawerBodyInset",
|
|
147
|
+
"resolvedType": "\"default\" | \"none\"",
|
|
148
|
+
"attribute": "body-inset",
|
|
149
|
+
"default": "'default'",
|
|
150
|
+
"required": false,
|
|
151
|
+
"mutable": false,
|
|
152
|
+
"description": "`none` removes the body inset for content that owns its own edges."
|
|
153
|
+
},
|
|
154
|
+
"closeAriaLabel": {
|
|
155
|
+
"type": "string",
|
|
156
|
+
"resolvedType": "string",
|
|
157
|
+
"attribute": "close-aria-label",
|
|
158
|
+
"default": "'Close'",
|
|
159
|
+
"required": false,
|
|
160
|
+
"mutable": false
|
|
161
|
+
},
|
|
162
|
+
"drawerWidth": {
|
|
163
|
+
"type": "DrawerWidth | string",
|
|
164
|
+
"resolvedType": "string",
|
|
165
|
+
"attribute": "drawer-width",
|
|
166
|
+
"default": "'sm'",
|
|
167
|
+
"required": false,
|
|
168
|
+
"mutable": false,
|
|
169
|
+
"description": "Panel width token (`sm`, `md`, `lg`) or a CSS length. Narrow viewports use the full width."
|
|
170
|
+
},
|
|
171
|
+
"heading": {
|
|
172
|
+
"type": "string",
|
|
173
|
+
"resolvedType": "string",
|
|
174
|
+
"attribute": "heading",
|
|
175
|
+
"required": true,
|
|
176
|
+
"mutable": false,
|
|
177
|
+
"description": "Generic label for what the drawer shows, such as \"Event details\". Record identity belongs in the body."
|
|
178
|
+
},
|
|
179
|
+
"modal": {
|
|
180
|
+
"type": "boolean",
|
|
181
|
+
"resolvedType": "boolean",
|
|
182
|
+
"attribute": "modal",
|
|
183
|
+
"default": "false",
|
|
184
|
+
"required": false,
|
|
185
|
+
"mutable": false,
|
|
186
|
+
"description": "Block the page behind a shade and contain focus. The default non-modal\ndrawer leaves the page interactive beside it."
|
|
187
|
+
},
|
|
188
|
+
"navigation": {
|
|
189
|
+
"type": "boolean",
|
|
190
|
+
"resolvedType": "boolean",
|
|
191
|
+
"attribute": "navigation",
|
|
192
|
+
"default": "false",
|
|
193
|
+
"required": false,
|
|
194
|
+
"mutable": false,
|
|
195
|
+
"description": "Shows Previous and Next record actions at the end of the footer."
|
|
196
|
+
},
|
|
197
|
+
"navigationAriaLabel": {
|
|
198
|
+
"type": "string",
|
|
199
|
+
"resolvedType": "string",
|
|
200
|
+
"attribute": "navigation-aria-label",
|
|
201
|
+
"default": "'Record navigation'",
|
|
202
|
+
"required": false,
|
|
203
|
+
"mutable": false
|
|
204
|
+
},
|
|
205
|
+
"navigationShortcutsBlocked": {
|
|
206
|
+
"type": "boolean",
|
|
207
|
+
"resolvedType": "boolean",
|
|
208
|
+
"attribute": "navigation-shortcuts-blocked",
|
|
209
|
+
"default": "false",
|
|
210
|
+
"required": false,
|
|
211
|
+
"mutable": false,
|
|
212
|
+
"description": "Suspends Left/Right record paging, for example while a menu in the drawer is open."
|
|
213
|
+
},
|
|
214
|
+
"nextAriaLabel": {
|
|
215
|
+
"type": "string",
|
|
216
|
+
"resolvedType": "string",
|
|
217
|
+
"attribute": "next-aria-label",
|
|
218
|
+
"default": "'Next item'",
|
|
219
|
+
"required": false,
|
|
220
|
+
"mutable": false
|
|
221
|
+
},
|
|
222
|
+
"nextAvailable": {
|
|
223
|
+
"type": "boolean",
|
|
224
|
+
"resolvedType": "boolean",
|
|
225
|
+
"attribute": "next-available",
|
|
226
|
+
"default": "false",
|
|
227
|
+
"required": false,
|
|
228
|
+
"mutable": false
|
|
229
|
+
},
|
|
230
|
+
"open": {
|
|
231
|
+
"type": "boolean",
|
|
232
|
+
"resolvedType": "boolean",
|
|
233
|
+
"attribute": "open",
|
|
234
|
+
"default": "false",
|
|
235
|
+
"required": false,
|
|
236
|
+
"mutable": true
|
|
237
|
+
},
|
|
238
|
+
"previousAriaLabel": {
|
|
239
|
+
"type": "string",
|
|
240
|
+
"resolvedType": "string",
|
|
241
|
+
"attribute": "previous-aria-label",
|
|
242
|
+
"default": "'Previous item'",
|
|
243
|
+
"required": false,
|
|
244
|
+
"mutable": false
|
|
245
|
+
},
|
|
246
|
+
"previousAvailable": {
|
|
247
|
+
"type": "boolean",
|
|
248
|
+
"resolvedType": "boolean",
|
|
249
|
+
"attribute": "previous-available",
|
|
250
|
+
"default": "false",
|
|
251
|
+
"required": false,
|
|
252
|
+
"mutable": false
|
|
253
|
+
},
|
|
254
|
+
"side": {
|
|
255
|
+
"type": "DrawerSide",
|
|
256
|
+
"resolvedType": "\"end\" | \"start\"",
|
|
257
|
+
"attribute": "side",
|
|
258
|
+
"default": "'end'",
|
|
259
|
+
"required": false,
|
|
260
|
+
"mutable": false,
|
|
261
|
+
"description": "Edge the panel attaches to."
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
"events": [
|
|
265
|
+
{
|
|
266
|
+
"name": "dsAfterClose",
|
|
267
|
+
"detail": "DrawerAfterCloseDetail",
|
|
268
|
+
"bubbles": true,
|
|
269
|
+
"cancelable": true,
|
|
270
|
+
"composed": true,
|
|
271
|
+
"description": "Emitted after exit motion completes."
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
"name": "dsBack",
|
|
275
|
+
"detail": "MouseEvent",
|
|
276
|
+
"bubbles": true,
|
|
277
|
+
"cancelable": true,
|
|
278
|
+
"composed": true,
|
|
279
|
+
"description": "Emitted when the leading Back action is activated. The owner swaps heading and body."
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
"name": "dsClose",
|
|
283
|
+
"detail": "DrawerCloseDetail",
|
|
284
|
+
"bubbles": true,
|
|
285
|
+
"cancelable": true,
|
|
286
|
+
"composed": true,
|
|
287
|
+
"description": "Emitted when the drawer requests to close; open is already false."
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
"name": "dsNext",
|
|
291
|
+
"detail": "void",
|
|
292
|
+
"bubbles": true,
|
|
293
|
+
"cancelable": true,
|
|
294
|
+
"composed": true,
|
|
295
|
+
"description": "Emitted by the Next action or ArrowRight while navigation is shown and available."
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
"name": "dsPrevious",
|
|
299
|
+
"detail": "void",
|
|
300
|
+
"bubbles": true,
|
|
301
|
+
"cancelable": true,
|
|
302
|
+
"composed": true,
|
|
303
|
+
"description": "Emitted by the Previous action or ArrowLeft while navigation is shown and available."
|
|
304
|
+
}
|
|
305
|
+
],
|
|
306
|
+
"methods": [],
|
|
307
|
+
"slots": [
|
|
308
|
+
{
|
|
309
|
+
"name": "",
|
|
310
|
+
"description": "The default slot"
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
"name": "footer"
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
"name": "header-actions"
|
|
317
|
+
}
|
|
318
|
+
]
|
|
319
|
+
},
|
|
320
|
+
"props": {
|
|
321
|
+
"ariaDescribedby": {
|
|
322
|
+
"type": "string | undefined",
|
|
323
|
+
"resolvedType": "string | undefined",
|
|
324
|
+
"attribute": "aria-describedby",
|
|
325
|
+
"required": false,
|
|
326
|
+
"mutable": false,
|
|
327
|
+
"description": "Optional id reference for explanatory content in the body."
|
|
328
|
+
},
|
|
329
|
+
"backLabel": {
|
|
330
|
+
"type": "string | undefined",
|
|
331
|
+
"resolvedType": "string | undefined",
|
|
332
|
+
"attribute": "back-label",
|
|
333
|
+
"required": false,
|
|
334
|
+
"mutable": false,
|
|
335
|
+
"description": "When set, shows a leading Back action for multi-depth content and emits dsBack."
|
|
336
|
+
},
|
|
337
|
+
"bodyInset": {
|
|
338
|
+
"type": "DrawerBodyInset",
|
|
339
|
+
"resolvedType": "\"default\" | \"none\"",
|
|
340
|
+
"attribute": "body-inset",
|
|
341
|
+
"default": "'default'",
|
|
342
|
+
"required": false,
|
|
343
|
+
"mutable": false,
|
|
344
|
+
"description": "`none` removes the body inset for content that owns its own edges."
|
|
345
|
+
},
|
|
346
|
+
"closeAriaLabel": {
|
|
347
|
+
"type": "string",
|
|
348
|
+
"resolvedType": "string",
|
|
349
|
+
"attribute": "close-aria-label",
|
|
350
|
+
"default": "'Close'",
|
|
351
|
+
"required": false,
|
|
352
|
+
"mutable": false
|
|
353
|
+
},
|
|
354
|
+
"drawerWidth": {
|
|
355
|
+
"type": "DrawerWidth | string",
|
|
356
|
+
"resolvedType": "string",
|
|
357
|
+
"attribute": "drawer-width",
|
|
358
|
+
"default": "'sm'",
|
|
359
|
+
"required": false,
|
|
360
|
+
"mutable": false,
|
|
361
|
+
"description": "Panel width token (`sm`, `md`, `lg`) or a CSS length. Narrow viewports use the full width."
|
|
362
|
+
},
|
|
363
|
+
"heading": {
|
|
364
|
+
"type": "string",
|
|
365
|
+
"resolvedType": "string",
|
|
366
|
+
"attribute": "heading",
|
|
367
|
+
"required": true,
|
|
368
|
+
"mutable": false,
|
|
369
|
+
"description": "Generic label for what the drawer shows, such as \"Event details\". Record identity belongs in the body."
|
|
370
|
+
},
|
|
371
|
+
"modal": {
|
|
372
|
+
"type": "boolean",
|
|
373
|
+
"resolvedType": "boolean",
|
|
374
|
+
"attribute": "modal",
|
|
375
|
+
"default": "false",
|
|
376
|
+
"required": false,
|
|
377
|
+
"mutable": false,
|
|
378
|
+
"description": "Block the page behind a shade and contain focus. The default non-modal\ndrawer leaves the page interactive beside it."
|
|
379
|
+
},
|
|
380
|
+
"navigation": {
|
|
381
|
+
"type": "boolean",
|
|
382
|
+
"resolvedType": "boolean",
|
|
383
|
+
"attribute": "navigation",
|
|
384
|
+
"default": "false",
|
|
385
|
+
"required": false,
|
|
386
|
+
"mutable": false,
|
|
387
|
+
"description": "Shows Previous and Next record actions at the end of the footer."
|
|
388
|
+
},
|
|
389
|
+
"navigationAriaLabel": {
|
|
390
|
+
"type": "string",
|
|
391
|
+
"resolvedType": "string",
|
|
392
|
+
"attribute": "navigation-aria-label",
|
|
393
|
+
"default": "'Record navigation'",
|
|
394
|
+
"required": false,
|
|
395
|
+
"mutable": false
|
|
396
|
+
},
|
|
397
|
+
"navigationShortcutsBlocked": {
|
|
398
|
+
"type": "boolean",
|
|
399
|
+
"resolvedType": "boolean",
|
|
400
|
+
"attribute": "navigation-shortcuts-blocked",
|
|
401
|
+
"default": "false",
|
|
402
|
+
"required": false,
|
|
403
|
+
"mutable": false,
|
|
404
|
+
"description": "Suspends Left/Right record paging, for example while a menu in the drawer is open."
|
|
405
|
+
},
|
|
406
|
+
"nextAriaLabel": {
|
|
407
|
+
"type": "string",
|
|
408
|
+
"resolvedType": "string",
|
|
409
|
+
"attribute": "next-aria-label",
|
|
410
|
+
"default": "'Next item'",
|
|
411
|
+
"required": false,
|
|
412
|
+
"mutable": false
|
|
413
|
+
},
|
|
414
|
+
"nextAvailable": {
|
|
415
|
+
"type": "boolean",
|
|
416
|
+
"resolvedType": "boolean",
|
|
417
|
+
"attribute": "next-available",
|
|
418
|
+
"default": "false",
|
|
419
|
+
"required": false,
|
|
420
|
+
"mutable": false
|
|
421
|
+
},
|
|
422
|
+
"open": {
|
|
423
|
+
"type": "boolean",
|
|
424
|
+
"resolvedType": "boolean",
|
|
425
|
+
"attribute": "open",
|
|
426
|
+
"default": "false",
|
|
427
|
+
"required": false,
|
|
428
|
+
"mutable": true
|
|
429
|
+
},
|
|
430
|
+
"previousAriaLabel": {
|
|
431
|
+
"type": "string",
|
|
432
|
+
"resolvedType": "string",
|
|
433
|
+
"attribute": "previous-aria-label",
|
|
434
|
+
"default": "'Previous item'",
|
|
435
|
+
"required": false,
|
|
436
|
+
"mutable": false
|
|
437
|
+
},
|
|
438
|
+
"previousAvailable": {
|
|
439
|
+
"type": "boolean",
|
|
440
|
+
"resolvedType": "boolean",
|
|
441
|
+
"attribute": "previous-available",
|
|
442
|
+
"default": "false",
|
|
443
|
+
"required": false,
|
|
444
|
+
"mutable": false
|
|
445
|
+
},
|
|
446
|
+
"side": {
|
|
447
|
+
"type": "DrawerSide",
|
|
448
|
+
"resolvedType": "\"end\" | \"start\"",
|
|
449
|
+
"attribute": "side",
|
|
450
|
+
"default": "'end'",
|
|
451
|
+
"required": false,
|
|
452
|
+
"mutable": false,
|
|
453
|
+
"description": "Edge the panel attaches to."
|
|
454
|
+
}
|
|
455
|
+
},
|
|
456
|
+
"events": [
|
|
457
|
+
{
|
|
458
|
+
"name": "dsAfterClose",
|
|
459
|
+
"detail": "DrawerAfterCloseDetail",
|
|
460
|
+
"bubbles": true,
|
|
461
|
+
"cancelable": true,
|
|
462
|
+
"composed": true,
|
|
463
|
+
"description": "Emitted after exit motion completes."
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
"name": "dsBack",
|
|
467
|
+
"detail": "MouseEvent",
|
|
468
|
+
"bubbles": true,
|
|
469
|
+
"cancelable": true,
|
|
470
|
+
"composed": true,
|
|
471
|
+
"description": "Emitted when the leading Back action is activated. The owner swaps heading and body."
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
"name": "dsClose",
|
|
475
|
+
"detail": "DrawerCloseDetail",
|
|
476
|
+
"bubbles": true,
|
|
477
|
+
"cancelable": true,
|
|
478
|
+
"composed": true,
|
|
479
|
+
"description": "Emitted when the drawer requests to close; open is already false."
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
"name": "dsNext",
|
|
483
|
+
"detail": "void",
|
|
484
|
+
"bubbles": true,
|
|
485
|
+
"cancelable": true,
|
|
486
|
+
"composed": true,
|
|
487
|
+
"description": "Emitted by the Next action or ArrowRight while navigation is shown and available."
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
"name": "dsPrevious",
|
|
491
|
+
"detail": "void",
|
|
492
|
+
"bubbles": true,
|
|
493
|
+
"cancelable": true,
|
|
494
|
+
"composed": true,
|
|
495
|
+
"description": "Emitted by the Previous action or ArrowLeft while navigation is shown and available."
|
|
496
|
+
}
|
|
497
|
+
],
|
|
498
|
+
"methods": [],
|
|
499
|
+
"slots": [
|
|
500
|
+
{
|
|
501
|
+
"name": "",
|
|
502
|
+
"description": "The default slot"
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
"name": "footer"
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
"name": "header-actions"
|
|
509
|
+
}
|
|
510
|
+
],
|
|
511
|
+
"exports": {
|
|
512
|
+
"customElement": "ds-drawer",
|
|
513
|
+
"react": "DsDrawer",
|
|
514
|
+
"vue": "DsDrawer",
|
|
515
|
+
"angular": "DsDrawer"
|
|
516
|
+
},
|
|
517
|
+
"consumption": {
|
|
518
|
+
"install": "npm install @ds-mo/ui @ds-mo/tokens @ds-mo/icons",
|
|
519
|
+
"cssSetup": "import '@ds-mo/tokens';\nimport '@ds-mo/tokens/reset';\nimport '@ds-mo/tokens/globals';",
|
|
520
|
+
"customElements": {
|
|
521
|
+
"import": "import '@ds-mo/ui/dist/components/ds-drawer.js';",
|
|
522
|
+
"example": "<ds-drawer></ds-drawer>"
|
|
523
|
+
},
|
|
524
|
+
"react": {
|
|
525
|
+
"import": "import { DsDrawer } from '@ds-mo/ui/react';",
|
|
526
|
+
"example": "<DsDrawer />"
|
|
527
|
+
},
|
|
528
|
+
"vue": {
|
|
529
|
+
"import": "import { DsDrawer } from '@ds-mo/ui/vue';",
|
|
530
|
+
"example": "<DsDrawer />"
|
|
531
|
+
},
|
|
532
|
+
"angular": {
|
|
533
|
+
"import": "import { DsDrawer } from '@ds-mo/ui/angular/ds-drawer';",
|
|
534
|
+
"example": "<ds-drawer></ds-drawer>"
|
|
535
|
+
},
|
|
536
|
+
"peerDependencies": {
|
|
537
|
+
"required": [
|
|
538
|
+
"@ds-mo/tokens ^9.0.0",
|
|
539
|
+
"@ds-mo/icons ^8.2.2"
|
|
540
|
+
],
|
|
541
|
+
"frameworks": "Custom Elements; React 18/19 wrappers; Vue 3 wrappers; Angular 19-22 standalone adapters."
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
},
|
|
545
|
+
"dependencies": [
|
|
546
|
+
"@ds-mo/ui",
|
|
547
|
+
"@ds-mo/tokens"
|
|
548
|
+
],
|
|
549
|
+
"registryDependencies": [
|
|
550
|
+
"button-unfilled",
|
|
551
|
+
"divider",
|
|
552
|
+
"text",
|
|
553
|
+
"tooltip"
|
|
554
|
+
],
|
|
555
|
+
"files": [
|
|
556
|
+
{
|
|
557
|
+
"path": "src/wc/components/Drawer/Drawer.css",
|
|
558
|
+
"content": "@import '../../utils/forced-colors.css';\n\n:host {\n display: contents;\n}\n\n/*\n * Edge-attached details panel. Non-modal by default: it sits beside the page,\n * which stays interactive. Inside ShellApp page content, the shell's content\n * frame contains fixed descendants, so the panel covers only the content lane\n * and stays put while the page scrolls; elsewhere it attaches to the viewport.\n * Modal drawers present in the top layer against the viewport edge.\n */\n.drawer-dialog {\n --_drawer-enter-from: translateX(100%);\n position: fixed;\n z-index: var(--dimension-z-index-overlay);\n inset-block: 0;\n inset-inline: auto 0;\n display: none;\n flex-direction: column;\n width: min(100%, var(--_drawer-width));\n min-width: 0;\n max-width: none;\n height: auto;\n max-height: none;\n margin: 0;\n padding: 0;\n overflow: visible;\n color: var(--color-foreground-primary);\n background-color: var(--color-background-primary);\n border: 0;\n border-radius: 0;\n box-shadow: var(--effect-elevation-elevated-panel-right);\n outline: none;\n box-sizing: border-box;\n pointer-events: auto;\n animation: drawerIn var(--effect-animation-duration-medium-1)\n var(--effect-animation-easing-ease-out) forwards;\n}\n\n.drawer-dialog--start {\n --_drawer-enter-from: translateX(-100%);\n inset-inline: 0 auto;\n box-shadow: var(--effect-elevation-elevated-panel-left);\n}\n\n.drawer-dialog[open],\n.drawer-dialog--shown {\n display: flex;\n}\n\n.drawer-dialog::backdrop {\n background-color: var(--color-background-shade);\n animation: drawerBackdropIn var(--effect-animation-duration-medium-1) forwards;\n}\n\n.drawer-dialog--closing {\n animation: drawerOut var(--effect-animation-duration-medium-1)\n var(--effect-animation-easing-ease-in) forwards;\n}\n\n.drawer-dialog--closing::backdrop {\n animation: drawerBackdropOut var(--effect-animation-duration-medium-1) forwards;\n}\n\n@keyframes drawerIn {\n from {\n transform: var(--_drawer-enter-from);\n }\n to {\n transform: none;\n }\n}\n\n@keyframes drawerOut {\n from {\n transform: none;\n }\n to {\n transform: var(--_drawer-enter-from);\n }\n}\n\n@keyframes drawerBackdropIn {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n\n@keyframes drawerBackdropOut {\n from {\n opacity: 1;\n }\n to {\n opacity: 0;\n }\n}\n\n/* Narrow viewports, matching the shell breakpoint, give the panel the full width. */\n@media (width < 768px) {\n .drawer-dialog {\n width: 100%;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .drawer-dialog,\n .drawer-dialog--closing {\n animation: none;\n transform: none;\n }\n\n .drawer-dialog::backdrop {\n animation: none;\n opacity: 1;\n }\n\n .drawer-dialog--closing,\n .drawer-dialog--closing::backdrop {\n opacity: 0;\n }\n}\n\n/*\n * Header: a generic label for what the drawer shows, never record data.\n * Title glyphs sit 16px from the edge; a leading Back action sits 4px before\n * the title's own 8px inset.\n */\n.drawer-header {\n display: flex;\n flex: 0 0 auto;\n align-items: center;\n gap: var(--dimension-space-050);\n min-width: 0;\n block-size: var(--dimension-size-600);\n padding: var(--dimension-space-100);\n box-sizing: border-box;\n border-block-end: var(--dimension-stroke-width-012) solid var(--color-border-tertiary);\n -webkit-user-select: none;\n user-select: none;\n}\n\nds-text.drawer-heading {\n display: block;\n flex: 1 1 0;\n min-width: 0;\n padding-inline: var(--dimension-space-100);\n text-align: start;\n}\n\n.drawer-back-tooltip,\n.drawer-close-tooltip,\n.drawer-previous-tooltip,\n.drawer-next-tooltip {\n flex: 0 0 auto;\n}\n\n.drawer-header__trailing {\n display: flex;\n flex: 0 0 auto;\n align-items: center;\n gap: var(--dimension-space-100);\n padding-inline-start: var(--dimension-space-050);\n}\n\n.drawer-header__actions {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-100);\n}\n\n.drawer-header__actions--empty {\n display: none;\n}\n\n.drawer-header__actions slot,\n.drawer-footer__actions slot {\n display: contents;\n}\n\n/* The body is a composable slot; the drawer owns only its inset and scrolling. */\n.drawer-body {\n flex: 1 1 auto;\n min-height: 0;\n padding: var(--dimension-space-200);\n overflow: auto;\n overscroll-behavior: contain;\n box-sizing: border-box;\n}\n\n.drawer-body--flush {\n padding: 0;\n}\n\n/* Footer: actions at the start, record navigation at the end. */\n.drawer-footer {\n display: flex;\n flex: 0 0 auto;\n flex-wrap: wrap;\n align-items: center;\n justify-content: flex-start;\n gap: var(--dimension-space-100);\n min-block-size: var(--dimension-size-600);\n padding: var(--dimension-space-200);\n box-sizing: border-box;\n border-block-start: var(--dimension-stroke-width-012) solid var(--color-border-tertiary);\n}\n\n.drawer-footer--empty {\n display: none;\n}\n\n.drawer-footer__actions {\n display: flex;\n flex: 1 1 0;\n align-items: center;\n gap: var(--dimension-space-100);\n min-width: min-content;\n}\n\n.drawer-footer__actions--empty {\n display: none;\n}\n\n.drawer-footer__navigation {\n display: flex;\n flex: 0 0 auto;\n align-items: center;\n gap: var(--dimension-space-100);\n margin-inline-start: auto;\n}\n\n@media (forced-colors: active) {\n .drawer-dialog {\n box-shadow: none;\n outline: var(--dimension-stroke-width-012) solid var(--ds-forced-color-content);\n outline-offset: calc(-1 * var(--dimension-stroke-width-012));\n }\n}\n",
|
|
559
|
+
"type": "registry:ui"
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
"path": "src/wc/components/Drawer/Drawer.tsx",
|
|
563
|
+
"content": "import {\n Component,\n Prop,\n State,\n Event,\n EventEmitter,\n Element,\n Watch,\n h,\n Host,\n} from '@stencil/core';\nimport { resolveMotionTimeMs, TOKEN_DEFAULTS } from '../../utils';\nimport { ConnectionTasks } from '../../utils/connection-tasks';\n\nexport type DrawerSide = 'end' | 'start';\nexport type DrawerWidth = 'sm' | 'md' | 'lg';\nexport type DrawerBodyInset = 'default' | 'none';\nexport type DrawerCloseReason = 'close-button' | 'escape' | 'backdrop' | 'replaced';\n\nexport interface DrawerCloseDetail {\n reason: DrawerCloseReason;\n originalEvent?: globalThis.Event;\n}\n\nexport interface DrawerAfterCloseDetail {\n /**\n * True when the owner should place focus, typically on the record now shown.\n * False when another drawer replaced this one, or a modal drawer already\n * returned focus to its trigger.\n */\n restoreFocus: boolean;\n}\n\nconst WIDTH_MAP: Record<DrawerWidth, string> = {\n sm: 'var(--dimension-panel-width-sm)',\n md: 'var(--dimension-panel-width-md)',\n lg: 'var(--dimension-panel-width-lg)',\n};\n\nconst FOCUSABLE_SEL = [\n 'a[href]',\n 'area[href]',\n 'button:not([disabled])',\n 'input:not([disabled]):not([type=\"hidden\"])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n 'iframe',\n '[contenteditable=\"true\"]',\n '[tabindex]:not([tabindex=\"-1\"])',\n].join(',');\n\n/** Targets that own Left/Right themselves, so record paging must not steal the keys. */\n/**\n * Drawer event names. Slotted components such as ds-menu emit some of the same\n * names; those stop at the drawer boundary so owner listeners on the drawer only\n * hear the drawer itself.\n */\nconst DRAWER_EVENT_NAMES = ['dsClose', 'dsAfterClose', 'dsBack', 'dsPrevious', 'dsNext'];\n\nconst stopDescendantDrawerEvent = (event: globalThis.Event) => event.stopPropagation();\n\nconst DIRECTIONAL_CONTROL_TAGS = new Set([\n 'DS-SELECT',\n 'DS-MENU',\n 'DS-FILTER-MENU',\n 'DS-SLIDER',\n 'INPUT',\n 'SELECT',\n 'TEXTAREA',\n]);\nconst DIRECTIONAL_CONTROL_ROLES = new Set(['slider', 'combobox', 'listbox', 'menu', 'tablist']);\n\nfunction directionalControlInPath(path: readonly EventTarget[]): boolean {\n return path.some(target => {\n if (!(target instanceof HTMLElement)) return false;\n if (DIRECTIONAL_CONTROL_TAGS.has(target.tagName) || target.isContentEditable) return true;\n const role = target.getAttribute('role');\n return role !== null && DIRECTIONAL_CONTROL_ROLES.has(role);\n });\n}\n\n/** One non-modal drawer per document: opening another replaces it. */\nconst activeNonModalDrawers = new WeakMap<Document, Drawer>();\n\nlet drawerIdCounter = 0;\n\n@Component({\n tag: 'ds-drawer',\n styleUrl: 'Drawer.css',\n scoped: true,\n})\nexport class Drawer {\n @Element() el!: HTMLElement;\n\n @Prop({ mutable: true }) open: boolean = false;\n /** Generic label for what the drawer shows, such as \"Event details\". Record identity belongs in the body. */\n @Prop() heading!: string;\n /**\n * Block the page behind a shade and contain focus. The default non-modal\n * drawer leaves the page interactive beside it.\n */\n @Prop() modal: boolean = false;\n /** Edge the panel attaches to. */\n @Prop() side: DrawerSide = 'end';\n /** Panel width token (`sm`, `md`, `lg`) or a CSS length. Narrow viewports use the full width. */\n @Prop() drawerWidth: DrawerWidth | string = 'sm';\n /** `none` removes the body inset for content that owns its own edges. */\n @Prop() bodyInset: DrawerBodyInset = 'default';\n /** When set, shows a leading Back action for multi-depth content and emits dsBack. */\n @Prop() backLabel: string | undefined;\n @Prop() closeAriaLabel: string = 'Close';\n /** Shows Previous and Next record actions at the end of the footer. */\n @Prop() navigation: boolean = false;\n @Prop() previousAvailable: boolean = false;\n @Prop() nextAvailable: boolean = false;\n @Prop() previousAriaLabel: string = 'Previous item';\n @Prop() nextAriaLabel: string = 'Next item';\n @Prop() navigationAriaLabel: string = 'Record navigation';\n /** Suspends Left/Right record paging, for example while a menu in the drawer is open. */\n @Prop() navigationShortcutsBlocked: boolean = false;\n /** Optional id reference for explanatory content in the body. */\n @Prop({ attribute: 'aria-describedby' }) ariaDescribedby: string | undefined;\n\n @State() private closing: boolean = false;\n @State() private shown: boolean = false;\n @State() private hasFooterActions: boolean = false;\n @State() private hasHeaderActions: boolean = false;\n\n /** Emitted when the drawer requests to close; open is already false. */\n @Event() dsClose!: EventEmitter<DrawerCloseDetail>;\n /** Emitted after exit motion completes. */\n @Event() dsAfterClose!: EventEmitter<DrawerAfterCloseDetail>;\n /** Emitted when the leading Back action is activated. The owner swaps heading and body. */\n @Event() dsBack!: EventEmitter<MouseEvent>;\n /** Emitted by the Previous action or ArrowLeft while navigation is shown and available. */\n @Event() dsPrevious!: EventEmitter<void>;\n /** Emitted by the Next action or ArrowRight while navigation is shown and available. */\n @Event() dsNext!: EventEmitter<void>;\n\n private titleId = `ds-drawer-title-${++drawerIdCounter}`;\n private dialogEl: HTMLDialogElement | null = null;\n private previousFocus: HTMLElement | null = null;\n private closeTimer: ReturnType<typeof setTimeout> | null = null;\n private loaded = false;\n private openedAsModal = false;\n private replaced = false;\n private documentListening: Document | null = null;\n private readonly tasks = new ConnectionTasks(() => this.el.isConnected);\n private slotObserver?: MutationObserver;\n private bodyEl: HTMLElement | null = null;\n private headerActionsEl: HTMLElement | null = null;\n private footerActionsEl: HTMLElement | null = null;\n\n connectedCallback() {\n if (!this.loaded) return;\n this.updateSlotPresence();\n this.observeSlottedContent();\n this.onOpenChange(this.open);\n }\n\n componentDidLoad() {\n this.loaded = true;\n this.placeSlottedContent();\n this.updateSlotPresence();\n this.observeSlottedContent();\n if (this.open) this.onOpenChange(true);\n }\n\n componentDidRender() {\n if (!this.el.isConnected) return;\n if (!this.closing || !this.isSurfaceOpen || this.closeTimer) return;\n const closeAnimationMs = this.closeAnimationMs;\n if (closeAnimationMs <= 0) {\n this.finishClose();\n return;\n }\n this.closeTimer = setTimeout(() => {\n this.closeTimer = null;\n this.finishClose();\n }, closeAnimationMs);\n }\n\n disconnectedCallback() {\n this.slotObserver?.disconnect();\n this.slotObserver = undefined;\n this.tasks.cancel();\n this.clearCloseTimer();\n this.releaseActive();\n this.stopDocumentListeners();\n this.hideSurface();\n this.closing = false;\n this.shown = false;\n this.previousFocus = null;\n }\n\n @Watch('open')\n onOpenChange(isOpen: boolean) {\n if (!this.loaded || !this.el.isConnected) return;\n this.tasks.cancel();\n if (isOpen) {\n this.clearCloseTimer();\n const alreadyOpen = this.isSurfaceOpen;\n this.closing = false;\n this.replaced = false;\n if (!this.dialogEl || alreadyOpen) return;\n this.previousFocus = this.el.ownerDocument.activeElement as HTMLElement | null;\n this.present();\n this.shown = true;\n this.startDocumentListeners();\n this.tasks.frame(() => {\n if (this.open && this.isSurfaceOpen) this.focusLeadingAction();\n });\n } else if (this.isSurfaceOpen) {\n this.releaseActive();\n this.stopDocumentListeners();\n this.closing = true;\n }\n }\n\n @Watch('modal')\n onModalChange() {\n if (!this.open || !this.isSurfaceOpen || this.openedAsModal === this.modal) return;\n this.hideSurface();\n this.present();\n }\n\n /** Drilling in or out moves focus to the action that now leads the header. */\n @Watch('backLabel')\n onBackLabelChange(next: string | undefined, previous: string | undefined) {\n if (!this.open || !this.isSurfaceOpen || !next === !previous) return;\n const active = this.el.ownerDocument.activeElement;\n const focusInside = !!active && !!this.dialogEl?.contains(active);\n if (!focusInside && active !== this.el.ownerDocument.body) return;\n this.tasks.frame(() => {\n if (this.open && this.isSurfaceOpen) this.focusLeadingAction();\n });\n }\n\n private present() {\n if (!this.dialogEl) return;\n this.openedAsModal = this.modal;\n if (this.modal) {\n this.releaseActive();\n this.dialogEl.showModal();\n return;\n }\n this.dialogEl.show();\n const doc = this.el.ownerDocument;\n const previous = activeNonModalDrawers.get(doc);\n activeNonModalDrawers.set(doc, this);\n if (previous && previous !== this) previous.replace();\n }\n\n /** Closes because another non-modal drawer opened; that drawer owns focus. */\n private replace() {\n if (!this.open || this.closing) return;\n this.replaced = true;\n this.requestClose('replaced');\n }\n\n private releaseActive() {\n const doc = this.el.ownerDocument;\n if (activeNonModalDrawers.get(doc) === this) activeNonModalDrawers.delete(doc);\n }\n\n private get isSurfaceOpen(): boolean {\n return !!this.dialogEl?.open;\n }\n\n private hideSurface() {\n if (this.dialogEl?.open) this.dialogEl.close();\n }\n\n private focusLeadingAction() {\n const selector = this.backLabel\n ? 'ds-button-unfilled.drawer-back'\n : 'ds-button-unfilled.drawer-close';\n const action = this.el.querySelector(selector) as\n | (HTMLElement & { setFocus?: () => Promise<void> })\n | null;\n void action?.setFocus?.();\n }\n\n private clearCloseTimer() {\n if (this.closeTimer) {\n clearTimeout(this.closeTimer);\n this.closeTimer = null;\n }\n }\n\n private requestClose(reason: DrawerCloseReason, originalEvent?: globalThis.Event) {\n if (!this.open || this.closing || !this.isSurfaceOpen) return;\n this.dsClose.emit({ reason, originalEvent });\n this.open = false;\n }\n\n private startDocumentListeners() {\n if (this.documentListening) return;\n const doc = this.el.ownerDocument;\n doc.addEventListener('keydown', this.handleDocumentKeyDown);\n doc.addEventListener('keydown', this.handleDirectionalKeyDown, true);\n this.documentListening = doc;\n }\n\n private stopDocumentListeners() {\n const doc = this.documentListening;\n if (!doc) return;\n doc.removeEventListener('keydown', this.handleDocumentKeyDown);\n doc.removeEventListener('keydown', this.handleDirectionalKeyDown, true);\n this.documentListening = null;\n }\n\n /** A modal surface other than this drawer owns the keyboard. */\n private get coveredByOtherModal(): boolean {\n const modal = this.el.ownerDocument.querySelector('dialog:modal');\n return !!modal && modal !== this.dialogEl;\n }\n\n /** Non-modal Escape works from anywhere in the page, after inner surfaces had their turn. */\n private readonly handleDocumentKeyDown = (event: KeyboardEvent) => {\n if (this.openedAsModal || event.key !== 'Escape' || event.defaultPrevented) return;\n if (!this.open || this.closing || this.coveredByOtherModal) return;\n event.preventDefault();\n this.requestClose('escape', event);\n };\n\n /** Captures Left/Right ahead of row and grid handlers so paging works from the page too. */\n private readonly handleDirectionalKeyDown = (event: KeyboardEvent) => {\n if (\n !this.open ||\n this.closing ||\n !this.navigation ||\n this.navigationShortcutsBlocked ||\n event.defaultPrevented ||\n event.altKey ||\n event.ctrlKey ||\n event.metaKey ||\n event.shiftKey ||\n (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') ||\n this.coveredByOtherModal ||\n directionalControlInPath(event.composedPath())\n ) {\n return;\n }\n const rtl = getComputedStyle(this.el).direction === 'rtl';\n const towardStart = (event.key === 'ArrowLeft') !== rtl;\n event.preventDefault();\n event.stopPropagation();\n this.page(towardStart ? 'previous' : 'next');\n };\n\n private page(direction: 'previous' | 'next') {\n if (direction === 'previous') {\n if (this.previousAvailable) this.dsPrevious.emit();\n return;\n }\n if (this.nextAvailable) this.dsNext.emit();\n }\n\n private hasAssignedContent(container: HTMLElement | null): boolean {\n if (!container) return false;\n return Array.from(container.childNodes).some(\n node =>\n node.nodeType === Node.ELEMENT_NODE ||\n (node.nodeType === Node.TEXT_NODE && !!node.textContent?.trim())\n );\n }\n\n private updateSlotPresence() {\n this.hasFooterActions = this.hasAssignedContent(this.footerActionsEl);\n this.hasHeaderActions = this.hasAssignedContent(this.headerActionsEl);\n }\n\n private namedSlotContainer(slotName: string | null): HTMLElement | null {\n if (slotName === 'header-actions') return this.headerActionsEl;\n if (slotName === 'footer') return this.footerActionsEl;\n return null;\n }\n\n /**\n * Scoped slots only place direct host children, and only during render.\n * Frameworks insert conditional content after first render, for example an\n * Angular @if around footer actions, next to an anchor that Stencil already\n * moved into the body. Move named-slot elements that arrive on the host or\n * directly in the body into their slot container. Deeper elements belong to\n * nested components and are left alone.\n */\n private placeSlottedContent() {\n const candidates = [\n ...Array.from(this.el.children),\n ...(this.bodyEl ? Array.from(this.bodyEl.children) : []),\n ];\n for (const child of candidates) {\n const container = this.namedSlotContainer(child.getAttribute('slot'));\n if (container && child.parentElement !== container) container.appendChild(child);\n }\n }\n\n private observeSlottedContent() {\n if (this.slotObserver || typeof MutationObserver === 'undefined') return;\n this.slotObserver = new MutationObserver(records => {\n const slottedChange = records.some(record =>\n [...Array.from(record.addedNodes), ...Array.from(record.removedNodes)].some(\n node => node.nodeType === Node.ELEMENT_NODE && (node as HTMLElement).hasAttribute('slot')\n )\n );\n if (!slottedChange) return;\n this.placeSlottedContent();\n this.updateSlotPresence();\n });\n this.slotObserver.observe(this.el, { childList: true, subtree: true });\n }\n\n private handleCancel(event: globalThis.Event) {\n event.preventDefault();\n this.requestClose('escape', event);\n }\n\n private handleBackdropPointerDown(event: PointerEvent) {\n if (this.openedAsModal && event.target === this.dialogEl) this.requestClose('backdrop', event);\n }\n\n private handleDialogKeyDown(event: KeyboardEvent) {\n if (!this.openedAsModal || event.key !== 'Tab' || !this.dialogEl) return;\n const focusables = Array.from(\n this.dialogEl.querySelectorAll<HTMLElement>(FOCUSABLE_SEL)\n ).filter(\n element => !element.hasAttribute('disabled') && element.getAttribute('aria-hidden') !== 'true'\n );\n if (!focusables.length) {\n event.preventDefault();\n this.dialogEl.focus();\n return;\n }\n const first = focusables[0];\n const last = focusables[focusables.length - 1];\n const active = document.activeElement;\n if (event.shiftKey && (active === first || active === this.dialogEl)) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && active === last) {\n event.preventDefault();\n first.focus();\n }\n }\n\n private get resolvedWidth(): string {\n if (this.drawerWidth === 'sm' || this.drawerWidth === 'md' || this.drawerWidth === 'lg') {\n return WIDTH_MAP[this.drawerWidth];\n }\n return this.drawerWidth as string;\n }\n\n private get closeAnimationMs(): number {\n return resolveMotionTimeMs(\n TOKEN_DEFAULTS.animationDurationMedium1,\n TOKEN_DEFAULTS.animationDurationMedium1\n );\n }\n\n private finishClose() {\n if (this.open || !this.el.isConnected || !this.isSurfaceOpen) return;\n let restoreFocus = !this.replaced;\n if (!this.openedAsModal) {\n // dialog.close() would jump focus back to the opener; the owner chooses instead.\n const active = this.el.ownerDocument.activeElement as HTMLElement | null;\n if (active && this.dialogEl?.contains(active)) active.blur();\n }\n this.hideSurface();\n this.closing = false;\n this.shown = false;\n this.clearCloseTimer();\n // A modal returns focus to its trigger. A non-modal drawer pages through records,\n // so the owner knows which row or marker now matches and places focus itself.\n if (restoreFocus && this.openedAsModal && this.previousFocus?.isConnected) {\n this.previousFocus.focus();\n restoreFocus = false;\n }\n this.previousFocus = null;\n this.replaced = false;\n this.dsAfterClose.emit({ restoreFocus });\n }\n\n private renderIconAction(\n className: string,\n icon: string,\n label: string,\n tooltipSide: 'top' | 'bottom',\n onActivate: (event: CustomEvent<MouseEvent>) => void,\n inactive = false\n ) {\n return (\n <ds-tooltip class={`${className}-tooltip`} label={label} side={tooltipSide} size=\"sm\">\n <ds-button-unfilled\n class={className}\n variant=\"icon\"\n icon={icon}\n size=\"md\"\n aria-label={label}\n activeFill={false}\n hasBorder={false}\n isInactive={inactive}\n onDsClick={onActivate}\n />\n </ds-tooltip>\n );\n }\n\n render() {\n const showFooter = this.hasFooterActions || this.navigation;\n return (\n <Host>\n <dialog\n ref={(element?: HTMLDialogElement) => {\n if (element && element !== this.dialogEl) {\n for (const name of DRAWER_EVENT_NAMES) {\n element.addEventListener(name, stopDescendantDrawerEvent);\n }\n }\n this.dialogEl = element ?? null;\n }}\n class={{\n 'drawer-dialog': true,\n [`drawer-dialog--${this.side === 'start' ? 'start' : 'end'}`]: true,\n 'drawer-dialog--shown': this.shown,\n 'drawer-dialog--closing': this.closing,\n }}\n aria-labelledby={this.titleId}\n aria-describedby={this.ariaDescribedby}\n onCancel={(event: globalThis.Event) => this.handleCancel(event)}\n onPointerDown={(event: PointerEvent) => this.handleBackdropPointerDown(event)}\n onKeyDown={(event: KeyboardEvent) => this.handleDialogKeyDown(event)}\n style={{ '--_drawer-width': this.resolvedWidth }}\n >\n <div class=\"drawer-header\">\n {this.backLabel\n ? this.renderIconAction(\n 'drawer-back',\n 'ChevronLeft',\n this.backLabel,\n 'bottom',\n event => this.dsBack.emit(event.detail)\n )\n : null}\n <ds-text\n class=\"drawer-heading\"\n as=\"h2\"\n variant=\"text-body-medium\"\n color=\"primary\"\n emphasis\n lineTruncation={1}\n textId={this.titleId}\n >\n {this.heading}\n </ds-text>\n <div class=\"drawer-header__trailing\">\n <div\n class={{\n 'drawer-header__actions': true,\n 'drawer-header__actions--empty': !this.hasHeaderActions,\n }}\n ref={element => (this.headerActionsEl = element ?? null)}\n >\n <slot name=\"header-actions\" />\n </div>\n {this.hasHeaderActions ? (\n <ds-divider\n class=\"drawer-header__divider\"\n orientation=\"vertical\"\n length=\"var(--dimension-size-300)\"\n />\n ) : null}\n {this.renderIconAction(\n 'drawer-close',\n 'Cross',\n this.closeAriaLabel,\n 'bottom',\n event => this.requestClose('close-button', event.detail)\n )}\n </div>\n </div>\n <div\n class={{ 'drawer-body': true, 'drawer-body--flush': this.bodyInset === 'none' }}\n ref={element => (this.bodyEl = element ?? null)}\n >\n <slot />\n </div>\n <div class={{ 'drawer-footer': true, 'drawer-footer--empty': !showFooter }}>\n <div\n class={{\n 'drawer-footer__actions': true,\n 'drawer-footer__actions--empty': !this.hasFooterActions,\n }}\n ref={element => (this.footerActionsEl = element ?? null)}\n >\n <slot name=\"footer\" />\n </div>\n {this.navigation ? (\n <div\n class=\"drawer-footer__navigation\"\n role=\"group\"\n aria-label={this.navigationAriaLabel}\n >\n {this.renderIconAction(\n 'drawer-previous',\n 'ChevronLeft',\n this.previousAriaLabel,\n 'top',\n () => this.page('previous'),\n !this.previousAvailable\n )}\n {this.renderIconAction(\n 'drawer-next',\n 'ChevronRight',\n this.nextAriaLabel,\n 'top',\n () => this.page('next'),\n !this.nextAvailable\n )}\n </div>\n ) : null}\n </div>\n </dialog>\n </Host>\n );\n }\n}\n",
|
|
564
|
+
"type": "registry:ui"
|
|
565
|
+
}
|
|
566
|
+
]
|
|
567
|
+
}
|