@magic-spells/dialog-panel 0.3.0 → 1.0.1

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.
@@ -1,290 +1,362 @@
1
- import '@magic-spells/focus-trap';
2
-
3
1
  /**
4
- * Custom element that creates an accessible modal dialog panel with focus management
2
+ * DialogPanel - A lightweight web component wrapper for native <dialog> elements
3
+ * with state-driven animations.
4
+ *
5
5
  * @extends HTMLElement
6
+ *
7
+ * @property {string} state - Current state: 'hidden' | 'showing' | 'shown' | 'hiding'
8
+ * @property {HTMLDialogElement} dialog - Reference to inner <dialog> element
9
+ * @property {boolean} isOpen - True if state is 'showing' or 'shown'
10
+ * @property {HTMLElement|null} triggerElement - Element that triggered current action
11
+ *
12
+ * @fires beforeShow - Fired before showing starts (cancelable)
13
+ * @fires shown - Fired after show animation completes
14
+ * @fires beforeHide - Fired before hiding starts (cancelable)
15
+ * @fires hidden - Fired after hide animation completes
6
16
  */
7
17
  class DialogPanel extends HTMLElement {
8
- #handleTransitionEnd;
9
-
10
- /**
11
- * Clean up event listeners when component is removed from DOM
12
- */
13
- disconnectedCallback() {
18
+ // Private fields
19
+ #state = 'hidden';
20
+ #triggerElement = null;
21
+ #dialog = null;
22
+ #result = null;
23
+
24
+ // Event handler references for cleanup
25
+ #handlers = {
26
+ click: null,
27
+ dialogClick: null,
28
+ cancel: null,
29
+ };
30
+
31
+ // Animation cleanup references
32
+ #pendingRAF = null;
33
+ #pendingTimeout = null;
34
+
35
+ // Fallback timeout for transitionend (in ms)
36
+ static TRANSITION_FALLBACK_TIMEOUT = 700;
37
+
38
+ connectedCallback() {
14
39
  const _ = this;
15
- if (_.contentPanel) {
16
- _.contentPanel.removeEventListener(
17
- 'transitionend',
18
- _.#handleTransitionEnd
40
+
41
+ // Find inner dialog element
42
+ _.#dialog = _.querySelector('dialog');
43
+
44
+ if (!_.#dialog) {
45
+ console.warn(
46
+ 'DialogPanel: No <dialog> element found inside <dialog-panel>'
19
47
  );
48
+ return;
20
49
  }
21
- }
22
- /**
23
- * Initializes the dialog panel, sets up focus trap and overlay
24
- */
25
- constructor() {
26
- super();
27
- const _ = this;
28
- _.id = _.getAttribute('id');
29
- _.setAttribute('role', 'dialog');
30
- _.setAttribute('aria-modal', 'true');
31
- _.setAttribute('aria-hidden', 'true');
32
-
33
- _.contentPanel = _.querySelector('dialog-content');
34
- _.triggerEl = null;
35
-
36
- // Create a handler for transition end events
37
- _.#handleTransitionEnd = (e) => {
38
- if (
39
- e.propertyName === 'opacity' &&
40
- _.getAttribute('aria-hidden') === 'true'
41
- ) {
42
- _.contentPanel.classList.add('hidden');
43
-
44
- // Dispatch afterHide event - dialog has completed its transition
45
- _.dispatchEvent(
46
- new CustomEvent('afterHide', {
47
- bubbles: true,
48
- detail: { triggerElement: _.triggerEl },
49
- })
50
- );
51
- }
52
- };
53
50
 
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');
51
+ // Auto-create dialog-backdrop if not present
52
+ if (!_.querySelector('dialog-backdrop')) {
53
+ const backdrop = document.createElement('dialog-backdrop');
54
+ _.insertBefore(backdrop, _.firstChild);
55
+ }
59
56
 
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
- );
57
+ // Set initial state attribute
58
+ _.#setState('hidden');
59
+
60
+ // Bind event handlers
61
+ _.#bindEvents();
62
+ }
63
+
64
+ disconnectedCallback() {
65
+ const _ = this;
65
66
 
66
- // Insert focus-trap inside dialog-content
67
- _.contentPanel.appendChild(_.focusTrap);
67
+ // Cancel pending animations
68
+ if (_.#pendingRAF) {
69
+ cancelAnimationFrame(_.#pendingRAF);
70
+ _.#pendingRAF = null;
71
+ }
72
+ if (_.#pendingTimeout) {
73
+ clearTimeout(_.#pendingTimeout);
74
+ _.#pendingTimeout = null;
68
75
  }
69
76
 
70
- // Ensure we have labelledby and describedby references
71
- if (!_.getAttribute('aria-labelledby')) {
72
- const heading = _.querySelector('h1, h2, h3');
73
- if (heading && !heading.id) {
74
- heading.id = `${_.id}-title`;
77
+ // Clean up event listeners
78
+ if (_.#handlers.click) {
79
+ _.removeEventListener('click', _.#handlers.click);
80
+ }
81
+ if (_.#dialog) {
82
+ if (_.#handlers.dialogClick) {
83
+ _.#dialog.removeEventListener(
84
+ 'click',
85
+ _.#handlers.dialogClick
86
+ );
75
87
  }
76
- if (heading?.id) {
77
- _.setAttribute('aria-labelledby', heading.id);
88
+ if (_.#handlers.cancel) {
89
+ _.#dialog.removeEventListener('cancel', _.#handlers.cancel);
78
90
  }
79
91
  }
80
-
81
- // Add modal overlay
82
- _.prepend(document.createElement('dialog-overlay'));
83
- _.#bindUI();
84
- _.#bindKeyboard();
85
92
  }
86
93
 
87
94
  /**
88
- * Binds click events for showing and hiding the dialog
95
+ * Bind event listeners for close buttons, backdrop, and escape key
89
96
  * @private
90
97
  */
91
- #bindUI() {
98
+ #bindEvents() {
92
99
  const _ = this;
93
100
 
94
- // Handle trigger buttons
95
- document.addEventListener('click', (e) => {
96
- const trigger = e.target.closest(`[aria-controls="${_.id}"]`);
97
- if (!trigger) return;
98
-
99
- if (trigger.getAttribute('data-prevent-default') === 'true') {
100
- e.preventDefault();
101
+ // Handle close buttons with data-action-hide-dialog
102
+ _.#handlers.click = (e) => {
103
+ const trigger = e.target.closest('[data-action-hide-dialog]');
104
+ if (trigger) {
105
+ e.stopPropagation();
106
+ _.hide(trigger);
101
107
  }
108
+ };
109
+ _.addEventListener('click', _.#handlers.click);
110
+
111
+ // Handle backdrop click - detect clicks outside dialog bounds
112
+ // This works because clicks on ::backdrop still fire on the dialog element
113
+ _.#handlers.dialogClick = (e) => {
114
+ const rect = _.#dialog.getBoundingClientRect();
115
+ const clickedOutside =
116
+ e.clientX < rect.left ||
117
+ e.clientX > rect.right ||
118
+ e.clientY < rect.top ||
119
+ e.clientY > rect.bottom;
120
+ if (clickedOutside) {
121
+ e.stopPropagation();
122
+ _.hide();
123
+ }
124
+ };
125
+ _.#dialog.addEventListener('click', _.#handlers.dialogClick);
102
126
 
103
- _.show(trigger);
104
- });
105
-
106
- // Handle close buttons
107
- _.addEventListener('click', (e) => {
108
- if (!e.target.closest('[data-action-hide-dialog]')) return;
127
+ // Handle escape key - intercept native cancel and animate close
128
+ _.#handlers.cancel = (e) => {
129
+ e.preventDefault();
130
+ e.stopPropagation();
109
131
  _.hide();
110
- });
111
-
112
- // Add transition end listener
113
- _.contentPanel.addEventListener(
114
- 'transitionend',
115
- _.#handleTransitionEnd
116
- );
117
- }
118
-
119
- /**
120
- * Binds keyboard events for accessibility
121
- * @private
122
- */
123
- #bindKeyboard() {
124
- this.addEventListener('keydown', (e) => {
125
- if (e.key === 'Escape') {
126
- this.hide();
127
- }
128
- });
132
+ };
133
+ _.#dialog.addEventListener('cancel', _.#handlers.cancel);
129
134
  }
130
135
 
131
136
  /**
132
- * Shows the dialog and traps focus within it
133
- * @param {HTMLElement} [triggerEl=null] - The element that triggered the dialog
134
- * @fires DialogPanel#beforeShow - Fired before the dialog starts to show
135
- * @fires DialogPanel#show - Fired when the dialog has been shown
136
- * @returns {boolean} False if the show was prevented by a beforeShow event handler
137
+ * Show the dialog with animation
138
+ * @param {HTMLElement} [triggerEl=null] - The element that triggered the show
139
+ * @returns {boolean} False if show was prevented via beforeShow event
137
140
  */
138
141
  show(triggerEl = null) {
139
142
  const _ = this;
140
- _.triggerEl = triggerEl || false;
141
143
 
142
- // Dispatch beforeShow event - allows preventing the dialog from opening
143
- const beforeShowEvent = new CustomEvent('beforeShow', {
144
- bubbles: true,
145
- cancelable: true,
146
- detail: { triggerElement: _.triggerEl },
147
- });
144
+ // Check if already showing/shown
145
+ if (_.#state === 'showing' || _.#state === 'shown') return true;
148
146
 
149
- const showAllowed = _.dispatchEvent(beforeShowEvent);
147
+ // If currently hiding, ignore (let hide complete first)
148
+ if (_.#state === 'hiding') return false;
150
149
 
151
- // If event was canceled (preventDefault was called), don't show the dialog
152
- if (!showAllowed) return false;
150
+ // Store trigger element for focus return later
151
+ _.#triggerElement = triggerEl || null;
153
152
 
154
- // Add open attribute for CSS :has() selector
155
- _.setAttribute('open', '');
153
+ // Fire beforeShow (cancelable)
154
+ if (!_.#emit('beforeShow', { cancelable: true })) return false;
156
155
 
157
- // Remove the hidden class first to ensure content is rendered
158
- _.contentPanel.classList.remove('hidden');
156
+ // Set state to 'showing' and open native dialog
157
+ _.#setState('showing');
158
+ _.#dialog.showModal();
159
159
 
160
- // Give the browser a moment to process before starting animation
161
- requestAnimationFrame(() => {
162
- // Update ARIA states
163
- _.setAttribute('aria-hidden', 'false');
160
+ // Double RAF ensures browser has painted the 'showing' state
161
+ // before we transition to 'shown'
162
+ _.#pendingRAF = requestAnimationFrame(() => {
163
+ _.#pendingRAF = requestAnimationFrame(() => {
164
+ _.#pendingRAF = null;
165
+ _.#setState('shown');
164
166
 
165
- // set trigger element to expanded: true
166
- if (_.triggerEl) {
167
- _.triggerEl.setAttribute('aria-expanded', 'true');
168
- }
169
-
170
- // Focus management
171
- const firstFocusable = _.querySelector(
172
- 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
173
- );
174
- if (firstFocusable) {
175
- requestAnimationFrame(() => {
176
- firstFocusable.focus();
167
+ // Wait for transition to complete before firing 'shown' event
168
+ _.#waitForTransition(() => {
169
+ _.#emit('shown');
177
170
  });
178
- }
179
-
180
- // Dispatch show event - dialog is now visible
181
- _.dispatchEvent(
182
- new CustomEvent('show', {
183
- bubbles: true,
184
- detail: { triggerElement: _.triggerEl },
185
- })
186
- );
171
+ });
187
172
  });
173
+
174
+ return true;
188
175
  }
189
176
 
190
177
  /**
191
- * Hides the dialog and restores focus
192
- * @fires DialogPanel#beforeHide - Fired before the dialog starts to hide
193
- * @fires DialogPanel#hide - Fired when the dialog has started hiding (transition begins)
194
- * @fires DialogPanel#afterHide - Fired when the dialog has completed its hide transition
195
- * @returns {boolean} False if the hide was prevented by a beforeHide event handler
178
+ * Hide the dialog with animation
179
+ * @param {HTMLElement} [triggerEl=null] - The element that triggered the hide
180
+ * @returns {boolean} False if hide was prevented via beforeHide event
196
181
  */
197
- hide() {
182
+ hide(triggerEl = null) {
198
183
  const _ = this;
199
184
 
200
- // Dispatch beforeHide event - allows preventing the dialog from closing
201
- const beforeHideEvent = new CustomEvent('beforeHide', {
202
- bubbles: true,
203
- cancelable: true,
204
- detail: { triggerElement: _.triggerEl },
205
- });
185
+ // Check if already hiding/hidden
186
+ if (_.#state === 'hiding' || _.#state === 'hidden') return true;
206
187
 
207
- const hideAllowed = _.dispatchEvent(beforeHideEvent);
188
+ // If currently showing, ignore (let show complete first)
189
+ if (_.#state === 'showing') return false;
208
190
 
209
- // If event was canceled (preventDefault was called), don't hide the dialog
210
- if (!hideAllowed) return false;
191
+ // Capture result from trigger element
192
+ _.#result = triggerEl?.dataset?.result ?? null;
211
193
 
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();
194
+ // Fire beforeHide (cancelable)
195
+ if (
196
+ !_.#emit('beforeHide', {
197
+ cancelable: true,
198
+ result: _.#result,
199
+ triggerElement: triggerEl,
200
+ })
201
+ ) {
202
+ return false;
217
203
  }
218
204
 
219
- // Update ARIA states and restore focus
220
- if (_.triggerEl) {
221
- // remove focus from modal panel first
222
- _.triggerEl.focus();
223
- // mark trigger as no longer expanded
224
- _.triggerEl.setAttribute('aria-expanded', 'false');
225
- } else {
226
- // Move focus to body to ensure it's not trapped
227
- document.body.focus();
228
- }
205
+ // Set state to 'hiding' - this triggers CSS exit animation
206
+ _.#setState('hiding');
229
207
 
230
- // Set aria-hidden to start transition
231
- // The transitionend event handler will add display:none when complete
232
- _.setAttribute('aria-hidden', 'true');
233
- // Remove open attribute for CSS :has() selector
208
+ // Wait for transition to complete
209
+ _.#waitForTransition(() => {
210
+ _.#dialog.close();
211
+ _.#setState('hidden');
212
+ _.#emit('hidden', {
213
+ result: _.#result,
214
+ triggerElement: triggerEl,
215
+ });
234
216
 
235
- _.removeAttribute('open');
217
+ // Return focus to trigger element
218
+ if (_.#triggerElement) _.#triggerElement.focus();
236
219
 
237
- // Dispatch hide event - dialog is now starting to hide
238
- _.dispatchEvent(
239
- new CustomEvent('hide', {
240
- bubbles: true,
241
- detail: { triggerElement: _.triggerEl },
242
- })
243
- );
220
+ // Clean up
221
+ _.#triggerElement = null;
222
+ _.#result = null;
223
+ });
244
224
 
245
225
  return true;
246
226
  }
247
- }
248
227
 
249
- /**
250
- * Custom element that creates a clickable overlay for the dialog
251
- * @extends HTMLElement
252
- */
253
- class DialogOverlay extends HTMLElement {
254
- constructor() {
255
- super();
256
- this.setAttribute('tabindex', '-1');
257
- this.dialogPanel = this.closest('dialog-panel');
258
- this.#bindUI();
228
+ /**
229
+ * Wait for CSS transition to complete with fallback timeout
230
+ * @param {Function} callback - Called when transition completes
231
+ * @private
232
+ */
233
+ #waitForTransition(callback) {
234
+ const _ = this;
235
+ let called = false;
236
+
237
+ const done = () => {
238
+ if (called) return;
239
+ called = true;
240
+ _.#dialog.removeEventListener('transitionend', onTransitionEnd);
241
+ if (_.#pendingTimeout) {
242
+ clearTimeout(_.#pendingTimeout);
243
+ _.#pendingTimeout = null;
244
+ }
245
+ callback();
246
+ };
247
+
248
+ const onTransitionEnd = (e) => {
249
+ if (e.target === _.#dialog) done();
250
+ };
251
+
252
+ _.#dialog.addEventListener('transitionend', onTransitionEnd);
253
+
254
+ _.#pendingTimeout = setTimeout(
255
+ done,
256
+ DialogPanel.TRANSITION_FALLBACK_TIMEOUT
257
+ );
259
258
  }
260
259
 
261
- #bindUI() {
262
- this.addEventListener('click', () => {
263
- this.dialogPanel.hide();
260
+ /**
261
+ * Set the component state
262
+ * @param {string} newState - The new state
263
+ * @private
264
+ */
265
+ #setState(newState) {
266
+ this.#state = newState;
267
+ this.setAttribute('state', newState);
268
+ }
269
+
270
+ /**
271
+ * Emit a custom event
272
+ * @param {string} name - Event name
273
+ * @param {Object} options - Event options
274
+ * @returns {boolean} False if event was cancelled
275
+ * @private
276
+ */
277
+ #emit(name, options = {}) {
278
+ const _ = this;
279
+ const { cancelable = false, ...detail } = options;
280
+
281
+ const event = new CustomEvent(name, {
282
+ bubbles: true,
283
+ composed: true,
284
+ cancelable,
285
+ detail: {
286
+ triggerElement: _.#triggerElement,
287
+ result: _.#result,
288
+ state: _.#state,
289
+ ...detail,
290
+ },
264
291
  });
292
+
293
+ return _.dispatchEvent(event);
294
+ }
295
+
296
+ // Read-only properties
297
+ get state() {
298
+ return this.#state;
299
+ }
300
+ get dialog() {
301
+ return this.#dialog;
302
+ }
303
+ get isOpen() {
304
+ return this.#state === 'showing' || this.#state === 'shown';
305
+ }
306
+ get triggerElement() {
307
+ return this.#triggerElement;
265
308
  }
266
309
  }
267
310
 
268
311
  /**
269
- * Custom element that wraps the content of the dialog
312
+ * DialogBackdrop - A custom backdrop element that animates with the dialog-panel state.
313
+ * Use this instead of native ::backdrop for consistent cross-browser animations.
314
+ *
270
315
  * @extends HTMLElement
271
316
  */
272
- class DialogContent extends HTMLElement {
273
- constructor() {
274
- super();
275
- this.setAttribute('role', 'document');
317
+ class DialogBackdrop extends HTMLElement {
318
+ #panel = null;
319
+ #handlers = {
320
+ click: null,
321
+ };
322
+
323
+ connectedCallback() {
324
+ const _ = this;
325
+
326
+ // Find parent dialog-panel
327
+ _.#panel = _.closest('dialog-panel');
328
+
329
+ if (!_.#panel) {
330
+ console.warn(
331
+ 'DialogBackdrop: Must be inside a <dialog-panel> element'
332
+ );
333
+ return;
334
+ }
335
+
336
+ // Handle click to close
337
+ _.#handlers.click = () => {
338
+ _.#panel.hide();
339
+ };
340
+ _.addEventListener('click', _.#handlers.click);
341
+ }
342
+
343
+ disconnectedCallback() {
344
+ const _ = this;
345
+ if (_.#handlers.click) {
346
+ _.removeEventListener('click', _.#handlers.click);
347
+ }
276
348
  }
277
349
  }
278
350
 
351
+ // Register the custom elements
352
+ // Note: dialog-backdrop must be defined BEFORE dialog-panel,
353
+ // because dialog-panel's connectedCallback may create dialog-backdrop elements
354
+ if (!customElements.get('dialog-backdrop')) {
355
+ customElements.define('dialog-backdrop', DialogBackdrop);
356
+ }
279
357
  if (!customElements.get('dialog-panel')) {
280
358
  customElements.define('dialog-panel', DialogPanel);
281
359
  }
282
- if (!customElements.get('dialog-overlay')) {
283
- customElements.define('dialog-overlay', DialogOverlay);
284
- }
285
- if (!customElements.get('dialog-content')) {
286
- customElements.define('dialog-content', DialogContent);
287
- }
288
360
 
289
- export { DialogContent, DialogOverlay, DialogPanel, DialogPanel as default };
361
+ export { DialogBackdrop, DialogPanel };
290
362
  //# sourceMappingURL=dialog-panel.esm.js.map
@@ -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\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
+ {"version":3,"file":"dialog-panel.esm.js","sources":["../src/dialog-panel.js"],"sourcesContent":["import './dialog-panel.css';\n\n/**\n * DialogPanel - A lightweight web component wrapper for native <dialog> elements\n * with state-driven animations.\n *\n * @extends HTMLElement\n *\n * @property {string} state - Current state: 'hidden' | 'showing' | 'shown' | 'hiding'\n * @property {HTMLDialogElement} dialog - Reference to inner <dialog> element\n * @property {boolean} isOpen - True if state is 'showing' or 'shown'\n * @property {HTMLElement|null} triggerElement - Element that triggered current action\n *\n * @fires beforeShow - Fired before showing starts (cancelable)\n * @fires shown - Fired after show animation completes\n * @fires beforeHide - Fired before hiding starts (cancelable)\n * @fires hidden - Fired after hide animation completes\n */\nclass DialogPanel extends HTMLElement {\n\t// Private fields\n\t#state = 'hidden';\n\t#triggerElement = null;\n\t#dialog = null;\n\t#result = null;\n\n\t// Event handler references for cleanup\n\t#handlers = {\n\t\tclick: null,\n\t\tdialogClick: null,\n\t\tcancel: null,\n\t};\n\n\t// Animation cleanup references\n\t#pendingRAF = null;\n\t#pendingTimeout = null;\n\n\t// Fallback timeout for transitionend (in ms)\n\tstatic TRANSITION_FALLBACK_TIMEOUT = 700;\n\n\tconnectedCallback() {\n\t\tconst _ = this;\n\n\t\t// Find inner dialog element\n\t\t_.#dialog = _.querySelector('dialog');\n\n\t\tif (!_.#dialog) {\n\t\t\tconsole.warn(\n\t\t\t\t'DialogPanel: No <dialog> element found inside <dialog-panel>'\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// Auto-create dialog-backdrop if not present\n\t\tif (!_.querySelector('dialog-backdrop')) {\n\t\t\tconst backdrop = document.createElement('dialog-backdrop');\n\t\t\t_.insertBefore(backdrop, _.firstChild);\n\t\t}\n\n\t\t// Set initial state attribute\n\t\t_.#setState('hidden');\n\n\t\t// Bind event handlers\n\t\t_.#bindEvents();\n\t}\n\n\tdisconnectedCallback() {\n\t\tconst _ = this;\n\n\t\t// Cancel pending animations\n\t\tif (_.#pendingRAF) {\n\t\t\tcancelAnimationFrame(_.#pendingRAF);\n\t\t\t_.#pendingRAF = null;\n\t\t}\n\t\tif (_.#pendingTimeout) {\n\t\t\tclearTimeout(_.#pendingTimeout);\n\t\t\t_.#pendingTimeout = null;\n\t\t}\n\n\t\t// Clean up event listeners\n\t\tif (_.#handlers.click) {\n\t\t\t_.removeEventListener('click', _.#handlers.click);\n\t\t}\n\t\tif (_.#dialog) {\n\t\t\tif (_.#handlers.dialogClick) {\n\t\t\t\t_.#dialog.removeEventListener(\n\t\t\t\t\t'click',\n\t\t\t\t\t_.#handlers.dialogClick\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (_.#handlers.cancel) {\n\t\t\t\t_.#dialog.removeEventListener('cancel', _.#handlers.cancel);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Bind event listeners for close buttons, backdrop, and escape key\n\t * @private\n\t */\n\t#bindEvents() {\n\t\tconst _ = this;\n\n\t\t// Handle close buttons with data-action-hide-dialog\n\t\t_.#handlers.click = (e) => {\n\t\t\tconst trigger = e.target.closest('[data-action-hide-dialog]');\n\t\t\tif (trigger) {\n\t\t\t\te.stopPropagation();\n\t\t\t\t_.hide(trigger);\n\t\t\t}\n\t\t};\n\t\t_.addEventListener('click', _.#handlers.click);\n\n\t\t// Handle backdrop click - detect clicks outside dialog bounds\n\t\t// This works because clicks on ::backdrop still fire on the dialog element\n\t\t_.#handlers.dialogClick = (e) => {\n\t\t\tconst rect = _.#dialog.getBoundingClientRect();\n\t\t\tconst clickedOutside =\n\t\t\t\te.clientX < rect.left ||\n\t\t\t\te.clientX > rect.right ||\n\t\t\t\te.clientY < rect.top ||\n\t\t\t\te.clientY > rect.bottom;\n\t\t\tif (clickedOutside) {\n\t\t\t\te.stopPropagation();\n\t\t\t\t_.hide();\n\t\t\t}\n\t\t};\n\t\t_.#dialog.addEventListener('click', _.#handlers.dialogClick);\n\n\t\t// Handle escape key - intercept native cancel and animate close\n\t\t_.#handlers.cancel = (e) => {\n\t\t\te.preventDefault();\n\t\t\te.stopPropagation();\n\t\t\t_.hide();\n\t\t};\n\t\t_.#dialog.addEventListener('cancel', _.#handlers.cancel);\n\t}\n\n\t/**\n\t * Show the dialog with animation\n\t * @param {HTMLElement} [triggerEl=null] - The element that triggered the show\n\t * @returns {boolean} False if show was prevented via beforeShow event\n\t */\n\tshow(triggerEl = null) {\n\t\tconst _ = this;\n\n\t\t// Check if already showing/shown\n\t\tif (_.#state === 'showing' || _.#state === 'shown') return true;\n\n\t\t// If currently hiding, ignore (let hide complete first)\n\t\tif (_.#state === 'hiding') return false;\n\n\t\t// Store trigger element for focus return later\n\t\t_.#triggerElement = triggerEl || null;\n\n\t\t// Fire beforeShow (cancelable)\n\t\tif (!_.#emit('beforeShow', { cancelable: true })) return false;\n\n\t\t// Set state to 'showing' and open native dialog\n\t\t_.#setState('showing');\n\t\t_.#dialog.showModal();\n\n\t\t// Double RAF ensures browser has painted the 'showing' state\n\t\t// before we transition to 'shown'\n\t\t_.#pendingRAF = requestAnimationFrame(() => {\n\t\t\t_.#pendingRAF = requestAnimationFrame(() => {\n\t\t\t\t_.#pendingRAF = null;\n\t\t\t\t_.#setState('shown');\n\n\t\t\t\t// Wait for transition to complete before firing 'shown' event\n\t\t\t\t_.#waitForTransition(() => {\n\t\t\t\t\t_.#emit('shown');\n\t\t\t\t});\n\t\t\t});\n\t\t});\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Hide the dialog with animation\n\t * @param {HTMLElement} [triggerEl=null] - The element that triggered the hide\n\t * @returns {boolean} False if hide was prevented via beforeHide event\n\t */\n\thide(triggerEl = null) {\n\t\tconst _ = this;\n\n\t\t// Check if already hiding/hidden\n\t\tif (_.#state === 'hiding' || _.#state === 'hidden') return true;\n\n\t\t// If currently showing, ignore (let show complete first)\n\t\tif (_.#state === 'showing') return false;\n\n\t\t// Capture result from trigger element\n\t\t_.#result = triggerEl?.dataset?.result ?? null;\n\n\t\t// Fire beforeHide (cancelable)\n\t\tif (\n\t\t\t!_.#emit('beforeHide', {\n\t\t\t\tcancelable: true,\n\t\t\t\tresult: _.#result,\n\t\t\t\ttriggerElement: triggerEl,\n\t\t\t})\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Set state to 'hiding' - this triggers CSS exit animation\n\t\t_.#setState('hiding');\n\n\t\t// Wait for transition to complete\n\t\t_.#waitForTransition(() => {\n\t\t\t_.#dialog.close();\n\t\t\t_.#setState('hidden');\n\t\t\t_.#emit('hidden', {\n\t\t\t\tresult: _.#result,\n\t\t\t\ttriggerElement: triggerEl,\n\t\t\t});\n\n\t\t\t// Return focus to trigger element\n\t\t\tif (_.#triggerElement) _.#triggerElement.focus();\n\n\t\t\t// Clean up\n\t\t\t_.#triggerElement = null;\n\t\t\t_.#result = null;\n\t\t});\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Wait for CSS transition to complete with fallback timeout\n\t * @param {Function} callback - Called when transition completes\n\t * @private\n\t */\n\t#waitForTransition(callback) {\n\t\tconst _ = this;\n\t\tlet called = false;\n\n\t\tconst done = () => {\n\t\t\tif (called) return;\n\t\t\tcalled = true;\n\t\t\t_.#dialog.removeEventListener('transitionend', onTransitionEnd);\n\t\t\tif (_.#pendingTimeout) {\n\t\t\t\tclearTimeout(_.#pendingTimeout);\n\t\t\t\t_.#pendingTimeout = null;\n\t\t\t}\n\t\t\tcallback();\n\t\t};\n\n\t\tconst onTransitionEnd = (e) => {\n\t\t\tif (e.target === _.#dialog) done();\n\t\t};\n\n\t\t_.#dialog.addEventListener('transitionend', onTransitionEnd);\n\n\t\t_.#pendingTimeout = setTimeout(\n\t\t\tdone,\n\t\t\tDialogPanel.TRANSITION_FALLBACK_TIMEOUT\n\t\t);\n\t}\n\n\t/**\n\t * Set the component state\n\t * @param {string} newState - The new state\n\t * @private\n\t */\n\t#setState(newState) {\n\t\tthis.#state = newState;\n\t\tthis.setAttribute('state', newState);\n\t}\n\n\t/**\n\t * Emit a custom event\n\t * @param {string} name - Event name\n\t * @param {Object} options - Event options\n\t * @returns {boolean} False if event was cancelled\n\t * @private\n\t */\n\t#emit(name, options = {}) {\n\t\tconst _ = this;\n\t\tconst { cancelable = false, ...detail } = options;\n\n\t\tconst event = new CustomEvent(name, {\n\t\t\tbubbles: true,\n\t\t\tcomposed: true,\n\t\t\tcancelable,\n\t\t\tdetail: {\n\t\t\t\ttriggerElement: _.#triggerElement,\n\t\t\t\tresult: _.#result,\n\t\t\t\tstate: _.#state,\n\t\t\t\t...detail,\n\t\t\t},\n\t\t});\n\n\t\treturn _.dispatchEvent(event);\n\t}\n\n\t// Read-only properties\n\tget state() {\n\t\treturn this.#state;\n\t}\n\tget dialog() {\n\t\treturn this.#dialog;\n\t}\n\tget isOpen() {\n\t\treturn this.#state === 'showing' || this.#state === 'shown';\n\t}\n\tget triggerElement() {\n\t\treturn this.#triggerElement;\n\t}\n}\n\n/**\n * DialogBackdrop - A custom backdrop element that animates with the dialog-panel state.\n * Use this instead of native ::backdrop for consistent cross-browser animations.\n *\n * @extends HTMLElement\n */\nclass DialogBackdrop extends HTMLElement {\n\t#panel = null;\n\t#handlers = {\n\t\tclick: null,\n\t};\n\n\tconnectedCallback() {\n\t\tconst _ = this;\n\n\t\t// Find parent dialog-panel\n\t\t_.#panel = _.closest('dialog-panel');\n\n\t\tif (!_.#panel) {\n\t\t\tconsole.warn(\n\t\t\t\t'DialogBackdrop: Must be inside a <dialog-panel> element'\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// Handle click to close\n\t\t_.#handlers.click = () => {\n\t\t\t_.#panel.hide();\n\t\t};\n\t\t_.addEventListener('click', _.#handlers.click);\n\t}\n\n\tdisconnectedCallback() {\n\t\tconst _ = this;\n\t\tif (_.#handlers.click) {\n\t\t\t_.removeEventListener('click', _.#handlers.click);\n\t\t}\n\t}\n}\n\n// Register the custom elements\n// Note: dialog-backdrop must be defined BEFORE dialog-panel,\n// because dialog-panel's connectedCallback may create dialog-backdrop elements\nif (!customElements.get('dialog-backdrop')) {\n\tcustomElements.define('dialog-backdrop', DialogBackdrop);\n}\nif (!customElements.get('dialog-panel')) {\n\tcustomElements.define('dialog-panel', DialogPanel);\n}\n\nexport { DialogPanel, DialogBackdrop };\n"],"names":[],"mappings":"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"}