@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.
package/README.md CHANGED
@@ -1,17 +1,17 @@
1
- # Dialog Panel Web Component
1
+ # Dialog Panel
2
2
 
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.
3
+ A lightweight web component wrapper for native `<dialog>` elements with state-driven animations.
4
4
 
5
5
  [**Live Demo**](https://magic-spells.github.io/dialog-panel/demo/)
6
6
 
7
7
  ## Features
8
8
 
9
- - No dependencies
10
- - Lightweight
11
- - Follows accessibility best practices with proper ARIA attributes
12
- - Smooth animations
13
- - Focus management and keyboard navigation
14
- - WAI-ARIA compliant modal dialog behavior
9
+ - **Zero dependencies** - Uses native `<dialog>` for focus trapping and accessibility
10
+ - **Lightweight** - ~3.4kb minified
11
+ - **State-driven animations** - CSS transitions based on `state` attribute
12
+ - **Cross-browser** - Custom `<dialog-backdrop>` for consistent animations (including Firefox)
13
+ - **Accessible** - Native dialog handles focus trap, escape key, and ARIA
14
+ - **Nested dialogs** - Proper stacking and event isolation
15
15
 
16
16
  ## Installation
17
17
 
@@ -20,172 +20,221 @@ npm install @magic-spells/dialog-panel
20
20
  ```
21
21
 
22
22
  ```javascript
23
- // Add to your JavaScript file
23
+ // Import for side effects (registers custom elements)
24
24
  import '@magic-spells/dialog-panel';
25
25
  ```
26
26
 
27
- Or include directly in your HTML:
27
+ Or include directly in HTML:
28
28
 
29
29
  ```html
30
30
  <script src="https://unpkg.com/@magic-spells/dialog-panel"></script>
31
+ <link rel="stylesheet" href="https://unpkg.com/@magic-spells/dialog-panel/css/min" />
31
32
  ```
32
33
 
33
34
  ## Usage
34
35
 
35
36
  ```html
36
- <button
37
- id="show-dialog-button"
38
- aria-haspopup="dialog"
39
- aria-controls="demo-dialog"
40
- aria-expanded="false">
41
- Open Dialog
42
- </button>
43
-
44
- <dialog-panel
45
- id="demo-dialog"
46
- role="dialog"
47
- aria-modal="true"
48
- aria-labelledby="dialog-title"
49
- aria-describedby="dialog-description"
50
- aria-hidden="true">
51
- <dialog-overlay></dialog-overlay>
52
- <dialog-content>
53
- <button aria-label="Close dialog" data-action-hide-dialog>
54
- &times;
55
- </button>
56
- <div>
57
- <h2 id="dialog-title">Dialog Title</h2>
58
- <p id="dialog-description">
59
- This is a demonstration of the dialog panel component. Add
60
- your content here.
61
- </p>
62
- </div>
63
- </dialog-content>
37
+ <button id="open-btn">Open Dialog</button>
38
+
39
+ <dialog-panel id="my-dialog">
40
+ <dialog aria-labelledby="dialog-title">
41
+ <div class="dialog-content">
42
+ <h2 id="dialog-title">Hello!</h2>
43
+ <p>This is a modal dialog.</p>
44
+ <button data-action-hide-dialog>Close</button>
45
+ </div>
46
+ </dialog>
64
47
  </dialog-panel>
65
48
 
66
49
  <script>
67
- const button = document.getElementById('show-dialog-button');
68
- const dialog = document.getElementById('demo-dialog');
69
- button.addEventListener('click', () => dialog.show());
50
+ const dialog = document.getElementById('my-dialog');
51
+ document.getElementById('open-btn').addEventListener('click', (e) => {
52
+ dialog.show(e.target);
53
+ });
70
54
  </script>
71
55
  ```
72
56
 
73
57
  ## How It Works
74
58
 
75
- - The dialog is initially hidden (`aria-hidden="true"`)
76
- - Clicking the button triggers the `show()` method, making the dialog visible
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
79
- - Keyboard focus is automatically trapped within the dialog when open
80
- - Pressing ESC closes the dialog
81
- - Focus returns to the trigger button when closed
59
+ 1. Wrap a native `<dialog>` element inside `<dialog-panel>`
60
+ 2. Call `show(triggerElement)` to open with animation
61
+ 3. The `state` attribute transitions: `hidden` `showing` `shown`
62
+ 4. Close via:
63
+ - Clicking backdrop
64
+ - Pressing Escape
65
+ - Clicking any element with `data-action-hide-dialog`
66
+ - Calling `hide()`
67
+ 5. The `state` attribute transitions: `shown` → `hiding` → `hidden`
68
+ 6. Focus returns to the trigger element
82
69
 
83
- ## Customization
70
+ ## State Machine
84
71
 
85
- ### Styling
86
-
87
- #### Using CSS Custom Properties
72
+ ```
73
+ hidden → showing → shown → hiding → hidden
74
+ ```
88
75
 
89
- You can style the Dialog Panel by overriding the CSS custom properties:
76
+ The `state` attribute on `<dialog-panel>` drives all CSS animations:
90
77
 
91
78
  ```css
92
- :root {
93
- /* Layout */
94
- --dp-panel-z-index: 100;
79
+ dialog-panel[state='showing'] > dialog { /* entering */ }
80
+ dialog-panel[state='shown'] > dialog { /* fully visible */ }
81
+ dialog-panel[state='hiding'] > dialog { /* exiting */ }
82
+ dialog-panel[state='hidden'] > dialog { /* hidden */ }
83
+ ```
95
84
 
96
- /* Overlay */
97
- --dp-overlay-background: rgba(0, 0, 0, 0.7);
98
- --dp-overlay-backdrop-filter: blur(5px) saturate(120%);
85
+ ## API
99
86
 
100
- /* Content */
101
- --dp-content-background: #f8f8f8;
87
+ ### Methods
102
88
 
103
- /* Animation */
104
- --dp-transition-duration: 400ms;
105
- --dp-transition-timing: cubic-bezier(0.4, 0, 0.2, 1);
106
- }
107
- ```
89
+ | Method | Description |
90
+ |--------|-------------|
91
+ | `show(triggerEl?)` | Opens the dialog. Pass trigger element for focus return. Returns `false` if cancelled. |
92
+ | `hide(triggerEl?)` | Closes the dialog. Returns `false` if cancelled. |
93
+
94
+ ### Properties (read-only)
95
+
96
+ | Property | Type | Description |
97
+ |----------|------|-------------|
98
+ | `state` | `string` | Current state: `'hidden'`, `'showing'`, `'shown'`, `'hiding'` |
99
+ | `dialog` | `HTMLDialogElement` | Reference to inner `<dialog>` |
100
+ | `isOpen` | `boolean` | `true` if `showing` or `shown` |
101
+ | `triggerElement` | `HTMLElement \| null` | Element that triggered the current action |
102
+
103
+ ### Events
108
104
 
109
- #### Using SCSS
105
+ | Event | Cancelable | Description |
106
+ |-------|------------|-------------|
107
+ | `beforeShow` | Yes | Fired before showing. Call `preventDefault()` to cancel. |
108
+ | `shown` | No | Fired after show animation completes. |
109
+ | `beforeHide` | Yes | Fired before hiding. Call `preventDefault()` to cancel. |
110
+ | `hidden` | No | Fired after hide animation completes. |
110
111
 
111
- For more advanced customization, you can import the SCSS directly:
112
+ Event `detail` includes:
113
+ - `triggerElement` - Element that triggered the action
114
+ - `result` - Value from `data-result` attribute (if any)
115
+ - `state` - Current state
112
116
 
113
- ```scss
114
- // Option 1: Import the compiled CSS
115
- @import '@magic-spells/dialog-panel/css';
117
+ ### Data Attributes
116
118
 
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
- );
119
+ | Attribute | Description |
120
+ |-----------|-------------|
121
+ | `data-action-hide-dialog` | Clicking this element closes the dialog |
122
+ | `data-result` | Value passed to `hidden` event when this element triggers close |
125
123
 
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';
124
+ ## Styling
125
+
126
+ ### Default CSS
127
+
128
+ Import the default styles:
129
+
130
+ ```javascript
131
+ import '@magic-spells/dialog-panel';
132
+ import '@magic-spells/dialog-panel/css';
133
+ ```
134
+
135
+ Or in HTML:
136
+
137
+ ```html
138
+ <link rel="stylesheet" href="https://unpkg.com/@magic-spells/dialog-panel/css/min" />
131
139
  ```
132
140
 
133
- #### Direct Element Styling
141
+ ### Custom Styling
134
142
 
135
- You can also style the elements directly:
143
+ Override styles using the `state` attribute:
136
144
 
137
145
  ```css
138
- dialog-panel {
139
- /* Customize your dialog panel */
146
+ /* Custom backdrop */
147
+ dialog-backdrop {
148
+ background: rgba(0, 0, 0, 0.5);
149
+ backdrop-filter: blur(5px);
140
150
  }
141
151
 
142
- dialog-overlay {
143
- background-color: rgba(0, 0, 0, 0.5);
152
+ /* Custom dialog appearance */
153
+ dialog-panel > dialog {
154
+ border-radius: 1rem;
155
+ box-shadow: 0 25px 50px rgba(0, 0, 0, 0.25);
156
+ }
157
+
158
+ /* Custom animations */
159
+ dialog-panel[state='shown'] > dialog {
160
+ opacity: 1;
161
+ transform: scale(1);
162
+ transition: all 0.3s ease-out;
163
+ }
164
+
165
+ dialog-panel[state='hiding'] > dialog {
166
+ opacity: 0;
167
+ transform: scale(0.95);
168
+ transition: all 0.2s ease-in;
144
169
  }
145
170
  ```
146
171
 
147
- ### JavaScript API
172
+ ## Nested Dialogs
148
173
 
149
- #### Methods
174
+ Dialogs can be nested inside other dialogs:
150
175
 
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.
176
+ ```html
177
+ <dialog-panel id="outer">
178
+ <dialog>
179
+ <p>Outer dialog content</p>
180
+ <button id="open-inner">Open Inner</button>
181
+
182
+ <dialog-panel id="inner">
183
+ <dialog>
184
+ <p>Inner dialog content</p>
185
+ <button data-action-hide-dialog>Close Inner</button>
186
+ </dialog>
187
+ </dialog-panel>
188
+ </dialog>
189
+ </dialog-panel>
190
+ ```
153
191
 
154
- #### Events
192
+ Each dialog manages its own backdrop and events independently.
155
193
 
156
- The dialog panel emits the following events that you can listen for:
194
+ ## Dialog Backdrop
157
195
 
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.
196
+ The `<dialog-backdrop>` element is auto-created if not present. It provides:
163
197
 
164
- Each event provides a `detail` object with the `triggerElement` that initiated the action (if any).
198
+ - Consistent cross-browser animations (native `::backdrop` doesn't animate in Firefox)
199
+ - Click-to-close functionality
200
+ - Custom styling support
165
201
 
166
- Example usage:
202
+ ## Preventing Close
167
203
 
168
- ```javascript
169
- const dialog = document.getElementById('my-dialog');
204
+ Use `beforeHide` to prevent closing:
170
205
 
171
- // Prevent dialog from closing based on some condition
206
+ ```javascript
172
207
  dialog.addEventListener('beforeHide', (e) => {
173
- if (someFormIsUnsaved) {
174
- e.preventDefault(); // Prevents the dialog from closing
175
- // Show a confirmation message instead
208
+ if (formHasUnsavedChanges) {
209
+ e.preventDefault();
210
+ showConfirmation();
176
211
  }
177
212
  });
213
+ ```
214
+
215
+ ## Result Values
216
+
217
+ Track which button closed the dialog:
218
+
219
+ ```html
220
+ <button data-action-hide-dialog data-result="save">Save</button>
221
+ <button data-action-hide-dialog data-result="cancel">Cancel</button>
222
+ ```
178
223
 
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.
224
+ ```javascript
225
+ dialog.addEventListener('hidden', (e) => {
226
+ if (e.detail.result === 'save') {
227
+ saveData();
228
+ }
183
229
  });
184
230
  ```
185
231
 
186
232
  ## Browser Support
187
233
 
188
- This component works in all modern browsers that support Web Components.
234
+ Modern browsers with support for:
235
+ - Custom Elements v1
236
+ - Native `<dialog>` element
237
+ - CSS `:has()` selector
189
238
 
190
239
  ## License
191
240