@duskmoon-dev/el-bottom-sheet 0.5.0 → 0.6.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 +157 -0
- package/dist/cjs/index.js +48 -80
- package/dist/cjs/index.js.map +3 -3
- package/dist/cjs/register.js +48 -80
- package/dist/cjs/register.js.map +3 -3
- package/dist/esm/index.js +46 -78
- package/dist/esm/index.js.map +3 -3
- package/dist/esm/register.js +46 -78
- package/dist/esm/register.js.map +3 -3
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/el-dm-bottom-sheet.d.ts +3 -2
- package/dist/types/el-dm-bottom-sheet.d.ts.map +1 -1
- package/package.json +6 -6
package/README.md
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# @duskmoon-dev/el-bottom-sheet
|
|
2
|
+
|
|
3
|
+
A mobile bottom sheet component built with Web Components.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @duskmoon-dev/el-bottom-sheet
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Auto-Register
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import '@duskmoon-dev/el-bottom-sheet/register';
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```html
|
|
20
|
+
<el-dm-bottom-sheet id="sheet">
|
|
21
|
+
<span slot="header">Sheet Title</span>
|
|
22
|
+
<p>Sheet content goes here.</p>
|
|
23
|
+
</el-dm-bottom-sheet>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Manual Registration
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { ElDmBottomSheet, register } from '@duskmoon-dev/el-bottom-sheet';
|
|
30
|
+
|
|
31
|
+
// Register with default tag name
|
|
32
|
+
register();
|
|
33
|
+
|
|
34
|
+
// Or register with custom tag name
|
|
35
|
+
customElements.define('my-bottom-sheet', ElDmBottomSheet);
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Attributes
|
|
39
|
+
|
|
40
|
+
| Attribute | Type | Default | Description |
|
|
41
|
+
|-----------|------|---------|-------------|
|
|
42
|
+
| `open` | `boolean` | `false` | Whether the sheet is open |
|
|
43
|
+
| `modal` | `boolean` | `true` | Show backdrop overlay |
|
|
44
|
+
| `persistent` | `boolean` | `false` | Prevent closing on backdrop click |
|
|
45
|
+
| `snap-points` | `string` | - | Snap points (e.g., "25,50,100") |
|
|
46
|
+
|
|
47
|
+
## Slots
|
|
48
|
+
|
|
49
|
+
| Slot | Description |
|
|
50
|
+
|------|-------------|
|
|
51
|
+
| (default) | Sheet content |
|
|
52
|
+
| `header` | Sheet header |
|
|
53
|
+
|
|
54
|
+
## CSS Parts
|
|
55
|
+
|
|
56
|
+
| Part | Description |
|
|
57
|
+
|------|-------------|
|
|
58
|
+
| `sheet` | The sheet container |
|
|
59
|
+
| `backdrop` | The backdrop overlay |
|
|
60
|
+
| `handle` | The drag handle |
|
|
61
|
+
| `content` | The content wrapper |
|
|
62
|
+
| `header` | The header section |
|
|
63
|
+
|
|
64
|
+
## Events
|
|
65
|
+
|
|
66
|
+
| Event | Detail | Description |
|
|
67
|
+
|-------|--------|-------------|
|
|
68
|
+
| `open` | - | Fired when sheet opens |
|
|
69
|
+
| `close` | - | Fired when sheet closes |
|
|
70
|
+
| `snap` | `{ point }` | Fired when sheet snaps to point |
|
|
71
|
+
|
|
72
|
+
## Examples
|
|
73
|
+
|
|
74
|
+
### Basic
|
|
75
|
+
|
|
76
|
+
```html
|
|
77
|
+
<el-dm-button onclick="document.querySelector('#sheet').open = true">
|
|
78
|
+
Open Sheet
|
|
79
|
+
</el-dm-button>
|
|
80
|
+
|
|
81
|
+
<el-dm-bottom-sheet id="sheet">
|
|
82
|
+
<span slot="header">Options</span>
|
|
83
|
+
<ul>
|
|
84
|
+
<li>Option 1</li>
|
|
85
|
+
<li>Option 2</li>
|
|
86
|
+
<li>Option 3</li>
|
|
87
|
+
</ul>
|
|
88
|
+
</el-dm-bottom-sheet>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### With Snap Points
|
|
92
|
+
|
|
93
|
+
```html
|
|
94
|
+
<el-dm-bottom-sheet snap-points="25,50,100">
|
|
95
|
+
<span slot="header">Draggable Sheet</span>
|
|
96
|
+
<p>Drag up or down to snap to different heights.</p>
|
|
97
|
+
</el-dm-bottom-sheet>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Persistent (Non-Dismissible)
|
|
101
|
+
|
|
102
|
+
```html
|
|
103
|
+
<el-dm-bottom-sheet persistent>
|
|
104
|
+
<span slot="header">Required Action</span>
|
|
105
|
+
<p>You must complete this action.</p>
|
|
106
|
+
<el-dm-button onclick="this.closest('el-dm-bottom-sheet').open = false">
|
|
107
|
+
Done
|
|
108
|
+
</el-dm-button>
|
|
109
|
+
</el-dm-bottom-sheet>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Without Backdrop
|
|
113
|
+
|
|
114
|
+
```html
|
|
115
|
+
<el-dm-bottom-sheet modal="false">
|
|
116
|
+
Content without backdrop
|
|
117
|
+
</el-dm-bottom-sheet>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Action Sheet Pattern
|
|
121
|
+
|
|
122
|
+
```html
|
|
123
|
+
<el-dm-bottom-sheet id="actions">
|
|
124
|
+
<span slot="header">Share</span>
|
|
125
|
+
<div class="action-list">
|
|
126
|
+
<button>Copy Link</button>
|
|
127
|
+
<button>Email</button>
|
|
128
|
+
<button>Twitter</button>
|
|
129
|
+
<button>Facebook</button>
|
|
130
|
+
</div>
|
|
131
|
+
</el-dm-bottom-sheet>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Programmatic Control
|
|
135
|
+
|
|
136
|
+
```javascript
|
|
137
|
+
const sheet = document.querySelector('el-dm-bottom-sheet');
|
|
138
|
+
|
|
139
|
+
// Open
|
|
140
|
+
sheet.open = true;
|
|
141
|
+
|
|
142
|
+
// Close
|
|
143
|
+
sheet.open = false;
|
|
144
|
+
|
|
145
|
+
// Listen for events
|
|
146
|
+
sheet.addEventListener('close', () => {
|
|
147
|
+
console.log('Sheet closed');
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
sheet.addEventListener('snap', (e) => {
|
|
151
|
+
console.log('Snapped to:', e.detail.point + '%');
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## License
|
|
156
|
+
|
|
157
|
+
MIT
|
package/dist/cjs/index.js
CHANGED
|
@@ -35,8 +35,10 @@ __export(exports_src, {
|
|
|
35
35
|
module.exports = __toCommonJS(exports_src);
|
|
36
36
|
|
|
37
37
|
// src/el-dm-bottom-sheet.ts
|
|
38
|
-
var
|
|
39
|
-
var
|
|
38
|
+
var import_el_base = require("@duskmoon-dev/el-base");
|
|
39
|
+
var import_bottomsheet = require("@duskmoon-dev/core/components/bottomsheet");
|
|
40
|
+
var coreStyles = import_bottomsheet.css.replace(/@layer\s+components\s*\{/, "").replace(/\}\s*$/, "");
|
|
41
|
+
var styles = import_el_base.css`
|
|
40
42
|
:host {
|
|
41
43
|
display: contents;
|
|
42
44
|
}
|
|
@@ -45,70 +47,43 @@ var styles = import_el_core.css`
|
|
|
45
47
|
display: none !important;
|
|
46
48
|
}
|
|
47
49
|
|
|
48
|
-
|
|
50
|
+
/* Import core bottomsheet styles */
|
|
51
|
+
${coreStyles}
|
|
52
|
+
|
|
53
|
+
/* Wrapper for fixed overlay with pointer-events control */
|
|
54
|
+
.bottomsheet-wrapper {
|
|
49
55
|
position: fixed;
|
|
50
56
|
inset: 0;
|
|
51
57
|
z-index: 1000;
|
|
52
58
|
pointer-events: none;
|
|
53
59
|
}
|
|
54
60
|
|
|
55
|
-
.
|
|
61
|
+
.bottomsheet-wrapper.open {
|
|
56
62
|
pointer-events: auto;
|
|
57
63
|
}
|
|
58
64
|
|
|
59
|
-
|
|
65
|
+
/* Override core's fixed positioning since sheet is inside a fixed wrapper */
|
|
66
|
+
.bottomsheet {
|
|
60
67
|
position: absolute;
|
|
61
|
-
|
|
62
|
-
background-color: rgba(0, 0, 0, 0.5);
|
|
63
|
-
opacity: 0;
|
|
64
|
-
transition: opacity 300ms ease;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
.bottom-sheet-wrapper.open .bottom-sheet-backdrop {
|
|
68
|
-
opacity: 1;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
.bottom-sheet {
|
|
72
|
-
position: absolute;
|
|
73
|
-
bottom: 0;
|
|
74
|
-
left: 0;
|
|
75
|
-
right: 0;
|
|
76
|
-
display: flex;
|
|
77
|
-
flex-direction: column;
|
|
78
|
-
max-height: 100vh;
|
|
79
|
-
max-height: 100dvh;
|
|
80
|
-
background-color: var(--color-surface);
|
|
81
|
-
border-radius: 1rem 1rem 0 0;
|
|
82
|
-
box-shadow: 0 -4px 20px rgb(0 0 0 / 0.15);
|
|
83
|
-
transform: translateY(100%);
|
|
84
|
-
transition: transform 300ms cubic-bezier(0.32, 0.72, 0, 1);
|
|
85
|
-
touch-action: none;
|
|
86
|
-
will-change: transform;
|
|
68
|
+
z-index: auto;
|
|
87
69
|
pointer-events: auto;
|
|
88
70
|
}
|
|
89
71
|
|
|
90
|
-
|
|
91
|
-
|
|
72
|
+
/* Override core's fixed positioning on backdrop */
|
|
73
|
+
.bottomsheet-backdrop {
|
|
74
|
+
position: absolute;
|
|
92
75
|
}
|
|
93
76
|
|
|
94
|
-
.
|
|
77
|
+
.bottomsheet.dragging {
|
|
95
78
|
transition: none;
|
|
96
79
|
}
|
|
97
80
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
justify-content: center;
|
|
102
|
-
padding: 0.75rem 0;
|
|
103
|
-
cursor: grab;
|
|
104
|
-
touch-action: none;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
.bottom-sheet-handle-area:active {
|
|
108
|
-
cursor: grabbing;
|
|
81
|
+
/* We render an explicit handle bar div; hide core's ::before pseudo-element */
|
|
82
|
+
.bottomsheet-handle::before {
|
|
83
|
+
display: none;
|
|
109
84
|
}
|
|
110
85
|
|
|
111
|
-
.
|
|
86
|
+
.bottomsheet-bar {
|
|
112
87
|
width: 2.5rem;
|
|
113
88
|
height: 0.25rem;
|
|
114
89
|
background-color: var(--color-outline);
|
|
@@ -116,44 +91,37 @@ var styles = import_el_core.css`
|
|
|
116
91
|
transition: background-color 150ms ease;
|
|
117
92
|
}
|
|
118
93
|
|
|
119
|
-
.
|
|
94
|
+
.bottomsheet-handle:hover .bottomsheet-bar {
|
|
120
95
|
background-color: var(--color-outline-variant, var(--color-outline));
|
|
121
96
|
}
|
|
122
97
|
|
|
123
|
-
.
|
|
124
|
-
display: flex;
|
|
125
|
-
align-items: center;
|
|
126
|
-
justify-content: space-between;
|
|
127
|
-
padding: 0 1rem 0.75rem;
|
|
98
|
+
.bottomsheet-header {
|
|
128
99
|
border-bottom: 1px solid var(--color-outline);
|
|
129
100
|
}
|
|
130
101
|
|
|
131
|
-
.
|
|
102
|
+
.bottomsheet-header:empty {
|
|
132
103
|
display: none;
|
|
133
104
|
}
|
|
134
105
|
|
|
135
|
-
.
|
|
136
|
-
flex: 1;
|
|
106
|
+
.bottomsheet-content {
|
|
137
107
|
padding: 1rem;
|
|
138
|
-
overflow-y: auto;
|
|
139
|
-
overscroll-behavior: contain;
|
|
140
108
|
}
|
|
141
109
|
|
|
142
110
|
/* Focus trap indicator for modal */
|
|
143
|
-
.
|
|
111
|
+
.bottomsheet:focus-visible {
|
|
144
112
|
outline: 2px solid var(--color-primary);
|
|
145
113
|
outline-offset: -2px;
|
|
146
114
|
}
|
|
147
115
|
|
|
148
116
|
/* Safe area padding for notched devices */
|
|
149
117
|
@supports (padding-bottom: env(safe-area-inset-bottom)) {
|
|
150
|
-
.
|
|
118
|
+
.bottomsheet-content {
|
|
151
119
|
padding-bottom: calc(1rem + env(safe-area-inset-bottom));
|
|
152
120
|
}
|
|
153
121
|
}
|
|
154
122
|
`;
|
|
155
123
|
|
|
156
|
-
class ElDmBottomSheet extends
|
|
124
|
+
class ElDmBottomSheet extends import_el_base.BaseElement {
|
|
157
125
|
static properties = {
|
|
158
126
|
open: { type: Boolean, reflect: true },
|
|
159
127
|
modal: { type: Boolean, reflect: true },
|
|
@@ -207,7 +175,7 @@ class ElDmBottomSheet extends import_el_core.BaseElement {
|
|
|
207
175
|
}
|
|
208
176
|
};
|
|
209
177
|
_trapFocus(event) {
|
|
210
|
-
const sheet = this.shadowRoot?.querySelector(".
|
|
178
|
+
const sheet = this.shadowRoot?.querySelector(".bottomsheet");
|
|
211
179
|
if (!sheet)
|
|
212
180
|
return;
|
|
213
181
|
this._focusableElements = Array.from(sheet.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'));
|
|
@@ -229,7 +197,7 @@ class ElDmBottomSheet extends import_el_core.BaseElement {
|
|
|
229
197
|
const touch = event.touches[0];
|
|
230
198
|
this._startY = touch.clientY;
|
|
231
199
|
this._isDragging = true;
|
|
232
|
-
const sheet = this.shadowRoot?.querySelector(".
|
|
200
|
+
const sheet = this.shadowRoot?.querySelector(".bottomsheet");
|
|
233
201
|
if (sheet) {
|
|
234
202
|
this._sheetHeight = sheet.getBoundingClientRect().height;
|
|
235
203
|
sheet.classList.add("dragging");
|
|
@@ -243,7 +211,7 @@ class ElDmBottomSheet extends import_el_core.BaseElement {
|
|
|
243
211
|
const deltaY = this._currentY - this._startY;
|
|
244
212
|
if (deltaY > 0) {
|
|
245
213
|
event.preventDefault();
|
|
246
|
-
const sheet = this.shadowRoot?.querySelector(".
|
|
214
|
+
const sheet = this.shadowRoot?.querySelector(".bottomsheet");
|
|
247
215
|
if (sheet) {
|
|
248
216
|
sheet.style.transform = `translateY(${deltaY}px)`;
|
|
249
217
|
}
|
|
@@ -254,7 +222,7 @@ class ElDmBottomSheet extends import_el_core.BaseElement {
|
|
|
254
222
|
return;
|
|
255
223
|
this._isDragging = false;
|
|
256
224
|
const deltaY = this._currentY - this._startY;
|
|
257
|
-
const sheet = this.shadowRoot?.querySelector(".
|
|
225
|
+
const sheet = this.shadowRoot?.querySelector(".bottomsheet");
|
|
258
226
|
if (sheet) {
|
|
259
227
|
sheet.classList.remove("dragging");
|
|
260
228
|
sheet.style.transform = "";
|
|
@@ -271,7 +239,7 @@ class ElDmBottomSheet extends import_el_core.BaseElement {
|
|
|
271
239
|
_handleMouseDown = (event) => {
|
|
272
240
|
this._startY = event.clientY;
|
|
273
241
|
this._isDragging = true;
|
|
274
|
-
const sheet = this.shadowRoot?.querySelector(".
|
|
242
|
+
const sheet = this.shadowRoot?.querySelector(".bottomsheet");
|
|
275
243
|
if (sheet) {
|
|
276
244
|
this._sheetHeight = sheet.getBoundingClientRect().height;
|
|
277
245
|
sheet.classList.add("dragging");
|
|
@@ -285,7 +253,7 @@ class ElDmBottomSheet extends import_el_core.BaseElement {
|
|
|
285
253
|
this._currentY = event.clientY;
|
|
286
254
|
const deltaY = this._currentY - this._startY;
|
|
287
255
|
if (deltaY > 0) {
|
|
288
|
-
const sheet = this.shadowRoot?.querySelector(".
|
|
256
|
+
const sheet = this.shadowRoot?.querySelector(".bottomsheet");
|
|
289
257
|
if (sheet) {
|
|
290
258
|
sheet.style.transform = `translateY(${deltaY}px)`;
|
|
291
259
|
}
|
|
@@ -296,7 +264,7 @@ class ElDmBottomSheet extends import_el_core.BaseElement {
|
|
|
296
264
|
return;
|
|
297
265
|
this._isDragging = false;
|
|
298
266
|
const deltaY = this._currentY - this._startY;
|
|
299
|
-
const sheet = this.shadowRoot?.querySelector(".
|
|
267
|
+
const sheet = this.shadowRoot?.querySelector(".bottomsheet");
|
|
300
268
|
if (sheet) {
|
|
301
269
|
sheet.classList.remove("dragging");
|
|
302
270
|
sheet.style.transform = "";
|
|
@@ -326,7 +294,7 @@ class ElDmBottomSheet extends import_el_core.BaseElement {
|
|
|
326
294
|
if (nearestIndex !== this._currentSnapIndex) {
|
|
327
295
|
this._currentSnapIndex = nearestIndex;
|
|
328
296
|
const snapHeight = this._parsedSnapPoints[nearestIndex];
|
|
329
|
-
const sheet = this.shadowRoot?.querySelector(".
|
|
297
|
+
const sheet = this.shadowRoot?.querySelector(".bottomsheet");
|
|
330
298
|
if (sheet) {
|
|
331
299
|
sheet.style.height = `${snapHeight}vh`;
|
|
332
300
|
}
|
|
@@ -344,7 +312,7 @@ class ElDmBottomSheet extends import_el_core.BaseElement {
|
|
|
344
312
|
document.body.style.overflow = "hidden";
|
|
345
313
|
if (this._parsedSnapPoints.length > 0) {
|
|
346
314
|
this._currentSnapIndex = this._parsedSnapPoints.length - 1;
|
|
347
|
-
const sheet = this.shadowRoot?.querySelector(".
|
|
315
|
+
const sheet = this.shadowRoot?.querySelector(".bottomsheet");
|
|
348
316
|
if (sheet) {
|
|
349
317
|
sheet.style.height = `${this._parsedSnapPoints[this._currentSnapIndex]}vh`;
|
|
350
318
|
}
|
|
@@ -352,7 +320,7 @@ class ElDmBottomSheet extends import_el_core.BaseElement {
|
|
|
352
320
|
this.emit("open");
|
|
353
321
|
if (this.modal) {
|
|
354
322
|
requestAnimationFrame(() => {
|
|
355
|
-
const sheet = this.shadowRoot?.querySelector(".
|
|
323
|
+
const sheet = this.shadowRoot?.querySelector(".bottomsheet");
|
|
356
324
|
sheet?.focus();
|
|
357
325
|
});
|
|
358
326
|
}
|
|
@@ -376,22 +344,22 @@ class ElDmBottomSheet extends import_el_core.BaseElement {
|
|
|
376
344
|
}
|
|
377
345
|
render() {
|
|
378
346
|
return `
|
|
379
|
-
<div class="
|
|
380
|
-
${this.modal ?
|
|
347
|
+
<div class="bottomsheet-wrapper ${this.open ? "open" : ""}" part="wrapper">
|
|
348
|
+
${this.modal ? `<div class="bottomsheet-backdrop ${this.open ? "show" : ""}" part="backdrop"></div>` : ""}
|
|
381
349
|
<div
|
|
382
|
-
class="
|
|
350
|
+
class="bottomsheet ${this.open ? "show" : ""}"
|
|
383
351
|
role="dialog"
|
|
384
352
|
aria-modal="${this.modal ? "true" : "false"}"
|
|
385
353
|
tabindex="-1"
|
|
386
354
|
part="sheet"
|
|
387
355
|
>
|
|
388
|
-
<div class="
|
|
389
|
-
<div class="
|
|
356
|
+
<div class="bottomsheet-handle" part="handle-area">
|
|
357
|
+
<div class="bottomsheet-bar" part="handle"></div>
|
|
390
358
|
</div>
|
|
391
|
-
<div class="
|
|
359
|
+
<div class="bottomsheet-header" part="header">
|
|
392
360
|
<slot name="header"></slot>
|
|
393
361
|
</div>
|
|
394
|
-
<div class="
|
|
362
|
+
<div class="bottomsheet-content" part="content">
|
|
395
363
|
<slot></slot>
|
|
396
364
|
</div>
|
|
397
365
|
</div>
|
|
@@ -400,9 +368,9 @@ class ElDmBottomSheet extends import_el_core.BaseElement {
|
|
|
400
368
|
}
|
|
401
369
|
update() {
|
|
402
370
|
super.update();
|
|
403
|
-
const backdrop = this.shadowRoot?.querySelector(".
|
|
371
|
+
const backdrop = this.shadowRoot?.querySelector(".bottomsheet-backdrop");
|
|
404
372
|
backdrop?.addEventListener("click", this._handleBackdropClick);
|
|
405
|
-
const handleArea = this.shadowRoot?.querySelector(".
|
|
373
|
+
const handleArea = this.shadowRoot?.querySelector(".bottomsheet-handle");
|
|
406
374
|
handleArea?.addEventListener("touchstart", this._handleTouchStart, {
|
|
407
375
|
passive: false
|
|
408
376
|
});
|
|
@@ -421,5 +389,5 @@ function register() {
|
|
|
421
389
|
}
|
|
422
390
|
}
|
|
423
391
|
|
|
424
|
-
//# debugId=
|
|
392
|
+
//# debugId=DE784ACD430B338E64756E2164756E21
|
|
425
393
|
//# sourceMappingURL=index.js.map
|
package/dist/cjs/index.js.map
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/el-dm-bottom-sheet.ts", "../../src/index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * DuskMoon Bottom Sheet Element\n *\n * A mobile bottom panel/sheet component for displaying content that slides up from the bottom.\n * Supports snap points, swipe gestures, and modal mode.\n *\n * @element el-dm-bottom-sheet\n *\n * @attr {boolean} open - Whether the bottom sheet is open\n * @attr {boolean} modal - Whether to show backdrop and trap focus\n * @attr {boolean} persistent - Prevent dismiss by outside click or swipe down\n * @attr {string} snap-points - Comma-separated list of snap point heights (e.g., \"25%,50%,100%\")\n *\n * @slot - Default slot for sheet content\n * @slot header - Header content above the drag handle\n *\n * @csspart sheet - The sheet container\n * @csspart backdrop - The backdrop overlay (modal mode)\n * @csspart handle - The drag handle\n * @csspart content - The content wrapper\n * @csspart header - The header section\n *\n * @fires open - Fired when sheet opens\n * @fires close - Fired when sheet closes\n * @fires snap - Fired when sheet snaps to a point, detail contains { height, index }\n */\n\nimport { BaseElement, css } from '@duskmoon-dev/el-core';\n\nconst styles = css`\n :host {\n display: contents;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n\n .bottom-sheet-wrapper {\n position: fixed;\n inset: 0;\n z-index: 1000;\n pointer-events: none;\n }\n\n .bottom-sheet-wrapper.open {\n pointer-events: auto;\n }\n\n .bottom-sheet-backdrop {\n position: absolute;\n inset: 0;\n background-color: rgba(0, 0, 0, 0.5);\n opacity: 0;\n transition: opacity 300ms ease;\n }\n\n .bottom-sheet-wrapper.open .bottom-sheet-backdrop {\n opacity: 1;\n }\n\n .bottom-sheet {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n display: flex;\n flex-direction: column;\n max-height: 100vh;\n max-height: 100dvh;\n background-color: var(--color-surface);\n border-radius: 1rem 1rem 0 0;\n box-shadow: 0 -4px 20px rgb(0 0 0 / 0.15);\n transform: translateY(100%);\n transition: transform 300ms cubic-bezier(0.32, 0.72, 0, 1);\n touch-action: none;\n will-change: transform;\n pointer-events: auto;\n }\n\n .bottom-sheet-wrapper.open .bottom-sheet {\n transform: translateY(0);\n }\n\n .bottom-sheet.dragging {\n transition: none;\n }\n\n .bottom-sheet-handle-area {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0.75rem 0;\n cursor: grab;\n touch-action: none;\n }\n\n .bottom-sheet-handle-area:active {\n cursor: grabbing;\n }\n\n .bottom-sheet-handle {\n width: 2.5rem;\n height: 0.25rem;\n background-color: var(--color-outline);\n border-radius: 0.125rem;\n transition: background-color 150ms ease;\n }\n\n .bottom-sheet-handle-area:hover .bottom-sheet-handle {\n background-color: var(--color-outline-variant, var(--color-outline));\n }\n\n .bottom-sheet-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0 1rem 0.75rem;\n border-bottom: 1px solid var(--color-outline);\n }\n\n .bottom-sheet-header:empty {\n display: none;\n }\n\n .bottom-sheet-content {\n flex: 1;\n padding: 1rem;\n overflow-y: auto;\n overscroll-behavior: contain;\n }\n\n /* Focus trap indicator for modal */\n .bottom-sheet:focus-visible {\n outline: 2px solid var(--color-primary);\n outline-offset: -2px;\n }\n\n /* Safe area padding for notched devices */\n @supports (padding-bottom: env(safe-area-inset-bottom)) {\n .bottom-sheet-content {\n padding-bottom: calc(1rem + env(safe-area-inset-bottom));\n }\n }\n`;\n\ninterface SnapEvent {\n height: string;\n index: number;\n}\n\nexport class ElDmBottomSheet extends BaseElement {\n static properties = {\n open: { type: Boolean, reflect: true },\n modal: { type: Boolean, reflect: true },\n persistent: { type: Boolean, reflect: true },\n snapPoints: { type: String, reflect: true, attribute: 'snap-points' },\n };\n\n declare open: boolean;\n declare modal: boolean;\n declare persistent: boolean;\n declare snapPoints: string;\n\n private _startY = 0;\n private _currentY = 0;\n private _sheetHeight = 0;\n private _isDragging = false;\n private _parsedSnapPoints: number[] = [];\n private _currentSnapIndex = -1;\n private _focusableElements: HTMLElement[] = [];\n private _previouslyFocused: HTMLElement | null = null;\n\n constructor() {\n super();\n this.attachStyles(styles);\n }\n\n connectedCallback(): void {\n super.connectedCallback?.();\n this._parseSnapPoints();\n }\n\n private _parseSnapPoints(): void {\n if (!this.snapPoints) {\n this._parsedSnapPoints = [100];\n return;\n }\n\n this._parsedSnapPoints = this.snapPoints\n .split(',')\n .map((point) => {\n const trimmed = point.trim();\n if (trimmed.endsWith('%')) {\n return parseFloat(trimmed);\n }\n return parseFloat(trimmed);\n })\n .filter((point) => !isNaN(point) && point > 0 && point <= 100)\n .sort((a, b) => a - b);\n\n if (this._parsedSnapPoints.length === 0) {\n this._parsedSnapPoints = [100];\n }\n }\n\n private _handleBackdropClick = (event: Event): void => {\n if (!this.persistent && event.target === event.currentTarget) {\n this.hide();\n }\n };\n\n private _handleKeyDown = (event: KeyboardEvent): void => {\n if (event.key === 'Escape' && !this.persistent) {\n this.hide();\n return;\n }\n\n if (this.modal && event.key === 'Tab') {\n this._trapFocus(event);\n }\n };\n\n private _trapFocus(event: KeyboardEvent): void {\n const sheet = this.shadowRoot?.querySelector('.bottom-sheet') as HTMLElement;\n if (!sheet) return;\n\n this._focusableElements = Array.from(\n sheet.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n ),\n );\n\n const slotContent = this.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n );\n this._focusableElements.push(...Array.from(slotContent));\n\n if (this._focusableElements.length === 0) return;\n\n const firstFocusable = this._focusableElements[0];\n const lastFocusable = this._focusableElements[this._focusableElements.length - 1];\n\n if (event.shiftKey && document.activeElement === firstFocusable) {\n event.preventDefault();\n lastFocusable.focus();\n } else if (!event.shiftKey && document.activeElement === lastFocusable) {\n event.preventDefault();\n firstFocusable.focus();\n }\n }\n\n private _handleTouchStart = (event: TouchEvent): void => {\n const touch = event.touches[0];\n this._startY = touch.clientY;\n this._isDragging = true;\n\n const sheet = this.shadowRoot?.querySelector('.bottom-sheet') as HTMLElement;\n if (sheet) {\n this._sheetHeight = sheet.getBoundingClientRect().height;\n sheet.classList.add('dragging');\n }\n };\n\n private _handleTouchMove = (event: TouchEvent): void => {\n if (!this._isDragging) return;\n\n const touch = event.touches[0];\n this._currentY = touch.clientY;\n const deltaY = this._currentY - this._startY;\n\n if (deltaY > 0) {\n event.preventDefault();\n const sheet = this.shadowRoot?.querySelector('.bottom-sheet') as HTMLElement;\n if (sheet) {\n sheet.style.transform = `translateY(${deltaY}px)`;\n }\n }\n };\n\n private _handleTouchEnd = (): void => {\n if (!this._isDragging) return;\n\n this._isDragging = false;\n const deltaY = this._currentY - this._startY;\n const sheet = this.shadowRoot?.querySelector('.bottom-sheet') as HTMLElement;\n\n if (sheet) {\n sheet.classList.remove('dragging');\n sheet.style.transform = '';\n }\n\n const threshold = this._sheetHeight * 0.25;\n\n if (!this.persistent && deltaY > threshold) {\n this.hide();\n } else if (this._parsedSnapPoints.length > 1) {\n this._snapToNearestPoint(deltaY);\n }\n\n this._startY = 0;\n this._currentY = 0;\n };\n\n private _handleMouseDown = (event: MouseEvent): void => {\n this._startY = event.clientY;\n this._isDragging = true;\n\n const sheet = this.shadowRoot?.querySelector('.bottom-sheet') as HTMLElement;\n if (sheet) {\n this._sheetHeight = sheet.getBoundingClientRect().height;\n sheet.classList.add('dragging');\n }\n\n document.addEventListener('mousemove', this._handleMouseMove);\n document.addEventListener('mouseup', this._handleMouseUp);\n };\n\n private _handleMouseMove = (event: MouseEvent): void => {\n if (!this._isDragging) return;\n\n this._currentY = event.clientY;\n const deltaY = this._currentY - this._startY;\n\n if (deltaY > 0) {\n const sheet = this.shadowRoot?.querySelector('.bottom-sheet') as HTMLElement;\n if (sheet) {\n sheet.style.transform = `translateY(${deltaY}px)`;\n }\n }\n };\n\n private _handleMouseUp = (): void => {\n if (!this._isDragging) return;\n\n this._isDragging = false;\n const deltaY = this._currentY - this._startY;\n const sheet = this.shadowRoot?.querySelector('.bottom-sheet') as HTMLElement;\n\n if (sheet) {\n sheet.classList.remove('dragging');\n sheet.style.transform = '';\n }\n\n document.removeEventListener('mousemove', this._handleMouseMove);\n document.removeEventListener('mouseup', this._handleMouseUp);\n\n const threshold = this._sheetHeight * 0.25;\n\n if (!this.persistent && deltaY > threshold) {\n this.hide();\n } else if (this._parsedSnapPoints.length > 1) {\n this._snapToNearestPoint(deltaY);\n }\n\n this._startY = 0;\n this._currentY = 0;\n };\n\n private _snapToNearestPoint(deltaY: number): void {\n const currentHeightPercent = ((this._sheetHeight - deltaY) / window.innerHeight) * 100;\n\n let nearestIndex = 0;\n let nearestDistance = Math.abs(this._parsedSnapPoints[0] - currentHeightPercent);\n\n for (let i = 1; i < this._parsedSnapPoints.length; i++) {\n const distance = Math.abs(this._parsedSnapPoints[i] - currentHeightPercent);\n if (distance < nearestDistance) {\n nearestDistance = distance;\n nearestIndex = i;\n }\n }\n\n if (nearestIndex !== this._currentSnapIndex) {\n this._currentSnapIndex = nearestIndex;\n const snapHeight = this._parsedSnapPoints[nearestIndex];\n\n const sheet = this.shadowRoot?.querySelector('.bottom-sheet') as HTMLElement;\n if (sheet) {\n sheet.style.height = `${snapHeight}vh`;\n }\n\n this.emit<SnapEvent>('snap', {\n height: `${snapHeight}%`,\n index: nearestIndex,\n });\n }\n }\n\n show(): void {\n this._parseSnapPoints();\n this._previouslyFocused = document.activeElement as HTMLElement;\n this.open = true;\n document.addEventListener('keydown', this._handleKeyDown);\n document.body.style.overflow = 'hidden';\n\n if (this._parsedSnapPoints.length > 0) {\n this._currentSnapIndex = this._parsedSnapPoints.length - 1;\n const sheet = this.shadowRoot?.querySelector('.bottom-sheet') as HTMLElement;\n if (sheet) {\n sheet.style.height = `${this._parsedSnapPoints[this._currentSnapIndex]}vh`;\n }\n }\n\n this.emit('open');\n\n if (this.modal) {\n requestAnimationFrame(() => {\n const sheet = this.shadowRoot?.querySelector('.bottom-sheet') as HTMLElement;\n sheet?.focus();\n });\n }\n }\n\n hide(): void {\n this.open = false;\n document.removeEventListener('keydown', this._handleKeyDown);\n document.body.style.overflow = '';\n\n this.emit('close');\n\n if (this._previouslyFocused) {\n this._previouslyFocused.focus();\n this._previouslyFocused = null;\n }\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback?.();\n document.removeEventListener('keydown', this._handleKeyDown);\n document.removeEventListener('mousemove', this._handleMouseMove);\n document.removeEventListener('mouseup', this._handleMouseUp);\n document.body.style.overflow = '';\n }\n\n render(): string {\n return `\n <div class=\"bottom-sheet-wrapper ${this.open ? 'open' : ''}\" part=\"wrapper\">\n ${this.modal ? '<div class=\"bottom-sheet-backdrop\" part=\"backdrop\"></div>' : ''}\n <div\n class=\"bottom-sheet\"\n role=\"dialog\"\n aria-modal=\"${this.modal ? 'true' : 'false'}\"\n tabindex=\"-1\"\n part=\"sheet\"\n >\n <div class=\"bottom-sheet-handle-area\" part=\"handle-area\">\n <div class=\"bottom-sheet-handle\" part=\"handle\"></div>\n </div>\n <div class=\"bottom-sheet-header\" part=\"header\">\n <slot name=\"header\"></slot>\n </div>\n <div class=\"bottom-sheet-content\" part=\"content\">\n <slot></slot>\n </div>\n </div>\n </div>\n `;\n }\n\n update(): void {\n super.update();\n\n const backdrop = this.shadowRoot?.querySelector('.bottom-sheet-backdrop');\n backdrop?.addEventListener('click', this._handleBackdropClick);\n\n const handleArea = this.shadowRoot?.querySelector('.bottom-sheet-handle-area');\n handleArea?.addEventListener('touchstart', this._handleTouchStart as EventListener, {\n passive: false,\n });\n handleArea?.addEventListener('touchmove', this._handleTouchMove as EventListener, {\n passive: false,\n });\n handleArea?.addEventListener('touchend', this._handleTouchEnd);\n handleArea?.addEventListener('mousedown', this._handleMouseDown as EventListener);\n }\n}\n",
|
|
5
|
+
"/**\n * DuskMoon Bottom Sheet Element\n *\n * A mobile bottom panel/sheet component for displaying content that slides up from the bottom.\n * Supports snap points, swipe gestures, and modal mode.\n *\n * @element el-dm-bottom-sheet\n *\n * @attr {boolean} open - Whether the bottom sheet is open\n * @attr {boolean} modal - Whether to show backdrop and trap focus\n * @attr {boolean} persistent - Prevent dismiss by outside click or swipe down\n * @attr {string} snap-points - Comma-separated list of snap point heights (e.g., \"25%,50%,100%\")\n *\n * @slot - Default slot for sheet content\n * @slot header - Header content above the drag handle\n *\n * @csspart sheet - The sheet container\n * @csspart backdrop - The backdrop overlay (modal mode)\n * @csspart handle - The drag handle bar\n * @csspart handle-area - The drag handle touch area\n * @csspart content - The content wrapper\n * @csspart header - The header section\n *\n * @fires open - Fired when sheet opens\n * @fires close - Fired when sheet closes\n * @fires snap - Fired when sheet snaps to a point, detail contains { height, index }\n */\n\nimport { BaseElement, css } from '@duskmoon-dev/el-base';\nimport { css as bottomsheetCSS } from '@duskmoon-dev/core/components/bottomsheet';\n\n// Strip @layer wrapper for Shadow DOM compatibility\nconst coreStyles = bottomsheetCSS.replace(/@layer\\s+components\\s*\\{/, '').replace(/\\}\\s*$/, '');\n\nconst styles = css`\n :host {\n display: contents;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n\n /* Import core bottomsheet styles */\n ${coreStyles}\n\n /* Wrapper for fixed overlay with pointer-events control */\n .bottomsheet-wrapper {\n position: fixed;\n inset: 0;\n z-index: 1000;\n pointer-events: none;\n }\n\n .bottomsheet-wrapper.open {\n pointer-events: auto;\n }\n\n /* Override core's fixed positioning since sheet is inside a fixed wrapper */\n .bottomsheet {\n position: absolute;\n z-index: auto;\n pointer-events: auto;\n }\n\n /* Override core's fixed positioning on backdrop */\n .bottomsheet-backdrop {\n position: absolute;\n }\n\n .bottomsheet.dragging {\n transition: none;\n }\n\n /* We render an explicit handle bar div; hide core's ::before pseudo-element */\n .bottomsheet-handle::before {\n display: none;\n }\n\n .bottomsheet-bar {\n width: 2.5rem;\n height: 0.25rem;\n background-color: var(--color-outline);\n border-radius: 0.125rem;\n transition: background-color 150ms ease;\n }\n\n .bottomsheet-handle:hover .bottomsheet-bar {\n background-color: var(--color-outline-variant, var(--color-outline));\n }\n\n .bottomsheet-header {\n border-bottom: 1px solid var(--color-outline);\n }\n\n .bottomsheet-header:empty {\n display: none;\n }\n\n .bottomsheet-content {\n padding: 1rem;\n }\n\n /* Focus trap indicator for modal */\n .bottomsheet:focus-visible {\n outline: 2px solid var(--color-primary);\n outline-offset: -2px;\n }\n\n /* Safe area padding for notched devices */\n @supports (padding-bottom: env(safe-area-inset-bottom)) {\n .bottomsheet-content {\n padding-bottom: calc(1rem + env(safe-area-inset-bottom));\n }\n }\n`;\n\ninterface SnapEvent {\n height: string;\n index: number;\n}\n\nexport class ElDmBottomSheet extends BaseElement {\n static properties = {\n open: { type: Boolean, reflect: true },\n modal: { type: Boolean, reflect: true },\n persistent: { type: Boolean, reflect: true },\n snapPoints: { type: String, reflect: true, attribute: 'snap-points' },\n };\n\n declare open: boolean;\n declare modal: boolean;\n declare persistent: boolean;\n declare snapPoints: string;\n\n private _startY = 0;\n private _currentY = 0;\n private _sheetHeight = 0;\n private _isDragging = false;\n private _parsedSnapPoints: number[] = [];\n private _currentSnapIndex = -1;\n private _focusableElements: HTMLElement[] = [];\n private _previouslyFocused: HTMLElement | null = null;\n\n constructor() {\n super();\n this.attachStyles(styles);\n }\n\n connectedCallback(): void {\n super.connectedCallback?.();\n this._parseSnapPoints();\n }\n\n private _parseSnapPoints(): void {\n if (!this.snapPoints) {\n this._parsedSnapPoints = [100];\n return;\n }\n\n this._parsedSnapPoints = this.snapPoints\n .split(',')\n .map((point) => {\n const trimmed = point.trim();\n if (trimmed.endsWith('%')) {\n return parseFloat(trimmed);\n }\n return parseFloat(trimmed);\n })\n .filter((point) => !isNaN(point) && point > 0 && point <= 100)\n .sort((a, b) => a - b);\n\n if (this._parsedSnapPoints.length === 0) {\n this._parsedSnapPoints = [100];\n }\n }\n\n private _handleBackdropClick = (event: Event): void => {\n if (!this.persistent && event.target === event.currentTarget) {\n this.hide();\n }\n };\n\n private _handleKeyDown = (event: KeyboardEvent): void => {\n if (event.key === 'Escape' && !this.persistent) {\n this.hide();\n return;\n }\n\n if (this.modal && event.key === 'Tab') {\n this._trapFocus(event);\n }\n };\n\n private _trapFocus(event: KeyboardEvent): void {\n const sheet = this.shadowRoot?.querySelector('.bottomsheet') as HTMLElement;\n if (!sheet) return;\n\n this._focusableElements = Array.from(\n sheet.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n ),\n );\n\n const slotContent = this.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n );\n this._focusableElements.push(...Array.from(slotContent));\n\n if (this._focusableElements.length === 0) return;\n\n const firstFocusable = this._focusableElements[0];\n const lastFocusable = this._focusableElements[this._focusableElements.length - 1];\n\n if (event.shiftKey && document.activeElement === firstFocusable) {\n event.preventDefault();\n lastFocusable.focus();\n } else if (!event.shiftKey && document.activeElement === lastFocusable) {\n event.preventDefault();\n firstFocusable.focus();\n }\n }\n\n private _handleTouchStart = (event: TouchEvent): void => {\n const touch = event.touches[0];\n this._startY = touch.clientY;\n this._isDragging = true;\n\n const sheet = this.shadowRoot?.querySelector('.bottomsheet') as HTMLElement;\n if (sheet) {\n this._sheetHeight = sheet.getBoundingClientRect().height;\n sheet.classList.add('dragging');\n }\n };\n\n private _handleTouchMove = (event: TouchEvent): void => {\n if (!this._isDragging) return;\n\n const touch = event.touches[0];\n this._currentY = touch.clientY;\n const deltaY = this._currentY - this._startY;\n\n if (deltaY > 0) {\n event.preventDefault();\n const sheet = this.shadowRoot?.querySelector('.bottomsheet') as HTMLElement;\n if (sheet) {\n sheet.style.transform = `translateY(${deltaY}px)`;\n }\n }\n };\n\n private _handleTouchEnd = (): void => {\n if (!this._isDragging) return;\n\n this._isDragging = false;\n const deltaY = this._currentY - this._startY;\n const sheet = this.shadowRoot?.querySelector('.bottomsheet') as HTMLElement;\n\n if (sheet) {\n sheet.classList.remove('dragging');\n sheet.style.transform = '';\n }\n\n const threshold = this._sheetHeight * 0.25;\n\n if (!this.persistent && deltaY > threshold) {\n this.hide();\n } else if (this._parsedSnapPoints.length > 1) {\n this._snapToNearestPoint(deltaY);\n }\n\n this._startY = 0;\n this._currentY = 0;\n };\n\n private _handleMouseDown = (event: MouseEvent): void => {\n this._startY = event.clientY;\n this._isDragging = true;\n\n const sheet = this.shadowRoot?.querySelector('.bottomsheet') as HTMLElement;\n if (sheet) {\n this._sheetHeight = sheet.getBoundingClientRect().height;\n sheet.classList.add('dragging');\n }\n\n document.addEventListener('mousemove', this._handleMouseMove);\n document.addEventListener('mouseup', this._handleMouseUp);\n };\n\n private _handleMouseMove = (event: MouseEvent): void => {\n if (!this._isDragging) return;\n\n this._currentY = event.clientY;\n const deltaY = this._currentY - this._startY;\n\n if (deltaY > 0) {\n const sheet = this.shadowRoot?.querySelector('.bottomsheet') as HTMLElement;\n if (sheet) {\n sheet.style.transform = `translateY(${deltaY}px)`;\n }\n }\n };\n\n private _handleMouseUp = (): void => {\n if (!this._isDragging) return;\n\n this._isDragging = false;\n const deltaY = this._currentY - this._startY;\n const sheet = this.shadowRoot?.querySelector('.bottomsheet') as HTMLElement;\n\n if (sheet) {\n sheet.classList.remove('dragging');\n sheet.style.transform = '';\n }\n\n document.removeEventListener('mousemove', this._handleMouseMove);\n document.removeEventListener('mouseup', this._handleMouseUp);\n\n const threshold = this._sheetHeight * 0.25;\n\n if (!this.persistent && deltaY > threshold) {\n this.hide();\n } else if (this._parsedSnapPoints.length > 1) {\n this._snapToNearestPoint(deltaY);\n }\n\n this._startY = 0;\n this._currentY = 0;\n };\n\n private _snapToNearestPoint(deltaY: number): void {\n const currentHeightPercent = ((this._sheetHeight - deltaY) / window.innerHeight) * 100;\n\n let nearestIndex = 0;\n let nearestDistance = Math.abs(this._parsedSnapPoints[0] - currentHeightPercent);\n\n for (let i = 1; i < this._parsedSnapPoints.length; i++) {\n const distance = Math.abs(this._parsedSnapPoints[i] - currentHeightPercent);\n if (distance < nearestDistance) {\n nearestDistance = distance;\n nearestIndex = i;\n }\n }\n\n if (nearestIndex !== this._currentSnapIndex) {\n this._currentSnapIndex = nearestIndex;\n const snapHeight = this._parsedSnapPoints[nearestIndex];\n\n const sheet = this.shadowRoot?.querySelector('.bottomsheet') as HTMLElement;\n if (sheet) {\n sheet.style.height = `${snapHeight}vh`;\n }\n\n this.emit<SnapEvent>('snap', {\n height: `${snapHeight}%`,\n index: nearestIndex,\n });\n }\n }\n\n show(): void {\n this._parseSnapPoints();\n this._previouslyFocused = document.activeElement as HTMLElement;\n this.open = true;\n document.addEventListener('keydown', this._handleKeyDown);\n document.body.style.overflow = 'hidden';\n\n if (this._parsedSnapPoints.length > 0) {\n this._currentSnapIndex = this._parsedSnapPoints.length - 1;\n const sheet = this.shadowRoot?.querySelector('.bottomsheet') as HTMLElement;\n if (sheet) {\n sheet.style.height = `${this._parsedSnapPoints[this._currentSnapIndex]}vh`;\n }\n }\n\n this.emit('open');\n\n if (this.modal) {\n requestAnimationFrame(() => {\n const sheet = this.shadowRoot?.querySelector('.bottomsheet') as HTMLElement;\n sheet?.focus();\n });\n }\n }\n\n hide(): void {\n this.open = false;\n document.removeEventListener('keydown', this._handleKeyDown);\n document.body.style.overflow = '';\n\n this.emit('close');\n\n if (this._previouslyFocused) {\n this._previouslyFocused.focus();\n this._previouslyFocused = null;\n }\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback?.();\n document.removeEventListener('keydown', this._handleKeyDown);\n document.removeEventListener('mousemove', this._handleMouseMove);\n document.removeEventListener('mouseup', this._handleMouseUp);\n document.body.style.overflow = '';\n }\n\n render(): string {\n return `\n <div class=\"bottomsheet-wrapper ${this.open ? 'open' : ''}\" part=\"wrapper\">\n ${this.modal ? `<div class=\"bottomsheet-backdrop ${this.open ? 'show' : ''}\" part=\"backdrop\"></div>` : ''}\n <div\n class=\"bottomsheet ${this.open ? 'show' : ''}\"\n role=\"dialog\"\n aria-modal=\"${this.modal ? 'true' : 'false'}\"\n tabindex=\"-1\"\n part=\"sheet\"\n >\n <div class=\"bottomsheet-handle\" part=\"handle-area\">\n <div class=\"bottomsheet-bar\" part=\"handle\"></div>\n </div>\n <div class=\"bottomsheet-header\" part=\"header\">\n <slot name=\"header\"></slot>\n </div>\n <div class=\"bottomsheet-content\" part=\"content\">\n <slot></slot>\n </div>\n </div>\n </div>\n `;\n }\n\n update(): void {\n super.update();\n\n const backdrop = this.shadowRoot?.querySelector('.bottomsheet-backdrop');\n backdrop?.addEventListener('click', this._handleBackdropClick);\n\n const handleArea = this.shadowRoot?.querySelector('.bottomsheet-handle');\n handleArea?.addEventListener('touchstart', this._handleTouchStart as EventListener, {\n passive: false,\n });\n handleArea?.addEventListener('touchmove', this._handleTouchMove as EventListener, {\n passive: false,\n });\n handleArea?.addEventListener('touchend', this._handleTouchEnd);\n handleArea?.addEventListener('mousedown', this._handleMouseDown as EventListener);\n }\n}\n",
|
|
6
6
|
"import { ElDmBottomSheet } from './el-dm-bottom-sheet.js';\n\nexport { ElDmBottomSheet };\n\nexport function register(): void {\n if (!customElements.get('el-dm-bottom-sheet')) {\n customElements.define('el-dm-bottom-sheet', ElDmBottomSheet);\n }\n}\n"
|
|
7
7
|
],
|
|
8
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
9
|
-
"debugId": "
|
|
8
|
+
"mappings": "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",
|
|
9
|
+
"debugId": "DE784ACD430B338E64756E2164756E21",
|
|
10
10
|
"names": []
|
|
11
11
|
}
|