@ds-mo/ui 8.3.1 → 9.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -3
- package/dist/.build-stamp +1 -1
- package/dist/agent-patterns.json +233 -0
- package/dist/agent.json +155 -174
- package/dist/angular/ds-menu.d.ts +6 -1
- package/dist/angular/ds-menu.js +7 -4
- package/dist/angular/ds-swatch-picker.d.ts +15 -0
- package/dist/angular/{ds-pagination.js → ds-swatch-picker.js} +15 -15
- 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/components/ds-app-shell.js +1 -1
- package/dist/components/ds-badge.js +1 -1
- package/dist/components/ds-banner.js +1 -1
- package/dist/components/ds-bar-nav.js +1 -1
- package/dist/components/ds-bar-nav.js.map +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-setting.js +1 -1
- package/dist/components/ds-card-shell-data-viz.js +1 -1
- package/dist/components/ds-card.js +1 -1
- package/dist/components/ds-chart-bar.js +1 -1
- package/dist/components/ds-chart-bar.js.map +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-empty-state.js +1 -1
- package/dist/components/ds-field.js +1 -1
- package/dist/components/ds-input.js +1 -1
- package/dist/components/ds-menu.js +1 -1
- package/dist/components/ds-modal.js +1 -1
- package/dist/components/ds-panel-nav.js +1 -1
- package/dist/components/ds-panel-nav.js.map +1 -1
- package/dist/components/ds-panel-sub-nav.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-multi.js.map +1 -1
- package/dist/components/ds-select.js +1 -1
- package/dist/components/ds-select.js.map +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-shell-gradient-swatch.js.map +1 -1
- package/dist/components/ds-skeleton.js +1 -1
- package/dist/components/ds-skeleton.js.map +1 -1
- package/dist/components/ds-slider.js +1 -1
- package/dist/components/ds-slider.js.map +1 -1
- package/dist/components/{ds-pagination.d.ts → ds-swatch-picker.d.ts} +4 -4
- package/dist/components/ds-swatch-picker.js +2 -0
- package/dist/components/ds-swatch-picker.js.map +1 -0
- 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-tooltip-data-viz.js +1 -1
- package/dist/components/ds-tooltip.js +1 -1
- package/dist/components/{p-CysJosvS.js → p-B-zhgAvL.js} +2 -2
- package/dist/components/{p-C21sIElQ.js → p-BLzivrE3.js} +2 -2
- package/dist/components/{p-DAuadHYu.js → p-BbV68F23.js} +2 -2
- package/dist/components/{p-_y99wWNU.js → p-C7RObioO.js} +2 -2
- package/dist/components/{p-DxQ4gzbA.js → p-CZ9wemOA.js} +2 -2
- package/dist/components/{p-BKA6houZ.js → p-CbS_WWs2.js} +2 -2
- package/dist/components/p-D1HFMTdC.js +2 -0
- package/dist/components/p-D1HFMTdC.js.map +1 -0
- package/dist/components/{p-BGcSrHKl.js → p-DT2MK8Kt.js} +2 -2
- package/dist/components/{p-DV23KMU-.js → p-DyZj3k9S.js} +2 -2
- package/dist/components/p-HBIxvS5C.js +2 -0
- package/dist/components/p-HBIxvS5C.js.map +1 -0
- package/dist/components/{p-D6hvtWDk.js → p-KRGBGTu5.js} +2 -2
- package/dist/components/p-l6PmnW_P.js +2 -0
- package/dist/components/p-l6PmnW_P.js.map +1 -0
- package/dist/components/p-tQ1QHeOE.js +2 -0
- package/dist/components/p-tQ1QHeOE.js.map +1 -0
- package/dist/docs/components.json +546 -1024
- package/dist/lib/components/Menu/menu-placement.d.ts +6 -3
- package/dist/lib/shell/index.d.ts +2 -2
- package/dist/lib/shell/index.js +40 -39
- package/dist/lib/shell/index.js.map +3 -3
- package/dist/lib/shell/shell-gradient-presets.d.ts +15 -0
- package/dist/lib/utils/choice-popup-alignment.d.ts +20 -0
- package/dist/lib/utils/index.d.ts +2 -0
- package/dist/lib/utils/index.js +16 -0
- package/dist/lib/utils/index.js.map +3 -3
- package/dist/mcp/cli.js +31409 -0
- package/dist/mcp-data/registry/app-shell.json +210 -0
- package/dist/mcp-data/registry/badge.json +289 -0
- package/dist/mcp-data/registry/banner.json +206 -0
- package/dist/mcp-data/registry/bar-nav.json +341 -0
- package/dist/mcp-data/registry/button-filled.json +386 -0
- package/dist/mcp-data/registry/button-unfilled.json +537 -0
- package/dist/mcp-data/registry/card-data-viz-donut.json +143 -0
- package/dist/mcp-data/registry/card-setting.json +194 -0
- package/dist/mcp-data/registry/card-shell-data-viz.json +173 -0
- package/dist/mcp-data/registry/card.json +126 -0
- package/dist/mcp-data/registry/chart-bar.json +121 -0
- package/dist/mcp-data/registry/chart-donut.json +257 -0
- package/dist/mcp-data/registry/chart-legend.json +209 -0
- package/dist/mcp-data/registry/chart-line.json +153 -0
- package/dist/mcp-data/registry/checkbox.json +361 -0
- package/dist/mcp-data/registry/chip.json +276 -0
- package/dist/mcp-data/registry/divider.json +225 -0
- package/dist/mcp-data/registry/empty-state.json +197 -0
- package/dist/mcp-data/registry/field.json +222 -0
- package/dist/mcp-data/registry/icon.json +242 -0
- package/dist/mcp-data/registry/input.json +597 -0
- package/dist/mcp-data/registry/loader.json +192 -0
- package/dist/mcp-data/registry/menu.json +459 -0
- package/dist/mcp-data/registry/modal.json +150 -0
- package/dist/mcp-data/registry/panel-nav.json +622 -0
- package/dist/mcp-data/registry/panel-sub-nav.json +249 -0
- package/dist/mcp-data/registry/panel-tools.json +374 -0
- package/dist/mcp-data/registry/radio.json +374 -0
- package/dist/mcp-data/registry/registry.json +5389 -0
- package/dist/mcp-data/registry/select-multi.json +755 -0
- package/dist/mcp-data/registry/select.json +740 -0
- package/dist/mcp-data/registry/shell-gradient-picker.json +195 -0
- package/dist/mcp-data/registry/shell-gradient-swatch.json +215 -0
- package/dist/mcp-data/registry/skeleton.json +282 -0
- package/dist/mcp-data/registry/slider.json +657 -0
- package/dist/mcp-data/registry/swatch-picker.json +231 -0
- package/dist/mcp-data/registry/switch.json +361 -0
- package/dist/mcp-data/registry/tab-group.json +255 -0
- package/dist/mcp-data/registry/tag.json +352 -0
- package/dist/mcp-data/registry/text.json +371 -0
- package/dist/mcp-data/registry/tooltip-data-viz.json +158 -0
- package/dist/mcp-data/registry/tooltip.json +311 -0
- package/dist/react/components.d.ts +1 -2
- package/dist/react/components.js +1 -2
- package/dist/react/ds-menu.d.ts +1 -0
- package/dist/react/ds-menu.js +2 -1
- package/dist/react/ds-swatch-picker.d.ts +12 -0
- package/dist/react/ds-swatch-picker.js +12 -0
- package/dist/types/components/Menu/Menu.d.ts +7 -1
- package/dist/types/components/Menu/menu-placement.d.ts +6 -3
- package/dist/types/components/Menu/menu-types.d.ts +14 -1
- package/dist/types/components/ShellGradientPicker/ShellGradientPicker.d.ts +2 -5
- package/dist/types/components/ShellGradientSwatch/ShellGradientSwatch.d.ts +1 -0
- package/dist/types/components/SwatchPicker/SwatchPicker.d.ts +21 -0
- package/dist/types/components/SwatchPicker/swatch-picker-types.d.ts +26 -0
- package/dist/types/components.d.ts +117 -237
- package/dist/types/shell/index.d.ts +2 -2
- package/dist/types/shell/shell-gradient-presets.d.ts +15 -0
- package/dist/types/utils/choice-popup-alignment.d.ts +21 -0
- package/dist/types/utils/index.d.ts +2 -0
- package/package.json +6 -2
- package/dist/angular/ds-pagination.d.ts +0 -15
- package/dist/angular/ds-table.d.ts +0 -33
- package/dist/angular/ds-table.js +0 -48
- package/dist/components/ds-pagination.js +0 -2
- package/dist/components/ds-pagination.js.map +0 -1
- package/dist/components/ds-table.d.ts +0 -11
- package/dist/components/ds-table.js +0 -2
- package/dist/components/ds-table.js.map +0 -1
- package/dist/components/p-CKV6rOGa.js +0 -2
- package/dist/components/p-CKV6rOGa.js.map +0 -1
- package/dist/components/p-CSTr2132.js +0 -2
- package/dist/components/p-CSTr2132.js.map +0 -1
- package/dist/components/p-C_cu-HIT.js +0 -2
- package/dist/components/p-C_cu-HIT.js.map +0 -1
- package/dist/components/p-D0B9uDTP.js +0 -2
- package/dist/components/p-D0B9uDTP.js.map +0 -1
- package/dist/components/p-DhxWfdX0.js +0 -2
- package/dist/components/p-DhxWfdX0.js.map +0 -1
- package/dist/components/p-Pip-LNhl.js +0 -2
- package/dist/components/p-Pip-LNhl.js.map +0 -1
- package/dist/react/ds-pagination.d.ts +0 -12
- package/dist/react/ds-pagination.js +0 -12
- package/dist/react/ds-table.d.ts +0 -21
- package/dist/react/ds-table.js +0 -16
- package/dist/types/components/Pagination/Pagination.d.ts +0 -14
- package/dist/types/components/Table/Table.d.ts +0 -56
- /package/dist/components/{p-CysJosvS.js.map → p-B-zhgAvL.js.map} +0 -0
- /package/dist/components/{p-C21sIElQ.js.map → p-BLzivrE3.js.map} +0 -0
- /package/dist/components/{p-DAuadHYu.js.map → p-BbV68F23.js.map} +0 -0
- /package/dist/components/{p-_y99wWNU.js.map → p-C7RObioO.js.map} +0 -0
- /package/dist/components/{p-DxQ4gzbA.js.map → p-CZ9wemOA.js.map} +0 -0
- /package/dist/components/{p-BKA6houZ.js.map → p-CbS_WWs2.js.map} +0 -0
- /package/dist/components/{p-BGcSrHKl.js.map → p-DT2MK8Kt.js.map} +0 -0
- /package/dist/components/{p-DV23KMU-.js.map → p-DyZj3k9S.js.map} +0 -0
- /package/dist/components/{p-D6hvtWDk.js.map → p-KRGBGTu5.js.map} +0 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "modal",
|
|
4
|
+
"title": "Modal",
|
|
5
|
+
"description": "Dialog overlay with portal rendering, escape-to-close, backdrop click, focus management, and entry/exit animations.",
|
|
6
|
+
"type": "registry:ui",
|
|
7
|
+
"meta": {
|
|
8
|
+
"library": "@ds-mo/ui",
|
|
9
|
+
"distribution": "npm",
|
|
10
|
+
"storybook": "https://zainadeel.github.io/compomo/?path=/story/modal",
|
|
11
|
+
"source": "src/wc/components/Modal/Modal.tsx",
|
|
12
|
+
"intentStatus": "migration-pending",
|
|
13
|
+
"intent": null,
|
|
14
|
+
"api": {
|
|
15
|
+
"props": {
|
|
16
|
+
"heading": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"resolvedType": "string",
|
|
19
|
+
"attribute": "heading",
|
|
20
|
+
"required": true,
|
|
21
|
+
"mutable": false
|
|
22
|
+
},
|
|
23
|
+
"modalWidth": {
|
|
24
|
+
"type": "ModalWidth | string",
|
|
25
|
+
"resolvedType": "string",
|
|
26
|
+
"attribute": "modal-width",
|
|
27
|
+
"default": "'md'",
|
|
28
|
+
"required": false,
|
|
29
|
+
"mutable": false
|
|
30
|
+
},
|
|
31
|
+
"open": {
|
|
32
|
+
"type": "boolean",
|
|
33
|
+
"resolvedType": "boolean",
|
|
34
|
+
"attribute": "open",
|
|
35
|
+
"default": "false",
|
|
36
|
+
"required": false,
|
|
37
|
+
"mutable": true
|
|
38
|
+
},
|
|
39
|
+
"subtitle": {
|
|
40
|
+
"type": "string | undefined",
|
|
41
|
+
"resolvedType": "string | undefined",
|
|
42
|
+
"attribute": "subtitle",
|
|
43
|
+
"required": false,
|
|
44
|
+
"mutable": false
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"events": [
|
|
48
|
+
{
|
|
49
|
+
"name": "dsClose",
|
|
50
|
+
"detail": "void",
|
|
51
|
+
"bubbles": true,
|
|
52
|
+
"cancelable": true,
|
|
53
|
+
"composed": true
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
"methods": [],
|
|
57
|
+
"slots": []
|
|
58
|
+
},
|
|
59
|
+
"props": {
|
|
60
|
+
"heading": {
|
|
61
|
+
"type": "string",
|
|
62
|
+
"resolvedType": "string",
|
|
63
|
+
"attribute": "heading",
|
|
64
|
+
"required": true,
|
|
65
|
+
"mutable": false
|
|
66
|
+
},
|
|
67
|
+
"modalWidth": {
|
|
68
|
+
"type": "ModalWidth | string",
|
|
69
|
+
"resolvedType": "string",
|
|
70
|
+
"attribute": "modal-width",
|
|
71
|
+
"default": "'md'",
|
|
72
|
+
"required": false,
|
|
73
|
+
"mutable": false
|
|
74
|
+
},
|
|
75
|
+
"open": {
|
|
76
|
+
"type": "boolean",
|
|
77
|
+
"resolvedType": "boolean",
|
|
78
|
+
"attribute": "open",
|
|
79
|
+
"default": "false",
|
|
80
|
+
"required": false,
|
|
81
|
+
"mutable": true
|
|
82
|
+
},
|
|
83
|
+
"subtitle": {
|
|
84
|
+
"type": "string | undefined",
|
|
85
|
+
"resolvedType": "string | undefined",
|
|
86
|
+
"attribute": "subtitle",
|
|
87
|
+
"required": false,
|
|
88
|
+
"mutable": false
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"events": [
|
|
92
|
+
{
|
|
93
|
+
"name": "dsClose",
|
|
94
|
+
"detail": "void",
|
|
95
|
+
"bubbles": true,
|
|
96
|
+
"cancelable": true,
|
|
97
|
+
"composed": true
|
|
98
|
+
}
|
|
99
|
+
],
|
|
100
|
+
"methods": [],
|
|
101
|
+
"slots": [],
|
|
102
|
+
"exports": {
|
|
103
|
+
"customElement": "ds-modal",
|
|
104
|
+
"react": "DsModal",
|
|
105
|
+
"angular": "DsModal"
|
|
106
|
+
},
|
|
107
|
+
"consumption": {
|
|
108
|
+
"install": "npm install @ds-mo/ui @ds-mo/tokens @ds-mo/icons",
|
|
109
|
+
"cssSetup": "import '@ds-mo/tokens';\nimport '@ds-mo/tokens/reset';\nimport '@ds-mo/tokens/globals';",
|
|
110
|
+
"customElements": {
|
|
111
|
+
"import": "import '@ds-mo/ui/dist/components/ds-modal.js';",
|
|
112
|
+
"example": "<ds-modal></ds-modal>"
|
|
113
|
+
},
|
|
114
|
+
"react": {
|
|
115
|
+
"import": "import { DsModal } from '@ds-mo/ui/react';",
|
|
116
|
+
"example": "<DsModal />"
|
|
117
|
+
},
|
|
118
|
+
"angular": {
|
|
119
|
+
"import": "import { DsModal } from '@ds-mo/ui/angular/ds-modal';",
|
|
120
|
+
"example": "<ds-modal></ds-modal>"
|
|
121
|
+
},
|
|
122
|
+
"peerDependencies": {
|
|
123
|
+
"required": [
|
|
124
|
+
"@ds-mo/tokens ^3.1.1",
|
|
125
|
+
"@ds-mo/icons ^6.2.0"
|
|
126
|
+
],
|
|
127
|
+
"frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
"dependencies": [
|
|
132
|
+
"@ds-mo/ui",
|
|
133
|
+
"@ds-mo/tokens"
|
|
134
|
+
],
|
|
135
|
+
"registryDependencies": [
|
|
136
|
+
"text"
|
|
137
|
+
],
|
|
138
|
+
"files": [
|
|
139
|
+
{
|
|
140
|
+
"path": "src/wc/components/Modal/Modal.css",
|
|
141
|
+
"content": ".modal-backdrop {\n background-color: var(--color-background-shade);\n backdrop-filter: blur(var(--effect-blur-sm));\n -webkit-backdrop-filter: blur(var(--effect-blur-sm));\n animation: modalBackdropIn var(--effect-motion-short-2) forwards;\n box-sizing: border-box;\n}\n\n.modal-backdrop--closing {\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.modal-dialog {\n display: flex;\n flex-direction: column;\n max-height: calc(100vh - 2 * var(--dimension-space-200));\n overflow: hidden;\n background-color: var(--color-background-primary);\n border-radius: var(--dimension-radius-300);\n box-shadow: var(--effect-elevation-elevated-floating);\n animation: modalDialogIn var(--effect-motion-short-2) forwards;\n outline: none;\n box-sizing: border-box;\n}\n\n.modal-backdrop--closing .modal-dialog {\n animation: modalDialogOut var(--effect-motion-short-3) forwards;\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-backdrop,\n .modal-dialog {\n animation: none;\n transform: none;\n }\n\n .modal-backdrop {\n opacity: 1;\n }\n\n .modal-backdrop--closing,\n .modal-backdrop--closing .modal-dialog {\n opacity: 0;\n }\n}\n\n.modal-header {\n padding: var(--dimension-space-200) var(--dimension-space-200) var(--dimension-space-100);\n flex-shrink: 0;\n}\n\n.modal-subtitle {\n margin-top: var(--dimension-space-100);\n}\n\n.modal-body {\n padding: var(--dimension-space-100) var(--dimension-space-200);\n overflow-y: auto;\n flex: 1;\n}\n\n.modal-footer {\n padding: var(--dimension-space-100) var(--dimension-space-200) var(--dimension-space-200);\n flex-shrink: 0;\n}\n\n.modal-footer:empty { display: none; }\n",
|
|
142
|
+
"type": "registry:ui"
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"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;\nconst activeModalStack: object[] = [];\nconst modalInertState = new WeakMap<HTMLElement, { count: number; wasInert: boolean }>();\n\nfunction makeInert(element: HTMLElement) {\n const state = modalInertState.get(element);\n if (state) {\n state.count += 1;\n return;\n }\n modalInertState.set(element, { count: 1, wasInert: element.inert });\n element.inert = true;\n}\n\nfunction releaseInert(element: HTMLElement) {\n const state = modalInertState.get(element);\n if (!state) return;\n state.count -= 1;\n if (state.count > 0) return;\n element.inert = state.wasInert;\n modalInertState.delete(element);\n}\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() subtitle: string | undefined;\n @Prop() modalWidth: ModalWidth | string = 'md';\n\n @State() private shouldRender: boolean = false;\n @State() private closing: boolean = false;\n\n @Event() dsClose!: EventEmitter<void>;\n\n private instanceId = ++modalIdCounter;\n private titleId = `ds-modal-title-${modalIdCounter}`;\n private subtitleId = `ds-modal-subtitle-${modalIdCounter}`;\n private previousFocus: HTMLElement | null = null;\n private escapeHandler: ((e: KeyboardEvent) => void) | null = null;\n private tabHandler: ((e: KeyboardEvent) => void) | null = null;\n private closeTimer: ReturnType<typeof setTimeout> | null = null;\n private inertedElements: HTMLElement[] = [];\n\n componentDidLoad() {\n if (this.open) this.onOpenChange(true);\n }\n\n disconnectedCallback() {\n this.teardownListeners();\n this.deactivateModal();\n }\n\n @Watch('open')\n onOpenChange(isOpen: boolean) {\n if (isOpen) {\n this.teardownListeners();\n this.shouldRender = true;\n this.closing = false;\n this.previousFocus = document.activeElement as HTMLElement | null;\n this.activateModal();\n requestAnimationFrame(() => {\n this.focusFirst();\n this.setupListeners();\n });\n } else if (this.shouldRender) {\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 focusFirst() {\n const dialog = this.el.querySelector('.modal-dialog') as HTMLElement | null;\n if (!dialog) return;\n const focusables = this.getFocusables(dialog);\n (focusables[0] ?? dialog).focus();\n }\n\n private getFocusables(root: HTMLElement): HTMLElement[] {\n return Array.from(root.querySelectorAll<HTMLElement>(FOCUSABLE_SEL)).filter(\n el => !el.hasAttribute('disabled') && el.getAttribute('aria-hidden') !== 'true'\n );\n }\n\n private setupListeners() {\n this.escapeHandler = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && this.isTopModal) { e.preventDefault(); this.close(); }\n };\n\n this.tabHandler = (e: KeyboardEvent) => {\n if (e.key !== 'Tab' || !this.isTopModal) return;\n const dialog = this.el.querySelector('.modal-dialog') as HTMLElement | null;\n if (!dialog) return;\n const focusables = this.getFocusables(dialog);\n if (!focusables.length) { e.preventDefault(); dialog.focus(); return; }\n const first = focusables[0];\n const last = focusables[focusables.length - 1];\n const active = document.activeElement;\n if (e.shiftKey) {\n if (active === first || active === dialog || !dialog.contains(active)) {\n e.preventDefault(); last.focus();\n }\n } else {\n if (active === last || !dialog.contains(active)) {\n e.preventDefault(); first.focus();\n }\n }\n };\n\n document.addEventListener('keydown', this.escapeHandler);\n document.addEventListener('keydown', this.tabHandler);\n }\n\n private teardownListeners() {\n if (this.escapeHandler) {\n document.removeEventListener('keydown', this.escapeHandler);\n this.escapeHandler = null;\n }\n if (this.tabHandler) {\n document.removeEventListener('keydown', this.tabHandler);\n this.tabHandler = null;\n }\n if (this.closeTimer) {\n clearTimeout(this.closeTimer);\n this.closeTimer = null;\n }\n }\n\n private close() {\n if (!this.open || this.closing || !this.isTopModal) return;\n this.dsClose.emit();\n this.open = false;\n }\n\n private handleBackdropMouseDown(e: MouseEvent) {\n if (e.target === e.currentTarget && this.isTopModal) this.close();\n }\n\n private get isTopModal(): boolean {\n return activeModalStack[activeModalStack.length - 1] === this;\n }\n\n /** Inert every sibling branch outside the modal without assuming it is a body child. */\n private activateModal() {\n if (!activeModalStack.includes(this)) activeModalStack.push(this);\n if (this.inertedElements.length) return;\n\n let branch: HTMLElement = this.el;\n let parent = branch.parentElement;\n while (parent) {\n for (const sibling of Array.from(parent.children)) {\n if (sibling === branch || !(sibling instanceof HTMLElement)) continue;\n makeInert(sibling);\n this.inertedElements.push(sibling);\n }\n if (parent === document.body) break;\n branch = parent;\n parent = parent.parentElement;\n }\n }\n\n private deactivateModal() {\n const stackIndex = activeModalStack.lastIndexOf(this);\n if (stackIndex >= 0) activeModalStack.splice(stackIndex, 1);\n for (const element of this.inertedElements) releaseInert(element);\n this.inertedElements = [];\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 this.shouldRender = false;\n this.closing = false;\n this.teardownListeners();\n this.deactivateModal();\n this.previousFocus?.focus?.();\n this.previousFocus = null;\n }\n\n render() {\n // Always render slots so scoped-mode slot distribution doesn't leak content.\n // The backdrop is hidden via CSS when not active.\n const hidden = !this.shouldRender && !this.closing;\n\n return (\n <Host style={{ display: 'contents' }}>\n <div\n class={{ 'modal-backdrop': true, 'modal-backdrop--closing': this.closing, 'modal-backdrop--hidden': hidden }}\n onMouseDown={(e: MouseEvent) => this.handleBackdropMouseDown(e)}\n style={{ position: 'fixed', inset: '0', zIndex: '9999', display: hidden ? 'none' : 'flex', alignItems: 'center', justifyContent: 'center', padding: 'var(--dimension-space-200)' }}\n >\n <div\n class={{ 'modal-dialog': true, 'modal-dialog--closing': this.closing }}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={this.titleId}\n aria-describedby={this.subtitle ? this.subtitleId : undefined}\n tabIndex={-1}\n style={{ width: `min(${this.resolvedWidth}, calc(100vw - 2 * var(--dimension-space-200)))` }}\n >\n <div class=\"modal-header\">\n <ds-text\n as=\"h2\"\n variant=\"text-title-small\"\n textId={this.titleId}\n >\n {this.heading}\n </ds-text>\n {this.subtitle && (\n <ds-text\n class=\"modal-subtitle\"\n as=\"div\"\n variant=\"text-body-medium\"\n color=\"secondary\"\n wrap=\"balance\"\n textId={this.subtitleId}\n >\n {this.subtitle}\n </ds-text>\n )}\n </div>\n <div class=\"modal-body\">\n <slot />\n </div>\n <div class=\"modal-footer\">\n <slot name=\"footer\" />\n </div>\n </div>\n </div>\n </Host>\n );\n }\n}\n",
|
|
147
|
+
"type": "registry:ui"
|
|
148
|
+
}
|
|
149
|
+
]
|
|
150
|
+
}
|