@magic-spells/dialog-panel 0.2.3 → 0.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/README.md CHANGED
@@ -50,7 +50,7 @@ Or include directly in your HTML:
50
50
  aria-hidden="true">
51
51
  <dialog-overlay></dialog-overlay>
52
52
  <dialog-content>
53
- <button aria-label="Close dialog" data-action="hide-dialog">
53
+ <button aria-label="Close dialog" data-action-hide-dialog>
54
54
  &times;
55
55
  </button>
56
56
  <div>
@@ -75,7 +75,7 @@ Or include directly in your HTML:
75
75
  - The dialog is initially hidden (`aria-hidden="true"`)
76
76
  - Clicking the button triggers the `show()` method, making the dialog visible
77
77
  - When opened, sets `aria-modal="true"` to indicate modal behavior
78
- - Clicking the overlay or a close button (`data-action="hide-dialog"`) triggers the `hide()` method
78
+ - Clicking the overlay or a close button (`data-action-hide-dialog`) triggers the `hide()` method
79
79
  - Keyboard focus is automatically trapped within the dialog when open
80
80
  - Pressing ESC closes the dialog
81
81
  - Focus returns to the trigger button when closed
@@ -10,7 +10,6 @@ require('@magic-spells/focus-trap');
10
10
  */
11
11
  class DialogPanel extends HTMLElement {
12
12
  #handleTransitionEnd;
13
- #scrollPosition = 0;
14
13
 
15
14
  /**
16
15
  * Clean up event listeners when component is removed from DOM
@@ -23,38 +22,6 @@ class DialogPanel extends HTMLElement {
23
22
  _.#handleTransitionEnd
24
23
  );
25
24
  }
26
-
27
- // Ensure body scroll is restored if component is removed while open
28
- document.body.classList.remove('overflow-hidden');
29
- this.#restoreScroll();
30
- }
31
-
32
- /**
33
- * Saves current scroll position and locks body scrolling
34
- * @private
35
- */
36
- #lockScroll() {
37
- const _ = this;
38
- // Save current scroll position
39
- _.#scrollPosition = window.pageYOffset;
40
-
41
- // Apply fixed position to body
42
- document.body.classList.add('overflow-hidden');
43
- document.body.style.top = `-${_.#scrollPosition}px`;
44
- }
45
-
46
- /**
47
- * Restores scroll position when dialog is closed
48
- * @private
49
- */
50
- #restoreScroll() {
51
- const _ = this;
52
- // Remove fixed positioning
53
- document.body.classList.remove('overflow-hidden');
54
- document.body.style.removeProperty('top');
55
-
56
- // Restore scroll position
57
- window.scrollTo(0, _.#scrollPosition);
58
25
  }
59
26
  /**
60
27
  * Initializes the dialog panel, sets up focus trap and overlay
@@ -68,7 +35,6 @@ class DialogPanel extends HTMLElement {
68
35
  _.setAttribute('aria-hidden', 'true');
69
36
 
70
37
  _.contentPanel = _.querySelector('dialog-content');
71
- _.focusTrap = document.createElement('focus-trap');
72
38
  _.triggerEl = null;
73
39
 
74
40
  // Create a handler for transition end events
@@ -89,6 +55,22 @@ class DialogPanel extends HTMLElement {
89
55
  }
90
56
  };
91
57
 
58
+ // Set up focus-trap inside dialog-content
59
+ // Check if focus-trap already exists
60
+ _.focusTrap = _.contentPanel.querySelector('focus-trap');
61
+ if (!_.focusTrap) {
62
+ _.focusTrap = document.createElement('focus-trap');
63
+
64
+ // Move all existing dialog-content children into focus-trap
65
+ const existingContent = Array.from(_.contentPanel.childNodes);
66
+ existingContent.forEach((child) =>
67
+ _.focusTrap.appendChild(child)
68
+ );
69
+
70
+ // Insert focus-trap inside dialog-content
71
+ _.contentPanel.appendChild(_.focusTrap);
72
+ }
73
+
92
74
  // Ensure we have labelledby and describedby references
93
75
  if (!_.getAttribute('aria-labelledby')) {
94
76
  const heading = _.querySelector('h1, h2, h3');
@@ -100,14 +82,6 @@ class DialogPanel extends HTMLElement {
100
82
  }
101
83
  }
102
84
 
103
- _.contentPanel.parentNode.insertBefore(
104
- _.focusTrap,
105
- _.contentPanel
106
- );
107
- _.focusTrap.appendChild(_.contentPanel);
108
-
109
- _.focusTrap.setupTrap();
110
-
111
85
  // Add modal overlay
112
86
  _.prepend(document.createElement('dialog-overlay'));
113
87
  _.#bindUI();
@@ -135,7 +109,7 @@ class DialogPanel extends HTMLElement {
135
109
 
136
110
  // Handle close buttons
137
111
  _.addEventListener('click', (e) => {
138
- if (!e.target.closest('[data-action="hide-dialog"]')) return;
112
+ if (!e.target.closest('[data-action-hide-dialog]')) return;
139
113
  _.hide();
140
114
  });
141
115
 
@@ -181,6 +155,9 @@ class DialogPanel extends HTMLElement {
181
155
  // If event was canceled (preventDefault was called), don't show the dialog
182
156
  if (!showAllowed) return false;
183
157
 
158
+ // Add open attribute for CSS :has() selector
159
+ _.setAttribute('open', '');
160
+
184
161
  // Remove the hidden class first to ensure content is rendered
185
162
  _.contentPanel.classList.remove('hidden');
186
163
 
@@ -188,13 +165,12 @@ class DialogPanel extends HTMLElement {
188
165
  requestAnimationFrame(() => {
189
166
  // Update ARIA states
190
167
  _.setAttribute('aria-hidden', 'false');
168
+
169
+ // set trigger element to expanded: true
191
170
  if (_.triggerEl) {
192
171
  _.triggerEl.setAttribute('aria-expanded', 'true');
193
172
  }
194
173
 
195
- // Lock body scrolling and save scroll position
196
- _.#lockScroll();
197
-
198
174
  // Focus management
199
175
  const firstFocusable = _.querySelector(
200
176
  'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
@@ -213,8 +189,6 @@ class DialogPanel extends HTMLElement {
213
189
  })
214
190
  );
215
191
  });
216
-
217
- return true;
218
192
  }
219
193
 
220
194
  /**
@@ -239,20 +213,30 @@ class DialogPanel extends HTMLElement {
239
213
  // If event was canceled (preventDefault was called), don't hide the dialog
240
214
  if (!hideAllowed) return false;
241
215
 
242
- // Restore body scroll and scroll position
243
- _.#restoreScroll();
216
+ // ensure focus is moved out of the dialog
217
+ const activeElement = document.activeElement;
218
+ if (activeElement && _.contains(activeElement)) {
219
+ // Blur the currently focused element if it's inside the dialog
220
+ activeElement.blur();
221
+ }
244
222
 
245
- // Update ARIA states
223
+ // Update ARIA states and restore focus
246
224
  if (_.triggerEl) {
247
225
  // remove focus from modal panel first
248
226
  _.triggerEl.focus();
249
227
  // mark trigger as no longer expanded
250
228
  _.triggerEl.setAttribute('aria-expanded', 'false');
229
+ } else {
230
+ // Move focus to body to ensure it's not trapped
231
+ document.body.focus();
251
232
  }
252
233
 
253
234
  // Set aria-hidden to start transition
254
235
  // The transitionend event handler will add display:none when complete
255
236
  _.setAttribute('aria-hidden', 'true');
237
+ // Remove open attribute for CSS :has() selector
238
+
239
+ _.removeAttribute('open');
256
240
 
257
241
  // Dispatch hide event - dialog is now starting to hide
258
242
  _.dispatchEvent(
@@ -273,8 +257,7 @@ class DialogPanel extends HTMLElement {
273
257
  class DialogOverlay extends HTMLElement {
274
258
  constructor() {
275
259
  super();
276
- this.setAttribute('tabindex', '-1'); // Changed to -1 as it shouldn't be focusable
277
- this.setAttribute('aria-hidden', 'true');
260
+ this.setAttribute('tabindex', '-1');
278
261
  this.dialogPanel = this.closest('dialog-panel');
279
262
  this.#bindUI();
280
263
  }
@@ -293,7 +276,7 @@ class DialogOverlay extends HTMLElement {
293
276
  class DialogContent extends HTMLElement {
294
277
  constructor() {
295
278
  super();
296
- this.setAttribute('role', 'document'); // Optional: helps with document structure
279
+ this.setAttribute('role', 'document');
297
280
  }
298
281
  }
299
282
 
@@ -1 +1 @@
1
- {"version":3,"file":"dialog-panel.cjs.js","sources":["../src/dialog-panel.js"],"sourcesContent":["import './index.scss';\nimport '@magic-spells/focus-trap';\n\n/**\n * Custom element that creates an accessible modal dialog panel with focus management\n * @extends HTMLElement\n */\nclass DialogPanel extends HTMLElement {\n\t#handleTransitionEnd;\n\t#scrollPosition = 0;\n\n\t/**\n\t * Clean up event listeners when component is removed from DOM\n\t */\n\tdisconnectedCallback() {\n\t\tconst _ = this;\n\t\tif (_.contentPanel) {\n\t\t\t_.contentPanel.removeEventListener(\n\t\t\t\t'transitionend',\n\t\t\t\t_.#handleTransitionEnd\n\t\t\t);\n\t\t}\n\n\t\t// Ensure body scroll is restored if component is removed while open\n\t\tdocument.body.classList.remove('overflow-hidden');\n\t\tthis.#restoreScroll();\n\t}\n\n\t/**\n\t * Saves current scroll position and locks body scrolling\n\t * @private\n\t */\n\t#lockScroll() {\n\t\tconst _ = this;\n\t\t// Save current scroll position\n\t\t_.#scrollPosition = window.pageYOffset;\n\n\t\t// Apply fixed position to body\n\t\tdocument.body.classList.add('overflow-hidden');\n\t\tdocument.body.style.top = `-${_.#scrollPosition}px`;\n\t}\n\n\t/**\n\t * Restores scroll position when dialog is closed\n\t * @private\n\t */\n\t#restoreScroll() {\n\t\tconst _ = this;\n\t\t// Remove fixed positioning\n\t\tdocument.body.classList.remove('overflow-hidden');\n\t\tdocument.body.style.removeProperty('top');\n\n\t\t// Restore scroll position\n\t\twindow.scrollTo(0, _.#scrollPosition);\n\t}\n\t/**\n\t * Initializes the dialog panel, sets up focus trap and overlay\n\t */\n\tconstructor() {\n\t\tsuper();\n\t\tconst _ = this;\n\t\t_.id = _.getAttribute('id');\n\t\t_.setAttribute('role', 'dialog');\n\t\t_.setAttribute('aria-modal', 'true');\n\t\t_.setAttribute('aria-hidden', 'true');\n\n\t\t_.contentPanel = _.querySelector('dialog-content');\n\t\t_.focusTrap = document.createElement('focus-trap');\n\t\t_.triggerEl = null;\n\n\t\t// Create a handler for transition end events\n\t\t_.#handleTransitionEnd = (e) => {\n\t\t\tif (\n\t\t\t\te.propertyName === 'opacity' &&\n\t\t\t\t_.getAttribute('aria-hidden') === 'true'\n\t\t\t) {\n\t\t\t\t_.contentPanel.classList.add('hidden');\n\n\t\t\t\t// Dispatch afterHide event - dialog has completed its transition\n\t\t\t\t_.dispatchEvent(\n\t\t\t\t\tnew CustomEvent('afterHide', {\n\t\t\t\t\t\tbubbles: true,\n\t\t\t\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t}\n\t\t};\n\n\t\t// Ensure we have labelledby and describedby references\n\t\tif (!_.getAttribute('aria-labelledby')) {\n\t\t\tconst heading = _.querySelector('h1, h2, h3');\n\t\t\tif (heading && !heading.id) {\n\t\t\t\theading.id = `${_.id}-title`;\n\t\t\t}\n\t\t\tif (heading?.id) {\n\t\t\t\t_.setAttribute('aria-labelledby', heading.id);\n\t\t\t}\n\t\t}\n\n\t\t_.contentPanel.parentNode.insertBefore(\n\t\t\t_.focusTrap,\n\t\t\t_.contentPanel\n\t\t);\n\t\t_.focusTrap.appendChild(_.contentPanel);\n\n\t\t_.focusTrap.setupTrap();\n\n\t\t// Add modal overlay\n\t\t_.prepend(document.createElement('dialog-overlay'));\n\t\t_.#bindUI();\n\t\t_.#bindKeyboard();\n\t}\n\n\t/**\n\t * Binds click events for showing and hiding the dialog\n\t * @private\n\t */\n\t#bindUI() {\n\t\tconst _ = this;\n\n\t\t// Handle trigger buttons\n\t\tdocument.addEventListener('click', (e) => {\n\t\t\tconst trigger = e.target.closest(`[aria-controls=\"${_.id}\"]`);\n\t\t\tif (!trigger) return;\n\n\t\t\tif (trigger.getAttribute('data-prevent-default') === 'true') {\n\t\t\t\te.preventDefault();\n\t\t\t}\n\n\t\t\t_.show(trigger);\n\t\t});\n\n\t\t// Handle close buttons\n\t\t_.addEventListener('click', (e) => {\n\t\t\tif (!e.target.closest('[data-action=\"hide-dialog\"]')) return;\n\t\t\t_.hide();\n\t\t});\n\n\t\t// Add transition end listener\n\t\t_.contentPanel.addEventListener(\n\t\t\t'transitionend',\n\t\t\t_.#handleTransitionEnd\n\t\t);\n\t}\n\n\t/**\n\t * Binds keyboard events for accessibility\n\t * @private\n\t */\n\t#bindKeyboard() {\n\t\tthis.addEventListener('keydown', (e) => {\n\t\t\tif (e.key === 'Escape') {\n\t\t\t\tthis.hide();\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Shows the dialog and traps focus within it\n\t * @param {HTMLElement} [triggerEl=null] - The element that triggered the dialog\n\t * @fires DialogPanel#beforeShow - Fired before the dialog starts to show\n\t * @fires DialogPanel#show - Fired when the dialog has been shown\n\t * @returns {boolean} False if the show was prevented by a beforeShow event handler\n\t */\n\tshow(triggerEl = null) {\n\t\tconst _ = this;\n\t\t_.triggerEl = triggerEl || false;\n\n\t\t// Dispatch beforeShow event - allows preventing the dialog from opening\n\t\tconst beforeShowEvent = new CustomEvent('beforeShow', {\n\t\t\tbubbles: true,\n\t\t\tcancelable: true,\n\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t});\n\n\t\tconst showAllowed = _.dispatchEvent(beforeShowEvent);\n\n\t\t// If event was canceled (preventDefault was called), don't show the dialog\n\t\tif (!showAllowed) return false;\n\n\t\t// Remove the hidden class first to ensure content is rendered\n\t\t_.contentPanel.classList.remove('hidden');\n\n\t\t// Give the browser a moment to process before starting animation\n\t\trequestAnimationFrame(() => {\n\t\t\t// Update ARIA states\n\t\t\t_.setAttribute('aria-hidden', 'false');\n\t\t\tif (_.triggerEl) {\n\t\t\t\t_.triggerEl.setAttribute('aria-expanded', 'true');\n\t\t\t}\n\n\t\t\t// Lock body scrolling and save scroll position\n\t\t\t_.#lockScroll();\n\n\t\t\t// Focus management\n\t\t\tconst firstFocusable = _.querySelector(\n\t\t\t\t'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n\t\t\t);\n\t\t\tif (firstFocusable) {\n\t\t\t\trequestAnimationFrame(() => {\n\t\t\t\t\tfirstFocusable.focus();\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Dispatch show event - dialog is now visible\n\t\t\t_.dispatchEvent(\n\t\t\t\tnew CustomEvent('show', {\n\t\t\t\t\tbubbles: true,\n\t\t\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t\t\t})\n\t\t\t);\n\t\t});\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Hides the dialog and restores focus\n\t * @fires DialogPanel#beforeHide - Fired before the dialog starts to hide\n\t * @fires DialogPanel#hide - Fired when the dialog has started hiding (transition begins)\n\t * @fires DialogPanel#afterHide - Fired when the dialog has completed its hide transition\n\t * @returns {boolean} False if the hide was prevented by a beforeHide event handler\n\t */\n\thide() {\n\t\tconst _ = this;\n\n\t\t// Dispatch beforeHide event - allows preventing the dialog from closing\n\t\tconst beforeHideEvent = new CustomEvent('beforeHide', {\n\t\t\tbubbles: true,\n\t\t\tcancelable: true,\n\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t});\n\n\t\tconst hideAllowed = _.dispatchEvent(beforeHideEvent);\n\n\t\t// If event was canceled (preventDefault was called), don't hide the dialog\n\t\tif (!hideAllowed) return false;\n\n\t\t// Restore body scroll and scroll position\n\t\t_.#restoreScroll();\n\n\t\t// Update ARIA states\n\t\tif (_.triggerEl) {\n\t\t\t// remove focus from modal panel first\n\t\t\t_.triggerEl.focus();\n\t\t\t// mark trigger as no longer expanded\n\t\t\t_.triggerEl.setAttribute('aria-expanded', 'false');\n\t\t}\n\n\t\t// Set aria-hidden to start transition\n\t\t// The transitionend event handler will add display:none when complete\n\t\t_.setAttribute('aria-hidden', 'true');\n\n\t\t// Dispatch hide event - dialog is now starting to hide\n\t\t_.dispatchEvent(\n\t\t\tnew CustomEvent('hide', {\n\t\t\t\tbubbles: true,\n\t\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t\t})\n\t\t);\n\n\t\treturn true;\n\t}\n}\n\n/**\n * Custom element that creates a clickable overlay for the dialog\n * @extends HTMLElement\n */\nclass DialogOverlay extends HTMLElement {\n\tconstructor() {\n\t\tsuper();\n\t\tthis.setAttribute('tabindex', '-1'); // Changed to -1 as it shouldn't be focusable\n\t\tthis.setAttribute('aria-hidden', 'true');\n\t\tthis.dialogPanel = this.closest('dialog-panel');\n\t\tthis.#bindUI();\n\t}\n\n\t#bindUI() {\n\t\tthis.addEventListener('click', () => {\n\t\t\tthis.dialogPanel.hide();\n\t\t});\n\t}\n}\n\n/**\n * Custom element that wraps the content of the dialog\n * @extends HTMLElement\n */\nclass DialogContent extends HTMLElement {\n\tconstructor() {\n\t\tsuper();\n\t\tthis.setAttribute('role', 'document'); // Optional: helps with document structure\n\t}\n}\n\nif (!customElements.get('dialog-panel')) {\n\tcustomElements.define('dialog-panel', DialogPanel);\n}\nif (!customElements.get('dialog-overlay')) {\n\tcustomElements.define('dialog-overlay', DialogOverlay);\n}\nif (!customElements.get('dialog-content')) {\n\tcustomElements.define('dialog-content', DialogContent);\n}\n\nexport { DialogPanel, DialogOverlay, DialogContent };\nexport default DialogPanel;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dialog-panel.cjs.js","sources":["../src/dialog-panel.js"],"sourcesContent":["import './index.scss';\nimport '@magic-spells/focus-trap';\n\n/**\n * Custom element that creates an accessible modal dialog panel with focus management\n * @extends HTMLElement\n */\nclass DialogPanel extends HTMLElement {\n\t#handleTransitionEnd;\n\n\t/**\n\t * Clean up event listeners when component is removed from DOM\n\t */\n\tdisconnectedCallback() {\n\t\tconst _ = this;\n\t\tif (_.contentPanel) {\n\t\t\t_.contentPanel.removeEventListener(\n\t\t\t\t'transitionend',\n\t\t\t\t_.#handleTransitionEnd\n\t\t\t);\n\t\t}\n\t}\n\t/**\n\t * Initializes the dialog panel, sets up focus trap and overlay\n\t */\n\tconstructor() {\n\t\tsuper();\n\t\tconst _ = this;\n\t\t_.id = _.getAttribute('id');\n\t\t_.setAttribute('role', 'dialog');\n\t\t_.setAttribute('aria-modal', 'true');\n\t\t_.setAttribute('aria-hidden', 'true');\n\n\t\t_.contentPanel = _.querySelector('dialog-content');\n\t\t_.triggerEl = null;\n\n\t\t// Create a handler for transition end events\n\t\t_.#handleTransitionEnd = (e) => {\n\t\t\tif (\n\t\t\t\te.propertyName === 'opacity' &&\n\t\t\t\t_.getAttribute('aria-hidden') === 'true'\n\t\t\t) {\n\t\t\t\t_.contentPanel.classList.add('hidden');\n\n\t\t\t\t// Dispatch afterHide event - dialog has completed its transition\n\t\t\t\t_.dispatchEvent(\n\t\t\t\t\tnew CustomEvent('afterHide', {\n\t\t\t\t\t\tbubbles: true,\n\t\t\t\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t}\n\t\t};\n\n\t\t// Set up focus-trap inside dialog-content\n\t\t// Check if focus-trap already exists\n\t\t_.focusTrap = _.contentPanel.querySelector('focus-trap');\n\t\tif (!_.focusTrap) {\n\t\t\t_.focusTrap = document.createElement('focus-trap');\n\n\t\t\t// Move all existing dialog-content children into focus-trap\n\t\t\tconst existingContent = Array.from(_.contentPanel.childNodes);\n\t\t\texistingContent.forEach((child) =>\n\t\t\t\t_.focusTrap.appendChild(child)\n\t\t\t);\n\n\t\t\t// Insert focus-trap inside dialog-content\n\t\t\t_.contentPanel.appendChild(_.focusTrap);\n\t\t}\n\n\t\t// Ensure we have labelledby and describedby references\n\t\tif (!_.getAttribute('aria-labelledby')) {\n\t\t\tconst heading = _.querySelector('h1, h2, h3');\n\t\t\tif (heading && !heading.id) {\n\t\t\t\theading.id = `${_.id}-title`;\n\t\t\t}\n\t\t\tif (heading?.id) {\n\t\t\t\t_.setAttribute('aria-labelledby', heading.id);\n\t\t\t}\n\t\t}\n\n\t\t// Add modal overlay\n\t\t_.prepend(document.createElement('dialog-overlay'));\n\t\t_.#bindUI();\n\t\t_.#bindKeyboard();\n\t}\n\n\t/**\n\t * Binds click events for showing and hiding the dialog\n\t * @private\n\t */\n\t#bindUI() {\n\t\tconst _ = this;\n\n\t\t// Handle trigger buttons\n\t\tdocument.addEventListener('click', (e) => {\n\t\t\tconst trigger = e.target.closest(`[aria-controls=\"${_.id}\"]`);\n\t\t\tif (!trigger) return;\n\n\t\t\tif (trigger.getAttribute('data-prevent-default') === 'true') {\n\t\t\t\te.preventDefault();\n\t\t\t}\n\n\t\t\t_.show(trigger);\n\t\t});\n\n\t\t// Handle close buttons\n\t\t_.addEventListener('click', (e) => {\n\t\t\tif (!e.target.closest('[data-action-hide-dialog]')) return;\n\t\t\t_.hide();\n\t\t});\n\n\t\t// Add transition end listener\n\t\t_.contentPanel.addEventListener(\n\t\t\t'transitionend',\n\t\t\t_.#handleTransitionEnd\n\t\t);\n\t}\n\n\t/**\n\t * Binds keyboard events for accessibility\n\t * @private\n\t */\n\t#bindKeyboard() {\n\t\tthis.addEventListener('keydown', (e) => {\n\t\t\tif (e.key === 'Escape') {\n\t\t\t\tthis.hide();\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Shows the dialog and traps focus within it\n\t * @param {HTMLElement} [triggerEl=null] - The element that triggered the dialog\n\t * @fires DialogPanel#beforeShow - Fired before the dialog starts to show\n\t * @fires DialogPanel#show - Fired when the dialog has been shown\n\t * @returns {boolean} False if the show was prevented by a beforeShow event handler\n\t */\n\tshow(triggerEl = null) {\n\t\tconst _ = this;\n\t\t_.triggerEl = triggerEl || false;\n\n\t\t// Dispatch beforeShow event - allows preventing the dialog from opening\n\t\tconst beforeShowEvent = new CustomEvent('beforeShow', {\n\t\t\tbubbles: true,\n\t\t\tcancelable: true,\n\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t});\n\n\t\tconst showAllowed = _.dispatchEvent(beforeShowEvent);\n\n\t\t// If event was canceled (preventDefault was called), don't show the dialog\n\t\tif (!showAllowed) return false;\n\n\t\t// Add open attribute for CSS :has() selector\n\t\t_.setAttribute('open', '');\n\n\t\t// Remove the hidden class first to ensure content is rendered\n\t\t_.contentPanel.classList.remove('hidden');\n\n\t\t// Give the browser a moment to process before starting animation\n\t\trequestAnimationFrame(() => {\n\t\t\t// Update ARIA states\n\t\t\t_.setAttribute('aria-hidden', 'false');\n\n\t\t\t// set trigger element to expanded: true\n\t\t\tif (_.triggerEl) {\n\t\t\t\t_.triggerEl.setAttribute('aria-expanded', 'true');\n\t\t\t}\n\n\t\t\t// Focus management\n\t\t\tconst firstFocusable = _.querySelector(\n\t\t\t\t'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n\t\t\t);\n\t\t\tif (firstFocusable) {\n\t\t\t\trequestAnimationFrame(() => {\n\t\t\t\t\tfirstFocusable.focus();\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Dispatch show event - dialog is now visible\n\t\t\t_.dispatchEvent(\n\t\t\t\tnew CustomEvent('show', {\n\t\t\t\t\tbubbles: true,\n\t\t\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t\t\t})\n\t\t\t);\n\t\t});\n\t}\n\n\t/**\n\t * Hides the dialog and restores focus\n\t * @fires DialogPanel#beforeHide - Fired before the dialog starts to hide\n\t * @fires DialogPanel#hide - Fired when the dialog has started hiding (transition begins)\n\t * @fires DialogPanel#afterHide - Fired when the dialog has completed its hide transition\n\t * @returns {boolean} False if the hide was prevented by a beforeHide event handler\n\t */\n\thide() {\n\t\tconst _ = this;\n\n\t\t// Dispatch beforeHide event - allows preventing the dialog from closing\n\t\tconst beforeHideEvent = new CustomEvent('beforeHide', {\n\t\t\tbubbles: true,\n\t\t\tcancelable: true,\n\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t});\n\n\t\tconst hideAllowed = _.dispatchEvent(beforeHideEvent);\n\n\t\t// If event was canceled (preventDefault was called), don't hide the dialog\n\t\tif (!hideAllowed) return false;\n\n\t\t// ensure focus is moved out of the dialog\n\t\tconst activeElement = document.activeElement;\n\t\tif (activeElement && _.contains(activeElement)) {\n\t\t\t// Blur the currently focused element if it's inside the dialog\n\t\t\tactiveElement.blur();\n\t\t}\n\n\t\t// Update ARIA states and restore focus\n\t\tif (_.triggerEl) {\n\t\t\t// remove focus from modal panel first\n\t\t\t_.triggerEl.focus();\n\t\t\t// mark trigger as no longer expanded\n\t\t\t_.triggerEl.setAttribute('aria-expanded', 'false');\n\t\t} else {\n\t\t\t// Move focus to body to ensure it's not trapped\n\t\t\tdocument.body.focus();\n\t\t}\n\n\t\t// Set aria-hidden to start transition\n\t\t// The transitionend event handler will add display:none when complete\n\t\t_.setAttribute('aria-hidden', 'true');\n\t\t// Remove open attribute for CSS :has() selector\n\n\t\t_.removeAttribute('open');\n\n\t\t// Dispatch hide event - dialog is now starting to hide\n\t\t_.dispatchEvent(\n\t\t\tnew CustomEvent('hide', {\n\t\t\t\tbubbles: true,\n\t\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t\t})\n\t\t);\n\n\t\treturn true;\n\t}\n}\n\n/**\n * Custom element that creates a clickable overlay for the dialog\n * @extends HTMLElement\n */\nclass DialogOverlay extends HTMLElement {\n\tconstructor() {\n\t\tsuper();\n\t\tthis.setAttribute('tabindex', '-1');\n\t\tthis.dialogPanel = this.closest('dialog-panel');\n\t\tthis.#bindUI();\n\t}\n\n\t#bindUI() {\n\t\tthis.addEventListener('click', () => {\n\t\t\tthis.dialogPanel.hide();\n\t\t});\n\t}\n}\n\n/**\n * Custom element that wraps the content of the dialog\n * @extends HTMLElement\n */\nclass DialogContent extends HTMLElement {\n\tconstructor() {\n\t\tsuper();\n\t\tthis.setAttribute('role', 'document');\n\t}\n}\n\nif (!customElements.get('dialog-panel')) {\n\tcustomElements.define('dialog-panel', DialogPanel);\n}\nif (!customElements.get('dialog-overlay')) {\n\tcustomElements.define('dialog-overlay', DialogOverlay);\n}\nif (!customElements.get('dialog-content')) {\n\tcustomElements.define('dialog-content', DialogContent);\n}\n\nexport { DialogPanel, DialogOverlay, DialogContent };\nexport default DialogPanel;\n"],"names":[],"mappings":"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"}
@@ -1,9 +1,4 @@
1
1
  :root {
2
- --dp-panel-top: 0;
3
- --dp-panel-left: 0;
4
- --dp-panel-width: 100vw;
5
- --dp-panel-height: 100vh;
6
- --dp-panel-z-index: 10;
7
2
  --dp-overlay-z-index: 1000;
8
3
  --dp-overlay-background: rgba(20, 23, 26, 0.4);
9
4
  --dp-overlay-backdrop-filter: blur(2px) saturate(120%);
@@ -61,11 +56,16 @@ dialog-content {
61
56
  overflow: auto;
62
57
  transition: opacity var(--dp-transition-duration, 300ms) var(--dp-transition-timing, ease-out), transform var(--dp-transition-duration, 300ms) var(--dp-transition-timing, ease-out);
63
58
  /* When shown, reset transform to center */
64
- /* When explicitly hidden, remove from layout */
65
59
  }
66
60
  dialog-panel[aria-hidden=false] dialog-content {
67
61
  transform: translate(-50%, -50%) scale(1);
68
62
  }
63
+
64
+ /* When explicitly hidden, remove from layout */
69
65
  dialog-content.hidden {
70
66
  display: none;
67
+ }
68
+
69
+ body:has(dialog-panel[open]) {
70
+ overflow: hidden;
71
71
  }
@@ -6,7 +6,6 @@ import '@magic-spells/focus-trap';
6
6
  */
7
7
  class DialogPanel extends HTMLElement {
8
8
  #handleTransitionEnd;
9
- #scrollPosition = 0;
10
9
 
11
10
  /**
12
11
  * Clean up event listeners when component is removed from DOM
@@ -19,38 +18,6 @@ class DialogPanel extends HTMLElement {
19
18
  _.#handleTransitionEnd
20
19
  );
21
20
  }
22
-
23
- // Ensure body scroll is restored if component is removed while open
24
- document.body.classList.remove('overflow-hidden');
25
- this.#restoreScroll();
26
- }
27
-
28
- /**
29
- * Saves current scroll position and locks body scrolling
30
- * @private
31
- */
32
- #lockScroll() {
33
- const _ = this;
34
- // Save current scroll position
35
- _.#scrollPosition = window.pageYOffset;
36
-
37
- // Apply fixed position to body
38
- document.body.classList.add('overflow-hidden');
39
- document.body.style.top = `-${_.#scrollPosition}px`;
40
- }
41
-
42
- /**
43
- * Restores scroll position when dialog is closed
44
- * @private
45
- */
46
- #restoreScroll() {
47
- const _ = this;
48
- // Remove fixed positioning
49
- document.body.classList.remove('overflow-hidden');
50
- document.body.style.removeProperty('top');
51
-
52
- // Restore scroll position
53
- window.scrollTo(0, _.#scrollPosition);
54
21
  }
55
22
  /**
56
23
  * Initializes the dialog panel, sets up focus trap and overlay
@@ -64,7 +31,6 @@ class DialogPanel extends HTMLElement {
64
31
  _.setAttribute('aria-hidden', 'true');
65
32
 
66
33
  _.contentPanel = _.querySelector('dialog-content');
67
- _.focusTrap = document.createElement('focus-trap');
68
34
  _.triggerEl = null;
69
35
 
70
36
  // Create a handler for transition end events
@@ -85,6 +51,22 @@ class DialogPanel extends HTMLElement {
85
51
  }
86
52
  };
87
53
 
54
+ // Set up focus-trap inside dialog-content
55
+ // Check if focus-trap already exists
56
+ _.focusTrap = _.contentPanel.querySelector('focus-trap');
57
+ if (!_.focusTrap) {
58
+ _.focusTrap = document.createElement('focus-trap');
59
+
60
+ // Move all existing dialog-content children into focus-trap
61
+ const existingContent = Array.from(_.contentPanel.childNodes);
62
+ existingContent.forEach((child) =>
63
+ _.focusTrap.appendChild(child)
64
+ );
65
+
66
+ // Insert focus-trap inside dialog-content
67
+ _.contentPanel.appendChild(_.focusTrap);
68
+ }
69
+
88
70
  // Ensure we have labelledby and describedby references
89
71
  if (!_.getAttribute('aria-labelledby')) {
90
72
  const heading = _.querySelector('h1, h2, h3');
@@ -96,14 +78,6 @@ class DialogPanel extends HTMLElement {
96
78
  }
97
79
  }
98
80
 
99
- _.contentPanel.parentNode.insertBefore(
100
- _.focusTrap,
101
- _.contentPanel
102
- );
103
- _.focusTrap.appendChild(_.contentPanel);
104
-
105
- _.focusTrap.setupTrap();
106
-
107
81
  // Add modal overlay
108
82
  _.prepend(document.createElement('dialog-overlay'));
109
83
  _.#bindUI();
@@ -131,7 +105,7 @@ class DialogPanel extends HTMLElement {
131
105
 
132
106
  // Handle close buttons
133
107
  _.addEventListener('click', (e) => {
134
- if (!e.target.closest('[data-action="hide-dialog"]')) return;
108
+ if (!e.target.closest('[data-action-hide-dialog]')) return;
135
109
  _.hide();
136
110
  });
137
111
 
@@ -177,6 +151,9 @@ class DialogPanel extends HTMLElement {
177
151
  // If event was canceled (preventDefault was called), don't show the dialog
178
152
  if (!showAllowed) return false;
179
153
 
154
+ // Add open attribute for CSS :has() selector
155
+ _.setAttribute('open', '');
156
+
180
157
  // Remove the hidden class first to ensure content is rendered
181
158
  _.contentPanel.classList.remove('hidden');
182
159
 
@@ -184,13 +161,12 @@ class DialogPanel extends HTMLElement {
184
161
  requestAnimationFrame(() => {
185
162
  // Update ARIA states
186
163
  _.setAttribute('aria-hidden', 'false');
164
+
165
+ // set trigger element to expanded: true
187
166
  if (_.triggerEl) {
188
167
  _.triggerEl.setAttribute('aria-expanded', 'true');
189
168
  }
190
169
 
191
- // Lock body scrolling and save scroll position
192
- _.#lockScroll();
193
-
194
170
  // Focus management
195
171
  const firstFocusable = _.querySelector(
196
172
  'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
@@ -209,8 +185,6 @@ class DialogPanel extends HTMLElement {
209
185
  })
210
186
  );
211
187
  });
212
-
213
- return true;
214
188
  }
215
189
 
216
190
  /**
@@ -235,20 +209,30 @@ class DialogPanel extends HTMLElement {
235
209
  // If event was canceled (preventDefault was called), don't hide the dialog
236
210
  if (!hideAllowed) return false;
237
211
 
238
- // Restore body scroll and scroll position
239
- _.#restoreScroll();
212
+ // ensure focus is moved out of the dialog
213
+ const activeElement = document.activeElement;
214
+ if (activeElement && _.contains(activeElement)) {
215
+ // Blur the currently focused element if it's inside the dialog
216
+ activeElement.blur();
217
+ }
240
218
 
241
- // Update ARIA states
219
+ // Update ARIA states and restore focus
242
220
  if (_.triggerEl) {
243
221
  // remove focus from modal panel first
244
222
  _.triggerEl.focus();
245
223
  // mark trigger as no longer expanded
246
224
  _.triggerEl.setAttribute('aria-expanded', 'false');
225
+ } else {
226
+ // Move focus to body to ensure it's not trapped
227
+ document.body.focus();
247
228
  }
248
229
 
249
230
  // Set aria-hidden to start transition
250
231
  // The transitionend event handler will add display:none when complete
251
232
  _.setAttribute('aria-hidden', 'true');
233
+ // Remove open attribute for CSS :has() selector
234
+
235
+ _.removeAttribute('open');
252
236
 
253
237
  // Dispatch hide event - dialog is now starting to hide
254
238
  _.dispatchEvent(
@@ -269,8 +253,7 @@ class DialogPanel extends HTMLElement {
269
253
  class DialogOverlay extends HTMLElement {
270
254
  constructor() {
271
255
  super();
272
- this.setAttribute('tabindex', '-1'); // Changed to -1 as it shouldn't be focusable
273
- this.setAttribute('aria-hidden', 'true');
256
+ this.setAttribute('tabindex', '-1');
274
257
  this.dialogPanel = this.closest('dialog-panel');
275
258
  this.#bindUI();
276
259
  }
@@ -289,7 +272,7 @@ class DialogOverlay extends HTMLElement {
289
272
  class DialogContent extends HTMLElement {
290
273
  constructor() {
291
274
  super();
292
- this.setAttribute('role', 'document'); // Optional: helps with document structure
275
+ this.setAttribute('role', 'document');
293
276
  }
294
277
  }
295
278
 
@@ -1 +1 @@
1
- {"version":3,"file":"dialog-panel.esm.js","sources":["../src/dialog-panel.js"],"sourcesContent":["import './index.scss';\nimport '@magic-spells/focus-trap';\n\n/**\n * Custom element that creates an accessible modal dialog panel with focus management\n * @extends HTMLElement\n */\nclass DialogPanel extends HTMLElement {\n\t#handleTransitionEnd;\n\t#scrollPosition = 0;\n\n\t/**\n\t * Clean up event listeners when component is removed from DOM\n\t */\n\tdisconnectedCallback() {\n\t\tconst _ = this;\n\t\tif (_.contentPanel) {\n\t\t\t_.contentPanel.removeEventListener(\n\t\t\t\t'transitionend',\n\t\t\t\t_.#handleTransitionEnd\n\t\t\t);\n\t\t}\n\n\t\t// Ensure body scroll is restored if component is removed while open\n\t\tdocument.body.classList.remove('overflow-hidden');\n\t\tthis.#restoreScroll();\n\t}\n\n\t/**\n\t * Saves current scroll position and locks body scrolling\n\t * @private\n\t */\n\t#lockScroll() {\n\t\tconst _ = this;\n\t\t// Save current scroll position\n\t\t_.#scrollPosition = window.pageYOffset;\n\n\t\t// Apply fixed position to body\n\t\tdocument.body.classList.add('overflow-hidden');\n\t\tdocument.body.style.top = `-${_.#scrollPosition}px`;\n\t}\n\n\t/**\n\t * Restores scroll position when dialog is closed\n\t * @private\n\t */\n\t#restoreScroll() {\n\t\tconst _ = this;\n\t\t// Remove fixed positioning\n\t\tdocument.body.classList.remove('overflow-hidden');\n\t\tdocument.body.style.removeProperty('top');\n\n\t\t// Restore scroll position\n\t\twindow.scrollTo(0, _.#scrollPosition);\n\t}\n\t/**\n\t * Initializes the dialog panel, sets up focus trap and overlay\n\t */\n\tconstructor() {\n\t\tsuper();\n\t\tconst _ = this;\n\t\t_.id = _.getAttribute('id');\n\t\t_.setAttribute('role', 'dialog');\n\t\t_.setAttribute('aria-modal', 'true');\n\t\t_.setAttribute('aria-hidden', 'true');\n\n\t\t_.contentPanel = _.querySelector('dialog-content');\n\t\t_.focusTrap = document.createElement('focus-trap');\n\t\t_.triggerEl = null;\n\n\t\t// Create a handler for transition end events\n\t\t_.#handleTransitionEnd = (e) => {\n\t\t\tif (\n\t\t\t\te.propertyName === 'opacity' &&\n\t\t\t\t_.getAttribute('aria-hidden') === 'true'\n\t\t\t) {\n\t\t\t\t_.contentPanel.classList.add('hidden');\n\n\t\t\t\t// Dispatch afterHide event - dialog has completed its transition\n\t\t\t\t_.dispatchEvent(\n\t\t\t\t\tnew CustomEvent('afterHide', {\n\t\t\t\t\t\tbubbles: true,\n\t\t\t\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t}\n\t\t};\n\n\t\t// Ensure we have labelledby and describedby references\n\t\tif (!_.getAttribute('aria-labelledby')) {\n\t\t\tconst heading = _.querySelector('h1, h2, h3');\n\t\t\tif (heading && !heading.id) {\n\t\t\t\theading.id = `${_.id}-title`;\n\t\t\t}\n\t\t\tif (heading?.id) {\n\t\t\t\t_.setAttribute('aria-labelledby', heading.id);\n\t\t\t}\n\t\t}\n\n\t\t_.contentPanel.parentNode.insertBefore(\n\t\t\t_.focusTrap,\n\t\t\t_.contentPanel\n\t\t);\n\t\t_.focusTrap.appendChild(_.contentPanel);\n\n\t\t_.focusTrap.setupTrap();\n\n\t\t// Add modal overlay\n\t\t_.prepend(document.createElement('dialog-overlay'));\n\t\t_.#bindUI();\n\t\t_.#bindKeyboard();\n\t}\n\n\t/**\n\t * Binds click events for showing and hiding the dialog\n\t * @private\n\t */\n\t#bindUI() {\n\t\tconst _ = this;\n\n\t\t// Handle trigger buttons\n\t\tdocument.addEventListener('click', (e) => {\n\t\t\tconst trigger = e.target.closest(`[aria-controls=\"${_.id}\"]`);\n\t\t\tif (!trigger) return;\n\n\t\t\tif (trigger.getAttribute('data-prevent-default') === 'true') {\n\t\t\t\te.preventDefault();\n\t\t\t}\n\n\t\t\t_.show(trigger);\n\t\t});\n\n\t\t// Handle close buttons\n\t\t_.addEventListener('click', (e) => {\n\t\t\tif (!e.target.closest('[data-action=\"hide-dialog\"]')) return;\n\t\t\t_.hide();\n\t\t});\n\n\t\t// Add transition end listener\n\t\t_.contentPanel.addEventListener(\n\t\t\t'transitionend',\n\t\t\t_.#handleTransitionEnd\n\t\t);\n\t}\n\n\t/**\n\t * Binds keyboard events for accessibility\n\t * @private\n\t */\n\t#bindKeyboard() {\n\t\tthis.addEventListener('keydown', (e) => {\n\t\t\tif (e.key === 'Escape') {\n\t\t\t\tthis.hide();\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Shows the dialog and traps focus within it\n\t * @param {HTMLElement} [triggerEl=null] - The element that triggered the dialog\n\t * @fires DialogPanel#beforeShow - Fired before the dialog starts to show\n\t * @fires DialogPanel#show - Fired when the dialog has been shown\n\t * @returns {boolean} False if the show was prevented by a beforeShow event handler\n\t */\n\tshow(triggerEl = null) {\n\t\tconst _ = this;\n\t\t_.triggerEl = triggerEl || false;\n\n\t\t// Dispatch beforeShow event - allows preventing the dialog from opening\n\t\tconst beforeShowEvent = new CustomEvent('beforeShow', {\n\t\t\tbubbles: true,\n\t\t\tcancelable: true,\n\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t});\n\n\t\tconst showAllowed = _.dispatchEvent(beforeShowEvent);\n\n\t\t// If event was canceled (preventDefault was called), don't show the dialog\n\t\tif (!showAllowed) return false;\n\n\t\t// Remove the hidden class first to ensure content is rendered\n\t\t_.contentPanel.classList.remove('hidden');\n\n\t\t// Give the browser a moment to process before starting animation\n\t\trequestAnimationFrame(() => {\n\t\t\t// Update ARIA states\n\t\t\t_.setAttribute('aria-hidden', 'false');\n\t\t\tif (_.triggerEl) {\n\t\t\t\t_.triggerEl.setAttribute('aria-expanded', 'true');\n\t\t\t}\n\n\t\t\t// Lock body scrolling and save scroll position\n\t\t\t_.#lockScroll();\n\n\t\t\t// Focus management\n\t\t\tconst firstFocusable = _.querySelector(\n\t\t\t\t'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n\t\t\t);\n\t\t\tif (firstFocusable) {\n\t\t\t\trequestAnimationFrame(() => {\n\t\t\t\t\tfirstFocusable.focus();\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Dispatch show event - dialog is now visible\n\t\t\t_.dispatchEvent(\n\t\t\t\tnew CustomEvent('show', {\n\t\t\t\t\tbubbles: true,\n\t\t\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t\t\t})\n\t\t\t);\n\t\t});\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Hides the dialog and restores focus\n\t * @fires DialogPanel#beforeHide - Fired before the dialog starts to hide\n\t * @fires DialogPanel#hide - Fired when the dialog has started hiding (transition begins)\n\t * @fires DialogPanel#afterHide - Fired when the dialog has completed its hide transition\n\t * @returns {boolean} False if the hide was prevented by a beforeHide event handler\n\t */\n\thide() {\n\t\tconst _ = this;\n\n\t\t// Dispatch beforeHide event - allows preventing the dialog from closing\n\t\tconst beforeHideEvent = new CustomEvent('beforeHide', {\n\t\t\tbubbles: true,\n\t\t\tcancelable: true,\n\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t});\n\n\t\tconst hideAllowed = _.dispatchEvent(beforeHideEvent);\n\n\t\t// If event was canceled (preventDefault was called), don't hide the dialog\n\t\tif (!hideAllowed) return false;\n\n\t\t// Restore body scroll and scroll position\n\t\t_.#restoreScroll();\n\n\t\t// Update ARIA states\n\t\tif (_.triggerEl) {\n\t\t\t// remove focus from modal panel first\n\t\t\t_.triggerEl.focus();\n\t\t\t// mark trigger as no longer expanded\n\t\t\t_.triggerEl.setAttribute('aria-expanded', 'false');\n\t\t}\n\n\t\t// Set aria-hidden to start transition\n\t\t// The transitionend event handler will add display:none when complete\n\t\t_.setAttribute('aria-hidden', 'true');\n\n\t\t// Dispatch hide event - dialog is now starting to hide\n\t\t_.dispatchEvent(\n\t\t\tnew CustomEvent('hide', {\n\t\t\t\tbubbles: true,\n\t\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t\t})\n\t\t);\n\n\t\treturn true;\n\t}\n}\n\n/**\n * Custom element that creates a clickable overlay for the dialog\n * @extends HTMLElement\n */\nclass DialogOverlay extends HTMLElement {\n\tconstructor() {\n\t\tsuper();\n\t\tthis.setAttribute('tabindex', '-1'); // Changed to -1 as it shouldn't be focusable\n\t\tthis.setAttribute('aria-hidden', 'true');\n\t\tthis.dialogPanel = this.closest('dialog-panel');\n\t\tthis.#bindUI();\n\t}\n\n\t#bindUI() {\n\t\tthis.addEventListener('click', () => {\n\t\t\tthis.dialogPanel.hide();\n\t\t});\n\t}\n}\n\n/**\n * Custom element that wraps the content of the dialog\n * @extends HTMLElement\n */\nclass DialogContent extends HTMLElement {\n\tconstructor() {\n\t\tsuper();\n\t\tthis.setAttribute('role', 'document'); // Optional: helps with document structure\n\t}\n}\n\nif (!customElements.get('dialog-panel')) {\n\tcustomElements.define('dialog-panel', DialogPanel);\n}\nif (!customElements.get('dialog-overlay')) {\n\tcustomElements.define('dialog-overlay', DialogOverlay);\n}\nif (!customElements.get('dialog-content')) {\n\tcustomElements.define('dialog-content', DialogContent);\n}\n\nexport { DialogPanel, DialogOverlay, DialogContent };\nexport default DialogPanel;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dialog-panel.esm.js","sources":["../src/dialog-panel.js"],"sourcesContent":["import './index.scss';\nimport '@magic-spells/focus-trap';\n\n/**\n * Custom element that creates an accessible modal dialog panel with focus management\n * @extends HTMLElement\n */\nclass DialogPanel extends HTMLElement {\n\t#handleTransitionEnd;\n\n\t/**\n\t * Clean up event listeners when component is removed from DOM\n\t */\n\tdisconnectedCallback() {\n\t\tconst _ = this;\n\t\tif (_.contentPanel) {\n\t\t\t_.contentPanel.removeEventListener(\n\t\t\t\t'transitionend',\n\t\t\t\t_.#handleTransitionEnd\n\t\t\t);\n\t\t}\n\t}\n\t/**\n\t * Initializes the dialog panel, sets up focus trap and overlay\n\t */\n\tconstructor() {\n\t\tsuper();\n\t\tconst _ = this;\n\t\t_.id = _.getAttribute('id');\n\t\t_.setAttribute('role', 'dialog');\n\t\t_.setAttribute('aria-modal', 'true');\n\t\t_.setAttribute('aria-hidden', 'true');\n\n\t\t_.contentPanel = _.querySelector('dialog-content');\n\t\t_.triggerEl = null;\n\n\t\t// Create a handler for transition end events\n\t\t_.#handleTransitionEnd = (e) => {\n\t\t\tif (\n\t\t\t\te.propertyName === 'opacity' &&\n\t\t\t\t_.getAttribute('aria-hidden') === 'true'\n\t\t\t) {\n\t\t\t\t_.contentPanel.classList.add('hidden');\n\n\t\t\t\t// Dispatch afterHide event - dialog has completed its transition\n\t\t\t\t_.dispatchEvent(\n\t\t\t\t\tnew CustomEvent('afterHide', {\n\t\t\t\t\t\tbubbles: true,\n\t\t\t\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t}\n\t\t};\n\n\t\t// Set up focus-trap inside dialog-content\n\t\t// Check if focus-trap already exists\n\t\t_.focusTrap = _.contentPanel.querySelector('focus-trap');\n\t\tif (!_.focusTrap) {\n\t\t\t_.focusTrap = document.createElement('focus-trap');\n\n\t\t\t// Move all existing dialog-content children into focus-trap\n\t\t\tconst existingContent = Array.from(_.contentPanel.childNodes);\n\t\t\texistingContent.forEach((child) =>\n\t\t\t\t_.focusTrap.appendChild(child)\n\t\t\t);\n\n\t\t\t// Insert focus-trap inside dialog-content\n\t\t\t_.contentPanel.appendChild(_.focusTrap);\n\t\t}\n\n\t\t// Ensure we have labelledby and describedby references\n\t\tif (!_.getAttribute('aria-labelledby')) {\n\t\t\tconst heading = _.querySelector('h1, h2, h3');\n\t\t\tif (heading && !heading.id) {\n\t\t\t\theading.id = `${_.id}-title`;\n\t\t\t}\n\t\t\tif (heading?.id) {\n\t\t\t\t_.setAttribute('aria-labelledby', heading.id);\n\t\t\t}\n\t\t}\n\n\t\t// Add modal overlay\n\t\t_.prepend(document.createElement('dialog-overlay'));\n\t\t_.#bindUI();\n\t\t_.#bindKeyboard();\n\t}\n\n\t/**\n\t * Binds click events for showing and hiding the dialog\n\t * @private\n\t */\n\t#bindUI() {\n\t\tconst _ = this;\n\n\t\t// Handle trigger buttons\n\t\tdocument.addEventListener('click', (e) => {\n\t\t\tconst trigger = e.target.closest(`[aria-controls=\"${_.id}\"]`);\n\t\t\tif (!trigger) return;\n\n\t\t\tif (trigger.getAttribute('data-prevent-default') === 'true') {\n\t\t\t\te.preventDefault();\n\t\t\t}\n\n\t\t\t_.show(trigger);\n\t\t});\n\n\t\t// Handle close buttons\n\t\t_.addEventListener('click', (e) => {\n\t\t\tif (!e.target.closest('[data-action-hide-dialog]')) return;\n\t\t\t_.hide();\n\t\t});\n\n\t\t// Add transition end listener\n\t\t_.contentPanel.addEventListener(\n\t\t\t'transitionend',\n\t\t\t_.#handleTransitionEnd\n\t\t);\n\t}\n\n\t/**\n\t * Binds keyboard events for accessibility\n\t * @private\n\t */\n\t#bindKeyboard() {\n\t\tthis.addEventListener('keydown', (e) => {\n\t\t\tif (e.key === 'Escape') {\n\t\t\t\tthis.hide();\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Shows the dialog and traps focus within it\n\t * @param {HTMLElement} [triggerEl=null] - The element that triggered the dialog\n\t * @fires DialogPanel#beforeShow - Fired before the dialog starts to show\n\t * @fires DialogPanel#show - Fired when the dialog has been shown\n\t * @returns {boolean} False if the show was prevented by a beforeShow event handler\n\t */\n\tshow(triggerEl = null) {\n\t\tconst _ = this;\n\t\t_.triggerEl = triggerEl || false;\n\n\t\t// Dispatch beforeShow event - allows preventing the dialog from opening\n\t\tconst beforeShowEvent = new CustomEvent('beforeShow', {\n\t\t\tbubbles: true,\n\t\t\tcancelable: true,\n\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t});\n\n\t\tconst showAllowed = _.dispatchEvent(beforeShowEvent);\n\n\t\t// If event was canceled (preventDefault was called), don't show the dialog\n\t\tif (!showAllowed) return false;\n\n\t\t// Add open attribute for CSS :has() selector\n\t\t_.setAttribute('open', '');\n\n\t\t// Remove the hidden class first to ensure content is rendered\n\t\t_.contentPanel.classList.remove('hidden');\n\n\t\t// Give the browser a moment to process before starting animation\n\t\trequestAnimationFrame(() => {\n\t\t\t// Update ARIA states\n\t\t\t_.setAttribute('aria-hidden', 'false');\n\n\t\t\t// set trigger element to expanded: true\n\t\t\tif (_.triggerEl) {\n\t\t\t\t_.triggerEl.setAttribute('aria-expanded', 'true');\n\t\t\t}\n\n\t\t\t// Focus management\n\t\t\tconst firstFocusable = _.querySelector(\n\t\t\t\t'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n\t\t\t);\n\t\t\tif (firstFocusable) {\n\t\t\t\trequestAnimationFrame(() => {\n\t\t\t\t\tfirstFocusable.focus();\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Dispatch show event - dialog is now visible\n\t\t\t_.dispatchEvent(\n\t\t\t\tnew CustomEvent('show', {\n\t\t\t\t\tbubbles: true,\n\t\t\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t\t\t})\n\t\t\t);\n\t\t});\n\t}\n\n\t/**\n\t * Hides the dialog and restores focus\n\t * @fires DialogPanel#beforeHide - Fired before the dialog starts to hide\n\t * @fires DialogPanel#hide - Fired when the dialog has started hiding (transition begins)\n\t * @fires DialogPanel#afterHide - Fired when the dialog has completed its hide transition\n\t * @returns {boolean} False if the hide was prevented by a beforeHide event handler\n\t */\n\thide() {\n\t\tconst _ = this;\n\n\t\t// Dispatch beforeHide event - allows preventing the dialog from closing\n\t\tconst beforeHideEvent = new CustomEvent('beforeHide', {\n\t\t\tbubbles: true,\n\t\t\tcancelable: true,\n\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t});\n\n\t\tconst hideAllowed = _.dispatchEvent(beforeHideEvent);\n\n\t\t// If event was canceled (preventDefault was called), don't hide the dialog\n\t\tif (!hideAllowed) return false;\n\n\t\t// ensure focus is moved out of the dialog\n\t\tconst activeElement = document.activeElement;\n\t\tif (activeElement && _.contains(activeElement)) {\n\t\t\t// Blur the currently focused element if it's inside the dialog\n\t\t\tactiveElement.blur();\n\t\t}\n\n\t\t// Update ARIA states and restore focus\n\t\tif (_.triggerEl) {\n\t\t\t// remove focus from modal panel first\n\t\t\t_.triggerEl.focus();\n\t\t\t// mark trigger as no longer expanded\n\t\t\t_.triggerEl.setAttribute('aria-expanded', 'false');\n\t\t} else {\n\t\t\t// Move focus to body to ensure it's not trapped\n\t\t\tdocument.body.focus();\n\t\t}\n\n\t\t// Set aria-hidden to start transition\n\t\t// The transitionend event handler will add display:none when complete\n\t\t_.setAttribute('aria-hidden', 'true');\n\t\t// Remove open attribute for CSS :has() selector\n\n\t\t_.removeAttribute('open');\n\n\t\t// Dispatch hide event - dialog is now starting to hide\n\t\t_.dispatchEvent(\n\t\t\tnew CustomEvent('hide', {\n\t\t\t\tbubbles: true,\n\t\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t\t})\n\t\t);\n\n\t\treturn true;\n\t}\n}\n\n/**\n * Custom element that creates a clickable overlay for the dialog\n * @extends HTMLElement\n */\nclass DialogOverlay extends HTMLElement {\n\tconstructor() {\n\t\tsuper();\n\t\tthis.setAttribute('tabindex', '-1');\n\t\tthis.dialogPanel = this.closest('dialog-panel');\n\t\tthis.#bindUI();\n\t}\n\n\t#bindUI() {\n\t\tthis.addEventListener('click', () => {\n\t\t\tthis.dialogPanel.hide();\n\t\t});\n\t}\n}\n\n/**\n * Custom element that wraps the content of the dialog\n * @extends HTMLElement\n */\nclass DialogContent extends HTMLElement {\n\tconstructor() {\n\t\tsuper();\n\t\tthis.setAttribute('role', 'document');\n\t}\n}\n\nif (!customElements.get('dialog-panel')) {\n\tcustomElements.define('dialog-panel', DialogPanel);\n}\nif (!customElements.get('dialog-overlay')) {\n\tcustomElements.define('dialog-overlay', DialogOverlay);\n}\nif (!customElements.get('dialog-content')) {\n\tcustomElements.define('dialog-content', DialogContent);\n}\n\nexport { DialogPanel, DialogOverlay, DialogContent };\nexport default DialogPanel;\n"],"names":[],"mappings":"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"}
@@ -193,9 +193,15 @@
193
193
  };
194
194
  }
195
195
 
196
- customElements.define('focus-trap', FocusTrap);
197
- customElements.define('focus-trap-start', FocusTrapStart);
198
- customElements.define('focus-trap-end', FocusTrapEnd);
196
+ if (!customElements.get('focus-trap')) {
197
+ customElements.define('focus-trap', FocusTrap);
198
+ }
199
+ if (!customElements.get('focus-trap-start')) {
200
+ customElements.define('focus-trap-start', FocusTrapStart);
201
+ }
202
+ if (!customElements.get('focus-trap-end')) {
203
+ customElements.define('focus-trap-end', FocusTrapEnd);
204
+ }
199
205
 
200
206
  /**
201
207
  * Custom element that creates an accessible modal dialog panel with focus management
@@ -203,7 +209,6 @@
203
209
  */
204
210
  class DialogPanel extends HTMLElement {
205
211
  #handleTransitionEnd;
206
- #scrollPosition = 0;
207
212
 
208
213
  /**
209
214
  * Clean up event listeners when component is removed from DOM
@@ -216,38 +221,6 @@
216
221
  _.#handleTransitionEnd
217
222
  );
218
223
  }
219
-
220
- // Ensure body scroll is restored if component is removed while open
221
- document.body.classList.remove('overflow-hidden');
222
- this.#restoreScroll();
223
- }
224
-
225
- /**
226
- * Saves current scroll position and locks body scrolling
227
- * @private
228
- */
229
- #lockScroll() {
230
- const _ = this;
231
- // Save current scroll position
232
- _.#scrollPosition = window.pageYOffset;
233
-
234
- // Apply fixed position to body
235
- document.body.classList.add('overflow-hidden');
236
- document.body.style.top = `-${_.#scrollPosition}px`;
237
- }
238
-
239
- /**
240
- * Restores scroll position when dialog is closed
241
- * @private
242
- */
243
- #restoreScroll() {
244
- const _ = this;
245
- // Remove fixed positioning
246
- document.body.classList.remove('overflow-hidden');
247
- document.body.style.removeProperty('top');
248
-
249
- // Restore scroll position
250
- window.scrollTo(0, _.#scrollPosition);
251
224
  }
252
225
  /**
253
226
  * Initializes the dialog panel, sets up focus trap and overlay
@@ -261,7 +234,6 @@
261
234
  _.setAttribute('aria-hidden', 'true');
262
235
 
263
236
  _.contentPanel = _.querySelector('dialog-content');
264
- _.focusTrap = document.createElement('focus-trap');
265
237
  _.triggerEl = null;
266
238
 
267
239
  // Create a handler for transition end events
@@ -282,6 +254,22 @@
282
254
  }
283
255
  };
284
256
 
257
+ // Set up focus-trap inside dialog-content
258
+ // Check if focus-trap already exists
259
+ _.focusTrap = _.contentPanel.querySelector('focus-trap');
260
+ if (!_.focusTrap) {
261
+ _.focusTrap = document.createElement('focus-trap');
262
+
263
+ // Move all existing dialog-content children into focus-trap
264
+ const existingContent = Array.from(_.contentPanel.childNodes);
265
+ existingContent.forEach((child) =>
266
+ _.focusTrap.appendChild(child)
267
+ );
268
+
269
+ // Insert focus-trap inside dialog-content
270
+ _.contentPanel.appendChild(_.focusTrap);
271
+ }
272
+
285
273
  // Ensure we have labelledby and describedby references
286
274
  if (!_.getAttribute('aria-labelledby')) {
287
275
  const heading = _.querySelector('h1, h2, h3');
@@ -293,14 +281,6 @@
293
281
  }
294
282
  }
295
283
 
296
- _.contentPanel.parentNode.insertBefore(
297
- _.focusTrap,
298
- _.contentPanel
299
- );
300
- _.focusTrap.appendChild(_.contentPanel);
301
-
302
- _.focusTrap.setupTrap();
303
-
304
284
  // Add modal overlay
305
285
  _.prepend(document.createElement('dialog-overlay'));
306
286
  _.#bindUI();
@@ -328,7 +308,7 @@
328
308
 
329
309
  // Handle close buttons
330
310
  _.addEventListener('click', (e) => {
331
- if (!e.target.closest('[data-action="hide-dialog"]')) return;
311
+ if (!e.target.closest('[data-action-hide-dialog]')) return;
332
312
  _.hide();
333
313
  });
334
314
 
@@ -374,6 +354,9 @@
374
354
  // If event was canceled (preventDefault was called), don't show the dialog
375
355
  if (!showAllowed) return false;
376
356
 
357
+ // Add open attribute for CSS :has() selector
358
+ _.setAttribute('open', '');
359
+
377
360
  // Remove the hidden class first to ensure content is rendered
378
361
  _.contentPanel.classList.remove('hidden');
379
362
 
@@ -381,13 +364,12 @@
381
364
  requestAnimationFrame(() => {
382
365
  // Update ARIA states
383
366
  _.setAttribute('aria-hidden', 'false');
367
+
368
+ // set trigger element to expanded: true
384
369
  if (_.triggerEl) {
385
370
  _.triggerEl.setAttribute('aria-expanded', 'true');
386
371
  }
387
372
 
388
- // Lock body scrolling and save scroll position
389
- _.#lockScroll();
390
-
391
373
  // Focus management
392
374
  const firstFocusable = _.querySelector(
393
375
  'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
@@ -406,8 +388,6 @@
406
388
  })
407
389
  );
408
390
  });
409
-
410
- return true;
411
391
  }
412
392
 
413
393
  /**
@@ -432,20 +412,30 @@
432
412
  // If event was canceled (preventDefault was called), don't hide the dialog
433
413
  if (!hideAllowed) return false;
434
414
 
435
- // Restore body scroll and scroll position
436
- _.#restoreScroll();
415
+ // ensure focus is moved out of the dialog
416
+ const activeElement = document.activeElement;
417
+ if (activeElement && _.contains(activeElement)) {
418
+ // Blur the currently focused element if it's inside the dialog
419
+ activeElement.blur();
420
+ }
437
421
 
438
- // Update ARIA states
422
+ // Update ARIA states and restore focus
439
423
  if (_.triggerEl) {
440
424
  // remove focus from modal panel first
441
425
  _.triggerEl.focus();
442
426
  // mark trigger as no longer expanded
443
427
  _.triggerEl.setAttribute('aria-expanded', 'false');
428
+ } else {
429
+ // Move focus to body to ensure it's not trapped
430
+ document.body.focus();
444
431
  }
445
432
 
446
433
  // Set aria-hidden to start transition
447
434
  // The transitionend event handler will add display:none when complete
448
435
  _.setAttribute('aria-hidden', 'true');
436
+ // Remove open attribute for CSS :has() selector
437
+
438
+ _.removeAttribute('open');
449
439
 
450
440
  // Dispatch hide event - dialog is now starting to hide
451
441
  _.dispatchEvent(
@@ -466,8 +456,7 @@
466
456
  class DialogOverlay extends HTMLElement {
467
457
  constructor() {
468
458
  super();
469
- this.setAttribute('tabindex', '-1'); // Changed to -1 as it shouldn't be focusable
470
- this.setAttribute('aria-hidden', 'true');
459
+ this.setAttribute('tabindex', '-1');
471
460
  this.dialogPanel = this.closest('dialog-panel');
472
461
  this.#bindUI();
473
462
  }
@@ -486,7 +475,7 @@
486
475
  class DialogContent extends HTMLElement {
487
476
  constructor() {
488
477
  super();
489
- this.setAttribute('role', 'document'); // Optional: helps with document structure
478
+ this.setAttribute('role', 'document');
490
479
  }
491
480
  }
492
481
 
@@ -1 +1 @@
1
- {"version":3,"file":"dialog-panel.js","sources":["../node_modules/@magic-spells/focus-trap/dist/focus-trap.esm.js","../src/dialog-panel.js"],"sourcesContent":["/**\n * Retrieves all focusable elements within a given container.\n *\n * @param {HTMLElement} container - The container element to search for focusable elements.\n * @returns {HTMLElement[]} An array of focusable elements found within the container.\n */\nconst getFocusableElements = (container) => {\n\tconst focusableSelectors =\n\t\t'summary, a[href], button:not(:disabled), [tabindex]:not([tabindex^=\"-\"]):not(focus-trap-start):not(focus-trap-end), [draggable], area, input:not([type=hidden]):not(:disabled), select:not(:disabled), textarea:not(:disabled), object, iframe';\n\treturn Array.from(container.querySelectorAll(focusableSelectors));\n};\n\nclass FocusTrap extends HTMLElement {\n\t/** @type {boolean} Indicates whether the styles have been injected into the DOM. */\n\tstatic styleInjected = false;\n\n\tconstructor() {\n\t\tsuper();\n\t\tthis.trapStart = null;\n\t\tthis.trapEnd = null;\n\n\t\t// Inject styles only once, when the first FocusTrap instance is created.\n\t\tif (!FocusTrap.styleInjected) {\n\t\t\tthis.injectStyles();\n\t\t\tFocusTrap.styleInjected = true;\n\t\t}\n\t}\n\n\t/**\n\t * Injects necessary styles for the focus trap into the document's head.\n\t * This ensures that focus-trap-start and focus-trap-end elements are hidden.\n\t */\n\tinjectStyles() {\n\t\tconst style = document.createElement('style');\n\t\tstyle.textContent = `\n focus-trap-start,\n focus-trap-end {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n border: 0;\n clip: rect(0, 0, 0, 0);\n overflow: hidden;\n white-space: nowrap;\n }\n `;\n\t\tdocument.head.appendChild(style);\n\t}\n\n\t/**\n\t * Called when the element is connected to the DOM.\n\t * Sets up the focus trap and adds the keydown event listener.\n\t */\n\tconnectedCallback() {\n\t\tthis.setupTrap();\n\t\tthis.addEventListener('keydown', this.handleKeyDown);\n\t}\n\n\t/**\n\t * Called when the element is disconnected from the DOM.\n\t * Removes the keydown event listener.\n\t */\n\tdisconnectedCallback() {\n\t\tthis.removeEventListener('keydown', this.handleKeyDown);\n\t}\n\n\t/**\n\t * Sets up the focus trap by adding trap start and trap end elements.\n\t * Focuses the trap start element to initiate the focus trap.\n\t */\n\tsetupTrap() {\n\t\t// check to see it there are any focusable children\n\t\tconst focusableElements = getFocusableElements(this);\n\t\t// exit if there aren't any\n\t\tif (focusableElements.length === 0) return;\n\n\t\t// create trap start and end elements\n\t\tthis.trapStart = document.createElement('focus-trap-start');\n\t\tthis.trapEnd = document.createElement('focus-trap-end');\n\n\t\t// add to DOM\n\t\tthis.prepend(this.trapStart);\n\t\tthis.append(this.trapEnd);\n\t}\n\n\t/**\n\t * Handles the keydown event. If the Escape key is pressed, the focus trap is exited.\n\t *\n\t * @param {KeyboardEvent} e - The keyboard event object.\n\t */\n\thandleKeyDown = (e) => {\n\t\tif (e.key === 'Escape') {\n\t\t\te.preventDefault();\n\t\t\tthis.exitTrap();\n\t\t}\n\t};\n\n\t/**\n\t * Exits the focus trap by hiding the current container and shifting focus\n\t * back to the trigger element that opened the trap.\n\t */\n\texitTrap() {\n\t\tconst container = this.closest('[aria-hidden=\"false\"]');\n\t\tif (!container) return;\n\n\t\tcontainer.setAttribute('aria-hidden', 'true');\n\n\t\tconst trigger = document.querySelector(\n\t\t\t`[aria-expanded=\"true\"][aria-controls=\"${container.id}\"]`\n\t\t);\n\t\tif (trigger) {\n\t\t\ttrigger.setAttribute('aria-expanded', 'false');\n\t\t\ttrigger.focus();\n\t\t}\n\t}\n}\n\nclass FocusTrapStart extends HTMLElement {\n\t/**\n\t * Called when the element is connected to the DOM.\n\t * Sets the tabindex and adds the focus event listener.\n\t */\n\tconnectedCallback() {\n\t\tthis.setAttribute('tabindex', '0');\n\t\tthis.addEventListener('focus', this.handleFocus);\n\t}\n\n\t/**\n\t * Called when the element is disconnected from the DOM.\n\t * Removes the focus event listener.\n\t */\n\tdisconnectedCallback() {\n\t\tthis.removeEventListener('focus', this.handleFocus);\n\t}\n\n\t/**\n\t * Handles the focus event. If focus moves backwards from the first focusable element,\n\t * it is cycled to the last focusable element, and vice versa.\n\t *\n\t * @param {FocusEvent} e - The focus event object.\n\t */\n\thandleFocus = (e) => {\n\t\tconst trap = this.closest('focus-trap');\n\t\tconst focusableElements = getFocusableElements(trap);\n\n\t\tif (focusableElements.length === 0) return;\n\n\t\tconst firstElement = focusableElements[0];\n\t\tconst lastElement =\n\t\t\tfocusableElements[focusableElements.length - 1];\n\n\t\tif (e.relatedTarget === firstElement) {\n\t\t\tlastElement.focus();\n\t\t} else {\n\t\t\tfirstElement.focus();\n\t\t}\n\t};\n}\n\nclass FocusTrapEnd extends HTMLElement {\n\t/**\n\t * Called when the element is connected to the DOM.\n\t * Sets the tabindex and adds the focus event listener.\n\t */\n\tconnectedCallback() {\n\t\tthis.setAttribute('tabindex', '0');\n\t\tthis.addEventListener('focus', this.handleFocus);\n\t}\n\n\t/**\n\t * Called when the element is disconnected from the DOM.\n\t * Removes the focus event listener.\n\t */\n\tdisconnectedCallback() {\n\t\tthis.removeEventListener('focus', this.handleFocus);\n\t}\n\n\t/**\n\t * Handles the focus event. When the trap end is focused, focus is shifted back to the trap start.\n\t */\n\thandleFocus = () => {\n\t\tconst trap = this.closest('focus-trap');\n\t\tconst trapStart = trap.querySelector('focus-trap-start');\n\t\ttrapStart.focus();\n\t};\n}\n\ncustomElements.define('focus-trap', FocusTrap);\ncustomElements.define('focus-trap-start', FocusTrapStart);\ncustomElements.define('focus-trap-end', FocusTrapEnd);\n//# sourceMappingURL=focus-trap.esm.js.map\n","import './index.scss';\nimport '@magic-spells/focus-trap';\n\n/**\n * Custom element that creates an accessible modal dialog panel with focus management\n * @extends HTMLElement\n */\nclass DialogPanel extends HTMLElement {\n\t#handleTransitionEnd;\n\t#scrollPosition = 0;\n\n\t/**\n\t * Clean up event listeners when component is removed from DOM\n\t */\n\tdisconnectedCallback() {\n\t\tconst _ = this;\n\t\tif (_.contentPanel) {\n\t\t\t_.contentPanel.removeEventListener(\n\t\t\t\t'transitionend',\n\t\t\t\t_.#handleTransitionEnd\n\t\t\t);\n\t\t}\n\n\t\t// Ensure body scroll is restored if component is removed while open\n\t\tdocument.body.classList.remove('overflow-hidden');\n\t\tthis.#restoreScroll();\n\t}\n\n\t/**\n\t * Saves current scroll position and locks body scrolling\n\t * @private\n\t */\n\t#lockScroll() {\n\t\tconst _ = this;\n\t\t// Save current scroll position\n\t\t_.#scrollPosition = window.pageYOffset;\n\n\t\t// Apply fixed position to body\n\t\tdocument.body.classList.add('overflow-hidden');\n\t\tdocument.body.style.top = `-${_.#scrollPosition}px`;\n\t}\n\n\t/**\n\t * Restores scroll position when dialog is closed\n\t * @private\n\t */\n\t#restoreScroll() {\n\t\tconst _ = this;\n\t\t// Remove fixed positioning\n\t\tdocument.body.classList.remove('overflow-hidden');\n\t\tdocument.body.style.removeProperty('top');\n\n\t\t// Restore scroll position\n\t\twindow.scrollTo(0, _.#scrollPosition);\n\t}\n\t/**\n\t * Initializes the dialog panel, sets up focus trap and overlay\n\t */\n\tconstructor() {\n\t\tsuper();\n\t\tconst _ = this;\n\t\t_.id = _.getAttribute('id');\n\t\t_.setAttribute('role', 'dialog');\n\t\t_.setAttribute('aria-modal', 'true');\n\t\t_.setAttribute('aria-hidden', 'true');\n\n\t\t_.contentPanel = _.querySelector('dialog-content');\n\t\t_.focusTrap = document.createElement('focus-trap');\n\t\t_.triggerEl = null;\n\n\t\t// Create a handler for transition end events\n\t\t_.#handleTransitionEnd = (e) => {\n\t\t\tif (\n\t\t\t\te.propertyName === 'opacity' &&\n\t\t\t\t_.getAttribute('aria-hidden') === 'true'\n\t\t\t) {\n\t\t\t\t_.contentPanel.classList.add('hidden');\n\n\t\t\t\t// Dispatch afterHide event - dialog has completed its transition\n\t\t\t\t_.dispatchEvent(\n\t\t\t\t\tnew CustomEvent('afterHide', {\n\t\t\t\t\t\tbubbles: true,\n\t\t\t\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t}\n\t\t};\n\n\t\t// Ensure we have labelledby and describedby references\n\t\tif (!_.getAttribute('aria-labelledby')) {\n\t\t\tconst heading = _.querySelector('h1, h2, h3');\n\t\t\tif (heading && !heading.id) {\n\t\t\t\theading.id = `${_.id}-title`;\n\t\t\t}\n\t\t\tif (heading?.id) {\n\t\t\t\t_.setAttribute('aria-labelledby', heading.id);\n\t\t\t}\n\t\t}\n\n\t\t_.contentPanel.parentNode.insertBefore(\n\t\t\t_.focusTrap,\n\t\t\t_.contentPanel\n\t\t);\n\t\t_.focusTrap.appendChild(_.contentPanel);\n\n\t\t_.focusTrap.setupTrap();\n\n\t\t// Add modal overlay\n\t\t_.prepend(document.createElement('dialog-overlay'));\n\t\t_.#bindUI();\n\t\t_.#bindKeyboard();\n\t}\n\n\t/**\n\t * Binds click events for showing and hiding the dialog\n\t * @private\n\t */\n\t#bindUI() {\n\t\tconst _ = this;\n\n\t\t// Handle trigger buttons\n\t\tdocument.addEventListener('click', (e) => {\n\t\t\tconst trigger = e.target.closest(`[aria-controls=\"${_.id}\"]`);\n\t\t\tif (!trigger) return;\n\n\t\t\tif (trigger.getAttribute('data-prevent-default') === 'true') {\n\t\t\t\te.preventDefault();\n\t\t\t}\n\n\t\t\t_.show(trigger);\n\t\t});\n\n\t\t// Handle close buttons\n\t\t_.addEventListener('click', (e) => {\n\t\t\tif (!e.target.closest('[data-action=\"hide-dialog\"]')) return;\n\t\t\t_.hide();\n\t\t});\n\n\t\t// Add transition end listener\n\t\t_.contentPanel.addEventListener(\n\t\t\t'transitionend',\n\t\t\t_.#handleTransitionEnd\n\t\t);\n\t}\n\n\t/**\n\t * Binds keyboard events for accessibility\n\t * @private\n\t */\n\t#bindKeyboard() {\n\t\tthis.addEventListener('keydown', (e) => {\n\t\t\tif (e.key === 'Escape') {\n\t\t\t\tthis.hide();\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Shows the dialog and traps focus within it\n\t * @param {HTMLElement} [triggerEl=null] - The element that triggered the dialog\n\t * @fires DialogPanel#beforeShow - Fired before the dialog starts to show\n\t * @fires DialogPanel#show - Fired when the dialog has been shown\n\t * @returns {boolean} False if the show was prevented by a beforeShow event handler\n\t */\n\tshow(triggerEl = null) {\n\t\tconst _ = this;\n\t\t_.triggerEl = triggerEl || false;\n\n\t\t// Dispatch beforeShow event - allows preventing the dialog from opening\n\t\tconst beforeShowEvent = new CustomEvent('beforeShow', {\n\t\t\tbubbles: true,\n\t\t\tcancelable: true,\n\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t});\n\n\t\tconst showAllowed = _.dispatchEvent(beforeShowEvent);\n\n\t\t// If event was canceled (preventDefault was called), don't show the dialog\n\t\tif (!showAllowed) return false;\n\n\t\t// Remove the hidden class first to ensure content is rendered\n\t\t_.contentPanel.classList.remove('hidden');\n\n\t\t// Give the browser a moment to process before starting animation\n\t\trequestAnimationFrame(() => {\n\t\t\t// Update ARIA states\n\t\t\t_.setAttribute('aria-hidden', 'false');\n\t\t\tif (_.triggerEl) {\n\t\t\t\t_.triggerEl.setAttribute('aria-expanded', 'true');\n\t\t\t}\n\n\t\t\t// Lock body scrolling and save scroll position\n\t\t\t_.#lockScroll();\n\n\t\t\t// Focus management\n\t\t\tconst firstFocusable = _.querySelector(\n\t\t\t\t'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n\t\t\t);\n\t\t\tif (firstFocusable) {\n\t\t\t\trequestAnimationFrame(() => {\n\t\t\t\t\tfirstFocusable.focus();\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Dispatch show event - dialog is now visible\n\t\t\t_.dispatchEvent(\n\t\t\t\tnew CustomEvent('show', {\n\t\t\t\t\tbubbles: true,\n\t\t\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t\t\t})\n\t\t\t);\n\t\t});\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Hides the dialog and restores focus\n\t * @fires DialogPanel#beforeHide - Fired before the dialog starts to hide\n\t * @fires DialogPanel#hide - Fired when the dialog has started hiding (transition begins)\n\t * @fires DialogPanel#afterHide - Fired when the dialog has completed its hide transition\n\t * @returns {boolean} False if the hide was prevented by a beforeHide event handler\n\t */\n\thide() {\n\t\tconst _ = this;\n\n\t\t// Dispatch beforeHide event - allows preventing the dialog from closing\n\t\tconst beforeHideEvent = new CustomEvent('beforeHide', {\n\t\t\tbubbles: true,\n\t\t\tcancelable: true,\n\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t});\n\n\t\tconst hideAllowed = _.dispatchEvent(beforeHideEvent);\n\n\t\t// If event was canceled (preventDefault was called), don't hide the dialog\n\t\tif (!hideAllowed) return false;\n\n\t\t// Restore body scroll and scroll position\n\t\t_.#restoreScroll();\n\n\t\t// Update ARIA states\n\t\tif (_.triggerEl) {\n\t\t\t// remove focus from modal panel first\n\t\t\t_.triggerEl.focus();\n\t\t\t// mark trigger as no longer expanded\n\t\t\t_.triggerEl.setAttribute('aria-expanded', 'false');\n\t\t}\n\n\t\t// Set aria-hidden to start transition\n\t\t// The transitionend event handler will add display:none when complete\n\t\t_.setAttribute('aria-hidden', 'true');\n\n\t\t// Dispatch hide event - dialog is now starting to hide\n\t\t_.dispatchEvent(\n\t\t\tnew CustomEvent('hide', {\n\t\t\t\tbubbles: true,\n\t\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t\t})\n\t\t);\n\n\t\treturn true;\n\t}\n}\n\n/**\n * Custom element that creates a clickable overlay for the dialog\n * @extends HTMLElement\n */\nclass DialogOverlay extends HTMLElement {\n\tconstructor() {\n\t\tsuper();\n\t\tthis.setAttribute('tabindex', '-1'); // Changed to -1 as it shouldn't be focusable\n\t\tthis.setAttribute('aria-hidden', 'true');\n\t\tthis.dialogPanel = this.closest('dialog-panel');\n\t\tthis.#bindUI();\n\t}\n\n\t#bindUI() {\n\t\tthis.addEventListener('click', () => {\n\t\t\tthis.dialogPanel.hide();\n\t\t});\n\t}\n}\n\n/**\n * Custom element that wraps the content of the dialog\n * @extends HTMLElement\n */\nclass DialogContent extends HTMLElement {\n\tconstructor() {\n\t\tsuper();\n\t\tthis.setAttribute('role', 'document'); // Optional: helps with document structure\n\t}\n}\n\nif (!customElements.get('dialog-panel')) {\n\tcustomElements.define('dialog-panel', DialogPanel);\n}\nif (!customElements.get('dialog-overlay')) {\n\tcustomElements.define('dialog-overlay', DialogOverlay);\n}\nif (!customElements.get('dialog-content')) {\n\tcustomElements.define('dialog-content', DialogContent);\n}\n\nexport { DialogPanel, DialogOverlay, DialogContent };\nexport default DialogPanel;\n"],"names":[],"mappings":"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","x_google_ignoreList":[0]}
1
+ {"version":3,"file":"dialog-panel.js","sources":["../node_modules/@magic-spells/focus-trap/dist/focus-trap.esm.js","../src/dialog-panel.js"],"sourcesContent":["/**\n * Retrieves all focusable elements within a given container.\n *\n * @param {HTMLElement} container - The container element to search for focusable elements.\n * @returns {HTMLElement[]} An array of focusable elements found within the container.\n */\nconst getFocusableElements = (container) => {\n\tconst focusableSelectors =\n\t\t'summary, a[href], button:not(:disabled), [tabindex]:not([tabindex^=\"-\"]):not(focus-trap-start):not(focus-trap-end), [draggable], area, input:not([type=hidden]):not(:disabled), select:not(:disabled), textarea:not(:disabled), object, iframe';\n\treturn Array.from(container.querySelectorAll(focusableSelectors));\n};\n\nclass FocusTrap extends HTMLElement {\n\t/** @type {boolean} Indicates whether the styles have been injected into the DOM. */\n\tstatic styleInjected = false;\n\n\tconstructor() {\n\t\tsuper();\n\t\tthis.trapStart = null;\n\t\tthis.trapEnd = null;\n\n\t\t// Inject styles only once, when the first FocusTrap instance is created.\n\t\tif (!FocusTrap.styleInjected) {\n\t\t\tthis.injectStyles();\n\t\t\tFocusTrap.styleInjected = true;\n\t\t}\n\t}\n\n\t/**\n\t * Injects necessary styles for the focus trap into the document's head.\n\t * This ensures that focus-trap-start and focus-trap-end elements are hidden.\n\t */\n\tinjectStyles() {\n\t\tconst style = document.createElement('style');\n\t\tstyle.textContent = `\n focus-trap-start,\n focus-trap-end {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n border: 0;\n clip: rect(0, 0, 0, 0);\n overflow: hidden;\n white-space: nowrap;\n }\n `;\n\t\tdocument.head.appendChild(style);\n\t}\n\n\t/**\n\t * Called when the element is connected to the DOM.\n\t * Sets up the focus trap and adds the keydown event listener.\n\t */\n\tconnectedCallback() {\n\t\tthis.setupTrap();\n\t\tthis.addEventListener('keydown', this.handleKeyDown);\n\t}\n\n\t/**\n\t * Called when the element is disconnected from the DOM.\n\t * Removes the keydown event listener.\n\t */\n\tdisconnectedCallback() {\n\t\tthis.removeEventListener('keydown', this.handleKeyDown);\n\t}\n\n\t/**\n\t * Sets up the focus trap by adding trap start and trap end elements.\n\t * Focuses the trap start element to initiate the focus trap.\n\t */\n\tsetupTrap() {\n\t\t// check to see it there are any focusable children\n\t\tconst focusableElements = getFocusableElements(this);\n\t\t// exit if there aren't any\n\t\tif (focusableElements.length === 0) return;\n\n\t\t// create trap start and end elements\n\t\tthis.trapStart = document.createElement('focus-trap-start');\n\t\tthis.trapEnd = document.createElement('focus-trap-end');\n\n\t\t// add to DOM\n\t\tthis.prepend(this.trapStart);\n\t\tthis.append(this.trapEnd);\n\t}\n\n\t/**\n\t * Handles the keydown event. If the Escape key is pressed, the focus trap is exited.\n\t *\n\t * @param {KeyboardEvent} e - The keyboard event object.\n\t */\n\thandleKeyDown = (e) => {\n\t\tif (e.key === 'Escape') {\n\t\t\te.preventDefault();\n\t\t\tthis.exitTrap();\n\t\t}\n\t};\n\n\t/**\n\t * Exits the focus trap by hiding the current container and shifting focus\n\t * back to the trigger element that opened the trap.\n\t */\n\texitTrap() {\n\t\tconst container = this.closest('[aria-hidden=\"false\"]');\n\t\tif (!container) return;\n\n\t\tcontainer.setAttribute('aria-hidden', 'true');\n\n\t\tconst trigger = document.querySelector(\n\t\t\t`[aria-expanded=\"true\"][aria-controls=\"${container.id}\"]`\n\t\t);\n\t\tif (trigger) {\n\t\t\ttrigger.setAttribute('aria-expanded', 'false');\n\t\t\ttrigger.focus();\n\t\t}\n\t}\n}\n\nclass FocusTrapStart extends HTMLElement {\n\t/**\n\t * Called when the element is connected to the DOM.\n\t * Sets the tabindex and adds the focus event listener.\n\t */\n\tconnectedCallback() {\n\t\tthis.setAttribute('tabindex', '0');\n\t\tthis.addEventListener('focus', this.handleFocus);\n\t}\n\n\t/**\n\t * Called when the element is disconnected from the DOM.\n\t * Removes the focus event listener.\n\t */\n\tdisconnectedCallback() {\n\t\tthis.removeEventListener('focus', this.handleFocus);\n\t}\n\n\t/**\n\t * Handles the focus event. If focus moves backwards from the first focusable element,\n\t * it is cycled to the last focusable element, and vice versa.\n\t *\n\t * @param {FocusEvent} e - The focus event object.\n\t */\n\thandleFocus = (e) => {\n\t\tconst trap = this.closest('focus-trap');\n\t\tconst focusableElements = getFocusableElements(trap);\n\n\t\tif (focusableElements.length === 0) return;\n\n\t\tconst firstElement = focusableElements[0];\n\t\tconst lastElement =\n\t\t\tfocusableElements[focusableElements.length - 1];\n\n\t\tif (e.relatedTarget === firstElement) {\n\t\t\tlastElement.focus();\n\t\t} else {\n\t\t\tfirstElement.focus();\n\t\t}\n\t};\n}\n\nclass FocusTrapEnd extends HTMLElement {\n\t/**\n\t * Called when the element is connected to the DOM.\n\t * Sets the tabindex and adds the focus event listener.\n\t */\n\tconnectedCallback() {\n\t\tthis.setAttribute('tabindex', '0');\n\t\tthis.addEventListener('focus', this.handleFocus);\n\t}\n\n\t/**\n\t * Called when the element is disconnected from the DOM.\n\t * Removes the focus event listener.\n\t */\n\tdisconnectedCallback() {\n\t\tthis.removeEventListener('focus', this.handleFocus);\n\t}\n\n\t/**\n\t * Handles the focus event. When the trap end is focused, focus is shifted back to the trap start.\n\t */\n\thandleFocus = () => {\n\t\tconst trap = this.closest('focus-trap');\n\t\tconst trapStart = trap.querySelector('focus-trap-start');\n\t\ttrapStart.focus();\n\t};\n}\n\nif (!customElements.get('focus-trap')) {\n\tcustomElements.define('focus-trap', FocusTrap);\n}\nif (!customElements.get('focus-trap-start')) {\n\tcustomElements.define('focus-trap-start', FocusTrapStart);\n}\nif (!customElements.get('focus-trap-end')) {\n\tcustomElements.define('focus-trap-end', FocusTrapEnd);\n}\n//# sourceMappingURL=focus-trap.esm.js.map\n","import './index.scss';\nimport '@magic-spells/focus-trap';\n\n/**\n * Custom element that creates an accessible modal dialog panel with focus management\n * @extends HTMLElement\n */\nclass DialogPanel extends HTMLElement {\n\t#handleTransitionEnd;\n\n\t/**\n\t * Clean up event listeners when component is removed from DOM\n\t */\n\tdisconnectedCallback() {\n\t\tconst _ = this;\n\t\tif (_.contentPanel) {\n\t\t\t_.contentPanel.removeEventListener(\n\t\t\t\t'transitionend',\n\t\t\t\t_.#handleTransitionEnd\n\t\t\t);\n\t\t}\n\t}\n\t/**\n\t * Initializes the dialog panel, sets up focus trap and overlay\n\t */\n\tconstructor() {\n\t\tsuper();\n\t\tconst _ = this;\n\t\t_.id = _.getAttribute('id');\n\t\t_.setAttribute('role', 'dialog');\n\t\t_.setAttribute('aria-modal', 'true');\n\t\t_.setAttribute('aria-hidden', 'true');\n\n\t\t_.contentPanel = _.querySelector('dialog-content');\n\t\t_.triggerEl = null;\n\n\t\t// Create a handler for transition end events\n\t\t_.#handleTransitionEnd = (e) => {\n\t\t\tif (\n\t\t\t\te.propertyName === 'opacity' &&\n\t\t\t\t_.getAttribute('aria-hidden') === 'true'\n\t\t\t) {\n\t\t\t\t_.contentPanel.classList.add('hidden');\n\n\t\t\t\t// Dispatch afterHide event - dialog has completed its transition\n\t\t\t\t_.dispatchEvent(\n\t\t\t\t\tnew CustomEvent('afterHide', {\n\t\t\t\t\t\tbubbles: true,\n\t\t\t\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t}\n\t\t};\n\n\t\t// Set up focus-trap inside dialog-content\n\t\t// Check if focus-trap already exists\n\t\t_.focusTrap = _.contentPanel.querySelector('focus-trap');\n\t\tif (!_.focusTrap) {\n\t\t\t_.focusTrap = document.createElement('focus-trap');\n\n\t\t\t// Move all existing dialog-content children into focus-trap\n\t\t\tconst existingContent = Array.from(_.contentPanel.childNodes);\n\t\t\texistingContent.forEach((child) =>\n\t\t\t\t_.focusTrap.appendChild(child)\n\t\t\t);\n\n\t\t\t// Insert focus-trap inside dialog-content\n\t\t\t_.contentPanel.appendChild(_.focusTrap);\n\t\t}\n\n\t\t// Ensure we have labelledby and describedby references\n\t\tif (!_.getAttribute('aria-labelledby')) {\n\t\t\tconst heading = _.querySelector('h1, h2, h3');\n\t\t\tif (heading && !heading.id) {\n\t\t\t\theading.id = `${_.id}-title`;\n\t\t\t}\n\t\t\tif (heading?.id) {\n\t\t\t\t_.setAttribute('aria-labelledby', heading.id);\n\t\t\t}\n\t\t}\n\n\t\t// Add modal overlay\n\t\t_.prepend(document.createElement('dialog-overlay'));\n\t\t_.#bindUI();\n\t\t_.#bindKeyboard();\n\t}\n\n\t/**\n\t * Binds click events for showing and hiding the dialog\n\t * @private\n\t */\n\t#bindUI() {\n\t\tconst _ = this;\n\n\t\t// Handle trigger buttons\n\t\tdocument.addEventListener('click', (e) => {\n\t\t\tconst trigger = e.target.closest(`[aria-controls=\"${_.id}\"]`);\n\t\t\tif (!trigger) return;\n\n\t\t\tif (trigger.getAttribute('data-prevent-default') === 'true') {\n\t\t\t\te.preventDefault();\n\t\t\t}\n\n\t\t\t_.show(trigger);\n\t\t});\n\n\t\t// Handle close buttons\n\t\t_.addEventListener('click', (e) => {\n\t\t\tif (!e.target.closest('[data-action-hide-dialog]')) return;\n\t\t\t_.hide();\n\t\t});\n\n\t\t// Add transition end listener\n\t\t_.contentPanel.addEventListener(\n\t\t\t'transitionend',\n\t\t\t_.#handleTransitionEnd\n\t\t);\n\t}\n\n\t/**\n\t * Binds keyboard events for accessibility\n\t * @private\n\t */\n\t#bindKeyboard() {\n\t\tthis.addEventListener('keydown', (e) => {\n\t\t\tif (e.key === 'Escape') {\n\t\t\t\tthis.hide();\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Shows the dialog and traps focus within it\n\t * @param {HTMLElement} [triggerEl=null] - The element that triggered the dialog\n\t * @fires DialogPanel#beforeShow - Fired before the dialog starts to show\n\t * @fires DialogPanel#show - Fired when the dialog has been shown\n\t * @returns {boolean} False if the show was prevented by a beforeShow event handler\n\t */\n\tshow(triggerEl = null) {\n\t\tconst _ = this;\n\t\t_.triggerEl = triggerEl || false;\n\n\t\t// Dispatch beforeShow event - allows preventing the dialog from opening\n\t\tconst beforeShowEvent = new CustomEvent('beforeShow', {\n\t\t\tbubbles: true,\n\t\t\tcancelable: true,\n\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t});\n\n\t\tconst showAllowed = _.dispatchEvent(beforeShowEvent);\n\n\t\t// If event was canceled (preventDefault was called), don't show the dialog\n\t\tif (!showAllowed) return false;\n\n\t\t// Add open attribute for CSS :has() selector\n\t\t_.setAttribute('open', '');\n\n\t\t// Remove the hidden class first to ensure content is rendered\n\t\t_.contentPanel.classList.remove('hidden');\n\n\t\t// Give the browser a moment to process before starting animation\n\t\trequestAnimationFrame(() => {\n\t\t\t// Update ARIA states\n\t\t\t_.setAttribute('aria-hidden', 'false');\n\n\t\t\t// set trigger element to expanded: true\n\t\t\tif (_.triggerEl) {\n\t\t\t\t_.triggerEl.setAttribute('aria-expanded', 'true');\n\t\t\t}\n\n\t\t\t// Focus management\n\t\t\tconst firstFocusable = _.querySelector(\n\t\t\t\t'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n\t\t\t);\n\t\t\tif (firstFocusable) {\n\t\t\t\trequestAnimationFrame(() => {\n\t\t\t\t\tfirstFocusable.focus();\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Dispatch show event - dialog is now visible\n\t\t\t_.dispatchEvent(\n\t\t\t\tnew CustomEvent('show', {\n\t\t\t\t\tbubbles: true,\n\t\t\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t\t\t})\n\t\t\t);\n\t\t});\n\t}\n\n\t/**\n\t * Hides the dialog and restores focus\n\t * @fires DialogPanel#beforeHide - Fired before the dialog starts to hide\n\t * @fires DialogPanel#hide - Fired when the dialog has started hiding (transition begins)\n\t * @fires DialogPanel#afterHide - Fired when the dialog has completed its hide transition\n\t * @returns {boolean} False if the hide was prevented by a beforeHide event handler\n\t */\n\thide() {\n\t\tconst _ = this;\n\n\t\t// Dispatch beforeHide event - allows preventing the dialog from closing\n\t\tconst beforeHideEvent = new CustomEvent('beforeHide', {\n\t\t\tbubbles: true,\n\t\t\tcancelable: true,\n\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t});\n\n\t\tconst hideAllowed = _.dispatchEvent(beforeHideEvent);\n\n\t\t// If event was canceled (preventDefault was called), don't hide the dialog\n\t\tif (!hideAllowed) return false;\n\n\t\t// ensure focus is moved out of the dialog\n\t\tconst activeElement = document.activeElement;\n\t\tif (activeElement && _.contains(activeElement)) {\n\t\t\t// Blur the currently focused element if it's inside the dialog\n\t\t\tactiveElement.blur();\n\t\t}\n\n\t\t// Update ARIA states and restore focus\n\t\tif (_.triggerEl) {\n\t\t\t// remove focus from modal panel first\n\t\t\t_.triggerEl.focus();\n\t\t\t// mark trigger as no longer expanded\n\t\t\t_.triggerEl.setAttribute('aria-expanded', 'false');\n\t\t} else {\n\t\t\t// Move focus to body to ensure it's not trapped\n\t\t\tdocument.body.focus();\n\t\t}\n\n\t\t// Set aria-hidden to start transition\n\t\t// The transitionend event handler will add display:none when complete\n\t\t_.setAttribute('aria-hidden', 'true');\n\t\t// Remove open attribute for CSS :has() selector\n\n\t\t_.removeAttribute('open');\n\n\t\t// Dispatch hide event - dialog is now starting to hide\n\t\t_.dispatchEvent(\n\t\t\tnew CustomEvent('hide', {\n\t\t\t\tbubbles: true,\n\t\t\t\tdetail: { triggerElement: _.triggerEl },\n\t\t\t})\n\t\t);\n\n\t\treturn true;\n\t}\n}\n\n/**\n * Custom element that creates a clickable overlay for the dialog\n * @extends HTMLElement\n */\nclass DialogOverlay extends HTMLElement {\n\tconstructor() {\n\t\tsuper();\n\t\tthis.setAttribute('tabindex', '-1');\n\t\tthis.dialogPanel = this.closest('dialog-panel');\n\t\tthis.#bindUI();\n\t}\n\n\t#bindUI() {\n\t\tthis.addEventListener('click', () => {\n\t\t\tthis.dialogPanel.hide();\n\t\t});\n\t}\n}\n\n/**\n * Custom element that wraps the content of the dialog\n * @extends HTMLElement\n */\nclass DialogContent extends HTMLElement {\n\tconstructor() {\n\t\tsuper();\n\t\tthis.setAttribute('role', 'document');\n\t}\n}\n\nif (!customElements.get('dialog-panel')) {\n\tcustomElements.define('dialog-panel', DialogPanel);\n}\nif (!customElements.get('dialog-overlay')) {\n\tcustomElements.define('dialog-overlay', DialogOverlay);\n}\nif (!customElements.get('dialog-content')) {\n\tcustomElements.define('dialog-content', DialogContent);\n}\n\nexport { DialogPanel, DialogOverlay, DialogContent };\nexport default DialogPanel;\n"],"names":[],"mappings":"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","x_google_ignoreList":[0]}
@@ -1 +1 @@
1
- :root{--dp-panel-top:0;--dp-panel-left:0;--dp-panel-width:100vw;--dp-panel-height:100vh;--dp-panel-z-index:10;--dp-overlay-z-index:1000;--dp-overlay-background:rgba(20,23,26,.4);--dp-overlay-backdrop-filter:blur(2px) saturate(120%);--dp-overlay-transition:all 400ms ease-out;--dp-content-display:block;--dp-content-background:#fff;--dp-content-z-index:1001;--dp-content-shadow:0 10px 25px rgba(0,0,0,.15);--dp-content-border-radius:8px;--dp-transition-duration:400ms;--dp-transition-timing:ease-out}dialog-panel{display:contents}dialog-panel[aria-hidden=false] dialog-content,dialog-panel[aria-hidden=false] dialog-overlay{filter:blur(0);opacity:1;pointer-events:auto;transform:scale(1)}dialog-overlay{backdrop-filter:var(--dp-overlay-backdrop-filter,blur(2px) saturate(120%));background-color:var(--dp-overlay-background,rgba(20,23,26,.4));height:100vh;left:0;top:0;transition:var(--dp-overlay-transition,all .3s ease-out);width:100vw;z-index:var(--dp-overlay-z-index,1000)}dialog-content,dialog-overlay{opacity:0;pointer-events:none;position:fixed}dialog-content{background:var(--dp-content-background,#fff);border-radius:var(--dp-content-border-radius,8px);box-shadow:var(--dp-content-shadow,0 10px 25px rgba(0,0,0,.15));display:var(--dp-content-display,block);left:50%;max-height:85vh;max-width:90vw;overflow:auto;top:50%;transform:translate(-50%,-50%) scale(.95);transition:opacity var(--dp-transition-duration,.3s) var(--dp-transition-timing,ease-out),transform var(--dp-transition-duration,.3s) var(--dp-transition-timing,ease-out);z-index:var(--dp-content-z-index,1001)}dialog-panel[aria-hidden=false] dialog-content{transform:translate(-50%,-50%) scale(1)}dialog-content.hidden{display:none}
1
+ :root{--dp-overlay-z-index:1000;--dp-overlay-background:rgba(20,23,26,.4);--dp-overlay-backdrop-filter:blur(2px) saturate(120%);--dp-overlay-transition:all 400ms ease-out;--dp-content-display:block;--dp-content-background:#fff;--dp-content-z-index:1001;--dp-content-shadow:0 10px 25px rgba(0,0,0,.15);--dp-content-border-radius:8px;--dp-transition-duration:400ms;--dp-transition-timing:ease-out}dialog-panel{display:contents}dialog-panel[aria-hidden=false] dialog-content,dialog-panel[aria-hidden=false] dialog-overlay{filter:blur(0);opacity:1;pointer-events:auto;transform:scale(1)}dialog-overlay{backdrop-filter:var(--dp-overlay-backdrop-filter,blur(2px) saturate(120%));background-color:var(--dp-overlay-background,rgba(20,23,26,.4));height:100vh;left:0;top:0;transition:var(--dp-overlay-transition,all .3s ease-out);width:100vw;z-index:var(--dp-overlay-z-index,1000)}dialog-content,dialog-overlay{opacity:0;pointer-events:none;position:fixed}dialog-content{background:var(--dp-content-background,#fff);border-radius:var(--dp-content-border-radius,8px);box-shadow:var(--dp-content-shadow,0 10px 25px rgba(0,0,0,.15));display:var(--dp-content-display,block);left:50%;max-height:85vh;max-width:90vw;overflow:auto;top:50%;transform:translate(-50%,-50%) scale(.95);transition:opacity var(--dp-transition-duration,.3s) var(--dp-transition-timing,ease-out),transform var(--dp-transition-duration,.3s) var(--dp-transition-timing,ease-out);z-index:var(--dp-content-z-index,1001)}dialog-panel[aria-hidden=false] dialog-content{transform:translate(-50%,-50%) scale(1)}dialog-content.hidden{display:none}body:has(dialog-panel[open]){overflow:hidden}
@@ -1 +1 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).DialogPanel={})}(this,(function(e){"use strict";const t=e=>Array.from(e.querySelectorAll('summary, a[href], button:not(:disabled), [tabindex]:not([tabindex^="-"]):not(focus-trap-start):not(focus-trap-end), [draggable], area, input:not([type=hidden]):not(:disabled), select:not(:disabled), textarea:not(:disabled), object, iframe'));class FocusTrap extends HTMLElement{static styleInjected=!1;constructor(){super(),this.trapStart=null,this.trapEnd=null,FocusTrap.styleInjected||(this.injectStyles(),FocusTrap.styleInjected=!0)}injectStyles(){const e=document.createElement("style");e.textContent="\n focus-trap-start,\n focus-trap-end {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n border: 0;\n clip: rect(0, 0, 0, 0);\n overflow: hidden;\n white-space: nowrap;\n }\n ",document.head.appendChild(e)}connectedCallback(){this.setupTrap(),this.addEventListener("keydown",this.handleKeyDown)}disconnectedCallback(){this.removeEventListener("keydown",this.handleKeyDown)}setupTrap(){0!==t(this).length&&(this.trapStart=document.createElement("focus-trap-start"),this.trapEnd=document.createElement("focus-trap-end"),this.prepend(this.trapStart),this.append(this.trapEnd))}handleKeyDown=e=>{"Escape"===e.key&&(e.preventDefault(),this.exitTrap())};exitTrap(){const e=this.closest('[aria-hidden="false"]');if(!e)return;e.setAttribute("aria-hidden","true");const t=document.querySelector(`[aria-expanded="true"][aria-controls="${e.id}"]`);t&&(t.setAttribute("aria-expanded","false"),t.focus())}}class FocusTrapStart extends HTMLElement{connectedCallback(){this.setAttribute("tabindex","0"),this.addEventListener("focus",this.handleFocus)}disconnectedCallback(){this.removeEventListener("focus",this.handleFocus)}handleFocus=e=>{const n=this.closest("focus-trap"),s=t(n);if(0===s.length)return;const a=s[0],i=s[s.length-1];e.relatedTarget===a?i.focus():a.focus()}}class FocusTrapEnd extends HTMLElement{connectedCallback(){this.setAttribute("tabindex","0"),this.addEventListener("focus",this.handleFocus)}disconnectedCallback(){this.removeEventListener("focus",this.handleFocus)}handleFocus=()=>{this.closest("focus-trap").querySelector("focus-trap-start").focus()}}customElements.define("focus-trap",FocusTrap),customElements.define("focus-trap-start",FocusTrapStart),customElements.define("focus-trap-end",FocusTrapEnd);class DialogPanel extends HTMLElement{#e;#t=0;disconnectedCallback(){const e=this;e.contentPanel&&e.contentPanel.removeEventListener("transitionend",e.#e),document.body.classList.remove("overflow-hidden"),this.#n()}#s(){this.#t=window.pageYOffset,document.body.classList.add("overflow-hidden"),document.body.style.top=`-${this.#t}px`}#n(){document.body.classList.remove("overflow-hidden"),document.body.style.removeProperty("top"),window.scrollTo(0,this.#t)}constructor(){super();const e=this;if(e.id=e.getAttribute("id"),e.setAttribute("role","dialog"),e.setAttribute("aria-modal","true"),e.setAttribute("aria-hidden","true"),e.contentPanel=e.querySelector("dialog-content"),e.focusTrap=document.createElement("focus-trap"),e.triggerEl=null,e.#e=t=>{"opacity"===t.propertyName&&"true"===e.getAttribute("aria-hidden")&&(e.contentPanel.classList.add("hidden"),e.dispatchEvent(new CustomEvent("afterHide",{bubbles:!0,detail:{triggerElement:e.triggerEl}})))},!e.getAttribute("aria-labelledby")){const t=e.querySelector("h1, h2, h3");t&&!t.id&&(t.id=`${e.id}-title`),t?.id&&e.setAttribute("aria-labelledby",t.id)}e.contentPanel.parentNode.insertBefore(e.focusTrap,e.contentPanel),e.focusTrap.appendChild(e.contentPanel),e.focusTrap.setupTrap(),e.prepend(document.createElement("dialog-overlay")),e.#a(),e.#i()}#a(){const e=this;document.addEventListener("click",(t=>{const n=t.target.closest(`[aria-controls="${e.id}"]`);n&&("true"===n.getAttribute("data-prevent-default")&&t.preventDefault(),e.show(n))})),e.addEventListener("click",(t=>{t.target.closest('[data-action="hide-dialog"]')&&e.hide()})),e.contentPanel.addEventListener("transitionend",e.#e)}#i(){this.addEventListener("keydown",(e=>{"Escape"===e.key&&this.hide()}))}show(e=null){const t=this;t.triggerEl=e||!1;const n=new CustomEvent("beforeShow",{bubbles:!0,cancelable:!0,detail:{triggerElement:t.triggerEl}});return!!t.dispatchEvent(n)&&(t.contentPanel.classList.remove("hidden"),requestAnimationFrame((()=>{t.setAttribute("aria-hidden","false"),t.triggerEl&&t.triggerEl.setAttribute("aria-expanded","true"),t.#s();const e=t.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');e&&requestAnimationFrame((()=>{e.focus()})),t.dispatchEvent(new CustomEvent("show",{bubbles:!0,detail:{triggerElement:t.triggerEl}}))})),!0)}hide(){const e=this,t=new CustomEvent("beforeHide",{bubbles:!0,cancelable:!0,detail:{triggerElement:e.triggerEl}});return!!e.dispatchEvent(t)&&(e.#n(),e.triggerEl&&(e.triggerEl.focus(),e.triggerEl.setAttribute("aria-expanded","false")),e.setAttribute("aria-hidden","true"),e.dispatchEvent(new CustomEvent("hide",{bubbles:!0,detail:{triggerElement:e.triggerEl}})),!0)}}class DialogOverlay extends HTMLElement{constructor(){super(),this.setAttribute("tabindex","-1"),this.setAttribute("aria-hidden","true"),this.dialogPanel=this.closest("dialog-panel"),this.#a()}#a(){this.addEventListener("click",(()=>{this.dialogPanel.hide()}))}}class DialogContent extends HTMLElement{constructor(){super(),this.setAttribute("role","document")}}customElements.get("dialog-panel")||customElements.define("dialog-panel",DialogPanel),customElements.get("dialog-overlay")||customElements.define("dialog-overlay",DialogOverlay),customElements.get("dialog-content")||customElements.define("dialog-content",DialogContent),e.DialogContent=DialogContent,e.DialogOverlay=DialogOverlay,e.DialogPanel=DialogPanel,e.default=DialogPanel,Object.defineProperty(e,"__esModule",{value:!0})}));
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).DialogPanel={})}(this,function(e){"use strict";const t=e=>Array.from(e.querySelectorAll('summary, a[href], button:not(:disabled), [tabindex]:not([tabindex^="-"]):not(focus-trap-start):not(focus-trap-end), [draggable], area, input:not([type=hidden]):not(:disabled), select:not(:disabled), textarea:not(:disabled), object, iframe'));class FocusTrap extends HTMLElement{static styleInjected=!1;constructor(){super(),this.trapStart=null,this.trapEnd=null,FocusTrap.styleInjected||(this.injectStyles(),FocusTrap.styleInjected=!0)}injectStyles(){const e=document.createElement("style");e.textContent="\n focus-trap-start,\n focus-trap-end {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n border: 0;\n clip: rect(0, 0, 0, 0);\n overflow: hidden;\n white-space: nowrap;\n }\n ",document.head.appendChild(e)}connectedCallback(){this.setupTrap(),this.addEventListener("keydown",this.handleKeyDown)}disconnectedCallback(){this.removeEventListener("keydown",this.handleKeyDown)}setupTrap(){0!==t(this).length&&(this.trapStart=document.createElement("focus-trap-start"),this.trapEnd=document.createElement("focus-trap-end"),this.prepend(this.trapStart),this.append(this.trapEnd))}handleKeyDown=e=>{"Escape"===e.key&&(e.preventDefault(),this.exitTrap())};exitTrap(){const e=this.closest('[aria-hidden="false"]');if(!e)return;e.setAttribute("aria-hidden","true");const t=document.querySelector(`[aria-expanded="true"][aria-controls="${e.id}"]`);t&&(t.setAttribute("aria-expanded","false"),t.focus())}}class FocusTrapStart extends HTMLElement{connectedCallback(){this.setAttribute("tabindex","0"),this.addEventListener("focus",this.handleFocus)}disconnectedCallback(){this.removeEventListener("focus",this.handleFocus)}handleFocus=e=>{const n=this.closest("focus-trap"),a=t(n);if(0===a.length)return;const s=a[0],i=a[a.length-1];e.relatedTarget===s?i.focus():s.focus()}}class FocusTrapEnd extends HTMLElement{connectedCallback(){this.setAttribute("tabindex","0"),this.addEventListener("focus",this.handleFocus)}disconnectedCallback(){this.removeEventListener("focus",this.handleFocus)}handleFocus=()=>{this.closest("focus-trap").querySelector("focus-trap-start").focus()}}customElements.get("focus-trap")||customElements.define("focus-trap",FocusTrap),customElements.get("focus-trap-start")||customElements.define("focus-trap-start",FocusTrapStart),customElements.get("focus-trap-end")||customElements.define("focus-trap-end",FocusTrapEnd);class DialogPanel extends HTMLElement{#e;disconnectedCallback(){const e=this;e.contentPanel&&e.contentPanel.removeEventListener("transitionend",e.#e)}constructor(){super();const e=this;if(e.id=e.getAttribute("id"),e.setAttribute("role","dialog"),e.setAttribute("aria-modal","true"),e.setAttribute("aria-hidden","true"),e.contentPanel=e.querySelector("dialog-content"),e.triggerEl=null,e.#e=t=>{"opacity"===t.propertyName&&"true"===e.getAttribute("aria-hidden")&&(e.contentPanel.classList.add("hidden"),e.dispatchEvent(new CustomEvent("afterHide",{bubbles:!0,detail:{triggerElement:e.triggerEl}})))},e.focusTrap=e.contentPanel.querySelector("focus-trap"),!e.focusTrap){e.focusTrap=document.createElement("focus-trap");Array.from(e.contentPanel.childNodes).forEach(t=>e.focusTrap.appendChild(t)),e.contentPanel.appendChild(e.focusTrap)}if(!e.getAttribute("aria-labelledby")){const t=e.querySelector("h1, h2, h3");t&&!t.id&&(t.id=`${e.id}-title`),t?.id&&e.setAttribute("aria-labelledby",t.id)}e.prepend(document.createElement("dialog-overlay")),e.#t(),e.#n()}#t(){const e=this;document.addEventListener("click",t=>{const n=t.target.closest(`[aria-controls="${e.id}"]`);n&&("true"===n.getAttribute("data-prevent-default")&&t.preventDefault(),e.show(n))}),e.addEventListener("click",t=>{t.target.closest("[data-action-hide-dialog]")&&e.hide()}),e.contentPanel.addEventListener("transitionend",e.#e)}#n(){this.addEventListener("keydown",e=>{"Escape"===e.key&&this.hide()})}show(e=null){const t=this;t.triggerEl=e||!1;const n=new CustomEvent("beforeShow",{bubbles:!0,cancelable:!0,detail:{triggerElement:t.triggerEl}});if(!t.dispatchEvent(n))return!1;t.setAttribute("open",""),t.contentPanel.classList.remove("hidden"),requestAnimationFrame(()=>{t.setAttribute("aria-hidden","false"),t.triggerEl&&t.triggerEl.setAttribute("aria-expanded","true");const e=t.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');e&&requestAnimationFrame(()=>{e.focus()}),t.dispatchEvent(new CustomEvent("show",{bubbles:!0,detail:{triggerElement:t.triggerEl}}))})}hide(){const e=this,t=new CustomEvent("beforeHide",{bubbles:!0,cancelable:!0,detail:{triggerElement:e.triggerEl}});if(!e.dispatchEvent(t))return!1;const n=document.activeElement;return n&&e.contains(n)&&n.blur(),e.triggerEl?(e.triggerEl.focus(),e.triggerEl.setAttribute("aria-expanded","false")):document.body.focus(),e.setAttribute("aria-hidden","true"),e.removeAttribute("open"),e.dispatchEvent(new CustomEvent("hide",{bubbles:!0,detail:{triggerElement:e.triggerEl}})),!0}}class DialogOverlay extends HTMLElement{constructor(){super(),this.setAttribute("tabindex","-1"),this.dialogPanel=this.closest("dialog-panel"),this.#t()}#t(){this.addEventListener("click",()=>{this.dialogPanel.hide()})}}class DialogContent extends HTMLElement{constructor(){super(),this.setAttribute("role","document")}}customElements.get("dialog-panel")||customElements.define("dialog-panel",DialogPanel),customElements.get("dialog-overlay")||customElements.define("dialog-overlay",DialogOverlay),customElements.get("dialog-content")||customElements.define("dialog-content",DialogContent),e.DialogContent=DialogContent,e.DialogOverlay=DialogOverlay,e.DialogPanel=DialogPanel,e.default=DialogPanel,Object.defineProperty(e,"__esModule",{value:!0})});
@@ -65,9 +65,14 @@ dialog-content {
65
65
  dialog-panel[aria-hidden='false'] & {
66
66
  transform: translate(-50%, -50%) scale(1);
67
67
  }
68
+ }
68
69
 
69
- /* When explicitly hidden, remove from layout */
70
- &.hidden {
71
- display: none;
72
- }
70
+ /* When explicitly hidden, remove from layout */
71
+ dialog-content.hidden {
72
+ display: none;
73
+ }
74
+
75
+ // Body scroll lock when dialog is open using :has() selector
76
+ body:has(dialog-panel[open]) {
77
+ overflow: hidden;
73
78
  }
@@ -2,13 +2,6 @@
2
2
  // SCSS variables for internal component use and customization
3
3
  // These variables can be customized by the user
4
4
 
5
- // Layout
6
- $panel-top: 0 !default;
7
- $panel-left: 0 !default;
8
- $panel-width: 100vw !default;
9
- $panel-height: 100vh !default;
10
- $panel-z-index: 10 !default;
11
-
12
5
  // Overlay
13
6
  $overlay-z-index: 1000 !default;
14
7
  $overlay-background: rgba(20, 23, 26, 0.4) !default;
@@ -28,13 +21,6 @@ $transition-timing: ease-out !default;
28
21
 
29
22
  // Define CSS Custom Properties using SCSS values
30
23
  :root {
31
- // Layout
32
- --dp-panel-top: #{$panel-top};
33
- --dp-panel-left: #{$panel-left};
34
- --dp-panel-width: #{$panel-width};
35
- --dp-panel-height: #{$panel-height};
36
- --dp-panel-z-index: #{$panel-z-index};
37
-
38
24
  // Overlay
39
25
  --dp-overlay-z-index: #{$overlay-z-index};
40
26
  --dp-overlay-background: #{$overlay-background};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magic-spells/dialog-panel",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "description": "A lightweight, customizable Dialog Panel web component for creating accessible and responsive modal dialogs.",
5
5
  "author": "Cory Schulz",
6
6
  "license": "MIT",
@@ -63,11 +63,11 @@
63
63
  "not ie <= 11"
64
64
  ],
65
65
  "devDependencies": {
66
- "@eslint/js": "^8.57.0",
66
+ "@eslint/js": "^9.38.0",
67
67
  "@rollup/plugin-node-resolve": "^15.2.3",
68
68
  "@rollup/plugin-terser": "^0.4.4",
69
- "eslint": "^8.0.0",
70
- "globals": "^13.24.0",
69
+ "eslint": "^9.38.0",
70
+ "globals": "^15.15.0",
71
71
  "prettier": "^3.3.3",
72
72
  "rollup": "^3.0.0",
73
73
  "rollup-plugin-copy": "^3.5.0",
@@ -7,7 +7,6 @@ import '@magic-spells/focus-trap';
7
7
  */
8
8
  class DialogPanel extends HTMLElement {
9
9
  #handleTransitionEnd;
10
- #scrollPosition = 0;
11
10
 
12
11
  /**
13
12
  * Clean up event listeners when component is removed from DOM
@@ -20,38 +19,6 @@ class DialogPanel extends HTMLElement {
20
19
  _.#handleTransitionEnd
21
20
  );
22
21
  }
23
-
24
- // Ensure body scroll is restored if component is removed while open
25
- document.body.classList.remove('overflow-hidden');
26
- this.#restoreScroll();
27
- }
28
-
29
- /**
30
- * Saves current scroll position and locks body scrolling
31
- * @private
32
- */
33
- #lockScroll() {
34
- const _ = this;
35
- // Save current scroll position
36
- _.#scrollPosition = window.pageYOffset;
37
-
38
- // Apply fixed position to body
39
- document.body.classList.add('overflow-hidden');
40
- document.body.style.top = `-${_.#scrollPosition}px`;
41
- }
42
-
43
- /**
44
- * Restores scroll position when dialog is closed
45
- * @private
46
- */
47
- #restoreScroll() {
48
- const _ = this;
49
- // Remove fixed positioning
50
- document.body.classList.remove('overflow-hidden');
51
- document.body.style.removeProperty('top');
52
-
53
- // Restore scroll position
54
- window.scrollTo(0, _.#scrollPosition);
55
22
  }
56
23
  /**
57
24
  * Initializes the dialog panel, sets up focus trap and overlay
@@ -65,7 +32,6 @@ class DialogPanel extends HTMLElement {
65
32
  _.setAttribute('aria-hidden', 'true');
66
33
 
67
34
  _.contentPanel = _.querySelector('dialog-content');
68
- _.focusTrap = document.createElement('focus-trap');
69
35
  _.triggerEl = null;
70
36
 
71
37
  // Create a handler for transition end events
@@ -86,6 +52,22 @@ class DialogPanel extends HTMLElement {
86
52
  }
87
53
  };
88
54
 
55
+ // Set up focus-trap inside dialog-content
56
+ // Check if focus-trap already exists
57
+ _.focusTrap = _.contentPanel.querySelector('focus-trap');
58
+ if (!_.focusTrap) {
59
+ _.focusTrap = document.createElement('focus-trap');
60
+
61
+ // Move all existing dialog-content children into focus-trap
62
+ const existingContent = Array.from(_.contentPanel.childNodes);
63
+ existingContent.forEach((child) =>
64
+ _.focusTrap.appendChild(child)
65
+ );
66
+
67
+ // Insert focus-trap inside dialog-content
68
+ _.contentPanel.appendChild(_.focusTrap);
69
+ }
70
+
89
71
  // Ensure we have labelledby and describedby references
90
72
  if (!_.getAttribute('aria-labelledby')) {
91
73
  const heading = _.querySelector('h1, h2, h3');
@@ -97,14 +79,6 @@ class DialogPanel extends HTMLElement {
97
79
  }
98
80
  }
99
81
 
100
- _.contentPanel.parentNode.insertBefore(
101
- _.focusTrap,
102
- _.contentPanel
103
- );
104
- _.focusTrap.appendChild(_.contentPanel);
105
-
106
- _.focusTrap.setupTrap();
107
-
108
82
  // Add modal overlay
109
83
  _.prepend(document.createElement('dialog-overlay'));
110
84
  _.#bindUI();
@@ -132,7 +106,7 @@ class DialogPanel extends HTMLElement {
132
106
 
133
107
  // Handle close buttons
134
108
  _.addEventListener('click', (e) => {
135
- if (!e.target.closest('[data-action="hide-dialog"]')) return;
109
+ if (!e.target.closest('[data-action-hide-dialog]')) return;
136
110
  _.hide();
137
111
  });
138
112
 
@@ -178,6 +152,9 @@ class DialogPanel extends HTMLElement {
178
152
  // If event was canceled (preventDefault was called), don't show the dialog
179
153
  if (!showAllowed) return false;
180
154
 
155
+ // Add open attribute for CSS :has() selector
156
+ _.setAttribute('open', '');
157
+
181
158
  // Remove the hidden class first to ensure content is rendered
182
159
  _.contentPanel.classList.remove('hidden');
183
160
 
@@ -185,13 +162,12 @@ class DialogPanel extends HTMLElement {
185
162
  requestAnimationFrame(() => {
186
163
  // Update ARIA states
187
164
  _.setAttribute('aria-hidden', 'false');
165
+
166
+ // set trigger element to expanded: true
188
167
  if (_.triggerEl) {
189
168
  _.triggerEl.setAttribute('aria-expanded', 'true');
190
169
  }
191
170
 
192
- // Lock body scrolling and save scroll position
193
- _.#lockScroll();
194
-
195
171
  // Focus management
196
172
  const firstFocusable = _.querySelector(
197
173
  'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
@@ -210,8 +186,6 @@ class DialogPanel extends HTMLElement {
210
186
  })
211
187
  );
212
188
  });
213
-
214
- return true;
215
189
  }
216
190
 
217
191
  /**
@@ -236,20 +210,30 @@ class DialogPanel extends HTMLElement {
236
210
  // If event was canceled (preventDefault was called), don't hide the dialog
237
211
  if (!hideAllowed) return false;
238
212
 
239
- // Restore body scroll and scroll position
240
- _.#restoreScroll();
213
+ // ensure focus is moved out of the dialog
214
+ const activeElement = document.activeElement;
215
+ if (activeElement && _.contains(activeElement)) {
216
+ // Blur the currently focused element if it's inside the dialog
217
+ activeElement.blur();
218
+ }
241
219
 
242
- // Update ARIA states
220
+ // Update ARIA states and restore focus
243
221
  if (_.triggerEl) {
244
222
  // remove focus from modal panel first
245
223
  _.triggerEl.focus();
246
224
  // mark trigger as no longer expanded
247
225
  _.triggerEl.setAttribute('aria-expanded', 'false');
226
+ } else {
227
+ // Move focus to body to ensure it's not trapped
228
+ document.body.focus();
248
229
  }
249
230
 
250
231
  // Set aria-hidden to start transition
251
232
  // The transitionend event handler will add display:none when complete
252
233
  _.setAttribute('aria-hidden', 'true');
234
+ // Remove open attribute for CSS :has() selector
235
+
236
+ _.removeAttribute('open');
253
237
 
254
238
  // Dispatch hide event - dialog is now starting to hide
255
239
  _.dispatchEvent(
@@ -270,8 +254,7 @@ class DialogPanel extends HTMLElement {
270
254
  class DialogOverlay extends HTMLElement {
271
255
  constructor() {
272
256
  super();
273
- this.setAttribute('tabindex', '-1'); // Changed to -1 as it shouldn't be focusable
274
- this.setAttribute('aria-hidden', 'true');
257
+ this.setAttribute('tabindex', '-1');
275
258
  this.dialogPanel = this.closest('dialog-panel');
276
259
  this.#bindUI();
277
260
  }
@@ -290,7 +273,7 @@ class DialogOverlay extends HTMLElement {
290
273
  class DialogContent extends HTMLElement {
291
274
  constructor() {
292
275
  super();
293
- this.setAttribute('role', 'document'); // Optional: helps with document structure
276
+ this.setAttribute('role', 'document');
294
277
  }
295
278
  }
296
279
 
@@ -65,9 +65,14 @@ dialog-content {
65
65
  dialog-panel[aria-hidden='false'] & {
66
66
  transform: translate(-50%, -50%) scale(1);
67
67
  }
68
+ }
68
69
 
69
- /* When explicitly hidden, remove from layout */
70
- &.hidden {
71
- display: none;
72
- }
70
+ /* When explicitly hidden, remove from layout */
71
+ dialog-content.hidden {
72
+ display: none;
73
+ }
74
+
75
+ // Body scroll lock when dialog is open using :has() selector
76
+ body:has(dialog-panel[open]) {
77
+ overflow: hidden;
73
78
  }
@@ -2,13 +2,6 @@
2
2
  // SCSS variables for internal component use and customization
3
3
  // These variables can be customized by the user
4
4
 
5
- // Layout
6
- $panel-top: 0 !default;
7
- $panel-left: 0 !default;
8
- $panel-width: 100vw !default;
9
- $panel-height: 100vh !default;
10
- $panel-z-index: 10 !default;
11
-
12
5
  // Overlay
13
6
  $overlay-z-index: 1000 !default;
14
7
  $overlay-background: rgba(20, 23, 26, 0.4) !default;
@@ -28,13 +21,6 @@ $transition-timing: ease-out !default;
28
21
 
29
22
  // Define CSS Custom Properties using SCSS values
30
23
  :root {
31
- // Layout
32
- --dp-panel-top: #{$panel-top};
33
- --dp-panel-left: #{$panel-left};
34
- --dp-panel-width: #{$panel-width};
35
- --dp-panel-height: #{$panel-height};
36
- --dp-panel-z-index: #{$panel-z-index};
37
-
38
24
  // Overlay
39
25
  --dp-overlay-z-index: #{$overlay-z-index};
40
26
  --dp-overlay-background: #{$overlay-background};