@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.
Files changed (184) hide show
  1. package/README.md +28 -3
  2. package/dist/.build-stamp +1 -1
  3. package/dist/agent-patterns.json +233 -0
  4. package/dist/agent.json +155 -174
  5. package/dist/angular/ds-menu.d.ts +6 -1
  6. package/dist/angular/ds-menu.js +7 -4
  7. package/dist/angular/ds-swatch-picker.d.ts +15 -0
  8. package/dist/angular/{ds-pagination.js → ds-swatch-picker.js} +15 -15
  9. package/dist/angular/index.d.ts +1 -1
  10. package/dist/angular/index.js +1 -2
  11. package/dist/angular/proxies.d.ts +1 -2
  12. package/dist/angular/proxies.js +1 -2
  13. package/dist/components/ds-app-shell.js +1 -1
  14. package/dist/components/ds-badge.js +1 -1
  15. package/dist/components/ds-banner.js +1 -1
  16. package/dist/components/ds-bar-nav.js +1 -1
  17. package/dist/components/ds-bar-nav.js.map +1 -1
  18. package/dist/components/ds-button-filled.js +1 -1
  19. package/dist/components/ds-button-unfilled.js +1 -1
  20. package/dist/components/ds-card-data-viz-donut.js +1 -1
  21. package/dist/components/ds-card-setting.js +1 -1
  22. package/dist/components/ds-card-shell-data-viz.js +1 -1
  23. package/dist/components/ds-card.js +1 -1
  24. package/dist/components/ds-chart-bar.js +1 -1
  25. package/dist/components/ds-chart-bar.js.map +1 -1
  26. package/dist/components/ds-chart-donut.js +1 -1
  27. package/dist/components/ds-chart-legend.js +1 -1
  28. package/dist/components/ds-chart-line.js +1 -1
  29. package/dist/components/ds-checkbox.js +1 -1
  30. package/dist/components/ds-chip.js +1 -1
  31. package/dist/components/ds-empty-state.js +1 -1
  32. package/dist/components/ds-field.js +1 -1
  33. package/dist/components/ds-input.js +1 -1
  34. package/dist/components/ds-menu.js +1 -1
  35. package/dist/components/ds-modal.js +1 -1
  36. package/dist/components/ds-panel-nav.js +1 -1
  37. package/dist/components/ds-panel-nav.js.map +1 -1
  38. package/dist/components/ds-panel-sub-nav.js +1 -1
  39. package/dist/components/ds-panel-tools.js +1 -1
  40. package/dist/components/ds-radio.js +1 -1
  41. package/dist/components/ds-select-multi.js +1 -1
  42. package/dist/components/ds-select-multi.js.map +1 -1
  43. package/dist/components/ds-select.js +1 -1
  44. package/dist/components/ds-select.js.map +1 -1
  45. package/dist/components/ds-shell-gradient-picker.js +1 -1
  46. package/dist/components/ds-shell-gradient-picker.js.map +1 -1
  47. package/dist/components/ds-shell-gradient-swatch.js +1 -1
  48. package/dist/components/ds-shell-gradient-swatch.js.map +1 -1
  49. package/dist/components/ds-skeleton.js +1 -1
  50. package/dist/components/ds-skeleton.js.map +1 -1
  51. package/dist/components/ds-slider.js +1 -1
  52. package/dist/components/ds-slider.js.map +1 -1
  53. package/dist/components/{ds-pagination.d.ts → ds-swatch-picker.d.ts} +4 -4
  54. package/dist/components/ds-swatch-picker.js +2 -0
  55. package/dist/components/ds-swatch-picker.js.map +1 -0
  56. package/dist/components/ds-tab-group.js +1 -1
  57. package/dist/components/ds-tag.js +1 -1
  58. package/dist/components/ds-text.js +1 -1
  59. package/dist/components/ds-tooltip-data-viz.js +1 -1
  60. package/dist/components/ds-tooltip.js +1 -1
  61. package/dist/components/{p-CysJosvS.js → p-B-zhgAvL.js} +2 -2
  62. package/dist/components/{p-C21sIElQ.js → p-BLzivrE3.js} +2 -2
  63. package/dist/components/{p-DAuadHYu.js → p-BbV68F23.js} +2 -2
  64. package/dist/components/{p-_y99wWNU.js → p-C7RObioO.js} +2 -2
  65. package/dist/components/{p-DxQ4gzbA.js → p-CZ9wemOA.js} +2 -2
  66. package/dist/components/{p-BKA6houZ.js → p-CbS_WWs2.js} +2 -2
  67. package/dist/components/p-D1HFMTdC.js +2 -0
  68. package/dist/components/p-D1HFMTdC.js.map +1 -0
  69. package/dist/components/{p-BGcSrHKl.js → p-DT2MK8Kt.js} +2 -2
  70. package/dist/components/{p-DV23KMU-.js → p-DyZj3k9S.js} +2 -2
  71. package/dist/components/p-HBIxvS5C.js +2 -0
  72. package/dist/components/p-HBIxvS5C.js.map +1 -0
  73. package/dist/components/{p-D6hvtWDk.js → p-KRGBGTu5.js} +2 -2
  74. package/dist/components/p-l6PmnW_P.js +2 -0
  75. package/dist/components/p-l6PmnW_P.js.map +1 -0
  76. package/dist/components/p-tQ1QHeOE.js +2 -0
  77. package/dist/components/p-tQ1QHeOE.js.map +1 -0
  78. package/dist/docs/components.json +546 -1024
  79. package/dist/lib/components/Menu/menu-placement.d.ts +6 -3
  80. package/dist/lib/shell/index.d.ts +2 -2
  81. package/dist/lib/shell/index.js +40 -39
  82. package/dist/lib/shell/index.js.map +3 -3
  83. package/dist/lib/shell/shell-gradient-presets.d.ts +15 -0
  84. package/dist/lib/utils/choice-popup-alignment.d.ts +20 -0
  85. package/dist/lib/utils/index.d.ts +2 -0
  86. package/dist/lib/utils/index.js +16 -0
  87. package/dist/lib/utils/index.js.map +3 -3
  88. package/dist/mcp/cli.js +31409 -0
  89. package/dist/mcp-data/registry/app-shell.json +210 -0
  90. package/dist/mcp-data/registry/badge.json +289 -0
  91. package/dist/mcp-data/registry/banner.json +206 -0
  92. package/dist/mcp-data/registry/bar-nav.json +341 -0
  93. package/dist/mcp-data/registry/button-filled.json +386 -0
  94. package/dist/mcp-data/registry/button-unfilled.json +537 -0
  95. package/dist/mcp-data/registry/card-data-viz-donut.json +143 -0
  96. package/dist/mcp-data/registry/card-setting.json +194 -0
  97. package/dist/mcp-data/registry/card-shell-data-viz.json +173 -0
  98. package/dist/mcp-data/registry/card.json +126 -0
  99. package/dist/mcp-data/registry/chart-bar.json +121 -0
  100. package/dist/mcp-data/registry/chart-donut.json +257 -0
  101. package/dist/mcp-data/registry/chart-legend.json +209 -0
  102. package/dist/mcp-data/registry/chart-line.json +153 -0
  103. package/dist/mcp-data/registry/checkbox.json +361 -0
  104. package/dist/mcp-data/registry/chip.json +276 -0
  105. package/dist/mcp-data/registry/divider.json +225 -0
  106. package/dist/mcp-data/registry/empty-state.json +197 -0
  107. package/dist/mcp-data/registry/field.json +222 -0
  108. package/dist/mcp-data/registry/icon.json +242 -0
  109. package/dist/mcp-data/registry/input.json +597 -0
  110. package/dist/mcp-data/registry/loader.json +192 -0
  111. package/dist/mcp-data/registry/menu.json +459 -0
  112. package/dist/mcp-data/registry/modal.json +150 -0
  113. package/dist/mcp-data/registry/panel-nav.json +622 -0
  114. package/dist/mcp-data/registry/panel-sub-nav.json +249 -0
  115. package/dist/mcp-data/registry/panel-tools.json +374 -0
  116. package/dist/mcp-data/registry/radio.json +374 -0
  117. package/dist/mcp-data/registry/registry.json +5389 -0
  118. package/dist/mcp-data/registry/select-multi.json +755 -0
  119. package/dist/mcp-data/registry/select.json +740 -0
  120. package/dist/mcp-data/registry/shell-gradient-picker.json +195 -0
  121. package/dist/mcp-data/registry/shell-gradient-swatch.json +215 -0
  122. package/dist/mcp-data/registry/skeleton.json +282 -0
  123. package/dist/mcp-data/registry/slider.json +657 -0
  124. package/dist/mcp-data/registry/swatch-picker.json +231 -0
  125. package/dist/mcp-data/registry/switch.json +361 -0
  126. package/dist/mcp-data/registry/tab-group.json +255 -0
  127. package/dist/mcp-data/registry/tag.json +352 -0
  128. package/dist/mcp-data/registry/text.json +371 -0
  129. package/dist/mcp-data/registry/tooltip-data-viz.json +158 -0
  130. package/dist/mcp-data/registry/tooltip.json +311 -0
  131. package/dist/react/components.d.ts +1 -2
  132. package/dist/react/components.js +1 -2
  133. package/dist/react/ds-menu.d.ts +1 -0
  134. package/dist/react/ds-menu.js +2 -1
  135. package/dist/react/ds-swatch-picker.d.ts +12 -0
  136. package/dist/react/ds-swatch-picker.js +12 -0
  137. package/dist/types/components/Menu/Menu.d.ts +7 -1
  138. package/dist/types/components/Menu/menu-placement.d.ts +6 -3
  139. package/dist/types/components/Menu/menu-types.d.ts +14 -1
  140. package/dist/types/components/ShellGradientPicker/ShellGradientPicker.d.ts +2 -5
  141. package/dist/types/components/ShellGradientSwatch/ShellGradientSwatch.d.ts +1 -0
  142. package/dist/types/components/SwatchPicker/SwatchPicker.d.ts +21 -0
  143. package/dist/types/components/SwatchPicker/swatch-picker-types.d.ts +26 -0
  144. package/dist/types/components.d.ts +117 -237
  145. package/dist/types/shell/index.d.ts +2 -2
  146. package/dist/types/shell/shell-gradient-presets.d.ts +15 -0
  147. package/dist/types/utils/choice-popup-alignment.d.ts +21 -0
  148. package/dist/types/utils/index.d.ts +2 -0
  149. package/package.json +6 -2
  150. package/dist/angular/ds-pagination.d.ts +0 -15
  151. package/dist/angular/ds-table.d.ts +0 -33
  152. package/dist/angular/ds-table.js +0 -48
  153. package/dist/components/ds-pagination.js +0 -2
  154. package/dist/components/ds-pagination.js.map +0 -1
  155. package/dist/components/ds-table.d.ts +0 -11
  156. package/dist/components/ds-table.js +0 -2
  157. package/dist/components/ds-table.js.map +0 -1
  158. package/dist/components/p-CKV6rOGa.js +0 -2
  159. package/dist/components/p-CKV6rOGa.js.map +0 -1
  160. package/dist/components/p-CSTr2132.js +0 -2
  161. package/dist/components/p-CSTr2132.js.map +0 -1
  162. package/dist/components/p-C_cu-HIT.js +0 -2
  163. package/dist/components/p-C_cu-HIT.js.map +0 -1
  164. package/dist/components/p-D0B9uDTP.js +0 -2
  165. package/dist/components/p-D0B9uDTP.js.map +0 -1
  166. package/dist/components/p-DhxWfdX0.js +0 -2
  167. package/dist/components/p-DhxWfdX0.js.map +0 -1
  168. package/dist/components/p-Pip-LNhl.js +0 -2
  169. package/dist/components/p-Pip-LNhl.js.map +0 -1
  170. package/dist/react/ds-pagination.d.ts +0 -12
  171. package/dist/react/ds-pagination.js +0 -12
  172. package/dist/react/ds-table.d.ts +0 -21
  173. package/dist/react/ds-table.js +0 -16
  174. package/dist/types/components/Pagination/Pagination.d.ts +0 -14
  175. package/dist/types/components/Table/Table.d.ts +0 -56
  176. /package/dist/components/{p-CysJosvS.js.map → p-B-zhgAvL.js.map} +0 -0
  177. /package/dist/components/{p-C21sIElQ.js.map → p-BLzivrE3.js.map} +0 -0
  178. /package/dist/components/{p-DAuadHYu.js.map → p-BbV68F23.js.map} +0 -0
  179. /package/dist/components/{p-_y99wWNU.js.map → p-C7RObioO.js.map} +0 -0
  180. /package/dist/components/{p-DxQ4gzbA.js.map → p-CZ9wemOA.js.map} +0 -0
  181. /package/dist/components/{p-BKA6houZ.js.map → p-CbS_WWs2.js.map} +0 -0
  182. /package/dist/components/{p-BGcSrHKl.js.map → p-DT2MK8Kt.js.map} +0 -0
  183. /package/dist/components/{p-DV23KMU-.js.map → p-DyZj3k9S.js.map} +0 -0
  184. /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
+ }