@duskmoon-dev/el-popover 0.4.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 +152 -0
- package/dist/cjs/index.js +35 -63
- package/dist/cjs/index.js.map +3 -3
- package/dist/cjs/register.js +35 -63
- package/dist/cjs/register.js.map +3 -3
- package/dist/esm/index.js +33 -61
- package/dist/esm/index.js.map +3 -3
- package/dist/esm/register.js +33 -61
- package/dist/esm/register.js.map +3 -3
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/el-dm-popover.d.ts +1 -1
- package/dist/types/el-dm-popover.d.ts.map +1 -1
- package/package.json +6 -5
package/README.md
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# @duskmoon-dev/el-popover
|
|
2
|
+
|
|
3
|
+
A floating content popover component built with Web Components.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @duskmoon-dev/el-popover
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Auto-Register
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import '@duskmoon-dev/el-popover/register';
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```html
|
|
20
|
+
<el-dm-popover>
|
|
21
|
+
<button slot="trigger">Click me</button>
|
|
22
|
+
<div>Popover content goes here</div>
|
|
23
|
+
</el-dm-popover>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Manual Registration
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { ElDmPopover, register } from '@duskmoon-dev/el-popover';
|
|
30
|
+
|
|
31
|
+
// Register with default tag name
|
|
32
|
+
register();
|
|
33
|
+
|
|
34
|
+
// Or register with custom tag name
|
|
35
|
+
customElements.define('my-popover', ElDmPopover);
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Placements
|
|
39
|
+
|
|
40
|
+
| Placement | Description |
|
|
41
|
+
|-----------|-------------|
|
|
42
|
+
| `top` | Above the trigger |
|
|
43
|
+
| `bottom` | Below the trigger (default) |
|
|
44
|
+
| `left` | Left of the trigger |
|
|
45
|
+
| `right` | Right of the trigger |
|
|
46
|
+
| `top-start` | Above, aligned to start |
|
|
47
|
+
| `top-end` | Above, aligned to end |
|
|
48
|
+
| `bottom-start` | Below, aligned to start |
|
|
49
|
+
| `bottom-end` | Below, aligned to end |
|
|
50
|
+
|
|
51
|
+
## Attributes
|
|
52
|
+
|
|
53
|
+
| Attribute | Type | Default | Description |
|
|
54
|
+
|-----------|------|---------|-------------|
|
|
55
|
+
| `open` | `boolean` | `false` | Whether the popover is open |
|
|
56
|
+
| `trigger` | `string` | `'click'` | Trigger: `click`, `hover`, `focus` |
|
|
57
|
+
| `placement` | `string` | `'bottom'` | Popover placement |
|
|
58
|
+
| `offset` | `number` | `8` | Distance from trigger |
|
|
59
|
+
| `arrow` | `boolean` | `true` | Show arrow |
|
|
60
|
+
|
|
61
|
+
## Slots
|
|
62
|
+
|
|
63
|
+
| Slot | Description |
|
|
64
|
+
|------|-------------|
|
|
65
|
+
| `trigger` | The trigger element |
|
|
66
|
+
| (default) | Popover content |
|
|
67
|
+
|
|
68
|
+
## CSS Parts
|
|
69
|
+
|
|
70
|
+
| Part | Description |
|
|
71
|
+
|------|-------------|
|
|
72
|
+
| `popover` | The popover container |
|
|
73
|
+
| `content` | The content wrapper |
|
|
74
|
+
| `arrow` | The arrow element |
|
|
75
|
+
|
|
76
|
+
## Events
|
|
77
|
+
|
|
78
|
+
| Event | Detail | Description |
|
|
79
|
+
|-------|--------|-------------|
|
|
80
|
+
| `open` | - | Fired when popover opens |
|
|
81
|
+
| `close` | - | Fired when popover closes |
|
|
82
|
+
|
|
83
|
+
## Examples
|
|
84
|
+
|
|
85
|
+
### Basic
|
|
86
|
+
|
|
87
|
+
```html
|
|
88
|
+
<el-dm-popover>
|
|
89
|
+
<button slot="trigger">Open Popover</button>
|
|
90
|
+
<div>
|
|
91
|
+
<h4>Popover Title</h4>
|
|
92
|
+
<p>Some popover content here.</p>
|
|
93
|
+
</div>
|
|
94
|
+
</el-dm-popover>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Placements
|
|
98
|
+
|
|
99
|
+
```html
|
|
100
|
+
<el-dm-popover placement="top">
|
|
101
|
+
<button slot="trigger">Top</button>
|
|
102
|
+
<div>Top popover</div>
|
|
103
|
+
</el-dm-popover>
|
|
104
|
+
|
|
105
|
+
<el-dm-popover placement="right">
|
|
106
|
+
<button slot="trigger">Right</button>
|
|
107
|
+
<div>Right popover</div>
|
|
108
|
+
</el-dm-popover>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Hover Trigger
|
|
112
|
+
|
|
113
|
+
```html
|
|
114
|
+
<el-dm-popover trigger="hover">
|
|
115
|
+
<span slot="trigger">Hover me</span>
|
|
116
|
+
<div>This appears on hover</div>
|
|
117
|
+
</el-dm-popover>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Focus Trigger
|
|
121
|
+
|
|
122
|
+
```html
|
|
123
|
+
<el-dm-popover trigger="focus">
|
|
124
|
+
<input slot="trigger" placeholder="Focus me" />
|
|
125
|
+
<div>Help text appears on focus</div>
|
|
126
|
+
</el-dm-popover>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### No Arrow
|
|
130
|
+
|
|
131
|
+
```html
|
|
132
|
+
<el-dm-popover arrow="false">
|
|
133
|
+
<button slot="trigger">No arrow</button>
|
|
134
|
+
<div>Popover without arrow</div>
|
|
135
|
+
</el-dm-popover>
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Programmatic Control
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
const popover = document.querySelector('el-dm-popover');
|
|
142
|
+
|
|
143
|
+
// Open
|
|
144
|
+
popover.open = true;
|
|
145
|
+
|
|
146
|
+
// Close
|
|
147
|
+
popover.open = false;
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## License
|
|
151
|
+
|
|
152
|
+
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-popover.ts
|
|
38
|
-
var
|
|
39
|
-
var
|
|
38
|
+
var import_el_base = require("@duskmoon-dev/el-base");
|
|
39
|
+
var import_popover = require("@duskmoon-dev/core/components/popover");
|
|
40
|
+
var coreStyles = import_popover.css.replace(/@layer\s+components\s*\{/, "").replace(/\}\s*$/, "");
|
|
41
|
+
var styles = import_el_base.css`
|
|
40
42
|
:host {
|
|
41
43
|
display: inline-block;
|
|
42
44
|
position: relative;
|
|
@@ -46,119 +48,89 @@ var styles = import_el_core.css`
|
|
|
46
48
|
display: none !important;
|
|
47
49
|
}
|
|
48
50
|
|
|
51
|
+
/* Import core popover styles */
|
|
52
|
+
${coreStyles}
|
|
53
|
+
|
|
49
54
|
.popover-trigger {
|
|
50
55
|
display: inline-flex;
|
|
51
56
|
}
|
|
52
57
|
|
|
53
|
-
|
|
58
|
+
/* Override core's position: absolute for JS-based positioning */
|
|
59
|
+
.popover-content {
|
|
54
60
|
position: fixed;
|
|
55
|
-
z-index: 1000;
|
|
56
61
|
min-width: 8rem;
|
|
57
|
-
max-width: 20rem;
|
|
58
|
-
padding: 0.75rem 1rem;
|
|
59
|
-
background-color: var(--color-surface, #ffffff);
|
|
60
|
-
border: 1px solid var(--color-outline, #e0e0e0);
|
|
61
|
-
border-radius: 0.5rem;
|
|
62
|
-
box-shadow:
|
|
63
|
-
0 4px 6px -1px rgb(0 0 0 / 0.1),
|
|
64
|
-
0 2px 4px -2px rgb(0 0 0 / 0.1);
|
|
65
|
-
opacity: 0;
|
|
66
|
-
visibility: hidden;
|
|
67
|
-
transform: scale(0.95);
|
|
68
|
-
transition:
|
|
69
|
-
opacity 150ms ease,
|
|
70
|
-
visibility 150ms ease,
|
|
71
|
-
transform 150ms ease;
|
|
72
62
|
pointer-events: none;
|
|
73
63
|
font-family: inherit;
|
|
74
64
|
}
|
|
75
65
|
|
|
76
|
-
.popover-
|
|
77
|
-
opacity: 1;
|
|
78
|
-
visibility: visible;
|
|
79
|
-
transform: scale(1);
|
|
66
|
+
.popover-content.show {
|
|
80
67
|
pointer-events: auto;
|
|
81
68
|
}
|
|
82
69
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
width: 0.75rem;
|
|
86
|
-
height: 0.75rem;
|
|
87
|
-
background-color: var(--color-surface, #ffffff);
|
|
88
|
-
border: 1px solid var(--color-outline, #e0e0e0);
|
|
89
|
-
transform: rotate(45deg);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/* Arrow positioning based on popover placement */
|
|
93
|
-
.popover-panel[data-placement^='top'] .popover-arrow {
|
|
70
|
+
/* Arrow positioning based on popover placement (data-attribute based) */
|
|
71
|
+
.popover-content[data-placement^='top'] .popover-arrow {
|
|
94
72
|
bottom: -0.4375rem;
|
|
95
73
|
border-top: none;
|
|
96
74
|
border-left: none;
|
|
97
75
|
}
|
|
98
76
|
|
|
99
|
-
.popover-
|
|
77
|
+
.popover-content[data-placement^='bottom'] .popover-arrow {
|
|
100
78
|
top: -0.4375rem;
|
|
101
79
|
border-bottom: none;
|
|
102
80
|
border-right: none;
|
|
103
81
|
}
|
|
104
82
|
|
|
105
|
-
.popover-
|
|
83
|
+
.popover-content[data-placement^='left'] .popover-arrow {
|
|
106
84
|
right: -0.4375rem;
|
|
107
85
|
border-top: none;
|
|
108
86
|
border-left: none;
|
|
109
87
|
}
|
|
110
88
|
|
|
111
|
-
.popover-
|
|
89
|
+
.popover-content[data-placement^='right'] .popover-arrow {
|
|
112
90
|
left: -0.4375rem;
|
|
113
91
|
border-bottom: none;
|
|
114
92
|
border-right: none;
|
|
115
93
|
}
|
|
116
94
|
|
|
117
95
|
/* Center aligned arrows */
|
|
118
|
-
.popover-
|
|
119
|
-
.popover-
|
|
96
|
+
.popover-content[data-placement='top'] .popover-arrow,
|
|
97
|
+
.popover-content[data-placement='bottom'] .popover-arrow {
|
|
120
98
|
left: 50%;
|
|
121
99
|
transform: translateX(-50%) rotate(45deg);
|
|
122
100
|
}
|
|
123
101
|
|
|
124
|
-
.popover-
|
|
125
|
-
.popover-
|
|
102
|
+
.popover-content[data-placement='left'] .popover-arrow,
|
|
103
|
+
.popover-content[data-placement='right'] .popover-arrow {
|
|
126
104
|
top: 50%;
|
|
127
105
|
transform: translateY(-50%) rotate(45deg);
|
|
128
106
|
}
|
|
129
107
|
|
|
130
108
|
/* Start aligned arrows */
|
|
131
|
-
.popover-
|
|
132
|
-
.popover-
|
|
109
|
+
.popover-content[data-placement='top-start'] .popover-arrow,
|
|
110
|
+
.popover-content[data-placement='bottom-start'] .popover-arrow {
|
|
133
111
|
left: 1rem;
|
|
134
112
|
}
|
|
135
113
|
|
|
136
|
-
.popover-
|
|
137
|
-
.popover-
|
|
114
|
+
.popover-content[data-placement='left-start'] .popover-arrow,
|
|
115
|
+
.popover-content[data-placement='right-start'] .popover-arrow {
|
|
138
116
|
top: 1rem;
|
|
139
117
|
}
|
|
140
118
|
|
|
141
119
|
/* End aligned arrows */
|
|
142
|
-
.popover-
|
|
143
|
-
.popover-
|
|
120
|
+
.popover-content[data-placement='top-end'] .popover-arrow,
|
|
121
|
+
.popover-content[data-placement='bottom-end'] .popover-arrow {
|
|
144
122
|
right: 1rem;
|
|
145
123
|
left: auto;
|
|
146
124
|
}
|
|
147
125
|
|
|
148
|
-
.popover-
|
|
149
|
-
.popover-
|
|
126
|
+
.popover-content[data-placement='left-end'] .popover-arrow,
|
|
127
|
+
.popover-content[data-placement='right-end'] .popover-arrow {
|
|
150
128
|
bottom: 1rem;
|
|
151
129
|
top: auto;
|
|
152
130
|
}
|
|
153
|
-
|
|
154
|
-
.popover-content {
|
|
155
|
-
color: var(--color-on-surface, #1f1f1f);
|
|
156
|
-
font-size: 0.875rem;
|
|
157
|
-
line-height: 1.5;
|
|
158
|
-
}
|
|
159
131
|
`;
|
|
160
132
|
|
|
161
|
-
class ElDmPopover extends
|
|
133
|
+
class ElDmPopover extends import_el_base.BaseElement {
|
|
162
134
|
static properties = {
|
|
163
135
|
open: { type: Boolean, reflect: true, default: false },
|
|
164
136
|
trigger: { type: String, reflect: true, default: "click" },
|
|
@@ -174,7 +146,7 @@ class ElDmPopover extends import_el_core.BaseElement {
|
|
|
174
146
|
_currentPlacement = "bottom";
|
|
175
147
|
constructor() {
|
|
176
148
|
super();
|
|
177
|
-
this.attachStyles(styles);
|
|
149
|
+
this.attachStyles([styles, import_el_base.animationStyles]);
|
|
178
150
|
this._boundHandleClickOutside = this._handleClickOutside.bind(this);
|
|
179
151
|
this._boundHandleKeyDown = this._handleKeyDown.bind(this);
|
|
180
152
|
this._boundHandleScroll = this._handleScroll.bind(this);
|
|
@@ -215,9 +187,9 @@ class ElDmPopover extends import_el_core.BaseElement {
|
|
|
215
187
|
}
|
|
216
188
|
}
|
|
217
189
|
_setVisible(visible) {
|
|
218
|
-
const panel = this.shadowRoot?.querySelector(".popover-
|
|
190
|
+
const panel = this.shadowRoot?.querySelector(".popover-content");
|
|
219
191
|
if (panel) {
|
|
220
|
-
panel.classList.toggle("
|
|
192
|
+
panel.classList.toggle("show", visible);
|
|
221
193
|
}
|
|
222
194
|
}
|
|
223
195
|
_setupTriggerListeners() {
|
|
@@ -338,7 +310,7 @@ class ElDmPopover extends import_el_core.BaseElement {
|
|
|
338
310
|
}
|
|
339
311
|
_updatePosition() {
|
|
340
312
|
const triggerEl = this._getTriggerElement();
|
|
341
|
-
const panel = this.shadowRoot?.querySelector(".popover-
|
|
313
|
+
const panel = this.shadowRoot?.querySelector(".popover-content");
|
|
342
314
|
if (!triggerEl || !panel)
|
|
343
315
|
return;
|
|
344
316
|
const triggerRect = triggerEl.getBoundingClientRect();
|
|
@@ -441,14 +413,14 @@ class ElDmPopover extends import_el_core.BaseElement {
|
|
|
441
413
|
<slot name="trigger"></slot>
|
|
442
414
|
</div>
|
|
443
415
|
<div
|
|
444
|
-
class="popover-
|
|
416
|
+
class="popover-content"
|
|
445
417
|
part="popover"
|
|
446
418
|
role="dialog"
|
|
447
419
|
aria-modal="false"
|
|
448
420
|
data-placement="${this.placement}"
|
|
449
421
|
>
|
|
450
422
|
${this.arrow ? '<div class="popover-arrow" part="arrow"></div>' : ""}
|
|
451
|
-
<div class="popover-
|
|
423
|
+
<div class="popover-body" part="content">
|
|
452
424
|
<slot></slot>
|
|
453
425
|
</div>
|
|
454
426
|
</div>
|
|
@@ -463,5 +435,5 @@ function register() {
|
|
|
463
435
|
}
|
|
464
436
|
}
|
|
465
437
|
|
|
466
|
-
//# debugId=
|
|
438
|
+
//# debugId=8FC410ACAB59B6AF64756E2164756E21
|
|
467
439
|
//# 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-popover.ts", "../../src/index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * DuskMoon Popover Element\n *\n * A contextual overlay/popup component that appears relative to a trigger element.\n * Supports multiple trigger modes, placements, and auto-flip when near viewport edges.\n *\n * @element el-dm-popover\n *\n * @attr {boolean} open - Whether the popover is visible\n * @attr {string} trigger - Trigger mode: click, hover, focus, manual\n * @attr {string} placement - Popover position relative to trigger\n * @attr {number} offset - Distance from trigger element in pixels\n * @attr {boolean} arrow - Whether to show arrow pointing to trigger\n *\n * @slot trigger - The element that triggers the popover\n * @slot - Default slot for popover content\n *\n * @fires open - Fired when popover opens\n * @fires close - Fired when popover closes\n *\n * @csspart popover - The popover container\n * @csspart content - The popover content area\n * @csspart arrow - The popover arrow\n */\n\nimport { BaseElement, css } from '@duskmoon-dev/el-core';\n\nexport type PopoverPlacement =\n | 'top'\n | 'top-start'\n | 'top-end'\n | 'bottom'\n | 'bottom-start'\n | 'bottom-end'\n | 'left'\n | 'left-start'\n | 'left-end'\n | 'right'\n | 'right-start'\n | 'right-end';\n\nexport type PopoverTrigger = 'click' | 'hover' | 'focus' | 'manual';\n\nconst styles = css`\n :host {\n display: inline-block;\n position: relative;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n\n .popover-trigger {\n display: inline-flex;\n }\n\n .popover-panel {\n position: fixed;\n z-index: 1000;\n min-width: 8rem;\n max-width: 20rem;\n padding: 0.75rem 1rem;\n background-color: var(--color-surface, #ffffff);\n border: 1px solid var(--color-outline, #e0e0e0);\n border-radius: 0.5rem;\n box-shadow:\n 0 4px 6px -1px rgb(0 0 0 / 0.1),\n 0 2px 4px -2px rgb(0 0 0 / 0.1);\n opacity: 0;\n visibility: hidden;\n transform: scale(0.95);\n transition:\n opacity 150ms ease,\n visibility 150ms ease,\n transform 150ms ease;\n pointer-events: none;\n font-family: inherit;\n }\n\n .popover-panel.visible {\n opacity: 1;\n visibility: visible;\n transform: scale(1);\n pointer-events: auto;\n }\n\n .popover-arrow {\n position: absolute;\n width: 0.75rem;\n height: 0.75rem;\n background-color: var(--color-surface, #ffffff);\n border: 1px solid var(--color-outline, #e0e0e0);\n transform: rotate(45deg);\n }\n\n /* Arrow positioning based on popover placement */\n .popover-panel[data-placement^='top'] .popover-arrow {\n bottom: -0.4375rem;\n border-top: none;\n border-left: none;\n }\n\n .popover-panel[data-placement^='bottom'] .popover-arrow {\n top: -0.4375rem;\n border-bottom: none;\n border-right: none;\n }\n\n .popover-panel[data-placement^='left'] .popover-arrow {\n right: -0.4375rem;\n border-top: none;\n border-left: none;\n }\n\n .popover-panel[data-placement^='right'] .popover-arrow {\n left: -0.4375rem;\n border-bottom: none;\n border-right: none;\n }\n\n /* Center aligned arrows */\n .popover-panel[data-placement='top'] .popover-arrow,\n .popover-panel[data-placement='bottom'] .popover-arrow {\n left: 50%;\n transform: translateX(-50%) rotate(45deg);\n }\n\n .popover-panel[data-placement='left'] .popover-arrow,\n .popover-panel[data-placement='right'] .popover-arrow {\n top: 50%;\n transform: translateY(-50%) rotate(45deg);\n }\n\n /* Start aligned arrows */\n .popover-panel[data-placement='top-start'] .popover-arrow,\n .popover-panel[data-placement='bottom-start'] .popover-arrow {\n left: 1rem;\n }\n\n .popover-panel[data-placement='left-start'] .popover-arrow,\n .popover-panel[data-placement='right-start'] .popover-arrow {\n top: 1rem;\n }\n\n /* End aligned arrows */\n .popover-panel[data-placement='top-end'] .popover-arrow,\n .popover-panel[data-placement='bottom-end'] .popover-arrow {\n right: 1rem;\n left: auto;\n }\n\n .popover-panel[data-placement='left-end'] .popover-arrow,\n .popover-panel[data-placement='right-end'] .popover-arrow {\n bottom: 1rem;\n top: auto;\n }\n\n .popover-content {\n color: var(--color-on-surface, #1f1f1f);\n font-size: 0.875rem;\n line-height: 1.5;\n }\n`;\n\nexport class ElDmPopover extends BaseElement {\n static properties = {\n open: { type: Boolean, reflect: true, default: false },\n trigger: { type: String, reflect: true, default: 'click' },\n placement: { type: String, reflect: true, default: 'bottom' },\n offset: { type: Number, reflect: true, default: 8 },\n arrow: { type: Boolean, reflect: true, default: true },\n };\n\n declare open: boolean;\n declare trigger: PopoverTrigger;\n declare placement: PopoverPlacement;\n declare offset: number;\n declare arrow: boolean;\n\n private _boundHandleClickOutside: (e: MouseEvent) => void;\n private _boundHandleKeyDown: (e: KeyboardEvent) => void;\n private _boundHandleScroll: () => void;\n private _boundUpdatePosition: () => void;\n private _hoverTimeout: number | null = null;\n private _currentPlacement: PopoverPlacement = 'bottom';\n\n constructor() {\n super();\n this.attachStyles(styles);\n this._boundHandleClickOutside = this._handleClickOutside.bind(this);\n this._boundHandleKeyDown = this._handleKeyDown.bind(this);\n this._boundHandleScroll = this._handleScroll.bind(this);\n this._boundUpdatePosition = this._updatePosition.bind(this);\n }\n\n connectedCallback(): void {\n super.connectedCallback();\n this._setupTriggerListeners();\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback?.();\n this._removeTriggerListeners();\n this._removeGlobalListeners();\n }\n\n /**\n * Show the popover\n */\n show(): void {\n if (this.open) return;\n this.open = true;\n this._currentPlacement = this.placement;\n this._addGlobalListeners();\n this._updatePosition();\n this._setVisible(true);\n this.emit('open');\n }\n\n /**\n * Hide the popover\n */\n hide(): void {\n if (!this.open) return;\n this.open = false;\n this._removeGlobalListeners();\n this._setVisible(false);\n this.emit('close');\n }\n\n /**\n * Toggle the popover visibility\n */\n toggle(): void {\n if (this.open) {\n this.hide();\n } else {\n this.show();\n }\n }\n\n private _setVisible(visible: boolean): void {\n const panel = this.shadowRoot?.querySelector('.popover-panel');\n if (panel) {\n panel.classList.toggle('visible', visible);\n }\n }\n\n private _setupTriggerListeners(): void {\n const triggerSlot = this.shadowRoot?.querySelector(\n 'slot[name=\"trigger\"]',\n ) as HTMLSlotElement | null;\n\n if (!triggerSlot) return;\n\n triggerSlot.addEventListener('slotchange', () => {\n this._attachTriggerEvents();\n });\n\n // Initial attachment\n this._attachTriggerEvents();\n }\n\n private _attachTriggerEvents(): void {\n const triggerEl = this._getTriggerElement();\n if (!triggerEl) return;\n\n // Remove any existing listeners first\n this._detachTriggerEvents(triggerEl);\n\n if (this.trigger === 'click') {\n triggerEl.addEventListener('click', this._handleTriggerClick);\n } else if (this.trigger === 'hover') {\n triggerEl.addEventListener('mouseenter', this._handleTriggerMouseEnter);\n triggerEl.addEventListener('mouseleave', this._handleTriggerMouseLeave);\n // Also track hover on the popover panel\n this.addEventListener('mouseenter', this._handlePopoverMouseEnter);\n this.addEventListener('mouseleave', this._handlePopoverMouseLeave);\n } else if (this.trigger === 'focus') {\n triggerEl.addEventListener('focusin', this._handleTriggerFocus);\n triggerEl.addEventListener('focusout', this._handleTriggerBlur);\n }\n }\n\n private _detachTriggerEvents(triggerEl: Element): void {\n triggerEl.removeEventListener('click', this._handleTriggerClick);\n triggerEl.removeEventListener('mouseenter', this._handleTriggerMouseEnter);\n triggerEl.removeEventListener('mouseleave', this._handleTriggerMouseLeave);\n triggerEl.removeEventListener('focusin', this._handleTriggerFocus);\n triggerEl.removeEventListener('focusout', this._handleTriggerBlur);\n this.removeEventListener('mouseenter', this._handlePopoverMouseEnter);\n this.removeEventListener('mouseleave', this._handlePopoverMouseLeave);\n }\n\n private _removeTriggerListeners(): void {\n const triggerEl = this._getTriggerElement();\n if (triggerEl) {\n this._detachTriggerEvents(triggerEl);\n }\n }\n\n private _getTriggerElement(): Element | null {\n const triggerSlot = this.shadowRoot?.querySelector(\n 'slot[name=\"trigger\"]',\n ) as HTMLSlotElement | null;\n if (!triggerSlot) return null;\n const assigned = triggerSlot.assignedElements();\n return assigned[0] || null;\n }\n\n private _handleTriggerClick = (): void => {\n this.toggle();\n };\n\n private _handleTriggerMouseEnter = (): void => {\n if (this._hoverTimeout) {\n clearTimeout(this._hoverTimeout);\n this._hoverTimeout = null;\n }\n this.show();\n };\n\n private _handleTriggerMouseLeave = (): void => {\n this._hoverTimeout = window.setTimeout(() => {\n this.hide();\n }, 100);\n };\n\n private _handlePopoverMouseEnter = (): void => {\n if (this._hoverTimeout) {\n clearTimeout(this._hoverTimeout);\n this._hoverTimeout = null;\n }\n };\n\n private _handlePopoverMouseLeave = (): void => {\n this._hoverTimeout = window.setTimeout(() => {\n this.hide();\n }, 100);\n };\n\n private _handleTriggerFocus = (): void => {\n this.show();\n };\n\n private _handleTriggerBlur = (): void => {\n // Delay to allow focus to move to popover content\n setTimeout(() => {\n if (!this.contains(document.activeElement)) {\n this.hide();\n }\n }, 0);\n };\n\n private _addGlobalListeners(): void {\n document.addEventListener('click', this._boundHandleClickOutside, true);\n document.addEventListener('keydown', this._boundHandleKeyDown);\n window.addEventListener('scroll', this._boundHandleScroll, true);\n window.addEventListener('resize', this._boundUpdatePosition);\n }\n\n private _removeGlobalListeners(): void {\n document.removeEventListener('click', this._boundHandleClickOutside, true);\n document.removeEventListener('keydown', this._boundHandleKeyDown);\n window.removeEventListener('scroll', this._boundHandleScroll, true);\n window.removeEventListener('resize', this._boundUpdatePosition);\n }\n\n private _handleClickOutside(e: MouseEvent): void {\n if (this.trigger !== 'click') return;\n\n const path = e.composedPath();\n if (!path.includes(this)) {\n this.hide();\n }\n }\n\n private _handleKeyDown(e: KeyboardEvent): void {\n if (e.key === 'Escape') {\n this.hide();\n // Return focus to trigger element\n const triggerEl = this._getTriggerElement() as HTMLElement | null;\n triggerEl?.focus?.();\n }\n }\n\n private _handleScroll(): void {\n if (this.open) {\n this._updatePosition();\n }\n }\n\n private _updatePosition(): void {\n const triggerEl = this._getTriggerElement();\n const panel = this.shadowRoot?.querySelector('.popover-panel') as HTMLElement | null;\n\n if (!triggerEl || !panel) return;\n\n const triggerRect = triggerEl.getBoundingClientRect();\n const panelRect = panel.getBoundingClientRect();\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n\n // Determine the best placement with auto-flip\n const effectivePlacement = this._getEffectivePlacement(\n triggerRect,\n panelRect,\n viewportWidth,\n viewportHeight,\n );\n\n this._currentPlacement = effectivePlacement;\n\n // Calculate position based on placement\n const position = this._calculatePosition(triggerRect, panelRect, effectivePlacement);\n\n // Apply position\n panel.style.left = `${position.x}px`;\n panel.style.top = `${position.y}px`;\n panel.setAttribute('data-placement', effectivePlacement);\n }\n\n private _getEffectivePlacement(\n triggerRect: DOMRect,\n panelRect: DOMRect,\n viewportWidth: number,\n viewportHeight: number,\n ): PopoverPlacement {\n const [mainAxis, alignment] = this.placement.split('-') as [\n 'top' | 'bottom' | 'left' | 'right',\n 'start' | 'end' | undefined,\n ];\n\n // Check if there's enough space in the preferred direction\n const spaceTop = triggerRect.top;\n const spaceBottom = viewportHeight - triggerRect.bottom;\n const spaceLeft = triggerRect.left;\n const spaceRight = viewportWidth - triggerRect.right;\n\n let effectiveMainAxis = mainAxis;\n\n // Flip main axis if needed\n if (mainAxis === 'top' && spaceTop < panelRect.height + this.offset) {\n if (spaceBottom >= panelRect.height + this.offset) {\n effectiveMainAxis = 'bottom';\n }\n } else if (mainAxis === 'bottom' && spaceBottom < panelRect.height + this.offset) {\n if (spaceTop >= panelRect.height + this.offset) {\n effectiveMainAxis = 'top';\n }\n } else if (mainAxis === 'left' && spaceLeft < panelRect.width + this.offset) {\n if (spaceRight >= panelRect.width + this.offset) {\n effectiveMainAxis = 'right';\n }\n } else if (mainAxis === 'right' && spaceRight < panelRect.width + this.offset) {\n if (spaceLeft >= panelRect.width + this.offset) {\n effectiveMainAxis = 'left';\n }\n }\n\n return alignment\n ? (`${effectiveMainAxis}-${alignment}` as PopoverPlacement)\n : effectiveMainAxis;\n }\n\n private _calculatePosition(\n triggerRect: DOMRect,\n panelRect: DOMRect,\n placement: PopoverPlacement,\n ): { x: number; y: number } {\n const [mainAxis, alignment] = placement.split('-') as [\n 'top' | 'bottom' | 'left' | 'right',\n 'start' | 'end' | undefined,\n ];\n\n let x = 0;\n let y = 0;\n\n // Calculate main axis position\n switch (mainAxis) {\n case 'top':\n y = triggerRect.top - panelRect.height - this.offset;\n break;\n case 'bottom':\n y = triggerRect.bottom + this.offset;\n break;\n case 'left':\n x = triggerRect.left - panelRect.width - this.offset;\n break;\n case 'right':\n x = triggerRect.right + this.offset;\n break;\n }\n\n // Calculate cross axis position based on alignment\n if (mainAxis === 'top' || mainAxis === 'bottom') {\n switch (alignment) {\n case 'start':\n x = triggerRect.left;\n break;\n case 'end':\n x = triggerRect.right - panelRect.width;\n break;\n default:\n x = triggerRect.left + (triggerRect.width - panelRect.width) / 2;\n }\n } else {\n switch (alignment) {\n case 'start':\n y = triggerRect.top;\n break;\n case 'end':\n y = triggerRect.bottom - panelRect.height;\n break;\n default:\n y = triggerRect.top + (triggerRect.height - panelRect.height) / 2;\n }\n }\n\n // Constrain to viewport\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n const padding = 8;\n\n x = Math.max(padding, Math.min(x, viewportWidth - panelRect.width - padding));\n y = Math.max(padding, Math.min(y, viewportHeight - panelRect.height - padding));\n\n return { x, y };\n }\n\n update(): void {\n super.update();\n if (this.open) {\n // Defer position update to allow DOM to render\n requestAnimationFrame(() => {\n this._updatePosition();\n this._setVisible(true);\n });\n }\n }\n\n render(): string {\n return `\n <div class=\"popover-trigger\">\n <slot name=\"trigger\"></slot>\n </div>\n <div\n class=\"popover-panel\"\n part=\"popover\"\n role=\"dialog\"\n aria-modal=\"false\"\n data-placement=\"${this.placement}\"\n >\n ${this.arrow ? '<div class=\"popover-arrow\" part=\"arrow\"></div>' : ''}\n <div class=\"popover-content\" part=\"content\">\n <slot></slot>\n </div>\n </div>\n `;\n }\n}\n",
|
|
5
|
+
"/**\n * DuskMoon Popover Element\n *\n * A contextual overlay/popup component that appears relative to a trigger element.\n * Supports multiple trigger modes, placements, and auto-flip when near viewport edges.\n *\n * @element el-dm-popover\n *\n * @attr {boolean} open - Whether the popover is visible\n * @attr {string} trigger - Trigger mode: click, hover, focus, manual\n * @attr {string} placement - Popover position relative to trigger\n * @attr {number} offset - Distance from trigger element in pixels\n * @attr {boolean} arrow - Whether to show arrow pointing to trigger\n *\n * @slot trigger - The element that triggers the popover\n * @slot - Default slot for popover content\n *\n * @fires open - Fired when popover opens\n * @fires close - Fired when popover closes\n *\n * @csspart popover - The popover container\n * @csspart content - The popover content area\n * @csspart arrow - The popover arrow\n */\n\nimport { BaseElement, css, animationStyles } from '@duskmoon-dev/el-base';\nimport { css as popoverCSS } from '@duskmoon-dev/core/components/popover';\n\nexport type PopoverPlacement =\n | 'top'\n | 'top-start'\n | 'top-end'\n | 'bottom'\n | 'bottom-start'\n | 'bottom-end'\n | 'left'\n | 'left-start'\n | 'left-end'\n | 'right'\n | 'right-start'\n | 'right-end';\n\nexport type PopoverTrigger = 'click' | 'hover' | 'focus' | 'manual';\n\n// Strip @layer wrapper for Shadow DOM compatibility\nconst coreStyles = popoverCSS.replace(/@layer\\s+components\\s*\\{/, '').replace(/\\}\\s*$/, '');\n\nconst styles = css`\n :host {\n display: inline-block;\n position: relative;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n\n /* Import core popover styles */\n ${coreStyles}\n\n .popover-trigger {\n display: inline-flex;\n }\n\n /* Override core's position: absolute for JS-based positioning */\n .popover-content {\n position: fixed;\n min-width: 8rem;\n pointer-events: none;\n font-family: inherit;\n }\n\n .popover-content.show {\n pointer-events: auto;\n }\n\n /* Arrow positioning based on popover placement (data-attribute based) */\n .popover-content[data-placement^='top'] .popover-arrow {\n bottom: -0.4375rem;\n border-top: none;\n border-left: none;\n }\n\n .popover-content[data-placement^='bottom'] .popover-arrow {\n top: -0.4375rem;\n border-bottom: none;\n border-right: none;\n }\n\n .popover-content[data-placement^='left'] .popover-arrow {\n right: -0.4375rem;\n border-top: none;\n border-left: none;\n }\n\n .popover-content[data-placement^='right'] .popover-arrow {\n left: -0.4375rem;\n border-bottom: none;\n border-right: none;\n }\n\n /* Center aligned arrows */\n .popover-content[data-placement='top'] .popover-arrow,\n .popover-content[data-placement='bottom'] .popover-arrow {\n left: 50%;\n transform: translateX(-50%) rotate(45deg);\n }\n\n .popover-content[data-placement='left'] .popover-arrow,\n .popover-content[data-placement='right'] .popover-arrow {\n top: 50%;\n transform: translateY(-50%) rotate(45deg);\n }\n\n /* Start aligned arrows */\n .popover-content[data-placement='top-start'] .popover-arrow,\n .popover-content[data-placement='bottom-start'] .popover-arrow {\n left: 1rem;\n }\n\n .popover-content[data-placement='left-start'] .popover-arrow,\n .popover-content[data-placement='right-start'] .popover-arrow {\n top: 1rem;\n }\n\n /* End aligned arrows */\n .popover-content[data-placement='top-end'] .popover-arrow,\n .popover-content[data-placement='bottom-end'] .popover-arrow {\n right: 1rem;\n left: auto;\n }\n\n .popover-content[data-placement='left-end'] .popover-arrow,\n .popover-content[data-placement='right-end'] .popover-arrow {\n bottom: 1rem;\n top: auto;\n }\n`;\n\nexport class ElDmPopover extends BaseElement {\n static properties = {\n open: { type: Boolean, reflect: true, default: false },\n trigger: { type: String, reflect: true, default: 'click' },\n placement: { type: String, reflect: true, default: 'bottom' },\n offset: { type: Number, reflect: true, default: 8 },\n arrow: { type: Boolean, reflect: true, default: true },\n };\n\n declare open: boolean;\n declare trigger: PopoverTrigger;\n declare placement: PopoverPlacement;\n declare offset: number;\n declare arrow: boolean;\n\n private _boundHandleClickOutside: (e: MouseEvent) => void;\n private _boundHandleKeyDown: (e: KeyboardEvent) => void;\n private _boundHandleScroll: () => void;\n private _boundUpdatePosition: () => void;\n private _hoverTimeout: number | null = null;\n private _currentPlacement: PopoverPlacement = 'bottom';\n\n constructor() {\n super();\n this.attachStyles([styles, animationStyles]);\n this._boundHandleClickOutside = this._handleClickOutside.bind(this);\n this._boundHandleKeyDown = this._handleKeyDown.bind(this);\n this._boundHandleScroll = this._handleScroll.bind(this);\n this._boundUpdatePosition = this._updatePosition.bind(this);\n }\n\n connectedCallback(): void {\n super.connectedCallback();\n this._setupTriggerListeners();\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback?.();\n this._removeTriggerListeners();\n this._removeGlobalListeners();\n }\n\n /**\n * Show the popover\n */\n show(): void {\n if (this.open) return;\n this.open = true;\n this._currentPlacement = this.placement;\n this._addGlobalListeners();\n this._updatePosition();\n this._setVisible(true);\n this.emit('open');\n }\n\n /**\n * Hide the popover\n */\n hide(): void {\n if (!this.open) return;\n this.open = false;\n this._removeGlobalListeners();\n this._setVisible(false);\n this.emit('close');\n }\n\n /**\n * Toggle the popover visibility\n */\n toggle(): void {\n if (this.open) {\n this.hide();\n } else {\n this.show();\n }\n }\n\n private _setVisible(visible: boolean): void {\n const panel = this.shadowRoot?.querySelector('.popover-content');\n if (panel) {\n panel.classList.toggle('show', visible);\n }\n }\n\n private _setupTriggerListeners(): void {\n const triggerSlot = this.shadowRoot?.querySelector(\n 'slot[name=\"trigger\"]',\n ) as HTMLSlotElement | null;\n\n if (!triggerSlot) return;\n\n triggerSlot.addEventListener('slotchange', () => {\n this._attachTriggerEvents();\n });\n\n // Initial attachment\n this._attachTriggerEvents();\n }\n\n private _attachTriggerEvents(): void {\n const triggerEl = this._getTriggerElement();\n if (!triggerEl) return;\n\n // Remove any existing listeners first\n this._detachTriggerEvents(triggerEl);\n\n if (this.trigger === 'click') {\n triggerEl.addEventListener('click', this._handleTriggerClick);\n } else if (this.trigger === 'hover') {\n triggerEl.addEventListener('mouseenter', this._handleTriggerMouseEnter);\n triggerEl.addEventListener('mouseleave', this._handleTriggerMouseLeave);\n // Also track hover on the popover panel\n this.addEventListener('mouseenter', this._handlePopoverMouseEnter);\n this.addEventListener('mouseleave', this._handlePopoverMouseLeave);\n } else if (this.trigger === 'focus') {\n triggerEl.addEventListener('focusin', this._handleTriggerFocus);\n triggerEl.addEventListener('focusout', this._handleTriggerBlur);\n }\n }\n\n private _detachTriggerEvents(triggerEl: Element): void {\n triggerEl.removeEventListener('click', this._handleTriggerClick);\n triggerEl.removeEventListener('mouseenter', this._handleTriggerMouseEnter);\n triggerEl.removeEventListener('mouseleave', this._handleTriggerMouseLeave);\n triggerEl.removeEventListener('focusin', this._handleTriggerFocus);\n triggerEl.removeEventListener('focusout', this._handleTriggerBlur);\n this.removeEventListener('mouseenter', this._handlePopoverMouseEnter);\n this.removeEventListener('mouseleave', this._handlePopoverMouseLeave);\n }\n\n private _removeTriggerListeners(): void {\n const triggerEl = this._getTriggerElement();\n if (triggerEl) {\n this._detachTriggerEvents(triggerEl);\n }\n }\n\n private _getTriggerElement(): Element | null {\n const triggerSlot = this.shadowRoot?.querySelector(\n 'slot[name=\"trigger\"]',\n ) as HTMLSlotElement | null;\n if (!triggerSlot) return null;\n const assigned = triggerSlot.assignedElements();\n return assigned[0] || null;\n }\n\n private _handleTriggerClick = (): void => {\n this.toggle();\n };\n\n private _handleTriggerMouseEnter = (): void => {\n if (this._hoverTimeout) {\n clearTimeout(this._hoverTimeout);\n this._hoverTimeout = null;\n }\n this.show();\n };\n\n private _handleTriggerMouseLeave = (): void => {\n this._hoverTimeout = window.setTimeout(() => {\n this.hide();\n }, 100);\n };\n\n private _handlePopoverMouseEnter = (): void => {\n if (this._hoverTimeout) {\n clearTimeout(this._hoverTimeout);\n this._hoverTimeout = null;\n }\n };\n\n private _handlePopoverMouseLeave = (): void => {\n this._hoverTimeout = window.setTimeout(() => {\n this.hide();\n }, 100);\n };\n\n private _handleTriggerFocus = (): void => {\n this.show();\n };\n\n private _handleTriggerBlur = (): void => {\n // Delay to allow focus to move to popover content\n setTimeout(() => {\n if (!this.contains(document.activeElement)) {\n this.hide();\n }\n }, 0);\n };\n\n private _addGlobalListeners(): void {\n document.addEventListener('click', this._boundHandleClickOutside, true);\n document.addEventListener('keydown', this._boundHandleKeyDown);\n window.addEventListener('scroll', this._boundHandleScroll, true);\n window.addEventListener('resize', this._boundUpdatePosition);\n }\n\n private _removeGlobalListeners(): void {\n document.removeEventListener('click', this._boundHandleClickOutside, true);\n document.removeEventListener('keydown', this._boundHandleKeyDown);\n window.removeEventListener('scroll', this._boundHandleScroll, true);\n window.removeEventListener('resize', this._boundUpdatePosition);\n }\n\n private _handleClickOutside(e: MouseEvent): void {\n if (this.trigger !== 'click') return;\n\n const path = e.composedPath();\n if (!path.includes(this)) {\n this.hide();\n }\n }\n\n private _handleKeyDown(e: KeyboardEvent): void {\n if (e.key === 'Escape') {\n this.hide();\n // Return focus to trigger element\n const triggerEl = this._getTriggerElement() as HTMLElement | null;\n triggerEl?.focus?.();\n }\n }\n\n private _handleScroll(): void {\n if (this.open) {\n this._updatePosition();\n }\n }\n\n private _updatePosition(): void {\n const triggerEl = this._getTriggerElement();\n const panel = this.shadowRoot?.querySelector('.popover-content') as HTMLElement | null;\n\n if (!triggerEl || !panel) return;\n\n const triggerRect = triggerEl.getBoundingClientRect();\n const panelRect = panel.getBoundingClientRect();\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n\n // Determine the best placement with auto-flip\n const effectivePlacement = this._getEffectivePlacement(\n triggerRect,\n panelRect,\n viewportWidth,\n viewportHeight,\n );\n\n this._currentPlacement = effectivePlacement;\n\n // Calculate position based on placement\n const position = this._calculatePosition(triggerRect, panelRect, effectivePlacement);\n\n // Apply position\n panel.style.left = `${position.x}px`;\n panel.style.top = `${position.y}px`;\n panel.setAttribute('data-placement', effectivePlacement);\n }\n\n private _getEffectivePlacement(\n triggerRect: DOMRect,\n panelRect: DOMRect,\n viewportWidth: number,\n viewportHeight: number,\n ): PopoverPlacement {\n const [mainAxis, alignment] = this.placement.split('-') as [\n 'top' | 'bottom' | 'left' | 'right',\n 'start' | 'end' | undefined,\n ];\n\n // Check if there's enough space in the preferred direction\n const spaceTop = triggerRect.top;\n const spaceBottom = viewportHeight - triggerRect.bottom;\n const spaceLeft = triggerRect.left;\n const spaceRight = viewportWidth - triggerRect.right;\n\n let effectiveMainAxis = mainAxis;\n\n // Flip main axis if needed\n if (mainAxis === 'top' && spaceTop < panelRect.height + this.offset) {\n if (spaceBottom >= panelRect.height + this.offset) {\n effectiveMainAxis = 'bottom';\n }\n } else if (mainAxis === 'bottom' && spaceBottom < panelRect.height + this.offset) {\n if (spaceTop >= panelRect.height + this.offset) {\n effectiveMainAxis = 'top';\n }\n } else if (mainAxis === 'left' && spaceLeft < panelRect.width + this.offset) {\n if (spaceRight >= panelRect.width + this.offset) {\n effectiveMainAxis = 'right';\n }\n } else if (mainAxis === 'right' && spaceRight < panelRect.width + this.offset) {\n if (spaceLeft >= panelRect.width + this.offset) {\n effectiveMainAxis = 'left';\n }\n }\n\n return alignment\n ? (`${effectiveMainAxis}-${alignment}` as PopoverPlacement)\n : effectiveMainAxis;\n }\n\n private _calculatePosition(\n triggerRect: DOMRect,\n panelRect: DOMRect,\n placement: PopoverPlacement,\n ): { x: number; y: number } {\n const [mainAxis, alignment] = placement.split('-') as [\n 'top' | 'bottom' | 'left' | 'right',\n 'start' | 'end' | undefined,\n ];\n\n let x = 0;\n let y = 0;\n\n // Calculate main axis position\n switch (mainAxis) {\n case 'top':\n y = triggerRect.top - panelRect.height - this.offset;\n break;\n case 'bottom':\n y = triggerRect.bottom + this.offset;\n break;\n case 'left':\n x = triggerRect.left - panelRect.width - this.offset;\n break;\n case 'right':\n x = triggerRect.right + this.offset;\n break;\n }\n\n // Calculate cross axis position based on alignment\n if (mainAxis === 'top' || mainAxis === 'bottom') {\n switch (alignment) {\n case 'start':\n x = triggerRect.left;\n break;\n case 'end':\n x = triggerRect.right - panelRect.width;\n break;\n default:\n x = triggerRect.left + (triggerRect.width - panelRect.width) / 2;\n }\n } else {\n switch (alignment) {\n case 'start':\n y = triggerRect.top;\n break;\n case 'end':\n y = triggerRect.bottom - panelRect.height;\n break;\n default:\n y = triggerRect.top + (triggerRect.height - panelRect.height) / 2;\n }\n }\n\n // Constrain to viewport\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n const padding = 8;\n\n x = Math.max(padding, Math.min(x, viewportWidth - panelRect.width - padding));\n y = Math.max(padding, Math.min(y, viewportHeight - panelRect.height - padding));\n\n return { x, y };\n }\n\n update(): void {\n super.update();\n if (this.open) {\n // Defer position update to allow DOM to render\n requestAnimationFrame(() => {\n this._updatePosition();\n this._setVisible(true);\n });\n }\n }\n\n render(): string {\n return `\n <div class=\"popover-trigger\">\n <slot name=\"trigger\"></slot>\n </div>\n <div\n class=\"popover-content\"\n part=\"popover\"\n role=\"dialog\"\n aria-modal=\"false\"\n data-placement=\"${this.placement}\"\n >\n ${this.arrow ? '<div class=\"popover-arrow\" part=\"arrow\"></div>' : ''}\n <div class=\"popover-body\" part=\"content\">\n <slot></slot>\n </div>\n </div>\n `;\n }\n}\n",
|
|
6
6
|
"/**\n * @duskmoon-dev/el-popover\n *\n * DuskMoon Popover custom element\n */\n\nimport { ElDmPopover } from './el-dm-popover.js';\n\nexport { ElDmPopover };\nexport type { PopoverPlacement, PopoverTrigger } from './el-dm-popover.js';\n\n/**\n * Register the el-dm-popover custom element\n *\n * @example\n * ```ts\n * import { register } from '@duskmoon-dev/el-popover';\n * register();\n * ```\n */\nexport function register(): void {\n if (!customElements.get('el-dm-popover')) {\n customElements.define('el-dm-popover', ElDmPopover);\n }\n}\n"
|
|
7
7
|
],
|
|
8
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
9
|
-
"debugId": "
|
|
8
|
+
"mappings": "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",
|
|
9
|
+
"debugId": "8FC410ACAB59B6AF64756E2164756E21",
|
|
10
10
|
"names": []
|
|
11
11
|
}
|