luxen-ui 0.1.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/LICENSE +21 -0
- package/README.md +98 -0
- package/dist/css/elements/avatar.css +20 -0
- package/dist/css/elements/badge.css +159 -0
- package/dist/css/elements/button.css +171 -0
- package/dist/css/elements/close-button/circle.css +66 -0
- package/dist/css/elements/close-button/ring.css +71 -0
- package/dist/css/elements/close-button/square.css +70 -0
- package/dist/css/elements/disclosure.css +137 -0
- package/dist/css/elements/divider.css +75 -0
- package/dist/css/elements/input-otp.css +164 -0
- package/dist/css/elements/input-stepper/default.css +245 -0
- package/dist/css/elements/input-stepper/rounded.css +238 -0
- package/dist/css/elements/kbd.css +21 -0
- package/dist/css/elements/progress.css +114 -0
- package/dist/css/elements/select.css +71 -0
- package/dist/css/elements/skeleton.css +89 -0
- package/dist/css/elements/tabs/enclosed.css +148 -0
- package/dist/css/elements/tabs/line.css +138 -0
- package/dist/css/elements/toast.css +260 -0
- package/dist/css/index.css +885 -0
- package/dist/custom-elements.json +14424 -0
- package/dist/define.d.ts +9 -0
- package/dist/define.d.ts.map +1 -0
- package/dist/define.js +16 -0
- package/dist/elements/avatar/avatar.css +128 -0
- package/dist/elements/avatar/avatar.d.ts +21 -0
- package/dist/elements/avatar/avatar.d.ts.map +1 -0
- package/dist/elements/avatar/avatar.js +106 -0
- package/dist/elements/avatar/index.d.ts +8 -0
- package/dist/elements/avatar/index.d.ts.map +1 -0
- package/dist/elements/avatar/index.js +4 -0
- package/dist/elements/badge/badge.d.ts +17 -0
- package/dist/elements/badge/badge.d.ts.map +1 -0
- package/dist/elements/badge/badge.js +34 -0
- package/dist/elements/badge/index.d.ts +8 -0
- package/dist/elements/badge/index.d.ts.map +1 -0
- package/dist/elements/badge/index.js +4 -0
- package/dist/elements/carousel/carousel.css +205 -0
- package/dist/elements/carousel/carousel.d.ts +148 -0
- package/dist/elements/carousel/carousel.d.ts.map +1 -0
- package/dist/elements/carousel/carousel.js +473 -0
- package/dist/elements/carousel/index.d.ts +8 -0
- package/dist/elements/carousel/index.d.ts.map +1 -0
- package/dist/elements/carousel/index.js +4 -0
- package/dist/elements/carousel-item/carousel-item.css +11 -0
- package/dist/elements/carousel-item/carousel-item.d.ts +13 -0
- package/dist/elements/carousel-item/carousel-item.d.ts.map +1 -0
- package/dist/elements/carousel-item/carousel-item.js +20 -0
- package/dist/elements/carousel-item/index.d.ts +8 -0
- package/dist/elements/carousel-item/index.d.ts.map +1 -0
- package/dist/elements/carousel-item/index.js +4 -0
- package/dist/elements/dialog/dialog.css +92 -0
- package/dist/elements/dialog/dialog.d.ts +56 -0
- package/dist/elements/dialog/dialog.d.ts.map +1 -0
- package/dist/elements/dialog/dialog.js +204 -0
- package/dist/elements/dialog/dialog.styles.d.ts +8 -0
- package/dist/elements/dialog/dialog.styles.d.ts.map +1 -0
- package/dist/elements/dialog/dialog.styles.js +8 -0
- package/dist/elements/dialog/index.d.ts +8 -0
- package/dist/elements/dialog/index.d.ts.map +1 -0
- package/dist/elements/dialog/index.js +4 -0
- package/dist/elements/divider/divider.d.ts +23 -0
- package/dist/elements/divider/divider.d.ts.map +1 -0
- package/dist/elements/divider/divider.js +49 -0
- package/dist/elements/divider/index.d.ts +8 -0
- package/dist/elements/divider/index.d.ts.map +1 -0
- package/dist/elements/divider/index.js +4 -0
- package/dist/elements/drawer/drawer.css +66 -0
- package/dist/elements/drawer/drawer.d.ts +34 -0
- package/dist/elements/drawer/drawer.d.ts.map +1 -0
- package/dist/elements/drawer/drawer.js +46 -0
- package/dist/elements/drawer/index.d.ts +8 -0
- package/dist/elements/drawer/index.d.ts.map +1 -0
- package/dist/elements/drawer/index.js +4 -0
- package/dist/elements/dropdown/dropdown.css +31 -0
- package/dist/elements/dropdown/dropdown.d.ts +64 -0
- package/dist/elements/dropdown/dropdown.d.ts.map +1 -0
- package/dist/elements/dropdown/dropdown.js +322 -0
- package/dist/elements/dropdown/index.d.ts +8 -0
- package/dist/elements/dropdown/index.d.ts.map +1 -0
- package/dist/elements/dropdown/index.js +4 -0
- package/dist/elements/dropdown-item/dropdown-item.css +51 -0
- package/dist/elements/dropdown-item/dropdown-item.d.ts +25 -0
- package/dist/elements/dropdown-item/dropdown-item.d.ts.map +1 -0
- package/dist/elements/dropdown-item/dropdown-item.js +110 -0
- package/dist/elements/dropdown-item/index.d.ts +8 -0
- package/dist/elements/dropdown-item/index.d.ts.map +1 -0
- package/dist/elements/dropdown-item/index.js +4 -0
- package/dist/elements/icon/icon.css +10 -0
- package/dist/elements/icon/icon.d.ts +19 -0
- package/dist/elements/icon/icon.d.ts.map +1 -0
- package/dist/elements/icon/icon.js +53 -0
- package/dist/elements/icon/index.d.ts +8 -0
- package/dist/elements/icon/index.d.ts.map +1 -0
- package/dist/elements/icon/index.js +4 -0
- package/dist/elements/input-otp/index.d.ts +8 -0
- package/dist/elements/input-otp/index.d.ts.map +1 -0
- package/dist/elements/input-otp/index.js +4 -0
- package/dist/elements/input-otp/input-otp.d.ts +31 -0
- package/dist/elements/input-otp/input-otp.d.ts.map +1 -0
- package/dist/elements/input-otp/input-otp.js +139 -0
- package/dist/elements/input-stepper/index.d.ts +8 -0
- package/dist/elements/input-stepper/index.d.ts.map +1 -0
- package/dist/elements/input-stepper/index.js +4 -0
- package/dist/elements/input-stepper/input-stepper.d.ts +63 -0
- package/dist/elements/input-stepper/input-stepper.d.ts.map +1 -0
- package/dist/elements/input-stepper/input-stepper.js +249 -0
- package/dist/elements/popover/index.d.ts +8 -0
- package/dist/elements/popover/index.d.ts.map +1 -0
- package/dist/elements/popover/index.js +4 -0
- package/dist/elements/popover/popover.css +61 -0
- package/dist/elements/popover/popover.d.ts +62 -0
- package/dist/elements/popover/popover.d.ts.map +1 -0
- package/dist/elements/popover/popover.js +244 -0
- package/dist/elements/rating/index.d.ts +8 -0
- package/dist/elements/rating/index.d.ts.map +1 -0
- package/dist/elements/rating/index.js +4 -0
- package/dist/elements/rating/rating.css +102 -0
- package/dist/elements/rating/rating.d.ts +38 -0
- package/dist/elements/rating/rating.d.ts.map +1 -0
- package/dist/elements/rating/rating.js +193 -0
- package/dist/elements/skeleton/index.d.ts +8 -0
- package/dist/elements/skeleton/index.d.ts.map +1 -0
- package/dist/elements/skeleton/index.js +4 -0
- package/dist/elements/skeleton/skeleton.d.ts +12 -0
- package/dist/elements/skeleton/skeleton.d.ts.map +1 -0
- package/dist/elements/skeleton/skeleton.js +13 -0
- package/dist/elements/spinner/index.d.ts +8 -0
- package/dist/elements/spinner/index.d.ts.map +1 -0
- package/dist/elements/spinner/index.js +4 -0
- package/dist/elements/spinner/spinner.css +28 -0
- package/dist/elements/spinner/spinner.d.ts +16 -0
- package/dist/elements/spinner/spinner.d.ts.map +1 -0
- package/dist/elements/spinner/spinner.js +37 -0
- package/dist/elements/tabs/index.d.ts +8 -0
- package/dist/elements/tabs/index.d.ts.map +1 -0
- package/dist/elements/tabs/index.js +4 -0
- package/dist/elements/tabs/tabs.d.ts +48 -0
- package/dist/elements/tabs/tabs.d.ts.map +1 -0
- package/dist/elements/tabs/tabs.js +210 -0
- package/dist/elements/toast/index.d.ts +9 -0
- package/dist/elements/toast/index.d.ts.map +1 -0
- package/dist/elements/toast/index.js +5 -0
- package/dist/elements/toast/toast.d.ts +72 -0
- package/dist/elements/toast/toast.d.ts.map +1 -0
- package/dist/elements/toast/toast.js +375 -0
- package/dist/elements/tooltip/index.d.ts +8 -0
- package/dist/elements/tooltip/index.d.ts.map +1 -0
- package/dist/elements/tooltip/index.js +4 -0
- package/dist/elements/tooltip/tooltip.css +37 -0
- package/dist/elements/tooltip/tooltip.d.ts +59 -0
- package/dist/elements/tooltip/tooltip.d.ts.map +1 -0
- package/dist/elements/tooltip/tooltip.js +231 -0
- package/dist/elements/tree/index.d.ts +8 -0
- package/dist/elements/tree/index.d.ts.map +1 -0
- package/dist/elements/tree/index.js +4 -0
- package/dist/elements/tree/tree.css +26 -0
- package/dist/elements/tree/tree.d.ts +76 -0
- package/dist/elements/tree/tree.d.ts.map +1 -0
- package/dist/elements/tree/tree.js +432 -0
- package/dist/elements/tree-item/index.d.ts +8 -0
- package/dist/elements/tree-item/index.d.ts.map +1 -0
- package/dist/elements/tree-item/index.js +4 -0
- package/dist/elements/tree-item/tree-item.css +172 -0
- package/dist/elements/tree-item/tree-item.d.ts +74 -0
- package/dist/elements/tree-item/tree-item.d.ts.map +1 -0
- package/dist/elements/tree-item/tree-item.js +301 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/registry.d.ts +22 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +33 -0
- package/dist/shared/controllers/popover.d.ts +44 -0
- package/dist/shared/controllers/popover.d.ts.map +1 -0
- package/dist/shared/controllers/popover.js +359 -0
- package/dist/shared/luxen-element.d.ts +20 -0
- package/dist/shared/luxen-element.d.ts.map +1 -0
- package/dist/shared/luxen-element.js +23 -0
- package/dist/shared/luxen-form-associated-element.d.ts +49 -0
- package/dist/shared/luxen-form-associated-element.d.ts.map +1 -0
- package/dist/shared/luxen-form-associated-element.js +123 -0
- package/dist/shared/styles/host.css +13 -0
- package/dist/shared/styles/host.styles.d.ts +9 -0
- package/dist/shared/styles/host.styles.d.ts.map +1 -0
- package/dist/shared/styles/host.styles.js +9 -0
- package/dist/skills/luxen-ui/SKILL.md +82 -0
- package/dist/skills/luxen-ui/references/avatar.md +259 -0
- package/dist/skills/luxen-ui/references/badge.md +289 -0
- package/dist/skills/luxen-ui/references/button.md +309 -0
- package/dist/skills/luxen-ui/references/close-button.md +104 -0
- package/dist/skills/luxen-ui/references/dialog.md +435 -0
- package/dist/skills/luxen-ui/references/drawer.md +400 -0
- package/dist/skills/luxen-ui/references/progress.md +133 -0
- package/dist/skills/luxen-ui/references/select.md +100 -0
- package/dist/skills/luxen-ui/references/toast.md +396 -0
- package/dist/skills/luxen-ui/references/tree.md +359 -0
- package/package.json +116 -0
- package/postcss-plugin-prefix.js +63 -0
- package/vite-plugin.ts +29 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import { computePosition, autoUpdate, offset, flip, shift, arrow, } from '@floating-ui/dom';
|
|
2
|
+
/** Check if a point is inside a polygon using ray-casting. */
|
|
3
|
+
function isPointInPolygon([px, py], polygon) {
|
|
4
|
+
let inside = false;
|
|
5
|
+
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
|
6
|
+
const [xi, yi] = polygon[i];
|
|
7
|
+
const [xj, yj] = polygon[j];
|
|
8
|
+
if (yi >= py !== yj >= py && px <= ((xj - xi) * (py - yi)) / (yj - yi) + xi) {
|
|
9
|
+
inside = !inside;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return inside;
|
|
13
|
+
}
|
|
14
|
+
/** Check if a point is inside a rect. */
|
|
15
|
+
function isPointInRect([px, py], rect) {
|
|
16
|
+
return px >= rect.left && px <= rect.right && py >= rect.top && py <= rect.bottom;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Build a safe polygon (triangle/quadrilateral) from cursor to floating element edges.
|
|
20
|
+
* Adapted from floating-ui/safePolygon.
|
|
21
|
+
* @see https://github.com/floating-ui/floating-ui/blob/master/packages/react/src/safePolygon.ts
|
|
22
|
+
*/
|
|
23
|
+
function getSafePolygon(cursorX, cursorY, rect, refRect, placement) {
|
|
24
|
+
const side = placement.split('-')[0];
|
|
25
|
+
const buffer = 2;
|
|
26
|
+
const isFloatingWider = rect.width > refRect.width;
|
|
27
|
+
const isFloatingTaller = rect.height > refRect.height;
|
|
28
|
+
const cursorLeaveFromRight = cursorX > rect.right - rect.width / 2;
|
|
29
|
+
const cursorLeaveFromBottom = cursorY > rect.bottom - rect.height / 2;
|
|
30
|
+
switch (side) {
|
|
31
|
+
case 'top': {
|
|
32
|
+
const c1 = [
|
|
33
|
+
isFloatingWider
|
|
34
|
+
? cursorX + buffer / 2
|
|
35
|
+
: cursorLeaveFromRight
|
|
36
|
+
? cursorX + buffer * 4
|
|
37
|
+
: cursorX - buffer * 4,
|
|
38
|
+
cursorY + buffer + 1,
|
|
39
|
+
];
|
|
40
|
+
const c2 = [
|
|
41
|
+
isFloatingWider
|
|
42
|
+
? cursorX - buffer / 2
|
|
43
|
+
: cursorLeaveFromRight
|
|
44
|
+
? cursorX + buffer * 4
|
|
45
|
+
: cursorX - buffer * 4,
|
|
46
|
+
cursorY + buffer + 1,
|
|
47
|
+
];
|
|
48
|
+
return [
|
|
49
|
+
c1,
|
|
50
|
+
c2,
|
|
51
|
+
[
|
|
52
|
+
rect.left,
|
|
53
|
+
cursorLeaveFromRight
|
|
54
|
+
? rect.bottom - buffer
|
|
55
|
+
: isFloatingWider
|
|
56
|
+
? rect.bottom - buffer
|
|
57
|
+
: rect.top,
|
|
58
|
+
],
|
|
59
|
+
[
|
|
60
|
+
rect.right,
|
|
61
|
+
cursorLeaveFromRight
|
|
62
|
+
? isFloatingWider
|
|
63
|
+
? rect.bottom - buffer
|
|
64
|
+
: rect.top
|
|
65
|
+
: rect.bottom - buffer,
|
|
66
|
+
],
|
|
67
|
+
];
|
|
68
|
+
}
|
|
69
|
+
case 'bottom': {
|
|
70
|
+
const c1 = [
|
|
71
|
+
isFloatingWider
|
|
72
|
+
? cursorX + buffer / 2
|
|
73
|
+
: cursorLeaveFromRight
|
|
74
|
+
? cursorX + buffer * 4
|
|
75
|
+
: cursorX - buffer * 4,
|
|
76
|
+
cursorY - buffer,
|
|
77
|
+
];
|
|
78
|
+
const c2 = [
|
|
79
|
+
isFloatingWider
|
|
80
|
+
? cursorX - buffer / 2
|
|
81
|
+
: cursorLeaveFromRight
|
|
82
|
+
? cursorX + buffer * 4
|
|
83
|
+
: cursorX - buffer * 4,
|
|
84
|
+
cursorY - buffer,
|
|
85
|
+
];
|
|
86
|
+
return [
|
|
87
|
+
c1,
|
|
88
|
+
c2,
|
|
89
|
+
[
|
|
90
|
+
rect.left,
|
|
91
|
+
cursorLeaveFromRight
|
|
92
|
+
? rect.top + buffer
|
|
93
|
+
: isFloatingWider
|
|
94
|
+
? rect.top + buffer
|
|
95
|
+
: rect.bottom,
|
|
96
|
+
],
|
|
97
|
+
[
|
|
98
|
+
rect.right,
|
|
99
|
+
cursorLeaveFromRight
|
|
100
|
+
? isFloatingWider
|
|
101
|
+
? rect.top + buffer
|
|
102
|
+
: rect.bottom
|
|
103
|
+
: rect.top + buffer,
|
|
104
|
+
],
|
|
105
|
+
];
|
|
106
|
+
}
|
|
107
|
+
case 'left': {
|
|
108
|
+
const c1 = [
|
|
109
|
+
cursorX + buffer + 1,
|
|
110
|
+
isFloatingTaller
|
|
111
|
+
? cursorY + buffer / 2
|
|
112
|
+
: cursorLeaveFromBottom
|
|
113
|
+
? cursorY + buffer * 4
|
|
114
|
+
: cursorY - buffer * 4,
|
|
115
|
+
];
|
|
116
|
+
const c2 = [
|
|
117
|
+
cursorX + buffer + 1,
|
|
118
|
+
isFloatingTaller
|
|
119
|
+
? cursorY - buffer / 2
|
|
120
|
+
: cursorLeaveFromBottom
|
|
121
|
+
? cursorY + buffer * 4
|
|
122
|
+
: cursorY - buffer * 4,
|
|
123
|
+
];
|
|
124
|
+
return [
|
|
125
|
+
[
|
|
126
|
+
cursorLeaveFromBottom
|
|
127
|
+
? rect.right - buffer
|
|
128
|
+
: isFloatingTaller
|
|
129
|
+
? rect.right - buffer
|
|
130
|
+
: rect.left,
|
|
131
|
+
rect.top,
|
|
132
|
+
],
|
|
133
|
+
[
|
|
134
|
+
cursorLeaveFromBottom
|
|
135
|
+
? isFloatingTaller
|
|
136
|
+
? rect.right - buffer
|
|
137
|
+
: rect.left
|
|
138
|
+
: rect.right - buffer,
|
|
139
|
+
rect.bottom,
|
|
140
|
+
],
|
|
141
|
+
c1,
|
|
142
|
+
c2,
|
|
143
|
+
];
|
|
144
|
+
}
|
|
145
|
+
case 'right': {
|
|
146
|
+
const c1 = [
|
|
147
|
+
cursorX - buffer,
|
|
148
|
+
isFloatingTaller
|
|
149
|
+
? cursorY + buffer / 2
|
|
150
|
+
: cursorLeaveFromBottom
|
|
151
|
+
? cursorY + buffer * 4
|
|
152
|
+
: cursorY - buffer * 4,
|
|
153
|
+
];
|
|
154
|
+
const c2 = [
|
|
155
|
+
cursorX - buffer,
|
|
156
|
+
isFloatingTaller
|
|
157
|
+
? cursorY - buffer / 2
|
|
158
|
+
: cursorLeaveFromBottom
|
|
159
|
+
? cursorY + buffer * 4
|
|
160
|
+
: cursorY - buffer * 4,
|
|
161
|
+
];
|
|
162
|
+
return [
|
|
163
|
+
[
|
|
164
|
+
cursorLeaveFromBottom
|
|
165
|
+
? rect.left + buffer
|
|
166
|
+
: isFloatingTaller
|
|
167
|
+
? rect.left + buffer
|
|
168
|
+
: rect.right,
|
|
169
|
+
rect.top,
|
|
170
|
+
],
|
|
171
|
+
[
|
|
172
|
+
cursorLeaveFromBottom
|
|
173
|
+
? isFloatingTaller
|
|
174
|
+
? rect.left + buffer
|
|
175
|
+
: rect.right
|
|
176
|
+
: rect.left + buffer,
|
|
177
|
+
rect.bottom,
|
|
178
|
+
],
|
|
179
|
+
c1,
|
|
180
|
+
c2,
|
|
181
|
+
];
|
|
182
|
+
}
|
|
183
|
+
default:
|
|
184
|
+
return [];
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// --- Constants ---
|
|
188
|
+
const STATIC_SIDE = { top: 'bottom', right: 'left', bottom: 'top', left: 'right' };
|
|
189
|
+
const reducedMotion = typeof window !== 'undefined' ? window.matchMedia('(prefers-reduced-motion: reduce)') : undefined;
|
|
190
|
+
export class PopoverController {
|
|
191
|
+
constructor(host, config) {
|
|
192
|
+
this._currentPlacement = '';
|
|
193
|
+
this._host = host;
|
|
194
|
+
this._config = config;
|
|
195
|
+
host.addController(this);
|
|
196
|
+
}
|
|
197
|
+
hostConnected() { }
|
|
198
|
+
hostDisconnected() {
|
|
199
|
+
this.stopPositioning();
|
|
200
|
+
this.cleanupSafePolygon();
|
|
201
|
+
this.removeTriggerListeners();
|
|
202
|
+
}
|
|
203
|
+
get currentPlacement() {
|
|
204
|
+
return this._currentPlacement;
|
|
205
|
+
}
|
|
206
|
+
// --- Positioning ---
|
|
207
|
+
async updatePosition(options) {
|
|
208
|
+
const trigger = this._config.getTriggerElement();
|
|
209
|
+
const floating = this._config.getFloatingElement();
|
|
210
|
+
const arrowEl = this._config.getArrowElement();
|
|
211
|
+
if (!trigger || !floating)
|
|
212
|
+
return;
|
|
213
|
+
const middleware = [
|
|
214
|
+
offset(options.distance),
|
|
215
|
+
flip(),
|
|
216
|
+
shift(),
|
|
217
|
+
...(arrowEl ? [arrow({ element: arrowEl, padding: 8 })] : []),
|
|
218
|
+
];
|
|
219
|
+
const { x, y, placement, middlewareData } = await computePosition(trigger, floating, {
|
|
220
|
+
placement: options.placement,
|
|
221
|
+
strategy: 'fixed',
|
|
222
|
+
middleware,
|
|
223
|
+
});
|
|
224
|
+
Object.assign(floating.style, {
|
|
225
|
+
position: 'fixed',
|
|
226
|
+
left: `${x}px`,
|
|
227
|
+
top: `${y}px`,
|
|
228
|
+
});
|
|
229
|
+
if (placement !== this._currentPlacement) {
|
|
230
|
+
this._currentPlacement = placement;
|
|
231
|
+
this._config.onPlacementChange?.(placement);
|
|
232
|
+
}
|
|
233
|
+
if (middlewareData.arrow && arrowEl) {
|
|
234
|
+
const { x: ax, y: ay } = middlewareData.arrow;
|
|
235
|
+
const side = STATIC_SIDE[placement.split('-')[0]];
|
|
236
|
+
Object.assign(arrowEl.style, {
|
|
237
|
+
left: ax != null ? `${ax}px` : '',
|
|
238
|
+
top: ay != null ? `${ay}px` : '',
|
|
239
|
+
[side]: `${-arrowEl.offsetWidth / 2}px`,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
startPositioning(options) {
|
|
244
|
+
const trigger = this._config.getTriggerElement();
|
|
245
|
+
const floating = this._config.getFloatingElement();
|
|
246
|
+
if (!trigger || !floating)
|
|
247
|
+
return;
|
|
248
|
+
this._cleanupAutoUpdate?.();
|
|
249
|
+
this._cleanupAutoUpdate = autoUpdate(trigger, floating, () => this.updatePosition(options));
|
|
250
|
+
}
|
|
251
|
+
stopPositioning() {
|
|
252
|
+
this._cleanupAutoUpdate?.();
|
|
253
|
+
this._cleanupAutoUpdate = undefined;
|
|
254
|
+
}
|
|
255
|
+
// --- Safe polygon ---
|
|
256
|
+
handlePointerLeave(e, onHide) {
|
|
257
|
+
const floating = this._config.getFloatingElement();
|
|
258
|
+
const trigger = this._config.getTriggerElement();
|
|
259
|
+
if (!floating || !trigger) {
|
|
260
|
+
onHide();
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
const cursorX = e.clientX;
|
|
264
|
+
const cursorY = e.clientY;
|
|
265
|
+
const onMove = (moveEvent) => {
|
|
266
|
+
const { clientX: mx, clientY: my } = moveEvent;
|
|
267
|
+
const cursor = [mx, my];
|
|
268
|
+
const rect = floating.getBoundingClientRect();
|
|
269
|
+
const refRect = trigger.getBoundingClientRect();
|
|
270
|
+
const side = this._currentPlacement.split('-')[0];
|
|
271
|
+
if (isPointInRect(cursor, rect))
|
|
272
|
+
return;
|
|
273
|
+
if (isPointInRect(cursor, refRect))
|
|
274
|
+
return;
|
|
275
|
+
if ((side === 'top' && cursorY >= refRect.bottom - 1) ||
|
|
276
|
+
(side === 'bottom' && cursorY <= refRect.top + 1) ||
|
|
277
|
+
(side === 'left' && cursorX >= refRect.right - 1) ||
|
|
278
|
+
(side === 'right' && cursorX <= refRect.left + 1)) {
|
|
279
|
+
cleanup();
|
|
280
|
+
onHide();
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
const polygon = getSafePolygon(cursorX, cursorY, rect, refRect, this._currentPlacement);
|
|
284
|
+
if (isPointInPolygon(cursor, polygon))
|
|
285
|
+
return;
|
|
286
|
+
cleanup();
|
|
287
|
+
onHide();
|
|
288
|
+
};
|
|
289
|
+
const cleanup = () => {
|
|
290
|
+
document.removeEventListener('pointermove', onMove);
|
|
291
|
+
};
|
|
292
|
+
document.addEventListener('pointermove', onMove);
|
|
293
|
+
this._cleanupSafePolygon?.();
|
|
294
|
+
this._cleanupSafePolygon = cleanup;
|
|
295
|
+
}
|
|
296
|
+
cleanupSafePolygon() {
|
|
297
|
+
this._cleanupSafePolygon?.();
|
|
298
|
+
this._cleanupSafePolygon = undefined;
|
|
299
|
+
}
|
|
300
|
+
// --- Trigger listener management ---
|
|
301
|
+
addTriggerListeners(handlers) {
|
|
302
|
+
const trigger = this._config.getTriggerElement();
|
|
303
|
+
if (!trigger)
|
|
304
|
+
return;
|
|
305
|
+
this._handlers = handlers;
|
|
306
|
+
this._triggerElement = trigger;
|
|
307
|
+
if (handlers.onPointerEnter)
|
|
308
|
+
trigger.addEventListener('pointerenter', handlers.onPointerEnter);
|
|
309
|
+
if (handlers.onPointerLeave)
|
|
310
|
+
trigger.addEventListener('pointerleave', handlers.onPointerLeave);
|
|
311
|
+
if (handlers.onFocusIn)
|
|
312
|
+
trigger.addEventListener('focusin', handlers.onFocusIn);
|
|
313
|
+
if (handlers.onFocusOut)
|
|
314
|
+
trigger.addEventListener('focusout', handlers.onFocusOut);
|
|
315
|
+
if (handlers.onClick)
|
|
316
|
+
trigger.addEventListener('click', handlers.onClick);
|
|
317
|
+
if (handlers.onKeyDown)
|
|
318
|
+
trigger.addEventListener('keydown', handlers.onKeyDown);
|
|
319
|
+
}
|
|
320
|
+
removeTriggerListeners(triggerOverride) {
|
|
321
|
+
const trigger = triggerOverride ?? this._triggerElement;
|
|
322
|
+
const handlers = this._handlers;
|
|
323
|
+
if (!trigger || !handlers)
|
|
324
|
+
return;
|
|
325
|
+
if (handlers.onPointerEnter)
|
|
326
|
+
trigger.removeEventListener('pointerenter', handlers.onPointerEnter);
|
|
327
|
+
if (handlers.onPointerLeave)
|
|
328
|
+
trigger.removeEventListener('pointerleave', handlers.onPointerLeave);
|
|
329
|
+
if (handlers.onFocusIn)
|
|
330
|
+
trigger.removeEventListener('focusin', handlers.onFocusIn);
|
|
331
|
+
if (handlers.onFocusOut)
|
|
332
|
+
trigger.removeEventListener('focusout', handlers.onFocusOut);
|
|
333
|
+
if (handlers.onClick)
|
|
334
|
+
trigger.removeEventListener('click', handlers.onClick);
|
|
335
|
+
if (handlers.onKeyDown)
|
|
336
|
+
trigger.removeEventListener('keydown', handlers.onKeyDown);
|
|
337
|
+
if (!triggerOverride) {
|
|
338
|
+
this._handlers = undefined;
|
|
339
|
+
this._triggerElement = undefined;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
// --- Animation ---
|
|
343
|
+
async animateShow(el, duration, keyframes) {
|
|
344
|
+
const d = reducedMotion?.matches ? 0 : duration;
|
|
345
|
+
el.getAnimations().forEach((a) => a.cancel());
|
|
346
|
+
el.animate(keyframes ?? [
|
|
347
|
+
{ opacity: 0, scale: 0.96 },
|
|
348
|
+
{ opacity: 1, scale: 1 },
|
|
349
|
+
], { duration: d, fill: 'forwards' });
|
|
350
|
+
}
|
|
351
|
+
async animateHide(el, duration, keyframes) {
|
|
352
|
+
const d = reducedMotion?.matches ? 0 : duration;
|
|
353
|
+
const anim = el.animate(keyframes ?? [
|
|
354
|
+
{ opacity: 1, scale: 1 },
|
|
355
|
+
{ opacity: 0, scale: 0.96 },
|
|
356
|
+
], { duration: d, fill: 'forwards' });
|
|
357
|
+
await anim.finished;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { LitElement } from 'lit';
|
|
2
|
+
/**
|
|
3
|
+
* Base class for all Luxen custom elements.
|
|
4
|
+
* Extends LitElement with shared utilities.
|
|
5
|
+
*/
|
|
6
|
+
export declare class LuxenElement extends LitElement {
|
|
7
|
+
/**
|
|
8
|
+
* Dispatch a custom event. Returns `true` if not cancelled.
|
|
9
|
+
*
|
|
10
|
+
* @param name - Event name (unprefixed, e.g. `'show'`).
|
|
11
|
+
* @param options - CustomEvent options. Defaults: bubbles + composed.
|
|
12
|
+
*/
|
|
13
|
+
emit<T = unknown>(name: string, options?: {
|
|
14
|
+
detail?: T;
|
|
15
|
+
bubbles?: boolean;
|
|
16
|
+
composed?: boolean;
|
|
17
|
+
cancelable?: boolean;
|
|
18
|
+
}): boolean;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=luxen-element.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"luxen-element.d.ts","sourceRoot":"","sources":["../../src/html/shared/luxen-element.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAEjC;;;GAGG;AACH,qBAAa,YAAa,SAAQ,UAAU;IAC1C;;;;;OAKG;IACH,IAAI,CAAC,CAAC,GAAG,OAAO,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QACR,MAAM,CAAC,EAAE,CAAC,CAAC;QACX,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,UAAU,CAAC,EAAE,OAAO,CAAC;KACtB,GACA,OAAO;CAUX"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { LitElement } from 'lit';
|
|
2
|
+
/**
|
|
3
|
+
* Base class for all Luxen custom elements.
|
|
4
|
+
* Extends LitElement with shared utilities.
|
|
5
|
+
*/
|
|
6
|
+
export class LuxenElement extends LitElement {
|
|
7
|
+
/**
|
|
8
|
+
* Dispatch a custom event. Returns `true` if not cancelled.
|
|
9
|
+
*
|
|
10
|
+
* @param name - Event name (unprefixed, e.g. `'show'`).
|
|
11
|
+
* @param options - CustomEvent options. Defaults: bubbles + composed.
|
|
12
|
+
*/
|
|
13
|
+
emit(name, options) {
|
|
14
|
+
const event = new CustomEvent(name, {
|
|
15
|
+
bubbles: options?.bubbles ?? true,
|
|
16
|
+
composed: options?.composed ?? true,
|
|
17
|
+
cancelable: options?.cancelable ?? false,
|
|
18
|
+
detail: options?.detail,
|
|
19
|
+
});
|
|
20
|
+
this.dispatchEvent(event);
|
|
21
|
+
return !event.defaultPrevented;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { LuxenElement } from './luxen-element';
|
|
2
|
+
/**
|
|
3
|
+
* Base class for form-associated Luxen custom elements.
|
|
4
|
+
* Implements the Form Associated Custom Elements API via ElementInternals.
|
|
5
|
+
*
|
|
6
|
+
* Subclasses own their typed `value` property and call `_syncFormValue()`
|
|
7
|
+
* whenever it changes.
|
|
8
|
+
*/
|
|
9
|
+
export declare class LuxenFormAssociatedElement extends LuxenElement {
|
|
10
|
+
static formAssociated: boolean;
|
|
11
|
+
/** @internal */
|
|
12
|
+
readonly _internals: ElementInternals;
|
|
13
|
+
name: string;
|
|
14
|
+
disabled: boolean;
|
|
15
|
+
required: boolean;
|
|
16
|
+
/** The serialised value the element resets to on form reset. */
|
|
17
|
+
protected _defaultFormValue: string;
|
|
18
|
+
/** Whether the user has interacted with this control. */
|
|
19
|
+
hasInteracted: boolean;
|
|
20
|
+
constructor();
|
|
21
|
+
/** The form this control is associated with. */
|
|
22
|
+
get form(): HTMLFormElement | null;
|
|
23
|
+
/** `<label>` elements associated with this control via `for` attribute. */
|
|
24
|
+
get formLabels(): NodeList;
|
|
25
|
+
/**
|
|
26
|
+
* Override to return the inner element where validation popups anchor.
|
|
27
|
+
* Passed as the 3rd arg to `ElementInternals.setValidity()`.
|
|
28
|
+
*/
|
|
29
|
+
get validationTarget(): HTMLElement | undefined;
|
|
30
|
+
/** Update the form submission value. */
|
|
31
|
+
protected _syncFormValue(value: string): void;
|
|
32
|
+
checkValidity(): boolean;
|
|
33
|
+
reportValidity(): boolean;
|
|
34
|
+
/** Set a custom validity message. Pass `''` to clear. */
|
|
35
|
+
setCustomValidity(message: string): void;
|
|
36
|
+
/** Directly set validity flags. With no args, clears all flags. */
|
|
37
|
+
protected setValidity(flags?: ValidityStateFlags, message?: string, anchor?: HTMLElement): void;
|
|
38
|
+
get validity(): ValidityState;
|
|
39
|
+
get validationMessage(): string;
|
|
40
|
+
get willValidate(): boolean;
|
|
41
|
+
/** Update `:state()` custom states based on current validity and interaction. */
|
|
42
|
+
private _updateCustomStates;
|
|
43
|
+
private _toggleState;
|
|
44
|
+
formAssociatedCallback(_form: HTMLFormElement | null): void;
|
|
45
|
+
formDisabledCallback(disabled: boolean): void;
|
|
46
|
+
formResetCallback(): void;
|
|
47
|
+
formStateRestoreCallback(state: string, _mode: 'restore' | 'autocomplete'): void;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=luxen-form-associated-element.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"luxen-form-associated-element.d.ts","sourceRoot":"","sources":["../../src/html/shared/luxen-form-associated-element.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C;;;;;;GAMG;AACH,qBAAa,0BAA2B,SAAQ,YAAY;IAC1D,MAAM,CAAC,cAAc,UAAQ;IAE7B,gBAAgB;IAChB,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC;IAGtC,IAAI,SAAM;IAGV,QAAQ,UAAS;IAGjB,QAAQ,UAAS;IAEjB,gEAAgE;IAChE,SAAS,CAAC,iBAAiB,SAAM;IAEjC,yDAAyD;IACzD,aAAa,UAAS;;IAOtB,gDAAgD;IAChD,IAAI,IAAI,IAAI,eAAe,GAAG,IAAI,CAEjC;IAED,2EAA2E;IAC3E,IAAI,UAAU,IAAI,QAAQ,CAEzB;IAED;;;OAGG;IACH,IAAI,gBAAgB,IAAI,WAAW,GAAG,SAAS,CAE9C;IAID,wCAAwC;IACxC,SAAS,CAAC,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAM7C,aAAa,IAAI,OAAO;IAIxB,cAAc,IAAI,OAAO;IAMzB,yDAAyD;IACzD,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IASxC,mEAAmE;IACnE,SAAS,CAAC,WAAW,CAAC,KAAK,GAAE,kBAAuB,EAAE,OAAO,SAAK,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,IAAI;IAK/F,IAAI,QAAQ,IAAI,aAAa,CAE5B;IAED,IAAI,iBAAiB,IAAI,MAAM,CAE9B;IAED,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED,iFAAiF;IACjF,OAAO,CAAC,mBAAmB;IAY3B,OAAO,CAAC,YAAY;IAUpB,sBAAsB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,GAAG,IAAI;IAE3D,oBAAoB,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI;IAI7C,iBAAiB,IAAI,IAAI;IAMzB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,cAAc,GAAG,IAAI;CAGjF"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { property } from 'lit/decorators.js';
|
|
8
|
+
import { LuxenElement } from './luxen-element';
|
|
9
|
+
/**
|
|
10
|
+
* Base class for form-associated Luxen custom elements.
|
|
11
|
+
* Implements the Form Associated Custom Elements API via ElementInternals.
|
|
12
|
+
*
|
|
13
|
+
* Subclasses own their typed `value` property and call `_syncFormValue()`
|
|
14
|
+
* whenever it changes.
|
|
15
|
+
*/
|
|
16
|
+
export class LuxenFormAssociatedElement extends LuxenElement {
|
|
17
|
+
constructor() {
|
|
18
|
+
super();
|
|
19
|
+
this.name = '';
|
|
20
|
+
this.disabled = false;
|
|
21
|
+
this.required = false;
|
|
22
|
+
/** The serialised value the element resets to on form reset. */
|
|
23
|
+
this._defaultFormValue = '';
|
|
24
|
+
/** Whether the user has interacted with this control. */
|
|
25
|
+
this.hasInteracted = false;
|
|
26
|
+
this._internals = this.attachInternals();
|
|
27
|
+
}
|
|
28
|
+
/** The form this control is associated with. */
|
|
29
|
+
get form() {
|
|
30
|
+
return this._internals.form;
|
|
31
|
+
}
|
|
32
|
+
/** `<label>` elements associated with this control via `for` attribute. */
|
|
33
|
+
get formLabels() {
|
|
34
|
+
return this._internals.labels;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Override to return the inner element where validation popups anchor.
|
|
38
|
+
* Passed as the 3rd arg to `ElementInternals.setValidity()`.
|
|
39
|
+
*/
|
|
40
|
+
get validationTarget() {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
// --- Form value ---
|
|
44
|
+
/** Update the form submission value. */
|
|
45
|
+
_syncFormValue(value) {
|
|
46
|
+
this._internals.setFormValue(value);
|
|
47
|
+
}
|
|
48
|
+
// --- Validity ---
|
|
49
|
+
checkValidity() {
|
|
50
|
+
return this._internals.checkValidity();
|
|
51
|
+
}
|
|
52
|
+
reportValidity() {
|
|
53
|
+
this.hasInteracted = true;
|
|
54
|
+
this._updateCustomStates();
|
|
55
|
+
return this._internals.reportValidity();
|
|
56
|
+
}
|
|
57
|
+
/** Set a custom validity message. Pass `''` to clear. */
|
|
58
|
+
setCustomValidity(message) {
|
|
59
|
+
if (message) {
|
|
60
|
+
this._internals.setValidity({ customError: true }, message, this.validationTarget);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
this._internals.setValidity({});
|
|
64
|
+
}
|
|
65
|
+
this._updateCustomStates();
|
|
66
|
+
}
|
|
67
|
+
/** Directly set validity flags. With no args, clears all flags. */
|
|
68
|
+
setValidity(flags = {}, message = '', anchor) {
|
|
69
|
+
this._internals.setValidity(flags, message, anchor ?? this.validationTarget);
|
|
70
|
+
this._updateCustomStates();
|
|
71
|
+
}
|
|
72
|
+
get validity() {
|
|
73
|
+
return this._internals.validity;
|
|
74
|
+
}
|
|
75
|
+
get validationMessage() {
|
|
76
|
+
return this._internals.validationMessage;
|
|
77
|
+
}
|
|
78
|
+
get willValidate() {
|
|
79
|
+
return this._internals.willValidate;
|
|
80
|
+
}
|
|
81
|
+
/** Update `:state()` custom states based on current validity and interaction. */
|
|
82
|
+
_updateCustomStates() {
|
|
83
|
+
const states = this._internals.states;
|
|
84
|
+
const isValid = this._internals.validity.valid;
|
|
85
|
+
this._toggleState(states, 'required', this.required);
|
|
86
|
+
this._toggleState(states, 'optional', !this.required);
|
|
87
|
+
this._toggleState(states, 'valid', isValid);
|
|
88
|
+
this._toggleState(states, 'invalid', !isValid);
|
|
89
|
+
this._toggleState(states, 'user-valid', isValid && this.hasInteracted);
|
|
90
|
+
this._toggleState(states, 'user-invalid', !isValid && this.hasInteracted);
|
|
91
|
+
}
|
|
92
|
+
_toggleState(states, name, force) {
|
|
93
|
+
if (force) {
|
|
94
|
+
states.add(name);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
states.delete(name);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// --- Form lifecycle callbacks ---
|
|
101
|
+
formAssociatedCallback(_form) { }
|
|
102
|
+
formDisabledCallback(disabled) {
|
|
103
|
+
this.disabled = disabled;
|
|
104
|
+
}
|
|
105
|
+
formResetCallback() {
|
|
106
|
+
this.hasInteracted = false;
|
|
107
|
+
this._syncFormValue(this._defaultFormValue);
|
|
108
|
+
this._updateCustomStates();
|
|
109
|
+
}
|
|
110
|
+
formStateRestoreCallback(state, _mode) {
|
|
111
|
+
this._syncFormValue(state);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
LuxenFormAssociatedElement.formAssociated = true;
|
|
115
|
+
__decorate([
|
|
116
|
+
property({ reflect: true })
|
|
117
|
+
], LuxenFormAssociatedElement.prototype, "name", void 0);
|
|
118
|
+
__decorate([
|
|
119
|
+
property({ type: Boolean, reflect: true })
|
|
120
|
+
], LuxenFormAssociatedElement.prototype, "disabled", void 0);
|
|
121
|
+
__decorate([
|
|
122
|
+
property({ type: Boolean, reflect: true })
|
|
123
|
+
], LuxenFormAssociatedElement.prototype, "required", void 0);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Styles applied to every shadow-DOM element via `static styles`.
|
|
3
|
+
* Wrapper module: `unsafeCSS()` is called once here so all 12+ importers
|
|
4
|
+
* share the same `CSSResult` instance (one constructed `CSSStyleSheet`,
|
|
5
|
+
* not one per importing element).
|
|
6
|
+
*/
|
|
7
|
+
declare const _default: import("lit").CSSResult;
|
|
8
|
+
export default _default;
|
|
9
|
+
//# sourceMappingURL=host.styles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.styles.d.ts","sourceRoot":"","sources":["../../../src/html/shared/styles/host.styles.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;;AACH,wBAA8B"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { unsafeCSS } from 'lit';
|
|
2
|
+
import raw from './host.css?inline';
|
|
3
|
+
/**
|
|
4
|
+
* Styles applied to every shadow-DOM element via `static styles`.
|
|
5
|
+
* Wrapper module: `unsafeCSS()` is called once here so all 12+ importers
|
|
6
|
+
* share the same `CSSResult` instance (one constructed `CSSStyleSheet`,
|
|
7
|
+
* not one per importing element).
|
|
8
|
+
*/
|
|
9
|
+
export default unsafeCSS(raw);
|