@magic-spells/dialog-panel 0.1.0 → 0.2.2

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
@@ -2,6 +2,8 @@
2
2
 
3
3
  A lightweight, customizable Web Component for creating accessible modal dialogs. Ideal for dialogs, alerts, cart panels, or content panels with smooth animations and accessibility features.
4
4
 
5
+ [**Live Demo**](https://magic-spells.github.io/dialog-panel/demo/)
6
+
5
7
  ## Features
6
8
 
7
9
  - No dependencies
@@ -82,7 +84,55 @@ Or include directly in your HTML:
82
84
 
83
85
  ### Styling
84
86
 
85
- You can style the Dialog Panel by overriding or extending the provided CSS:
87
+ #### Using CSS Custom Properties
88
+
89
+ You can style the Dialog Panel by overriding the CSS custom properties:
90
+
91
+ ```css
92
+ :root {
93
+ /* Layout */
94
+ --dp-panel-z-index: 100;
95
+
96
+ /* Overlay */
97
+ --dp-overlay-background: rgba(0, 0, 0, 0.7);
98
+ --dp-overlay-backdrop-filter: blur(5px) saturate(120%);
99
+
100
+ /* Content */
101
+ --dp-content-background: #f8f8f8;
102
+
103
+ /* Animation */
104
+ --dp-transition-duration: 400ms;
105
+ --dp-transition-timing: cubic-bezier(0.4, 0, 0.2, 1);
106
+ }
107
+ ```
108
+
109
+ #### Using SCSS
110
+
111
+ For more advanced customization, you can import the SCSS directly:
112
+
113
+ ```scss
114
+ // Option 1: Import the compiled CSS
115
+ @import '@magic-spells/dialog-panel/css';
116
+
117
+ // Option 2: Import the SCSS and override variables
118
+ @use '@magic-spells/dialog-panel/scss' with (
119
+ $overlay-background: rgba(0, 0, 0, 0.7),
120
+ $overlay-backdrop-filter: blur(5px) saturate(120%),
121
+ $content-background: #f8f8f8,
122
+ $transition-duration: 400ms,
123
+ $transition-timing: cubic-bezier(0.4, 0, 0.2, 1)
124
+ );
125
+
126
+ // Option 3: Import specific parts
127
+ @use '@magic-spells/dialog-panel/scss/variables' with (
128
+ $panel-z-index: 100
129
+ );
130
+ @use '@magic-spells/dialog-panel/scss/dialog-panel';
131
+ ```
132
+
133
+ #### Direct Element Styling
134
+
135
+ You can also style the elements directly:
86
136
 
87
137
  ```css
88
138
  dialog-panel {
@@ -92,19 +142,46 @@ dialog-panel {
92
142
  dialog-overlay {
93
143
  background-color: rgba(0, 0, 0, 0.5);
94
144
  }
95
-
96
- [data-action='hide-dialog'] {
97
- font-size: 24px;
98
- color: #333;
99
- }
100
145
  ```
101
146
 
102
147
  ### JavaScript API
103
148
 
104
149
  #### Methods
105
150
 
106
- - `show()`: Opens the dialog panel
107
- - `hide()`: Closes the dialog panel
151
+ - `show(triggerEl)`: Opens the dialog panel. Returns false if the action was prevented.
152
+ - `hide()`: Closes the dialog panel. Returns false if the action was prevented.
153
+
154
+ #### Events
155
+
156
+ The dialog panel emits the following events that you can listen for:
157
+
158
+ - `beforeShow`: Fired before the dialog starts to show. Cancelable - you can call `preventDefault()` to prevent the dialog from opening.
159
+ - `show`: Fired when the dialog has been shown (after transitions).
160
+ - `beforeHide`: Fired before the dialog starts to hide. Cancelable - you can call `preventDefault()` to prevent the dialog from closing.
161
+ - `hide`: Fired when the dialog has started hiding (transition begins).
162
+ - `afterHide`: Fired when the dialog has completed its hide transition.
163
+
164
+ Each event provides a `detail` object with the `triggerElement` that initiated the action (if any).
165
+
166
+ Example usage:
167
+
168
+ ```javascript
169
+ const dialog = document.getElementById('my-dialog');
170
+
171
+ // Prevent dialog from closing based on some condition
172
+ dialog.addEventListener('beforeHide', (e) => {
173
+ if (someFormIsUnsaved) {
174
+ e.preventDefault(); // Prevents the dialog from closing
175
+ // Show a confirmation message instead
176
+ }
177
+ });
178
+
179
+ // Do something after the dialog is fully hidden
180
+ dialog.addEventListener('afterHide', () => {
181
+ console.log('Dialog is now fully hidden');
182
+ // Clean up or reset form fields, etc.
183
+ });
184
+ ```
108
185
 
109
186
  ## Browser Support
110
187
 
@@ -2,204 +2,60 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
+ require('@magic-spells/focus-trap');
6
+
5
7
  /**
6
- * Retrieves all focusable elements within a given container.
7
- *
8
- * @param {HTMLElement} container - The container element to search for focusable elements.
9
- * @returns {HTMLElement[]} An array of focusable elements found within the container.
8
+ * Custom element that creates an accessible modal dialog panel with focus management
9
+ * @extends HTMLElement
10
10
  */
11
- const getFocusableElements = (container) => {
12
- const focusableSelectors =
13
- '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';
14
- return Array.from(container.querySelectorAll(focusableSelectors));
15
- };
16
-
17
- class FocusTrap extends HTMLElement {
18
- /** @type {boolean} Indicates whether the styles have been injected into the DOM. */
19
- static styleInjected = false;
20
-
21
- constructor() {
22
- super();
23
- this.trapStart = null;
24
- this.trapEnd = null;
25
-
26
- // Inject styles only once, when the first FocusTrap instance is created.
27
- if (!FocusTrap.styleInjected) {
28
- this.injectStyles();
29
- FocusTrap.styleInjected = true;
30
- }
31
- }
32
-
33
- /**
34
- * Injects necessary styles for the focus trap into the document's head.
35
- * This ensures that focus-trap-start and focus-trap-end elements are hidden.
36
- */
37
- injectStyles() {
38
- const style = document.createElement('style');
39
- style.textContent = `
40
- focus-trap-start,
41
- focus-trap-end {
42
- position: absolute;
43
- width: 1px;
44
- height: 1px;
45
- margin: -1px;
46
- padding: 0;
47
- border: 0;
48
- clip: rect(0, 0, 0, 0);
49
- overflow: hidden;
50
- white-space: nowrap;
51
- }
52
- `;
53
- document.head.appendChild(style);
54
- }
55
-
56
- /**
57
- * Called when the element is connected to the DOM.
58
- * Sets up the focus trap and adds the keydown event listener.
59
- */
60
- connectedCallback() {
61
- this.setupTrap();
62
- this.addEventListener('keydown', this.handleKeyDown);
63
- }
11
+ class DialogPanel extends HTMLElement {
12
+ #handleTransitionEnd;
13
+ #scrollPosition = 0;
64
14
 
65
15
  /**
66
- * Called when the element is disconnected from the DOM.
67
- * Removes the keydown event listener.
16
+ * Clean up event listeners when component is removed from DOM
68
17
  */
69
18
  disconnectedCallback() {
70
- this.removeEventListener('keydown', this.handleKeyDown);
71
- }
72
-
73
- /**
74
- * Sets up the focus trap by adding trap start and trap end elements.
75
- * Focuses the trap start element to initiate the focus trap.
76
- */
77
- setupTrap() {
78
- // check to see it there are any focusable children
79
- const focusableElements = getFocusableElements(this);
80
- // exit if there aren't any
81
- if (focusableElements.length === 0) return;
82
-
83
- // create trap start and end elements
84
- this.trapStart = document.createElement('focus-trap-start');
85
- this.trapEnd = document.createElement('focus-trap-end');
86
-
87
- // add to DOM
88
- this.prepend(this.trapStart);
89
- this.append(this.trapEnd);
90
- }
91
-
92
- /**
93
- * Handles the keydown event. If the Escape key is pressed, the focus trap is exited.
94
- *
95
- * @param {KeyboardEvent} e - The keyboard event object.
96
- */
97
- handleKeyDown = (e) => {
98
- if (e.key === 'Escape') {
99
- e.preventDefault();
100
- this.exitTrap();
19
+ const _ = this;
20
+ if (_.contentPanel) {
21
+ _.contentPanel.removeEventListener(
22
+ 'transitionend',
23
+ _.#handleTransitionEnd
24
+ );
101
25
  }
102
- };
103
-
104
- /**
105
- * Exits the focus trap by hiding the current container and shifting focus
106
- * back to the trigger element that opened the trap.
107
- */
108
- exitTrap() {
109
- const container = this.closest('[aria-hidden="false"]');
110
- if (!container) return;
111
26
 
112
- container.setAttribute('aria-hidden', 'true');
113
-
114
- const trigger = document.querySelector(
115
- `[aria-expanded="true"][aria-controls="${container.id}"]`
116
- );
117
- if (trigger) {
118
- trigger.setAttribute('aria-expanded', 'false');
119
- trigger.focus();
120
- }
27
+ // Ensure body scroll is restored if component is removed while open
28
+ document.body.classList.remove('overflow-hidden');
29
+ this.#restoreScroll();
121
30
  }
122
- }
123
31
 
124
- class FocusTrapStart extends HTMLElement {
125
32
  /**
126
- * Called when the element is connected to the DOM.
127
- * Sets the tabindex and adds the focus event listener.
33
+ * Saves current scroll position and locks body scrolling
34
+ * @private
128
35
  */
129
- connectedCallback() {
130
- this.setAttribute('tabindex', '0');
131
- this.addEventListener('focus', this.handleFocus);
132
- }
36
+ #lockScroll() {
37
+ const _ = this;
38
+ // Save current scroll position
39
+ _.#scrollPosition = window.pageYOffset;
133
40
 
134
- /**
135
- * Called when the element is disconnected from the DOM.
136
- * Removes the focus event listener.
137
- */
138
- disconnectedCallback() {
139
- this.removeEventListener('focus', this.handleFocus);
41
+ // Apply fixed position to body
42
+ document.body.classList.add('overflow-hidden');
43
+ document.body.style.top = `-${_.#scrollPosition}px`;
140
44
  }
141
45
 
142
46
  /**
143
- * Handles the focus event. If focus moves backwards from the first focusable element,
144
- * it is cycled to the last focusable element, and vice versa.
145
- *
146
- * @param {FocusEvent} e - The focus event object.
147
- */
148
- handleFocus = (e) => {
149
- const trap = this.closest('focus-trap');
150
- const focusableElements = getFocusableElements(trap);
151
-
152
- if (focusableElements.length === 0) return;
153
-
154
- const firstElement = focusableElements[0];
155
- const lastElement =
156
- focusableElements[focusableElements.length - 1];
157
-
158
- if (e.relatedTarget === firstElement) {
159
- lastElement.focus();
160
- } else {
161
- firstElement.focus();
162
- }
163
- };
164
- }
165
-
166
- class FocusTrapEnd extends HTMLElement {
167
- /**
168
- * Called when the element is connected to the DOM.
169
- * Sets the tabindex and adds the focus event listener.
47
+ * Restores scroll position when dialog is closed
48
+ * @private
170
49
  */
171
- connectedCallback() {
172
- this.setAttribute('tabindex', '0');
173
- this.addEventListener('focus', this.handleFocus);
174
- }
50
+ #restoreScroll() {
51
+ const _ = this;
52
+ // Remove fixed positioning
53
+ document.body.classList.remove('overflow-hidden');
54
+ document.body.style.removeProperty('top');
175
55
 
176
- /**
177
- * Called when the element is disconnected from the DOM.
178
- * Removes the focus event listener.
179
- */
180
- disconnectedCallback() {
181
- this.removeEventListener('focus', this.handleFocus);
56
+ // Restore scroll position
57
+ window.scrollTo(0, _.#scrollPosition);
182
58
  }
183
-
184
- /**
185
- * Handles the focus event. When the trap end is focused, focus is shifted back to the trap start.
186
- */
187
- handleFocus = () => {
188
- const trap = this.closest('focus-trap');
189
- const trapStart = trap.querySelector('focus-trap-start');
190
- trapStart.focus();
191
- };
192
- }
193
-
194
- customElements.define('focus-trap', FocusTrap);
195
- customElements.define('focus-trap-start', FocusTrapStart);
196
- customElements.define('focus-trap-end', FocusTrapEnd);
197
-
198
- /**
199
- * Custom element that creates an accessible modal dialog panel with focus management
200
- * @extends HTMLElement
201
- */
202
- class DialogPanel extends HTMLElement {
203
59
  /**
204
60
  * Initializes the dialog panel, sets up focus trap and overlay
205
61
  */
@@ -215,6 +71,24 @@ class DialogPanel extends HTMLElement {
215
71
  _.focusTrap = document.createElement('focus-trap');
216
72
  _.triggerEl = null;
217
73
 
74
+ // Create a handler for transition end events
75
+ _.#handleTransitionEnd = (e) => {
76
+ if (
77
+ e.propertyName === 'opacity' &&
78
+ _.getAttribute('aria-hidden') === 'true'
79
+ ) {
80
+ _.contentPanel.classList.add('hidden');
81
+
82
+ // Dispatch afterHide event - dialog has completed its transition
83
+ _.dispatchEvent(
84
+ new CustomEvent('afterHide', {
85
+ bubbles: true,
86
+ detail: { triggerElement: _.triggerEl },
87
+ })
88
+ );
89
+ }
90
+ };
91
+
218
92
  // Ensure we have labelledby and describedby references
219
93
  if (!_.getAttribute('aria-labelledby')) {
220
94
  const heading = _.querySelector('h1, h2, h3');
@@ -245,26 +119,31 @@ class DialogPanel extends HTMLElement {
245
119
  * @private
246
120
  */
247
121
  #bindUI() {
122
+ const _ = this;
123
+
248
124
  // Handle trigger buttons
249
125
  document.addEventListener('click', (e) => {
250
- const trigger = e.target.closest(
251
- `[aria-controls="${this.id}"]`
252
- );
126
+ const trigger = e.target.closest(`[aria-controls="${_.id}"]`);
253
127
  if (!trigger) return;
254
128
 
255
129
  if (trigger.getAttribute('data-prevent-default') === 'true') {
256
130
  e.preventDefault();
257
131
  }
258
132
 
259
- // this.triggerEl = trigger;
260
- this.show(trigger);
133
+ _.show(trigger);
261
134
  });
262
135
 
263
136
  // Handle close buttons
264
- this.addEventListener('click', (e) => {
137
+ _.addEventListener('click', (e) => {
265
138
  if (!e.target.closest('[data-action="hide-dialog"]')) return;
266
- this.hide();
139
+ _.hide();
267
140
  });
141
+
142
+ // Add transition end listener
143
+ _.contentPanel.addEventListener(
144
+ 'transitionend',
145
+ _.#handleTransitionEnd
146
+ );
268
147
  }
269
148
 
270
149
  /**
@@ -282,50 +161,108 @@ class DialogPanel extends HTMLElement {
282
161
  /**
283
162
  * Shows the dialog and traps focus within it
284
163
  * @param {HTMLElement} [triggerEl=null] - The element that triggered the dialog
164
+ * @fires DialogPanel#beforeShow - Fired before the dialog starts to show
165
+ * @fires DialogPanel#show - Fired when the dialog has been shown
166
+ * @returns {boolean} False if the show was prevented by a beforeShow event handler
285
167
  */
286
168
  show(triggerEl = null) {
287
- this.triggerEl = triggerEl || false;
169
+ const _ = this;
170
+ _.triggerEl = triggerEl || false;
288
171
 
289
- // Update ARIA states
290
- this.setAttribute('aria-hidden', 'false');
291
- if (this.triggerEl) {
292
- this.triggerEl.setAttribute('aria-expanded', 'true');
293
- }
172
+ // Dispatch beforeShow event - allows preventing the dialog from opening
173
+ const beforeShowEvent = new CustomEvent('beforeShow', {
174
+ bubbles: true,
175
+ cancelable: true,
176
+ detail: { triggerElement: _.triggerEl },
177
+ });
294
178
 
295
- // prevent body from scrolling
296
- document.body.classList.add('overflow-hidden');
179
+ const showAllowed = _.dispatchEvent(beforeShowEvent);
297
180
 
298
- // Focus management
299
- const firstFocusable = this.querySelector(
300
- 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
301
- );
302
- if (firstFocusable) {
303
- requestAnimationFrame(() => {
304
- firstFocusable.focus();
305
- });
306
- }
181
+ // If event was canceled (preventDefault was called), don't show the dialog
182
+ if (!showAllowed) return false;
183
+
184
+ // Remove the hidden class first to ensure content is rendered
185
+ _.contentPanel.classList.remove('hidden');
186
+
187
+ // Give the browser a moment to process before starting animation
188
+ requestAnimationFrame(() => {
189
+ // Update ARIA states
190
+ _.setAttribute('aria-hidden', 'false');
191
+ if (_.triggerEl) {
192
+ _.triggerEl.setAttribute('aria-expanded', 'true');
193
+ }
194
+
195
+ // Lock body scrolling and save scroll position
196
+ _.#lockScroll();
197
+
198
+ // Focus management
199
+ const firstFocusable = _.querySelector(
200
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
201
+ );
202
+ if (firstFocusable) {
203
+ requestAnimationFrame(() => {
204
+ firstFocusable.focus();
205
+ });
206
+ }
207
+
208
+ // Dispatch show event - dialog is now visible
209
+ _.dispatchEvent(
210
+ new CustomEvent('show', {
211
+ bubbles: true,
212
+ detail: { triggerElement: _.triggerEl },
213
+ })
214
+ );
215
+ });
216
+
217
+ return true;
307
218
  }
308
219
 
309
220
  /**
310
221
  * Hides the dialog and restores focus
222
+ * @fires DialogPanel#beforeHide - Fired before the dialog starts to hide
223
+ * @fires DialogPanel#hide - Fired when the dialog has started hiding (transition begins)
224
+ * @fires DialogPanel#afterHide - Fired when the dialog has completed its hide transition
225
+ * @returns {boolean} False if the hide was prevented by a beforeHide event handler
311
226
  */
312
227
  hide() {
313
- // allow body to scroll
314
- document.body.classList.remove('overflow-hidden');
228
+ const _ = this;
229
+
230
+ // Dispatch beforeHide event - allows preventing the dialog from closing
231
+ const beforeHideEvent = new CustomEvent('beforeHide', {
232
+ bubbles: true,
233
+ cancelable: true,
234
+ detail: { triggerElement: _.triggerEl },
235
+ });
236
+
237
+ const hideAllowed = _.dispatchEvent(beforeHideEvent);
238
+
239
+ // If event was canceled (preventDefault was called), don't hide the dialog
240
+ if (!hideAllowed) return false;
241
+
242
+ // Restore body scroll and scroll position
243
+ _.#restoreScroll();
315
244
 
316
245
  // Update ARIA states
317
- if (this.triggerEl) {
318
- this.triggerEl.setAttribute('aria-expanded', 'false');
319
- // Restore focus to trigger element
320
- this.triggerEl.focus();
321
- } else {
322
- console.log('we need to blur focus');
246
+ if (_.triggerEl) {
247
+ // remove focus from modal panel first
248
+ _.triggerEl.focus();
249
+ // mark trigger as no longer expanded
250
+ _.triggerEl.setAttribute('aria-expanded', 'false');
323
251
  }
324
252
 
325
- // hide dialog panel
326
- setTimeout(() => {
327
- this.setAttribute('aria-hidden', 'true');
328
- }, 1);
253
+ // Set aria-hidden to start transition
254
+ // The transitionend event handler will add display:none when complete
255
+ _.setAttribute('aria-hidden', 'true');
256
+
257
+ // Dispatch hide event - dialog is now starting to hide
258
+ _.dispatchEvent(
259
+ new CustomEvent('hide', {
260
+ bubbles: true,
261
+ detail: { triggerElement: _.triggerEl },
262
+ })
263
+ );
264
+
265
+ return true;
329
266
  }
330
267
  }
331
268
 
@@ -360,9 +297,15 @@ class DialogContent extends HTMLElement {
360
297
  }
361
298
  }
362
299
 
363
- customElements.define('dialog-panel', DialogPanel);
364
- customElements.define('dialog-overlay', DialogOverlay);
365
- customElements.define('dialog-content', DialogContent);
300
+ if (!customElements.get('dialog-panel')) {
301
+ customElements.define('dialog-panel', DialogPanel);
302
+ }
303
+ if (!customElements.get('dialog-overlay')) {
304
+ customElements.define('dialog-overlay', DialogOverlay);
305
+ }
306
+ if (!customElements.get('dialog-content')) {
307
+ customElements.define('dialog-content', DialogContent);
308
+ }
366
309
 
367
310
  exports.DialogContent = DialogContent;
368
311
  exports.DialogOverlay = DialogOverlay;
@@ -1 +1 @@
1
- {"version":3,"file":"dialog-panel.cjs.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 './dialog-panel.css';\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/**\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// 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\t// Handle trigger buttons\n\t\tdocument.addEventListener('click', (e) => {\n\t\t\tconst trigger = e.target.closest(\n\t\t\t\t`[aria-controls=\"${this.id}\"]`\n\t\t\t);\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// this.triggerEl = trigger;\n\t\t\tthis.show(trigger);\n\t\t});\n\n\t\t// Handle close buttons\n\t\tthis.addEventListener('click', (e) => {\n\t\t\tif (!e.target.closest('[data-action=\"hide-dialog\"]')) return;\n\t\t\tthis.hide();\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 */\n\tshow(triggerEl = null) {\n\t\tthis.triggerEl = triggerEl || false;\n\n\t\t// Update ARIA states\n\t\tthis.setAttribute('aria-hidden', 'false');\n\t\tif (this.triggerEl) {\n\t\t\tthis.triggerEl.setAttribute('aria-expanded', 'true');\n\t\t}\n\n\t\t// prevent body from scrolling\n\t\tdocument.body.classList.add('overflow-hidden');\n\n\t\t// Focus management\n\t\tconst firstFocusable = this.querySelector(\n\t\t\t'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n\t\t);\n\t\tif (firstFocusable) {\n\t\t\trequestAnimationFrame(() => {\n\t\t\t\tfirstFocusable.focus();\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Hides the dialog and restores focus\n\t */\n\thide() {\n\t\t// allow body to scroll\n\t\tdocument.body.classList.remove('overflow-hidden');\n\n\t\t// Update ARIA states\n\t\tif (this.triggerEl) {\n\t\t\tthis.triggerEl.setAttribute('aria-expanded', 'false');\n\t\t\t// Restore focus to trigger element\n\t\t\tthis.triggerEl.focus();\n\t\t} else {\n\t\t\tconsole.log('we need to blur focus');\n\t\t}\n\n\t\t// hide dialog panel\n\t\tsetTimeout(() => {\n\t\t\tthis.setAttribute('aria-hidden', 'true');\n\t\t}, 1);\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\ncustomElements.define('dialog-panel', DialogPanel);\ncustomElements.define('dialog-overlay', DialogOverlay);\ncustomElements.define('dialog-content', DialogContent);\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.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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"}