@ds-mo/ui 9.2.1 → 10.0.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/README.md +2 -2
- package/dist/.build-stamp +1 -1
- package/dist/agent-patterns.json +1 -1
- package/dist/agent.json +384 -135
- package/dist/angular/boolean-value-accessor.js +3 -3
- package/dist/angular/ds-agent-activity.js +3 -3
- package/dist/angular/ds-agent-response.js +3 -3
- package/dist/angular/ds-agent-source-list.js +3 -3
- package/dist/angular/ds-agent-tool-call.js +3 -3
- package/dist/angular/ds-app-shell.js +3 -3
- package/dist/angular/ds-attachment-list.js +3 -3
- package/dist/angular/ds-avatar.js +3 -3
- package/dist/angular/ds-badge.js +3 -3
- package/dist/angular/ds-bar-nav.js +3 -3
- package/dist/angular/ds-button-filled.js +3 -3
- package/dist/angular/ds-button-unfilled.js +3 -3
- package/dist/angular/ds-card-data-viz-donut.js +3 -3
- package/dist/angular/ds-card-setting.d.ts +5 -4
- package/dist/angular/ds-card-setting.js +6 -6
- package/dist/angular/ds-card-shell-data-viz.js +3 -3
- package/dist/angular/ds-chart-bar.js +3 -3
- package/dist/angular/ds-chart-donut.js +3 -3
- package/dist/angular/ds-chart-legend.js +3 -3
- package/dist/angular/ds-chart-line.js +3 -3
- package/dist/angular/ds-checkbox.js +3 -3
- package/dist/angular/ds-chip.js +3 -3
- package/dist/angular/ds-code-block.js +3 -3
- package/dist/angular/ds-conversation-list-item.js +3 -3
- package/dist/angular/ds-conversation-list-section.js +3 -3
- package/dist/angular/ds-conversation-list.js +3 -3
- package/dist/angular/ds-divider.js +3 -3
- package/dist/angular/ds-empty-state.js +3 -3
- package/dist/angular/ds-field.js +3 -3
- package/dist/angular/ds-icon.js +3 -3
- package/dist/angular/ds-input.js +3 -3
- package/dist/angular/ds-loader.js +3 -3
- package/dist/angular/ds-markdown.js +3 -3
- package/dist/angular/ds-menu.js +3 -3
- package/dist/angular/ds-message-bubble.js +3 -3
- package/dist/angular/ds-message-composer.js +3 -3
- package/dist/angular/ds-message-scroller.js +3 -3
- package/dist/angular/ds-message.js +3 -3
- package/dist/angular/ds-modal.d.ts +12 -3
- package/dist/angular/ds-modal.js +9 -6
- package/dist/angular/ds-panel-nav.js +3 -3
- package/dist/angular/ds-panel-sub-nav.js +3 -3
- package/dist/angular/ds-panel-tool-header.js +3 -3
- package/dist/angular/ds-panel-tool-search.js +3 -3
- package/dist/angular/ds-panel-tools.js +3 -3
- package/dist/angular/ds-radio.js +3 -3
- package/dist/angular/ds-select-multi.js +3 -3
- package/dist/angular/ds-select.js +3 -3
- package/dist/angular/ds-shell-gradient-picker.js +3 -3
- package/dist/angular/ds-shell-gradient-swatch.js +3 -3
- package/dist/angular/ds-skeleton.js +3 -3
- package/dist/angular/ds-slider.js +3 -3
- package/dist/angular/ds-swatch-picker.js +3 -3
- package/dist/angular/ds-switch.js +3 -3
- package/dist/angular/ds-tab-group.js +3 -3
- package/dist/angular/ds-tag.js +3 -3
- package/dist/angular/ds-text.js +3 -3
- package/dist/angular/ds-toast.d.ts +22 -0
- package/dist/angular/{ds-banner.js → ds-toast.js} +21 -15
- package/dist/angular/ds-tooltip-data-viz.js +3 -3
- package/dist/angular/ds-tooltip.js +3 -3
- package/dist/angular/index.d.ts +1 -1
- package/dist/angular/index.js +1 -2
- package/dist/angular/proxies.d.ts +1 -2
- package/dist/angular/proxies.js +1 -2
- package/dist/angular/radio-value-accessor.js +3 -3
- package/dist/angular/select-value-accessor.js +3 -3
- package/dist/angular/text-value-accessor.js +3 -3
- package/dist/angular/value-accessor.js +3 -3
- package/dist/components/ds-agent-activity.js +1 -1
- package/dist/components/ds-agent-response.js +1 -1
- package/dist/components/ds-agent-response.js.map +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-app-shell.js +1 -1
- package/dist/components/ds-attachment-list.js +1 -1
- package/dist/components/ds-avatar.js +1 -1
- package/dist/components/ds-badge.js +1 -1
- package/dist/components/ds-bar-nav.js +1 -1
- package/dist/components/ds-button-filled.js +1 -1
- package/dist/components/ds-button-unfilled.js +1 -1
- package/dist/components/ds-card-data-viz-donut.js +1 -1
- package/dist/components/ds-card-data-viz-donut.js.map +1 -1
- package/dist/components/ds-card-setting.js +1 -1
- package/dist/components/ds-card-setting.js.map +1 -1
- package/dist/components/ds-card-shell-data-viz.js +1 -1
- package/dist/components/ds-chart-bar.js +1 -1
- package/dist/components/ds-chart-donut.js +1 -1
- package/dist/components/ds-chart-legend.js +1 -1
- package/dist/components/ds-chart-line.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-item.js.map +1 -1
- package/dist/components/ds-conversation-list-section.js +1 -1
- package/dist/components/ds-conversation-list.js +1 -1
- package/dist/components/ds-divider.js +1 -1
- package/dist/components/ds-empty-state.js +1 -1
- package/dist/components/ds-field.js +1 -1
- package/dist/components/ds-icon.js +1 -1
- package/dist/components/ds-input.js +1 -1
- package/dist/components/ds-markdown.js +1 -1
- package/dist/components/ds-menu.js +1 -1
- package/dist/components/ds-message-bubble.js +1 -1
- package/dist/components/ds-message-composer.js +1 -1
- package/dist/components/ds-message-composer.js.map +1 -1
- package/dist/components/ds-message-scroller.js +1 -1
- package/dist/components/ds-message-scroller.js.map +1 -1
- package/dist/components/ds-message.js +1 -1
- package/dist/components/ds-modal.js +1 -1
- package/dist/components/ds-modal.js.map +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-radio.js +1 -1
- package/dist/components/ds-select-multi.js +1 -1
- package/dist/components/ds-select.js +1 -1
- package/dist/components/ds-shell-gradient-picker.js +1 -1
- package/dist/components/ds-shell-gradient-picker.js.map +1 -1
- package/dist/components/ds-shell-gradient-swatch.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-tag.js +1 -1
- package/dist/components/ds-text.js +1 -1
- package/dist/components/{ds-card.d.ts → ds-toast.d.ts} +4 -4
- package/dist/components/ds-toast.js +2 -0
- package/dist/components/ds-toast.js.map +1 -0
- package/dist/components/ds-tooltip-data-viz.js +1 -1
- package/dist/components/ds-tooltip.js +1 -1
- package/dist/components/{p-DnMCRAwx.js → p-0kEg-Z4f.js} +2 -2
- package/dist/components/{p-Cijzn3YQ.js → p-B4RQoNmL.js} +2 -2
- package/dist/components/p-B8C_yfLQ.js +2 -0
- package/dist/components/p-B8C_yfLQ.js.map +1 -0
- package/dist/components/{p-BHpWYKtP.js → p-BJUcsBHt.js} +2 -2
- package/dist/components/{p-DaQ0ZE_V.js → p-BTo4pNdp.js} +2 -2
- package/dist/components/p-Bet_UjML.js +2 -0
- package/dist/components/{p-CJlgONwR.js.map → p-Bet_UjML.js.map} +1 -1
- package/dist/components/{p-Ci_nVk5P.js → p-BkwBtZs3.js} +2 -2
- package/dist/components/{p-Dst-OliP.js → p-CKnoWZd9.js} +2 -2
- package/dist/components/{p-BRfOCc_h.js → p-CLn7QnE0.js} +2 -2
- package/dist/components/{p-kezp2_Xu.js → p-CPPAndX2.js} +2 -2
- package/dist/components/{p-xB0GPG3-.js → p-CS12e52R.js} +2 -2
- package/dist/components/{p-BSOZXagj.js → p-CZZYGqKz.js} +2 -2
- package/dist/components/p-C_0Q8Cnp.js +2 -0
- package/dist/components/p-C_0Q8Cnp.js.map +1 -0
- package/dist/components/{p-DiooIO2B.js → p-CxyjoTN1.js} +2 -2
- package/dist/components/p-CyoHRqy6.js +2 -0
- package/dist/components/p-CyoHRqy6.js.map +1 -0
- package/dist/components/p-DBJ2I3mH.js +2 -0
- package/dist/components/p-DBJ2I3mH.js.map +1 -0
- package/dist/components/p-DM-95BX1.js +2 -0
- package/dist/components/p-DM-95BX1.js.map +1 -0
- package/dist/components/{p-T0Mzhv3-.js → p-DZAtgSZo.js} +2 -2
- package/dist/components/{p-CeFBA7Dx.js → p-Dh4q5TSS.js} +2 -2
- package/dist/components/{p-Dj5MejHv.js → p-DhsO0939.js} +2 -2
- package/dist/components/{p-CiwdnKKp.js → p-DrDHC7V6.js} +2 -2
- package/dist/components/{p-CblAABEo.js → p-DsbhBb5F.js} +2 -2
- package/dist/components/p-DuNZGFkV.js +2 -0
- package/dist/components/p-DuNZGFkV.js.map +1 -0
- package/dist/components/{p-B1pWGQn1.js → p-P_4vE1ub.js} +2 -2
- package/dist/docs/components.json +447 -515
- package/dist/framework/select-multi-value-accessor.js +3 -3
- package/dist/lib/toast/index.d.ts +2 -0
- package/dist/lib/toast/index.js +158 -0
- package/dist/lib/toast/index.js.map +7 -0
- package/dist/lib/toast/toast-manager.d.ts +3 -0
- package/dist/lib/toast/toast-types.d.ts +85 -0
- package/dist/mcp-data/registry/agent-activity.json +1 -1
- package/dist/mcp-data/registry/agent-response.json +2 -2
- package/dist/mcp-data/registry/agent-source-list.json +1 -1
- package/dist/mcp-data/registry/agent-tool-call.json +1 -1
- package/dist/mcp-data/registry/app-shell.json +1 -1
- package/dist/mcp-data/registry/attachment-list.json +1 -1
- package/dist/mcp-data/registry/avatar.json +1 -1
- package/dist/mcp-data/registry/badge.json +1 -1
- package/dist/mcp-data/registry/bar-nav.json +1 -1
- package/dist/mcp-data/registry/button-filled.json +1 -1
- package/dist/mcp-data/registry/button-unfilled.json +1 -1
- package/dist/mcp-data/registry/card-data-viz-donut.json +2 -2
- package/dist/mcp-data/registry/card-setting.json +99 -13
- package/dist/mcp-data/registry/card-shell-data-viz.json +8 -8
- package/dist/mcp-data/registry/chart-bar.json +1 -1
- package/dist/mcp-data/registry/chart-donut.json +1 -1
- package/dist/mcp-data/registry/chart-legend.json +1 -1
- package/dist/mcp-data/registry/chart-line.json +1 -1
- package/dist/mcp-data/registry/checkbox.json +1 -1
- package/dist/mcp-data/registry/chip.json +1 -1
- package/dist/mcp-data/registry/code-block.json +1 -1
- package/dist/mcp-data/registry/conversation-list-item.json +4 -4
- package/dist/mcp-data/registry/conversation-list-section.json +1 -1
- package/dist/mcp-data/registry/conversation-list.json +1 -1
- package/dist/mcp-data/registry/divider.json +5 -5
- package/dist/mcp-data/registry/empty-state.json +1 -1
- package/dist/mcp-data/registry/field.json +1 -1
- package/dist/mcp-data/registry/icon.json +2 -2
- package/dist/mcp-data/registry/input.json +1 -1
- package/dist/mcp-data/registry/loader.json +1 -1
- package/dist/mcp-data/registry/markdown.json +1 -1
- package/dist/mcp-data/registry/menu.json +1 -1
- package/dist/mcp-data/registry/message-bubble.json +7 -7
- package/dist/mcp-data/registry/message-composer.json +2 -2
- package/dist/mcp-data/registry/message-scroller.json +1 -1
- package/dist/mcp-data/registry/message.json +7 -6
- package/dist/mcp-data/registry/modal.json +154 -24
- package/dist/mcp-data/registry/panel-nav.json +1 -1
- package/dist/mcp-data/registry/panel-sub-nav.json +2 -2
- package/dist/mcp-data/registry/panel-tool-header.json +1 -1
- package/dist/mcp-data/registry/panel-tool-search.json +1 -1
- package/dist/mcp-data/registry/panel-tools.json +1 -1
- package/dist/mcp-data/registry/radio.json +1 -1
- package/dist/mcp-data/registry/registry.json +387 -141
- package/dist/mcp-data/registry/select-multi.json +1 -1
- package/dist/mcp-data/registry/select.json +1 -1
- package/dist/mcp-data/registry/shell-gradient-picker.json +1 -1
- package/dist/mcp-data/registry/shell-gradient-swatch.json +1 -1
- package/dist/mcp-data/registry/skeleton.json +2 -2
- package/dist/mcp-data/registry/slider.json +1 -1
- package/dist/mcp-data/registry/swatch-picker.json +1 -1
- package/dist/mcp-data/registry/switch.json +1 -1
- package/dist/mcp-data/registry/tab-group.json +1 -1
- package/dist/mcp-data/registry/tag.json +2 -2
- package/dist/mcp-data/registry/text.json +2 -2
- package/dist/mcp-data/registry/toast.json +318 -0
- package/dist/mcp-data/registry/tooltip-data-viz.json +1 -1
- package/dist/mcp-data/registry/tooltip.json +1 -1
- package/dist/react/components.d.ts +1 -2
- package/dist/react/components.js +1 -2
- package/dist/react/ds-card-setting.d.ts +2 -2
- package/dist/react/ds-card-setting.js +1 -1
- package/dist/react/ds-modal.d.ts +3 -2
- package/dist/react/ds-modal.js +4 -1
- package/dist/react/ds-toast.d.ts +14 -0
- package/dist/react/ds-toast.js +16 -0
- package/dist/types/components/CardSetting/CardSetting.d.ts +9 -6
- package/dist/types/components/CardSetting/index.d.ts +1 -1
- package/dist/types/components/MessageBubble/MessageBubble.d.ts +1 -1
- package/dist/types/components/Modal/Modal.d.ts +21 -18
- package/dist/types/components/Toast/Toast.d.ts +106 -0
- package/dist/types/components/Toast/index.d.ts +2 -0
- package/dist/types/components.d.ts +136 -180
- package/dist/types/toast/index.d.ts +3 -0
- package/dist/types/toast/toast-manager.d.ts +4 -0
- package/dist/types/toast/toast-types.d.ts +86 -0
- package/package.json +12 -8
- package/dist/angular/ds-banner.d.ts +0 -15
- package/dist/angular/ds-card.d.ts +0 -12
- package/dist/angular/ds-card.js +0 -38
- package/dist/components/ds-banner.d.ts +0 -11
- package/dist/components/ds-banner.js +0 -2
- package/dist/components/ds-banner.js.map +0 -1
- package/dist/components/ds-card.js +0 -2
- package/dist/components/ds-card.js.map +0 -1
- package/dist/components/p-BO6C8abO.js +0 -2
- package/dist/components/p-BO6C8abO.js.map +0 -1
- package/dist/components/p-CBYuvZKM.js +0 -2
- package/dist/components/p-CBYuvZKM.js.map +0 -1
- package/dist/components/p-CJlgONwR.js +0 -2
- package/dist/components/p-CWNdl52Y.js +0 -2
- package/dist/components/p-CWNdl52Y.js.map +0 -1
- package/dist/components/p-CiKGqsA2.js +0 -2
- package/dist/components/p-CiKGqsA2.js.map +0 -1
- package/dist/components/p-DFxqk71w.js +0 -2
- package/dist/components/p-DFxqk71w.js.map +0 -1
- package/dist/components/p-drBA73M_.js +0 -2
- package/dist/components/p-drBA73M_.js.map +0 -1
- package/dist/mcp-data/registry/banner.json +0 -206
- package/dist/mcp-data/registry/card.json +0 -126
- package/dist/react/ds-banner.d.ts +0 -12
- package/dist/react/ds-banner.js +0 -12
- package/dist/react/ds-card.d.ts +0 -9
- package/dist/react/ds-card.js +0 -12
- package/dist/types/components/Banner/Banner.d.ts +0 -31
- package/dist/types/components/Card/Card.d.ts +0 -21
- package/dist/types/components/Card/index.d.ts +0 -2
- /package/dist/components/{p-DnMCRAwx.js.map → p-0kEg-Z4f.js.map} +0 -0
- /package/dist/components/{p-Cijzn3YQ.js.map → p-B4RQoNmL.js.map} +0 -0
- /package/dist/components/{p-BHpWYKtP.js.map → p-BJUcsBHt.js.map} +0 -0
- /package/dist/components/{p-DaQ0ZE_V.js.map → p-BTo4pNdp.js.map} +0 -0
- /package/dist/components/{p-Ci_nVk5P.js.map → p-BkwBtZs3.js.map} +0 -0
- /package/dist/components/{p-Dst-OliP.js.map → p-CKnoWZd9.js.map} +0 -0
- /package/dist/components/{p-BRfOCc_h.js.map → p-CLn7QnE0.js.map} +0 -0
- /package/dist/components/{p-kezp2_Xu.js.map → p-CPPAndX2.js.map} +0 -0
- /package/dist/components/{p-xB0GPG3-.js.map → p-CS12e52R.js.map} +0 -0
- /package/dist/components/{p-BSOZXagj.js.map → p-CZZYGqKz.js.map} +0 -0
- /package/dist/components/{p-DiooIO2B.js.map → p-CxyjoTN1.js.map} +0 -0
- /package/dist/components/{p-T0Mzhv3-.js.map → p-DZAtgSZo.js.map} +0 -0
- /package/dist/components/{p-CeFBA7Dx.js.map → p-Dh4q5TSS.js.map} +0 -0
- /package/dist/components/{p-Dj5MejHv.js.map → p-DhsO0939.js.map} +0 -0
- /package/dist/components/{p-CiwdnKKp.js.map → p-DrDHC7V6.js.map} +0 -0
- /package/dist/components/{p-CblAABEo.js.map → p-DsbhBb5F.js.map} +0 -0
- /package/dist/components/{p-B1pWGQn1.js.map → p-P_4vE1ub.js.map} +0 -0
|
@@ -2,17 +2,126 @@
|
|
|
2
2
|
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
3
|
"name": "modal",
|
|
4
4
|
"title": "Modal",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "Dismissible native top-layer dialog for a focused confirmation or short blocking task, with a fixed title-and-close header, composable content, and an optional two-action footer.",
|
|
6
6
|
"type": "registry:ui",
|
|
7
7
|
"meta": {
|
|
8
8
|
"library": "@ds-mo/ui",
|
|
9
9
|
"distribution": "npm",
|
|
10
10
|
"storybook": "https://zainadeel.github.io/compomo/?path=/story/modal",
|
|
11
11
|
"source": "src/wc/components/Modal/Modal.tsx",
|
|
12
|
-
"intentStatus": "
|
|
13
|
-
"intent":
|
|
12
|
+
"intentStatus": "complete",
|
|
13
|
+
"intent": {
|
|
14
|
+
"audience": "general",
|
|
15
|
+
"status": "stable",
|
|
16
|
+
"summary": "Dismissible native top-layer dialog for a focused confirmation or short blocking task, with a fixed title-and-close header, composable content, and an optional two-action footer.",
|
|
17
|
+
"useWhen": [
|
|
18
|
+
"The user must confirm or cancel one focused decision before returning to the underlying interface.",
|
|
19
|
+
"A short blocking task or compact form needs dialog semantics, focus containment, and explicit completion actions.",
|
|
20
|
+
"The content can be safely dismissed through Close, Escape, or the backdrop."
|
|
21
|
+
],
|
|
22
|
+
"avoidWhen": [
|
|
23
|
+
"The message is non-blocking feedback or a transient status update.",
|
|
24
|
+
"The interaction is a contextual list of commands anchored to a trigger.",
|
|
25
|
+
"The workflow is multi-step, long-form, or requires substantial navigation.",
|
|
26
|
+
"Dismissal would lose work or leave an irreversible operation in an unsafe state.",
|
|
27
|
+
"A second modal would be opened over an existing modal."
|
|
28
|
+
],
|
|
29
|
+
"alternatives": [
|
|
30
|
+
{
|
|
31
|
+
"when": "The user needs non-blocking feedback or a transient status message.",
|
|
32
|
+
"component": "component:ds-toast",
|
|
33
|
+
"reason": "Toast communicates transient status without trapping focus or blocking the page."
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"when": "A trigger reveals a short contextual list of commands.",
|
|
37
|
+
"component": "component:ds-menu",
|
|
38
|
+
"reason": "Menu provides anchored command semantics without creating a blocking dialog."
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"when": "Editable settings should remain visible without blocking the rest of the interface.",
|
|
42
|
+
"component": "component:ds-card-setting",
|
|
43
|
+
"reason": "CardSetting provides a persistent controlled settings workflow without modal focus containment."
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
"commonlyComposedWith": [
|
|
47
|
+
"component:ds-button-filled",
|
|
48
|
+
"component:ds-button-unfilled",
|
|
49
|
+
"component:ds-field",
|
|
50
|
+
"component:ds-text"
|
|
51
|
+
],
|
|
52
|
+
"accessibility": [
|
|
53
|
+
"Provide a concise visible heading; the component uses it as the dialog's accessible name.",
|
|
54
|
+
"Give the primary explanatory copy a stable id and pass it through aria-describedby so assistive technology receives the decision context.",
|
|
55
|
+
"Opening the native modal dialog moves focus to the header Close action, traps Tab navigation inside the top layer, and makes outside content inert.",
|
|
56
|
+
"Close, Escape, and backdrop activation emit dsClose with close-button, escape, or backdrop reason before beginning dismissal; use the component only when every dismissal path is safe.",
|
|
57
|
+
"After exit motion completes, the native dialog leaves the top layer, focus returns to the previously focused element, and dsAfterClose emits.",
|
|
58
|
+
"Use visible label-only footer buttons. Keep at most two actions: one bold filled primary action followed by one bordered unfilled action, or two bordered unfilled actions.",
|
|
59
|
+
"Use negative intent for a filled destructive action and state the irreversible consequence in the content.",
|
|
60
|
+
"Do not open a modal from another modal; keep one blocking decision in focus at a time."
|
|
61
|
+
],
|
|
62
|
+
"states": [
|
|
63
|
+
"The owner opens the modal through the open property and responds to the typed dsClose detail for application state synchronization or dismissal analytics.",
|
|
64
|
+
"The component mutates open to false for Close, Escape, and backdrop dismissal while retaining its rendered surface through exit motion.",
|
|
65
|
+
"The default slot owns task content; the named footer slot is optional and is hidden when no actions are assigned.",
|
|
66
|
+
"Primary actions use a bold filled button with semantic intent. Secondary and cancel actions use bordered label-only unfilled buttons.",
|
|
67
|
+
"The owner performs save, delete, cancel, and other product consequences before or while closing the modal."
|
|
68
|
+
],
|
|
69
|
+
"responsiveBehavior": [
|
|
70
|
+
"Choose the smallest sm, md, or lg width that fits the focused task; the top-layer dialog clamps to the viewport with token-based outer space.",
|
|
71
|
+
"Content scrolls independently when needed while the 64px header and optional 64px footer remain fixed.",
|
|
72
|
+
"Avoid layouts whose minimum content width exceeds the narrow viewport clamp.",
|
|
73
|
+
"The native top layer escapes transformed ancestors, clipping containers, and application stacking contexts without moving slotted content."
|
|
74
|
+
],
|
|
75
|
+
"frameworkCaveats": {
|
|
76
|
+
"customElements": [
|
|
77
|
+
"Set open as a JavaScript property when controlling the modal; inspect dsClose.detail.reason and use dsAfterClose for cleanup that must wait for exit motion and focus restoration.",
|
|
78
|
+
"Connect the most relevant slotted explanatory text through aria-describedby.",
|
|
79
|
+
"Footer actions are slotted application-owned controls and must implement their own product consequence."
|
|
80
|
+
],
|
|
81
|
+
"react": [
|
|
82
|
+
"Pass open to DsModal, inspect onDsClose event detail, and use onDsAfterClose only for cleanup after exit motion and focus restoration.",
|
|
83
|
+
"Connect the most relevant slotted explanatory text through aria-describedby.",
|
|
84
|
+
"Assign footer controls with slot=\"footer\" and handle each action before updating open."
|
|
85
|
+
],
|
|
86
|
+
"angular": [
|
|
87
|
+
"Bind [open], inspect $event.detail from (dsClose), and use (dsAfterClose) only for cleanup after exit motion and focus restoration.",
|
|
88
|
+
"Connect the most relevant slotted explanatory text through aria-describedby.",
|
|
89
|
+
"Assign footer controls with slot=\"footer\" and handle each action before updating open."
|
|
90
|
+
]
|
|
91
|
+
},
|
|
92
|
+
"references": [
|
|
93
|
+
{
|
|
94
|
+
"label": "Storybook confirmation and footer examples",
|
|
95
|
+
"path": "src/wc/components/Modal/Modal.stories.ts"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"label": "Cross-browser focus, dismissal, geometry, and accessibility coverage",
|
|
99
|
+
"path": "tests/e2e/accessibility-overlays.spec.ts"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"label": "Reduced-motion overlay coverage",
|
|
103
|
+
"path": "tests/e2e/reduced-motion.spec.ts"
|
|
104
|
+
}
|
|
105
|
+
]
|
|
106
|
+
},
|
|
14
107
|
"api": {
|
|
15
108
|
"props": {
|
|
109
|
+
"ariaDescribedby": {
|
|
110
|
+
"type": "string | undefined",
|
|
111
|
+
"resolvedType": "string | undefined",
|
|
112
|
+
"attribute": "aria-describedby",
|
|
113
|
+
"required": false,
|
|
114
|
+
"mutable": false,
|
|
115
|
+
"description": "Optional id reference for explanatory content in the default slot."
|
|
116
|
+
},
|
|
117
|
+
"closeAriaLabel": {
|
|
118
|
+
"type": "string",
|
|
119
|
+
"resolvedType": "string",
|
|
120
|
+
"attribute": "close-aria-label",
|
|
121
|
+
"default": "'Close'",
|
|
122
|
+
"required": false,
|
|
123
|
+
"mutable": false
|
|
124
|
+
},
|
|
16
125
|
"heading": {
|
|
17
126
|
"type": "string",
|
|
18
127
|
"resolvedType": "string",
|
|
@@ -35,28 +144,46 @@
|
|
|
35
144
|
"default": "false",
|
|
36
145
|
"required": false,
|
|
37
146
|
"mutable": true
|
|
38
|
-
},
|
|
39
|
-
"subtitle": {
|
|
40
|
-
"type": "string | undefined",
|
|
41
|
-
"resolvedType": "string | undefined",
|
|
42
|
-
"attribute": "subtitle",
|
|
43
|
-
"required": false,
|
|
44
|
-
"mutable": false
|
|
45
147
|
}
|
|
46
148
|
},
|
|
47
149
|
"events": [
|
|
48
150
|
{
|
|
49
|
-
"name": "
|
|
151
|
+
"name": "dsAfterClose",
|
|
50
152
|
"detail": "void",
|
|
51
153
|
"bubbles": true,
|
|
52
154
|
"cancelable": true,
|
|
53
|
-
"composed": true
|
|
155
|
+
"composed": true,
|
|
156
|
+
"description": "Emitted after exit motion completes, the top layer closes, and focus is restored."
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"name": "dsClose",
|
|
160
|
+
"detail": "ModalCloseDetail",
|
|
161
|
+
"bubbles": true,
|
|
162
|
+
"cancelable": true,
|
|
163
|
+
"composed": true,
|
|
164
|
+
"description": "Emitted when an internal dismissal control requests that the modal close."
|
|
54
165
|
}
|
|
55
166
|
],
|
|
56
167
|
"methods": [],
|
|
57
168
|
"slots": []
|
|
58
169
|
},
|
|
59
170
|
"props": {
|
|
171
|
+
"ariaDescribedby": {
|
|
172
|
+
"type": "string | undefined",
|
|
173
|
+
"resolvedType": "string | undefined",
|
|
174
|
+
"attribute": "aria-describedby",
|
|
175
|
+
"required": false,
|
|
176
|
+
"mutable": false,
|
|
177
|
+
"description": "Optional id reference for explanatory content in the default slot."
|
|
178
|
+
},
|
|
179
|
+
"closeAriaLabel": {
|
|
180
|
+
"type": "string",
|
|
181
|
+
"resolvedType": "string",
|
|
182
|
+
"attribute": "close-aria-label",
|
|
183
|
+
"default": "'Close'",
|
|
184
|
+
"required": false,
|
|
185
|
+
"mutable": false
|
|
186
|
+
},
|
|
60
187
|
"heading": {
|
|
61
188
|
"type": "string",
|
|
62
189
|
"resolvedType": "string",
|
|
@@ -79,22 +206,24 @@
|
|
|
79
206
|
"default": "false",
|
|
80
207
|
"required": false,
|
|
81
208
|
"mutable": true
|
|
82
|
-
},
|
|
83
|
-
"subtitle": {
|
|
84
|
-
"type": "string | undefined",
|
|
85
|
-
"resolvedType": "string | undefined",
|
|
86
|
-
"attribute": "subtitle",
|
|
87
|
-
"required": false,
|
|
88
|
-
"mutable": false
|
|
89
209
|
}
|
|
90
210
|
},
|
|
91
211
|
"events": [
|
|
92
212
|
{
|
|
93
|
-
"name": "
|
|
213
|
+
"name": "dsAfterClose",
|
|
94
214
|
"detail": "void",
|
|
95
215
|
"bubbles": true,
|
|
96
216
|
"cancelable": true,
|
|
97
|
-
"composed": true
|
|
217
|
+
"composed": true,
|
|
218
|
+
"description": "Emitted after exit motion completes, the top layer closes, and focus is restored."
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"name": "dsClose",
|
|
222
|
+
"detail": "ModalCloseDetail",
|
|
223
|
+
"bubbles": true,
|
|
224
|
+
"cancelable": true,
|
|
225
|
+
"composed": true,
|
|
226
|
+
"description": "Emitted when an internal dismissal control requests that the modal close."
|
|
98
227
|
}
|
|
99
228
|
],
|
|
100
229
|
"methods": [],
|
|
@@ -122,7 +251,7 @@
|
|
|
122
251
|
"peerDependencies": {
|
|
123
252
|
"required": [
|
|
124
253
|
"@ds-mo/tokens ^3.1.1",
|
|
125
|
-
"@ds-mo/icons ^6.
|
|
254
|
+
"@ds-mo/icons ^6.3.0"
|
|
126
255
|
],
|
|
127
256
|
"frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
|
|
128
257
|
}
|
|
@@ -133,17 +262,18 @@
|
|
|
133
262
|
"@ds-mo/tokens"
|
|
134
263
|
],
|
|
135
264
|
"registryDependencies": [
|
|
265
|
+
"button-unfilled",
|
|
136
266
|
"text"
|
|
137
267
|
],
|
|
138
268
|
"files": [
|
|
139
269
|
{
|
|
140
270
|
"path": "src/wc/components/Modal/Modal.css",
|
|
141
|
-
"content": ".modal-
|
|
271
|
+
"content": ":host {\n display: contents;\n}\n\n.modal-dialog {\n display: none;\n flex-direction: column;\n max-width: none;\n max-height: calc(100vh - 2 * var(--dimension-space-200));\n margin: auto;\n padding: 0;\n overflow: hidden;\n color: var(--color-foreground-primary);\n background-color: var(--color-background-primary);\n border: 0;\n border-radius: var(--dimension-radius-050);\n box-shadow: var(--effect-elevation-elevated-floating);\n outline: none;\n box-sizing: border-box;\n}\n\n.modal-dialog[open] {\n display: flex;\n animation: modalDialogIn var(--effect-motion-short-2) forwards;\n}\n\n.modal-dialog::backdrop {\n background-color: var(--color-background-shade);\n animation: modalBackdropIn var(--effect-motion-short-2) forwards;\n}\n\n.modal-dialog--closing {\n animation: modalDialogOut var(--effect-motion-short-3) forwards;\n}\n\n.modal-dialog--closing::backdrop {\n animation: modalBackdropOut var(--effect-motion-short-3) forwards;\n}\n\n@keyframes modalBackdropIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n@keyframes modalBackdropOut {\n from { opacity: 1; }\n to { opacity: 0; }\n}\n\n@keyframes modalDialogIn {\n from { opacity: 0; transform: scale(0.97) translateY(4px); }\n to { opacity: 1; transform: scale(1) translateY(0); }\n}\n\n@keyframes modalDialogOut {\n from { opacity: 1; transform: scale(1) translateY(0); }\n to { opacity: 0; transform: scale(0.97) translateY(4px); }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .modal-dialog[open],\n .modal-dialog--closing {\n animation: none;\n transform: none;\n }\n\n .modal-dialog::backdrop {\n animation: none;\n opacity: 1;\n }\n\n .modal-dialog--closing,\n .modal-dialog--closing::backdrop {\n opacity: 0;\n }\n}\n\n.modal-header {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-050);\n height: var(--dimension-size-800);\n padding: var(--dimension-space-200);\n flex: 0 0 auto;\n box-sizing: border-box;\n border-bottom: var(--dimension-stroke-width-012) solid var(--color-border-tertiary);\n color: var(--color-foreground-primary);\n}\n\nds-text.modal-heading {\n flex: 1 1 0;\n width: auto;\n min-width: 0;\n text-align: left;\n -webkit-user-select: none;\n user-select: none;\n}\n\n.modal-close {\n flex: 0 0 auto;\n}\n\n.modal-content {\n min-height: 0;\n padding: var(--dimension-space-200);\n overflow-y: auto;\n flex: 1 1 auto;\n box-sizing: border-box;\n}\n\n.modal-footer {\n display: flex;\n align-items: center;\n justify-content: flex-start;\n gap: var(--dimension-space-100);\n height: var(--dimension-size-800);\n padding: var(--dimension-space-200);\n flex: 0 0 auto;\n box-sizing: border-box;\n border-top: var(--dimension-stroke-width-012) solid var(--color-border-tertiary);\n}\n\n.modal-footer--empty {\n display: none;\n}\n\n.modal-footer slot {\n display: contents;\n}\n",
|
|
142
272
|
"type": "registry:ui"
|
|
143
273
|
},
|
|
144
274
|
{
|
|
145
275
|
"path": "src/wc/components/Modal/Modal.tsx",
|
|
146
|
-
"content": "import { Component, Prop, State, Event, EventEmitter, Element, Watch, h, Host } from '@stencil/core';\nimport { resolveMotionTimeMs, TOKEN_DEFAULTS } from '../../utils';\n\nexport type ModalWidth = 'sm' | 'md' | 'lg';\n\nconst WIDTH_MAP: Record<ModalWidth, string> = {\n sm: 'var(--dimension-modal-width-sm)',\n md: 'var(--dimension-modal-width-md)',\n lg: 'var(--dimension-modal-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\nlet modalIdCounter = 0;\
|
|
276
|
+
"content": "import { Component, Prop, State, Event, EventEmitter, Element, Watch, h, Host } from '@stencil/core';\nimport { resolveMotionTimeMs, TOKEN_DEFAULTS } from '../../utils';\n\nexport type ModalWidth = 'sm' | 'md' | 'lg';\nexport type ModalCloseReason = 'close-button' | 'escape' | 'backdrop';\n\nexport interface ModalCloseDetail {\n reason: ModalCloseReason;\n originalEvent: Event;\n}\n\nconst WIDTH_MAP: Record<ModalWidth, string> = {\n sm: 'var(--dimension-modal-width-sm)',\n md: 'var(--dimension-modal-width-md)',\n lg: 'var(--dimension-modal-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\nlet modalIdCounter = 0;\n\n@Component({\n tag: 'ds-modal',\n styleUrl: 'Modal.css',\n scoped: true,\n})\nexport class Modal {\n @Element() el!: HTMLElement;\n\n @Prop({ mutable: true }) open: boolean = false;\n @Prop() heading!: string;\n @Prop() closeAriaLabel: string = 'Close';\n @Prop() modalWidth: ModalWidth | string = 'md';\n /** Optional id reference for explanatory content in the default slot. */\n @Prop({ attribute: 'aria-describedby' }) ariaDescribedby: string | undefined;\n\n @State() private closing: boolean = false;\n @State() private hasFooter: boolean = false;\n\n /** Emitted when an internal dismissal control requests that the modal close. */\n @Event() dsClose!: EventEmitter<ModalCloseDetail>;\n /** Emitted after exit motion completes, the top layer closes, and focus is restored. */\n @Event() dsAfterClose!: EventEmitter<void>;\n\n private titleId = `ds-modal-title-${++modalIdCounter}`;\n private dialogEl: HTMLDialogElement | null = null;\n private previousFocus: HTMLElement | null = null;\n private closeTimer: ReturnType<typeof setTimeout> | null = null;\n\n componentDidLoad() {\n this.updateFooterPresence();\n if (this.open) this.onOpenChange(true);\n }\n\n disconnectedCallback() {\n this.clearCloseTimer();\n if (this.dialogEl?.open) this.dialogEl.close();\n }\n\n @Watch('open')\n onOpenChange(isOpen: boolean) {\n if (isOpen) {\n this.clearCloseTimer();\n const alreadyOpen = !!this.dialogEl?.open;\n this.closing = false;\n if (!this.dialogEl || alreadyOpen) return;\n this.previousFocus = document.activeElement as HTMLElement | null;\n this.dialogEl.showModal();\n requestAnimationFrame(() => {\n if (this.open && this.dialogEl?.open) this.focusClose();\n });\n } else if (this.dialogEl?.open) {\n this.closing = true;\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\n private focusClose() {\n const close = this.el.querySelector('ds-button-unfilled.modal-close') as\n | (HTMLElement & { setFocus?: () => Promise<void> })\n | null;\n void close?.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: ModalCloseReason, originalEvent: Event) {\n if (!this.open || this.closing || !this.dialogEl?.open) return;\n this.dsClose.emit({ reason, originalEvent });\n this.open = false;\n }\n\n private updateFooterPresence(slot?: HTMLSlotElement) {\n const assignedNodes = slot?.assignedNodes({ flatten: true }) ?? [];\n this.hasFooter =\n assignedNodes.some(node => node.nodeType !== Node.TEXT_NODE || !!node.textContent?.trim()) ||\n this.el.querySelector('[slot=\"footer\"]') !== null;\n }\n\n private handleCancel(event: Event) {\n event.preventDefault();\n this.requestClose('escape', event);\n }\n\n private handleBackdropPointerDown(event: PointerEvent) {\n if (event.target === this.dialogEl) this.requestClose('backdrop', event);\n }\n\n private handleKeyDown(event: KeyboardEvent) {\n if (event.key !== 'Tab' || !this.dialogEl) return;\n const focusables = Array.from(\n this.dialogEl.querySelectorAll<HTMLElement>(FOCUSABLE_SEL)\n ).filter(element =>\n !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.modalWidth === 'sm' || this.modalWidth === 'md' || this.modalWidth === 'lg') {\n return WIDTH_MAP[this.modalWidth];\n }\n return this.modalWidth as string;\n }\n\n private get closeAnimationMs(): number {\n return resolveMotionTimeMs(TOKEN_DEFAULTS.motionShort3, TOKEN_DEFAULTS.animationDurationShort3);\n }\n\n private finishClose() {\n if (!this.dialogEl?.open) return;\n this.dialogEl.close();\n this.closing = false;\n this.clearCloseTimer();\n this.previousFocus?.focus?.();\n this.previousFocus = null;\n this.dsAfterClose.emit();\n }\n\n render() {\n return (\n <Host>\n <dialog\n ref={(element?: HTMLDialogElement) => {\n this.dialogEl = element ?? null;\n }}\n class={{ 'modal-dialog': true, 'modal-dialog--closing': this.closing }}\n aria-labelledby={this.titleId}\n aria-describedby={this.ariaDescribedby}\n onCancel={(event: Event) => this.handleCancel(event)}\n onPointerDown={(event: PointerEvent) => this.handleBackdropPointerDown(event)}\n onKeyDown={(event: KeyboardEvent) => this.handleKeyDown(event)}\n style={{\n width: `min(${this.resolvedWidth}, calc(100vw - 2 * var(--dimension-space-200)))`,\n }}\n >\n <div class=\"modal-header\">\n <ds-text\n class=\"modal-heading\"\n as=\"h2\"\n variant=\"text-title-medium\"\n emphasis\n color=\"primary\"\n lineTruncation={1}\n textId={this.titleId}\n >\n {this.heading}\n </ds-text>\n <ds-button-unfilled\n class=\"modal-close\"\n variant=\"icon\"\n icon=\"Cross\"\n size=\"md\"\n aria-label={this.closeAriaLabel}\n activeFill={false}\n hasBorder={false}\n onDsClick={(event: CustomEvent<MouseEvent>) =>\n this.requestClose('close-button', event.detail)\n }\n />\n </div>\n <div class=\"modal-content\">\n <slot />\n </div>\n <div\n class={{\n 'modal-footer': true,\n 'modal-footer--empty': !this.hasFooter,\n }}\n >\n <slot\n name=\"footer\"\n onSlotchange={(event: Event) =>\n this.updateFooterPresence(event.currentTarget as HTMLSlotElement)\n }\n />\n </div>\n </dialog>\n </Host>\n );\n }\n}\n",
|
|
147
277
|
"type": "registry:ui"
|
|
148
278
|
}
|
|
149
279
|
]
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
}
|
|
49
49
|
],
|
|
50
50
|
"commonlyComposedWith": [
|
|
51
|
-
"component:ds-card",
|
|
51
|
+
"component:ds-card-setting",
|
|
52
52
|
"component:ds-text"
|
|
53
53
|
],
|
|
54
54
|
"accessibility": [
|
|
@@ -216,7 +216,7 @@
|
|
|
216
216
|
"peerDependencies": {
|
|
217
217
|
"required": [
|
|
218
218
|
"@ds-mo/tokens ^3.1.1",
|
|
219
|
-
"@ds-mo/icons ^6.
|
|
219
|
+
"@ds-mo/icons ^6.3.0"
|
|
220
220
|
],
|
|
221
221
|
"frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
|
|
222
222
|
}
|