@liwe3/webcomponents 1.0.2 → 1.0.14
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/dist/DateSelector.js +372 -0
- package/dist/DateSelector.js.map +1 -0
- package/dist/PopoverMenu.js +312 -0
- package/dist/PopoverMenu.js.map +1 -0
- package/dist/SmartSelect.js.map +1 -1
- package/dist/Toast.js +477 -0
- package/dist/Toast.js.map +1 -0
- package/dist/index.js +14 -4
- package/dist/index.js.map +1 -1
- package/package.json +16 -1
- package/src/DateSelector.ts +550 -0
- package/src/PopoverMenu.ts +595 -0
- package/src/SmartSelect.ts +231 -231
- package/src/Toast.ts +770 -0
- package/src/index.ts +28 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
class u extends HTMLElement {
|
|
2
|
+
constructor() {
|
|
3
|
+
super(), this.items = [], this.openPopovers = /* @__PURE__ */ new Map(), this.hoverTimeouts = /* @__PURE__ */ new Map(), this.initialized = !1, this.globalClickHandler = null, this.attachShadow({ mode: "open" });
|
|
4
|
+
}
|
|
5
|
+
connectedCallback() {
|
|
6
|
+
this.initialized || (this.render(), this.setupGlobalListeners(), this.initialized = !0);
|
|
7
|
+
}
|
|
8
|
+
disconnectedCallback() {
|
|
9
|
+
this.cleanupGlobalListeners();
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Set up global event listeners
|
|
13
|
+
*/
|
|
14
|
+
setupGlobalListeners() {
|
|
15
|
+
this.globalClickHandler = (e) => {
|
|
16
|
+
!this.contains(e.target) && !this.shadowRoot.contains(e.target) && this.closeAllMenus();
|
|
17
|
+
}, document.addEventListener("click", this.globalClickHandler);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Clean up global event listeners
|
|
21
|
+
*/
|
|
22
|
+
cleanupGlobalListeners() {
|
|
23
|
+
this.globalClickHandler && (document.removeEventListener("click", this.globalClickHandler), this.globalClickHandler = null);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Set menu items
|
|
27
|
+
*/
|
|
28
|
+
setItems(e) {
|
|
29
|
+
this.items = e, this.render();
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Get current menu items
|
|
33
|
+
*/
|
|
34
|
+
getItems() {
|
|
35
|
+
return this.items;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Add a new menu item
|
|
39
|
+
*/
|
|
40
|
+
addMenuItem(e, i = null) {
|
|
41
|
+
i === null ? this.items.push(e) : this.items.splice(i, 0, e), this.render();
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Remove a menu item
|
|
45
|
+
*/
|
|
46
|
+
removeMenuItem(e) {
|
|
47
|
+
e >= 0 && e < this.items.length && (this.items.splice(e, 1), this.render());
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Update a menu item
|
|
51
|
+
*/
|
|
52
|
+
updateMenuItem(e, i) {
|
|
53
|
+
e >= 0 && e < this.items.length && (this.items[e] = i, this.render());
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Render the menu component
|
|
57
|
+
*/
|
|
58
|
+
render() {
|
|
59
|
+
const e = this.shadowRoot.querySelector(".popover-menu-bar");
|
|
60
|
+
e && e.remove(), this.openPopovers.clear(), this.hoverTimeouts.forEach((t) => clearTimeout(t)), this.hoverTimeouts.clear(), this.shadowRoot.innerHTML = `
|
|
61
|
+
<style>
|
|
62
|
+
:host {
|
|
63
|
+
display: block;
|
|
64
|
+
font-family: var(--font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* PopoverMenu Component Styles */
|
|
68
|
+
.popover-menu-bar {
|
|
69
|
+
display: flex;
|
|
70
|
+
background: var(--popover-menu-bar-background, #fff);
|
|
71
|
+
border: 1px solid var(--popover-menu-bar-border, #ddd);
|
|
72
|
+
border-radius: var(--popover-menu-bar-radius, 6px);
|
|
73
|
+
padding: var(--popover-menu-bar-padding, 4px);
|
|
74
|
+
box-shadow: var(--popover-menu-bar-shadow, 0 1px 3px rgba(0,0,0,0.1));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.popover-menu-trigger {
|
|
78
|
+
background: none;
|
|
79
|
+
border: none;
|
|
80
|
+
padding: 8px 16px;
|
|
81
|
+
cursor: pointer;
|
|
82
|
+
border-radius: 4px;
|
|
83
|
+
font-size: 14px;
|
|
84
|
+
transition: background-color 0.2s;
|
|
85
|
+
font-family: inherit;
|
|
86
|
+
color: var(--popover-menu-trigger-color, #333);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.popover-menu-trigger:hover {
|
|
90
|
+
background: var(--popover-menu-trigger-hover-bg, #f0f0f0);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.popover-menu-trigger.active {
|
|
94
|
+
background: var(--popover-menu-trigger-active-bg, #e3f2fd);
|
|
95
|
+
color: var(--popover-menu-trigger-active-color, #1976d2);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.popover-menu-popover {
|
|
99
|
+
margin: 0;
|
|
100
|
+
padding: 4px;
|
|
101
|
+
border: 1px solid var(--popover-menu-border, #ccc);
|
|
102
|
+
border-radius: var(--popover-menu-radius, 6px);
|
|
103
|
+
background: var(--popover-menu-background, white);
|
|
104
|
+
box-shadow: var(--popover-menu-shadow, 0 4px 12px rgba(0,0,0,0.15));
|
|
105
|
+
min-width: 180px;
|
|
106
|
+
z-index: 1000;
|
|
107
|
+
font-family: inherit;
|
|
108
|
+
position: fixed;
|
|
109
|
+
display: none;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.popover-menu-item {
|
|
113
|
+
display: flex;
|
|
114
|
+
align-items: center;
|
|
115
|
+
justify-content: space-between;
|
|
116
|
+
padding: 8px 12px;
|
|
117
|
+
cursor: pointer;
|
|
118
|
+
border-radius: 4px;
|
|
119
|
+
font-size: 14px;
|
|
120
|
+
transition: background-color 0.2s;
|
|
121
|
+
position: relative;
|
|
122
|
+
color: var(--popover-menu-item-color, #333);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.popover-menu-item:hover {
|
|
126
|
+
background: var(--popover-menu-item-hover-bg, #f5f5f5);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.popover-menu-item.disabled {
|
|
130
|
+
color: var(--popover-menu-item-disabled-color, #999);
|
|
131
|
+
cursor: not-allowed;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.popover-menu-item.disabled:hover {
|
|
135
|
+
background: transparent;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.popover-menu-item.has-submenu::after {
|
|
139
|
+
content: '▶';
|
|
140
|
+
font-size: 10px;
|
|
141
|
+
color: var(--popover-menu-submenu-arrow-color, #666);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.popover-menu-separator {
|
|
145
|
+
height: 1px;
|
|
146
|
+
background: var(--popover-menu-separator-color, #e0e0e0);
|
|
147
|
+
margin: 4px 0;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.popover-submenu-popover {
|
|
151
|
+
margin: 0;
|
|
152
|
+
padding: 4px;
|
|
153
|
+
border: 1px solid var(--popover-menu-border, #ccc);
|
|
154
|
+
border-radius: var(--popover-menu-radius, 6px);
|
|
155
|
+
background: var(--popover-menu-background, white);
|
|
156
|
+
box-shadow: var(--popover-menu-shadow, 0 4px 12px rgba(0,0,0,0.15));
|
|
157
|
+
min-width: 160px;
|
|
158
|
+
z-index: 1001;
|
|
159
|
+
font-family: inherit;
|
|
160
|
+
position: fixed;
|
|
161
|
+
display: none;
|
|
162
|
+
}
|
|
163
|
+
</style>
|
|
164
|
+
<div class="popover-menu-bar"></div>
|
|
165
|
+
`;
|
|
166
|
+
const i = this.shadowRoot.querySelector(".popover-menu-bar");
|
|
167
|
+
this.items.forEach((t, o) => {
|
|
168
|
+
const r = this.createMenuTrigger(t, o);
|
|
169
|
+
i.appendChild(r);
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Create a menu trigger button
|
|
174
|
+
*/
|
|
175
|
+
createMenuTrigger(e, i) {
|
|
176
|
+
const t = document.createElement("button");
|
|
177
|
+
return t.className = "popover-menu-trigger", t.textContent = e.label, t.dataset.menuIndex = i.toString(), t.addEventListener("click", (o) => {
|
|
178
|
+
o.preventDefault(), o.stopPropagation(), this.handleMenuTriggerClick(t, e.items, i);
|
|
179
|
+
}), t;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Create a popover if it doesn't exist
|
|
183
|
+
*/
|
|
184
|
+
createPopoverIfNeeded(e, i, t = !1) {
|
|
185
|
+
let o = this.shadowRoot.querySelector(`#${i}`);
|
|
186
|
+
return o || (o = document.createElement("div"), o.id = i, o.className = t ? "popover-submenu-popover" : "popover-menu-popover", o.style.display = "none", o.style.position = "fixed", this.shadowRoot.appendChild(o), this.openPopovers.set(i, o)), this.populatePopover(o, e, i), o;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Populate a popover with menu items
|
|
190
|
+
*/
|
|
191
|
+
populatePopover(e, i, t) {
|
|
192
|
+
e.innerHTML = "", i.forEach((o, r) => {
|
|
193
|
+
if (o.label === "---sep") {
|
|
194
|
+
const s = document.createElement("div");
|
|
195
|
+
s.className = "popover-menu-separator", e.appendChild(s);
|
|
196
|
+
} else {
|
|
197
|
+
const s = this.createMenuItem(o, `${t}-item-${r}`);
|
|
198
|
+
e.appendChild(s);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Create a menu item element
|
|
204
|
+
*/
|
|
205
|
+
createMenuItem(e, i) {
|
|
206
|
+
const t = document.createElement("div");
|
|
207
|
+
if (t.className = "popover-menu-item", t.textContent = e.label, e.enabled === !1)
|
|
208
|
+
return t.classList.add("disabled"), t;
|
|
209
|
+
if (e.items && e.items.length > 0) {
|
|
210
|
+
t.classList.add("has-submenu");
|
|
211
|
+
const o = `${i}-submenu`;
|
|
212
|
+
let r;
|
|
213
|
+
t.addEventListener("mouseenter", (s) => {
|
|
214
|
+
s.stopPropagation(), this.hoverTimeouts.has(o) && clearTimeout(this.hoverTimeouts.get(o)), r = window.setTimeout(() => {
|
|
215
|
+
this.closeOtherSubmenus(o), this.showSubmenu(e.items, o, t);
|
|
216
|
+
}, 100), this.hoverTimeouts.set(o, r);
|
|
217
|
+
}), t.addEventListener("mouseleave", (s) => {
|
|
218
|
+
s.stopPropagation();
|
|
219
|
+
}), t.addEventListener("click", (s) => {
|
|
220
|
+
s.stopPropagation(), this.closeOtherSubmenus(o), this.showSubmenu(e.items, o, t);
|
|
221
|
+
});
|
|
222
|
+
} else e.onclick && t.addEventListener("click", (o) => {
|
|
223
|
+
o.stopPropagation(), e.onclick(), this.closeAllMenus();
|
|
224
|
+
});
|
|
225
|
+
return t;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Show a submenu
|
|
229
|
+
*/
|
|
230
|
+
showSubmenu(e, i, t) {
|
|
231
|
+
const o = this.createPopoverIfNeeded(e, i, !0);
|
|
232
|
+
o.style.display = "block";
|
|
233
|
+
const r = t.getBoundingClientRect();
|
|
234
|
+
o.style.position = "fixed", o.style.left = `${r.right + 5}px`, o.style.top = `${r.top}px`, requestAnimationFrame(() => {
|
|
235
|
+
const s = t.getBoundingClientRect();
|
|
236
|
+
this.adjustPopoverPosition(o, s, "submenu");
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Close other submenus except the specified one and its ancestors
|
|
241
|
+
*/
|
|
242
|
+
closeOtherSubmenus(e = null) {
|
|
243
|
+
const i = /* @__PURE__ */ new Set();
|
|
244
|
+
if (e) {
|
|
245
|
+
i.add(e);
|
|
246
|
+
let t = e;
|
|
247
|
+
for (; t.includes("-submenu"); ) {
|
|
248
|
+
const o = t.lastIndexOf("-submenu");
|
|
249
|
+
if (o > 0) {
|
|
250
|
+
const s = t.substring(0, o).lastIndexOf("-item-");
|
|
251
|
+
if (s > 0)
|
|
252
|
+
t = t.substring(0, s), t.endsWith("-submenu") && i.add(t);
|
|
253
|
+
else
|
|
254
|
+
break;
|
|
255
|
+
} else
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
this.openPopovers.forEach((t, o) => {
|
|
260
|
+
!i.has(o) && t.classList.contains("popover-submenu-popover") && (t.style.display = "none");
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Close all menus
|
|
265
|
+
*/
|
|
266
|
+
closeAllMenus() {
|
|
267
|
+
this.hoverTimeouts.forEach((e) => clearTimeout(e)), this.hoverTimeouts.clear(), this.openPopovers.forEach((e) => {
|
|
268
|
+
e.style.display = "none";
|
|
269
|
+
}), this.shadowRoot.querySelectorAll(".popover-menu-trigger").forEach((e) => e.classList.remove("active"));
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Handle menu trigger click
|
|
273
|
+
*/
|
|
274
|
+
handleMenuTriggerClick(e, i, t) {
|
|
275
|
+
const o = `menu-${t}`, r = this.shadowRoot.querySelector(`#${o}`);
|
|
276
|
+
if (r && r.style.display === "block")
|
|
277
|
+
this.closeAllMenus();
|
|
278
|
+
else {
|
|
279
|
+
this.closeAllMenus();
|
|
280
|
+
const s = this.createPopoverIfNeeded(i, o);
|
|
281
|
+
e.classList.add("active"), this.showMainMenu(s, e);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Show the main menu
|
|
286
|
+
*/
|
|
287
|
+
showMainMenu(e, i) {
|
|
288
|
+
e.style.display = "block";
|
|
289
|
+
const t = i.getBoundingClientRect();
|
|
290
|
+
e.style.position = "fixed", e.style.left = `${t.left}px`, e.style.top = `${t.bottom + 2}px`, requestAnimationFrame(() => {
|
|
291
|
+
const o = i.getBoundingClientRect();
|
|
292
|
+
this.adjustPopoverPosition(e, o, "main");
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Adjust popover position to handle overflow
|
|
297
|
+
*/
|
|
298
|
+
adjustPopoverPosition(e, i, t) {
|
|
299
|
+
const o = e.getBoundingClientRect(), r = window.innerWidth, s = window.innerHeight, n = 10;
|
|
300
|
+
let l = parseFloat(e.style.left), a = parseFloat(e.style.top);
|
|
301
|
+
t === "main" ? (o.right > r - n && (l = i.right - o.width), l < n && (l = n), o.width > r - 2 * n && (l = n), o.bottom > s - n && (a = i.top - o.height - 2, a < n && (a = s - o.height - n)), a < n && (a = n), o.height > s - 2 * n && (a = n)) : t === "submenu" && (o.right > r - n && (l = i.left - o.width - 5, l < n && (l = n)), l < n && (l = n), o.bottom > s - n && (a = i.bottom - o.height, a < n && (a = s - o.height - n)), a < n && (a = n), o.height > s - 2 * n && (a = n)), e.style.left = `${l}px`, e.style.top = `${a}px`;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
const d = (p = "liwe3-popover-menu") => {
|
|
305
|
+
typeof window < "u" && !window.customElements.get(p) && customElements.define(p, u);
|
|
306
|
+
};
|
|
307
|
+
d();
|
|
308
|
+
export {
|
|
309
|
+
u as PopoverMenuElement,
|
|
310
|
+
d as definePopoverMenu
|
|
311
|
+
};
|
|
312
|
+
//# sourceMappingURL=PopoverMenu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PopoverMenu.js","sources":["../src/PopoverMenu.ts"],"sourcesContent":["/**\n * PopoverMenu Web Component\n * A customizable menu component using fixed positioning with support for nested submenus\n */\n\nexport type PopoverMenuItem = {\n label: string;\n enabled?: boolean;\n items?: PopoverMenuItem[];\n onclick?: () => void;\n};\n\nexport type PopoverMenuConfig = {\n label: string;\n items: PopoverMenuItem[];\n};\n\nexport class PopoverMenuElement extends HTMLElement {\n declare shadowRoot: ShadowRoot;\n private items: PopoverMenuConfig[] = [];\n private openPopovers: Map<string, HTMLElement> = new Map();\n private hoverTimeouts: Map<string, number> = new Map();\n private initialized = false;\n private globalClickHandler: ( ( e: MouseEvent ) => void ) | null = null;\n\n constructor () {\n super();\n this.attachShadow( { mode: 'open' } );\n }\n\n connectedCallback (): void {\n if ( !this.initialized ) {\n this.render();\n this.setupGlobalListeners();\n this.initialized = true;\n }\n }\n\n disconnectedCallback (): void {\n this.cleanupGlobalListeners();\n }\n\n /**\n * Set up global event listeners\n */\n private setupGlobalListeners (): void {\n // Add global click listener to close menus when clicking outside\n this.globalClickHandler = ( e: MouseEvent ) => {\n if ( !this.contains( e.target as Node ) && !this.shadowRoot.contains( e.target as Node ) ) {\n this.closeAllMenus();\n }\n };\n document.addEventListener( 'click', this.globalClickHandler );\n }\n\n /**\n * Clean up global event listeners\n */\n private cleanupGlobalListeners (): void {\n if ( this.globalClickHandler ) {\n document.removeEventListener( 'click', this.globalClickHandler );\n this.globalClickHandler = null;\n }\n }\n\n /**\n * Set menu items\n */\n setItems ( items: PopoverMenuConfig[] ): void {\n this.items = items;\n this.render();\n }\n\n /**\n * Get current menu items\n */\n getItems (): PopoverMenuConfig[] {\n return this.items;\n }\n\n /**\n * Add a new menu item\n */\n addMenuItem ( item: PopoverMenuConfig, index: number | null = null ): void {\n if ( index === null ) {\n this.items.push( item );\n } else {\n this.items.splice( index, 0, item );\n }\n this.render();\n }\n\n /**\n * Remove a menu item\n */\n removeMenuItem ( index: number ): void {\n if ( index >= 0 && index < this.items.length ) {\n this.items.splice( index, 1 );\n this.render();\n }\n }\n\n /**\n * Update a menu item\n */\n updateMenuItem ( index: number, item: PopoverMenuConfig ): void {\n if ( index >= 0 && index < this.items.length ) {\n this.items[ index ] = item;\n this.render();\n }\n }\n\n /**\n * Render the menu component\n */\n private render (): void {\n // Clear all existing content\n const existingContainer = this.shadowRoot.querySelector( '.popover-menu-bar' );\n if ( existingContainer ) {\n existingContainer.remove();\n }\n\n // Clear popover tracking\n this.openPopovers.clear();\n\n // Clear any existing hover timeouts\n this.hoverTimeouts.forEach( timeout => clearTimeout( timeout ) );\n this.hoverTimeouts.clear();\n\n this.shadowRoot.innerHTML = `\n <style>\n :host {\n display: block;\n font-family: var(--font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif);\n }\n\n /* PopoverMenu Component Styles */\n .popover-menu-bar {\n display: flex;\n background: var(--popover-menu-bar-background, #fff);\n border: 1px solid var(--popover-menu-bar-border, #ddd);\n border-radius: var(--popover-menu-bar-radius, 6px);\n padding: var(--popover-menu-bar-padding, 4px);\n box-shadow: var(--popover-menu-bar-shadow, 0 1px 3px rgba(0,0,0,0.1));\n }\n\n .popover-menu-trigger {\n background: none;\n border: none;\n padding: 8px 16px;\n cursor: pointer;\n border-radius: 4px;\n font-size: 14px;\n transition: background-color 0.2s;\n font-family: inherit;\n color: var(--popover-menu-trigger-color, #333);\n }\n\n .popover-menu-trigger:hover {\n background: var(--popover-menu-trigger-hover-bg, #f0f0f0);\n }\n\n .popover-menu-trigger.active {\n background: var(--popover-menu-trigger-active-bg, #e3f2fd);\n color: var(--popover-menu-trigger-active-color, #1976d2);\n }\n\n .popover-menu-popover {\n margin: 0;\n padding: 4px;\n border: 1px solid var(--popover-menu-border, #ccc);\n border-radius: var(--popover-menu-radius, 6px);\n background: var(--popover-menu-background, white);\n box-shadow: var(--popover-menu-shadow, 0 4px 12px rgba(0,0,0,0.15));\n min-width: 180px;\n z-index: 1000;\n font-family: inherit;\n position: fixed;\n display: none;\n }\n\n .popover-menu-item {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 12px;\n cursor: pointer;\n border-radius: 4px;\n font-size: 14px;\n transition: background-color 0.2s;\n position: relative;\n color: var(--popover-menu-item-color, #333);\n }\n\n .popover-menu-item:hover {\n background: var(--popover-menu-item-hover-bg, #f5f5f5);\n }\n\n .popover-menu-item.disabled {\n color: var(--popover-menu-item-disabled-color, #999);\n cursor: not-allowed;\n }\n\n .popover-menu-item.disabled:hover {\n background: transparent;\n }\n\n .popover-menu-item.has-submenu::after {\n content: '▶';\n font-size: 10px;\n color: var(--popover-menu-submenu-arrow-color, #666);\n }\n\n .popover-menu-separator {\n height: 1px;\n background: var(--popover-menu-separator-color, #e0e0e0);\n margin: 4px 0;\n }\n\n .popover-submenu-popover {\n margin: 0;\n padding: 4px;\n border: 1px solid var(--popover-menu-border, #ccc);\n border-radius: var(--popover-menu-radius, 6px);\n background: var(--popover-menu-background, white);\n box-shadow: var(--popover-menu-shadow, 0 4px 12px rgba(0,0,0,0.15));\n min-width: 160px;\n z-index: 1001;\n font-family: inherit;\n position: fixed;\n display: none;\n }\n </style>\n <div class=\"popover-menu-bar\"></div>\n `;\n\n const menuBar = this.shadowRoot.querySelector( '.popover-menu-bar' ) as HTMLElement;\n\n this.items.forEach( ( item, index ) => {\n const trigger = this.createMenuTrigger( item, index );\n menuBar.appendChild( trigger );\n } );\n }\n\n /**\n * Create a menu trigger button\n */\n private createMenuTrigger ( item: PopoverMenuConfig, index: number ): HTMLElement {\n const trigger = document.createElement( 'button' );\n trigger.className = 'popover-menu-trigger';\n trigger.textContent = item.label;\n trigger.dataset.menuIndex = index.toString();\n\n trigger.addEventListener( 'click', ( e ) => {\n e.preventDefault();\n e.stopPropagation();\n this.handleMenuTriggerClick( trigger, item.items, index );\n } );\n\n return trigger;\n }\n\n /**\n * Create a popover if it doesn't exist\n */\n private createPopoverIfNeeded ( items: PopoverMenuItem[], id: string, isSubmenu = false ): HTMLElement {\n let popover = this.shadowRoot.querySelector( `#${ id }` ) as HTMLElement;\n\n if ( !popover ) {\n popover = document.createElement( 'div' );\n popover.id = id;\n popover.className = isSubmenu ? 'popover-submenu-popover' : 'popover-menu-popover';\n popover.style.display = 'none';\n popover.style.position = 'fixed';\n this.shadowRoot.appendChild( popover );\n\n // Track this popover\n this.openPopovers.set( id, popover );\n }\n\n // Always repopulate with current items to ensure content is up-to-date\n this.populatePopover( popover, items, id );\n\n return popover;\n }\n\n /**\n * Populate a popover with menu items\n */\n private populatePopover ( popover: HTMLElement, items: PopoverMenuItem[], baseId: string ): void {\n popover.innerHTML = '';\n\n items.forEach( ( item, index ) => {\n if ( item.label === '---sep' ) {\n const separator = document.createElement( 'div' );\n separator.className = 'popover-menu-separator';\n popover.appendChild( separator );\n } else {\n const menuItem = this.createMenuItem( item, `${ baseId }-item-${ index }` );\n popover.appendChild( menuItem );\n }\n } );\n }\n\n /**\n * Create a menu item element\n */\n private createMenuItem ( item: PopoverMenuItem, id: string ): HTMLElement {\n const menuItem = document.createElement( 'div' );\n menuItem.className = 'popover-menu-item';\n menuItem.textContent = item.label;\n\n if ( item.enabled === false ) {\n menuItem.classList.add( 'disabled' );\n return menuItem;\n }\n\n if ( item.items && item.items.length > 0 ) {\n // Has submenu\n menuItem.classList.add( 'has-submenu' );\n const submenuId = `${ id }-submenu`;\n\n let hoverTimeout: number;\n\n menuItem.addEventListener( 'mouseenter', ( e ) => {\n e.stopPropagation();\n\n // Clear any existing timeout\n if ( this.hoverTimeouts.has( submenuId ) ) {\n clearTimeout( this.hoverTimeouts.get( submenuId ) );\n }\n\n hoverTimeout = window.setTimeout( () => {\n this.closeOtherSubmenus( submenuId );\n this.showSubmenu( item.items!, submenuId, menuItem );\n }, 100 );\n\n this.hoverTimeouts.set( submenuId, hoverTimeout );\n } );\n\n menuItem.addEventListener( 'mouseleave', ( e ) => {\n e.stopPropagation();\n } );\n\n menuItem.addEventListener( 'click', ( e ) => {\n e.stopPropagation();\n this.closeOtherSubmenus( submenuId );\n this.showSubmenu( item.items!, submenuId, menuItem );\n } );\n } else if ( item.onclick ) {\n // Regular menu item with click handler\n menuItem.addEventListener( 'click', ( e ) => {\n e.stopPropagation();\n item.onclick!();\n this.closeAllMenus();\n } );\n }\n\n return menuItem;\n }\n\n /**\n * Show a submenu\n */\n private showSubmenu ( items: PopoverMenuItem[], submenuId: string, parentItem: HTMLElement ): void {\n const submenu = this.createPopoverIfNeeded( items, submenuId, true );\n\n // Show the menu first so it's in the DOM and can be measured\n submenu.style.display = 'block';\n\n // Get the parent item's position AFTER showing the menu\n const rect = parentItem.getBoundingClientRect();\n submenu.style.position = 'fixed';\n submenu.style.left = `${ rect.right + 5 }px`;\n submenu.style.top = `${ rect.top }px`;\n\n // Adjust position with comprehensive overflow handling\n requestAnimationFrame( () => {\n const newRect = parentItem.getBoundingClientRect();\n this.adjustPopoverPosition( submenu, newRect, 'submenu' );\n } );\n }\n\n /**\n * Close other submenus except the specified one and its ancestors\n */\n private closeOtherSubmenus ( exceptSubmenuId: string | null = null ): void {\n // Build a set of IDs to keep open (the submenu being opened and all its ancestors)\n const idsToKeep = new Set<string>();\n if ( exceptSubmenuId ) {\n idsToKeep.add( exceptSubmenuId );\n\n // Add all ancestor IDs by traversing up the ID hierarchy\n let currentId = exceptSubmenuId;\n while ( currentId.includes( '-submenu' ) ) {\n // Remove the last submenu part to get the parent\n const lastSubmenuIndex = currentId.lastIndexOf( '-submenu' );\n if ( lastSubmenuIndex > 0 ) {\n // Find the item part before this submenu\n const beforeSubmenu = currentId.substring( 0, lastSubmenuIndex );\n const lastItemIndex = beforeSubmenu.lastIndexOf( '-item-' );\n if ( lastItemIndex > 0 ) {\n currentId = currentId.substring( 0, lastItemIndex );\n if ( currentId.endsWith( '-submenu' ) ) {\n idsToKeep.add( currentId );\n }\n } else {\n break;\n }\n } else {\n break;\n }\n }\n }\n\n // Close all submenus that are not in the keep set\n this.openPopovers.forEach( ( popover, id ) => {\n if ( !idsToKeep.has( id ) && popover.classList.contains( 'popover-submenu-popover' ) ) {\n popover.style.display = 'none';\n }\n } );\n }\n\n /**\n * Close all menus\n */\n private closeAllMenus (): void {\n // Clear all hover timeouts\n this.hoverTimeouts.forEach( timeout => clearTimeout( timeout ) );\n this.hoverTimeouts.clear();\n\n this.openPopovers.forEach( popover => {\n popover.style.display = 'none';\n } );\n\n // Remove active class from all triggers\n this.shadowRoot.querySelectorAll( '.popover-menu-trigger' ).forEach( t => t.classList.remove( 'active' ) );\n }\n\n /**\n * Handle menu trigger click\n */\n private handleMenuTriggerClick ( trigger: HTMLElement, items: PopoverMenuItem[], index: number ): void {\n const popoverId = `menu-${ index }`;\n const popover = this.shadowRoot.querySelector( `#${ popoverId }` ) as HTMLElement;\n\n if ( popover && popover.style.display === 'block' ) {\n // Menu is open, close it\n this.closeAllMenus();\n } else {\n // Close other menus first\n this.closeAllMenus();\n\n // Create the popover and show it\n const mainPopover = this.createPopoverIfNeeded( items, popoverId );\n\n // Add active class to clicked trigger\n trigger.classList.add( 'active' );\n\n // Position and show the popover\n this.showMainMenu( mainPopover, trigger );\n }\n }\n\n /**\n * Show the main menu\n */\n private showMainMenu ( popover: HTMLElement, trigger: HTMLElement ): void {\n // Show the menu first so it's in the DOM and can be measured\n popover.style.display = 'block';\n\n const rect = trigger.getBoundingClientRect();\n popover.style.position = 'fixed';\n popover.style.left = `${ rect.left }px`;\n popover.style.top = `${ rect.bottom + 2 }px`;\n\n // Adjust position with comprehensive overflow handling\n requestAnimationFrame( () => {\n const updatedRect = trigger.getBoundingClientRect();\n this.adjustPopoverPosition( popover, updatedRect, 'main' );\n } );\n }\n\n /**\n * Adjust popover position to handle overflow\n */\n private adjustPopoverPosition ( popover: HTMLElement, triggerRect: DOMRect, type: 'main' | 'submenu' ): void {\n const popoverRect = popover.getBoundingClientRect();\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n const margin = 10; // Minimum margin from screen edges\n\n let newLeft = parseFloat( popover.style.left );\n let newTop = parseFloat( popover.style.top );\n\n if ( type === 'main' ) {\n // Main menu positioning\n\n // Handle horizontal overflow - check right edge first\n if ( popoverRect.right > viewportWidth - margin ) {\n // If overflowing right, align right edge of popover with right edge of trigger\n newLeft = triggerRect.right - popoverRect.width;\n }\n\n // Then ensure minimum margin from left edge\n if ( newLeft < margin ) {\n newLeft = margin;\n }\n\n // If popover is wider than viewport, center it\n if ( popoverRect.width > viewportWidth - ( 2 * margin ) ) {\n newLeft = margin;\n }\n\n // Handle vertical overflow - check bottom first\n if ( popoverRect.bottom > viewportHeight - margin ) {\n // Try positioning above the trigger\n newTop = triggerRect.top - popoverRect.height - 2;\n\n // If positioning above would go off-screen, position at bottom margin\n if ( newTop < margin ) {\n newTop = viewportHeight - popoverRect.height - margin;\n }\n }\n\n // Ensure minimum margin from top\n if ( newTop < margin ) {\n newTop = margin;\n }\n\n // If popover is taller than viewport, position at top margin\n if ( popoverRect.height > viewportHeight - ( 2 * margin ) ) {\n newTop = margin;\n }\n\n } else if ( type === 'submenu' ) {\n // Submenu positioning\n\n // Handle horizontal overflow\n if ( popoverRect.right > viewportWidth - margin ) {\n // Position to the left of the parent item\n newLeft = triggerRect.left - popoverRect.width - 5;\n\n // If still overflowing left, position at margin from left edge\n if ( newLeft < margin ) {\n newLeft = margin;\n }\n }\n\n // Ensure minimum margin from left edge\n if ( newLeft < margin ) {\n newLeft = margin;\n }\n\n // Handle vertical overflow\n if ( popoverRect.bottom > viewportHeight - margin ) {\n // Try aligning bottom of submenu with bottom of parent item\n newTop = triggerRect.bottom - popoverRect.height;\n\n // If that would go off-screen at top, position at bottom margin\n if ( newTop < margin ) {\n newTop = viewportHeight - popoverRect.height - margin;\n }\n }\n\n // Ensure minimum margin from top\n if ( newTop < margin ) {\n newTop = margin;\n }\n\n // If submenu is taller than viewport, position at top margin\n if ( popoverRect.height > viewportHeight - ( 2 * margin ) ) {\n newTop = margin;\n }\n }\n\n // Apply the new position\n popover.style.left = `${ newLeft }px`;\n popover.style.top = `${ newTop }px`;\n }\n}\n\n/**\n * Conditionally defines the custom element if in a browser environment.\n */\nconst definePopoverMenu = ( tagName = 'liwe3-popover-menu' ): void => {\n if ( typeof window !== 'undefined' && !window.customElements.get( tagName ) ) {\n customElements.define( tagName, PopoverMenuElement );\n }\n};\n\n// Auto-register with default tag name\ndefinePopoverMenu();\n\nexport { definePopoverMenu };\n"],"names":["PopoverMenuElement","items","item","index","existingContainer","timeout","menuBar","trigger","e","id","isSubmenu","popover","baseId","separator","menuItem","submenuId","hoverTimeout","parentItem","submenu","rect","newRect","exceptSubmenuId","idsToKeep","currentId","lastSubmenuIndex","lastItemIndex","t","popoverId","mainPopover","updatedRect","triggerRect","type","popoverRect","viewportWidth","viewportHeight","margin","newLeft","newTop","definePopoverMenu","tagName"],"mappings":"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"}
|
package/dist/SmartSelect.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SmartSelect.js","sources":["../src/SmartSelect.ts"],"sourcesContent":["/**\n * SmartSelect Web Component\n * A customizable select dropdown with search, multi-select, and keyboard navigation\n */\n\nexport interface SelectOption {\n value: string;\n label: string;\n}\n\nexport class SmartSelectElement extends HTMLElement {\n declare shadowRoot: ShadowRoot;\n private isOpen: boolean = false;\n private selectedOptions: SelectOption[] = [];\n private filteredOptions: SelectOption[] = [];\n private focusedIndex: number = -1;\n private searchValue: string = '';\n private keyboardNavigating: boolean = false;\n private keyboardTimer?: number;\n\n constructor() {\n super();\n this.attachShadow({ mode: 'open' });\n\n // Make component focusable\n if (!this.hasAttribute('tabindex')) {\n this.setAttribute('tabindex', '0');\n }\n\n this.render();\n this.bindEvents();\n }\n\n static get observedAttributes(): string[] {\n return ['multiple', 'searchable', 'placeholder', 'disabled', 'value', 'options'];\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue !== newValue) {\n if (name === 'options') {\n this.filteredOptions = [...this.options];\n }\n this.render();\n }\n }\n\n get multiple(): boolean {\n return this.hasAttribute('multiple');\n }\n\n set multiple(value: boolean) {\n if (value) {\n this.setAttribute('multiple', '');\n } else {\n this.removeAttribute('multiple');\n }\n }\n\n get searchable(): boolean {\n return this.hasAttribute('searchable');\n }\n\n set searchable(value: boolean) {\n if (value) {\n this.setAttribute('searchable', '');\n } else {\n this.removeAttribute('searchable');\n }\n }\n\n get placeholder(): string {\n return this.getAttribute('placeholder') || 'Select an option';\n }\n\n set placeholder(value: string) {\n this.setAttribute('placeholder', value);\n }\n\n get disabled(): boolean {\n return this.hasAttribute('disabled');\n }\n\n set disabled(value: boolean) {\n if (value) {\n this.setAttribute('disabled', '');\n } else {\n this.removeAttribute('disabled');\n }\n }\n\n get value(): string | string[] {\n if (this.multiple) {\n return this.selectedOptions.map(opt => opt.value);\n }\n return this.selectedOptions.length > 0 ? this.selectedOptions[0].value : '';\n }\n\n set value(val: string | string[]) {\n if (this.multiple && Array.isArray(val)) {\n this.selectedOptions = this.options.filter(opt => val.includes(opt.value));\n } else {\n const option = this.options.find(opt => opt.value === val);\n this.selectedOptions = option ? [option] : [];\n }\n this.render();\n }\n\n get options(): SelectOption[] {\n const optionsAttr = this.getAttribute('options');\n if (optionsAttr) {\n try {\n return JSON.parse(optionsAttr);\n } catch (e) {\n console.error('Invalid options format:', e);\n return [];\n }\n }\n return [];\n }\n\n set options(opts: SelectOption[]) {\n this.setAttribute('options', JSON.stringify(opts));\n }\n\n /**\n * Opens the dropdown\n */\n open(): void {\n if (this.disabled) return;\n this.isOpen = true;\n this.focusedIndex = -1;\n if (this.options.length > 0) {\n this.filteredOptions = [...this.options];\n }\n this.render();\n\n // Update dropdown position based on viewport\n this._updateDropdownPosition();\n\n // Focus search input if searchable\n if (this.searchable) {\n requestAnimationFrame(() => {\n const searchInput = this.shadowRoot.querySelector('.search-input') as HTMLInputElement;\n if (searchInput) {\n searchInput.focus();\n }\n });\n }\n\n this.dispatchEvent(new CustomEvent('open'));\n }\n\n /**\n * Closes the dropdown\n */\n close(): void {\n this.isOpen = false;\n this.focusedIndex = -1;\n this.searchValue = '';\n\n // Reset filtered options when closing\n if (this.searchable && this.options.length > 0) {\n this.filteredOptions = [...this.options];\n }\n\n // Clear any inline positioning styles\n const dropdown = this.shadowRoot.querySelector('.dropdown') as HTMLElement;\n if (dropdown) {\n dropdown.style.top = '';\n dropdown.style.left = '';\n dropdown.style.width = '';\n dropdown.style.maxHeight = '';\n }\n\n this.render();\n this.dispatchEvent(new CustomEvent('close'));\n }\n\n /**\n * Toggles the dropdown open/closed state\n */\n toggle(): void {\n if (this.isOpen) {\n this.close();\n } else {\n this.open();\n }\n }\n\n /**\n * Selects an option by its value\n */\n selectOption(value: string): void {\n const option = this.options.find(opt => opt.value === value);\n if (!option) return;\n\n if (this.multiple) {\n if (!this.selectedOptions.find(opt => opt.value === value)) {\n this.selectedOptions.push(option);\n }\n } else {\n this.selectedOptions = [option];\n this.close();\n }\n\n this.render();\n this.dispatchEvent(new CustomEvent('change', { detail: { value: this.value } }));\n }\n\n /**\n * Deselects an option by its value\n */\n deselectOption(value: string): void {\n this.selectedOptions = this.selectedOptions.filter(opt => opt.value !== value);\n this.render();\n this.dispatchEvent(new CustomEvent('change', { detail: { value: this.value } }));\n }\n\n /**\n * Returns an array of currently selected options\n */\n getSelectedOptions(): SelectOption[] {\n return [...this.selectedOptions];\n }\n\n /**\n * Sets the options for the select component\n */\n setOptions(options: SelectOption[]): void {\n this.options = options;\n this.filteredOptions = [...options];\n this.selectedOptions = [];\n this.render();\n }\n\n /**\n * Handles search functionality\n */\n private handleSearch(query: string): void {\n this.searchValue = query;\n this.filteredOptions = this.options.filter(option =>\n option.label.toLowerCase().includes(query.toLowerCase())\n );\n this.focusedIndex = -1;\n this.render();\n this.dispatchEvent(new CustomEvent('search', { detail: { query } }));\n }\n\n /**\n * Updates the visual focus state without full re-render\n */\n private updateFocusedOption(): void {\n const options = this.shadowRoot.querySelectorAll('.option');\n\n // Remove focused class from all options\n options.forEach(option => option.classList.remove('focused'));\n\n // Add focused class to current option\n if (this.focusedIndex >= 0 && this.focusedIndex < options.length) {\n options[this.focusedIndex].classList.add('focused');\n }\n\n this.scrollToFocusedOption();\n }\n\n /**\n * Scrolls the focused option into view\n */\n private scrollToFocusedOption(): void {\n if (this.focusedIndex < 0) return;\n\n requestAnimationFrame(() => {\n const dropdown = this.shadowRoot.querySelector('.dropdown') as HTMLElement;\n const focusedOption = this.shadowRoot.querySelector('.option.focused') as HTMLElement;\n\n if (dropdown && focusedOption) {\n const dropdownRect = dropdown.getBoundingClientRect();\n const optionRect = focusedOption.getBoundingClientRect();\n\n // Check if option is above visible area\n if (optionRect.top < dropdownRect.top) {\n dropdown.scrollTop -= (dropdownRect.top - optionRect.top);\n }\n // Check if option is below visible area\n else if (optionRect.bottom > dropdownRect.bottom) {\n dropdown.scrollTop += (optionRect.bottom - dropdownRect.bottom);\n }\n }\n });\n }\n\n /**\n * Calculates the optimal dropdown position based on viewport constraints\n */\n private _calculateDropdownPosition(): { top: number; left: number; width: number; maxHeight: number } | null {\n const trigger = this.shadowRoot.querySelector('.select-trigger') as HTMLElement;\n if (!trigger) return null;\n\n const triggerRect = trigger.getBoundingClientRect();\n const viewportHeight = window.innerHeight;\n const viewportWidth = window.innerWidth;\n const dropdownMaxHeight = 200;\n const dropdownPadding = 10;\n const margin = 2;\n\n // Calculate available space\n const spaceBelow = viewportHeight - triggerRect.bottom;\n const spaceAbove = triggerRect.top;\n\n // Determine if dropdown should open upward\n const shouldOpenUpward = spaceBelow < dropdownMaxHeight + dropdownPadding && spaceAbove > spaceBelow;\n\n // Calculate dimensions\n const width = triggerRect.width;\n const left = Math.max(0, Math.min(triggerRect.left, viewportWidth - width));\n\n let top: number;\n let maxHeight: number;\n\n if (shouldOpenUpward) {\n // Position above the trigger\n maxHeight = Math.min(dropdownMaxHeight, spaceAbove - dropdownPadding);\n top = triggerRect.top - maxHeight - margin;\n } else {\n // Position below the trigger\n maxHeight = Math.min(dropdownMaxHeight, spaceBelow - dropdownPadding);\n top = triggerRect.bottom + margin;\n }\n\n return {\n top: Math.max(0, top),\n left,\n width,\n maxHeight: Math.max(100, maxHeight) // Ensure minimum height\n };\n }\n\n /**\n * Updates dropdown position using fixed positioning relative to viewport\n */\n private _updateDropdownPosition(): void {\n requestAnimationFrame(() => {\n const dropdown = this.shadowRoot.querySelector('.dropdown') as HTMLElement;\n if (!dropdown) return;\n\n const position = this._calculateDropdownPosition();\n if (!position) return;\n\n // Apply calculated position as inline styles\n dropdown.style.top = `${position.top}px`;\n dropdown.style.left = `${position.left}px`;\n dropdown.style.width = `${position.width}px`;\n dropdown.style.maxHeight = `${position.maxHeight}px`;\n });\n }\n\n /**\n * Handles keyboard navigation\n */\n private handleKeydown(event: KeyboardEvent): void {\n if (this.disabled) return;\n\n // Prevent double execution if event has already been handled\n if ((event as any)._smartSelectHandled) return;\n (event as any)._smartSelectHandled = true;\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n this.keyboardNavigating = true;\n clearTimeout(this.keyboardTimer);\n this.keyboardTimer = window.setTimeout(() => { this.keyboardNavigating = false; }, 100);\n\n if (!this.isOpen) {\n this.open();\n } else {\n // If searchable and search input is focused, move to first option\n const searchInput = this.shadowRoot.querySelector('.search-input') as HTMLInputElement;\n const isSearchFocused = this.searchable && searchInput === this.shadowRoot.activeElement;\n\n if (isSearchFocused) {\n this.focusedIndex = 0;\n searchInput.blur(); // Blur search input to allow normal navigation\n // Focus the component to ensure it receives keyboard events\n this.focus();\n this.updateFocusedOption();\n return;\n }\n // Navigate through options\n const newIndex = Math.min(this.focusedIndex + 1, this.filteredOptions.length - 1);\n this.focusedIndex = newIndex;\n this.updateFocusedOption();\n }\n break;\n\n case 'ArrowUp':\n event.preventDefault();\n this.keyboardNavigating = true;\n clearTimeout(this.keyboardTimer);\n this.keyboardTimer = window.setTimeout(() => { this.keyboardNavigating = false; }, 100);\n\n if (this.isOpen) {\n // If at first option and searchable, focus search input\n if (this.focusedIndex === 0 && this.searchable) {\n this.focusedIndex = -1;\n this.updateFocusedOption();\n requestAnimationFrame(() => {\n const searchInput = this.shadowRoot.querySelector('.search-input') as HTMLInputElement;\n if (searchInput) {\n searchInput.focus();\n searchInput.setSelectionRange(searchInput.value.length, searchInput.value.length);\n }\n });\n return;\n }\n // If searchable and search input is focused, do nothing\n const searchInput = this.shadowRoot.querySelector('.search-input') as HTMLInputElement;\n const isSearchFocused = this.searchable && searchInput === this.shadowRoot.activeElement;\n\n if (isSearchFocused) {\n return;\n }\n // Navigate through options\n const newIndex = Math.max(this.focusedIndex - 1, -1);\n this.focusedIndex = newIndex;\n this.updateFocusedOption();\n }\n break;\n\n case 'Enter':\n event.preventDefault();\n if (this.isOpen && this.focusedIndex >= 0 && this.focusedIndex < this.filteredOptions.length) {\n this.selectOption(this.filteredOptions[this.focusedIndex].value);\n } else if (!this.isOpen) {\n this.open();\n }\n break;\n\n case 'Escape':\n event.preventDefault();\n this.close();\n break;\n\n case 'Tab':\n this.close();\n break;\n }\n }\n\n /**\n * Binds all event listeners\n */\n private bindEvents(): void {\n // Listen for keydown events on both the component and shadow root\n const keydownHandler = this.handleKeydown.bind(this);\n this.addEventListener('keydown', keydownHandler);\n this.shadowRoot.addEventListener('keydown', keydownHandler as EventListener);\n\n // Use event delegation on the shadow root\n this.shadowRoot.addEventListener('click', (e) => {\n e.stopPropagation();\n const target = e.target as HTMLElement;\n\n if (target.closest('.remove-tag')) {\n const value = (target.closest('.remove-tag') as HTMLElement).dataset.value;\n if (value) this.deselectOption(value);\n } else if (target.closest('.option')) {\n const value = (target.closest('.option') as HTMLElement).dataset.value;\n if (value) this.selectOption(value);\n } else if (target.closest('.select-trigger')) {\n this.toggle();\n }\n });\n\n // Handle mouse hover on options to update focused index\n this.shadowRoot.addEventListener('mouseover', (e) => {\n // Don't interfere with keyboard navigation\n if (this.keyboardNavigating) return;\n\n const target = e.target as HTMLElement;\n if (target.closest('.option')) {\n const option = target.closest('.option') as HTMLElement;\n const options = Array.from(this.shadowRoot.querySelectorAll('.option'));\n const newFocusedIndex = options.indexOf(option);\n\n // Only update if the focused index actually changed\n if (this.focusedIndex !== newFocusedIndex) {\n // Remove focused class from current option\n const currentFocused = this.shadowRoot.querySelector('.option.focused');\n if (currentFocused) {\n currentFocused.classList.remove('focused');\n }\n\n // Add focused class to new option\n option.classList.add('focused');\n this.focusedIndex = newFocusedIndex;\n }\n }\n });\n\n // Handle mouse leaving dropdown to clear focus\n this.shadowRoot.addEventListener('mouseleave', (e) => {\n // Don't interfere with keyboard navigation\n if (this.keyboardNavigating) return;\n\n const target = e.target as HTMLElement;\n if (target.closest('.dropdown')) {\n const currentFocused = this.shadowRoot.querySelector('.option.focused');\n if (currentFocused) {\n currentFocused.classList.remove('focused');\n }\n this.focusedIndex = -1;\n }\n });\n\n // Handle search input\n this.shadowRoot.addEventListener('input', (e) => {\n const target = e.target as HTMLInputElement;\n if (target.classList.contains('search-input')) {\n this.handleSearch(target.value);\n }\n });\n\n // Close dropdown when clicking outside\n document.addEventListener('click', (e) => {\n if (!this.contains(e.target as Node)) {\n this.close();\n }\n });\n\n // Update dropdown position on window resize or scroll\n window.addEventListener('resize', () => {\n if (this.isOpen) {\n this._updateDropdownPosition();\n }\n });\n\n window.addEventListener('scroll', () => {\n if (this.isOpen) {\n this._updateDropdownPosition();\n }\n }, true); // Use capture to catch all scroll events\n }\n\n /**\n * Renders the component\n */\n private render(): void {\n // Initialize filteredOptions if not set\n if (this.filteredOptions.length === 0 && this.options.length > 0) {\n this.filteredOptions = [...this.options];\n }\n\n // Remember if search input was focused before render\n const wasSearchFocused = this.shadowRoot.querySelector('.search-input') === this.shadowRoot.activeElement;\n\n const displayText = this.selectedOptions.length > 0\n ? (this.multiple\n ? `${this.selectedOptions.length} selected`\n : this.selectedOptions[0].label)\n : this.placeholder;\n\n this.shadowRoot.innerHTML = `\n <style>\n :host {\n display: inline-block;\n position: relative;\n min-width: 200px;\n font-family: var(--font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif);\n font-size: var(--font-size, 14px);\n outline: none;\n }\n\n :host(:focus) .select-trigger {\n border-color: var(--focus-color, #007bff);\n box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);\n }\n\n :host([disabled]) {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .select-container {\n position: relative;\n }\n\n .select-trigger {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--padding, 8px 12px);\n border: var(--border, 1px solid #ccc);\n border-radius: var(--border-radius, 4px);\n background: var(--background, white);\n cursor: pointer;\n min-height: 36px;\n box-sizing: border-box;\n color: #333;\n user-select: none;\n }\n\n .select-trigger:focus {\n outline: none;\n border-color: var(--focus-color, #007bff);\n box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);\n }\n\n .select-trigger[disabled] {\n cursor: not-allowed;\n }\n\n .selected-content {\n display: flex;\n align-items: center;\n gap: 4px;\n flex-wrap: wrap;\n flex: 1;\n }\n\n .tag {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 8px;\n background: var(--tag-background, #e9ecef);\n border-radius: var(--tag-border-radius, 12px);\n font-size: 12px;\n color: var(--tag-color, #495057);\n user-select: none;\n }\n\n .remove-tag {\n cursor: pointer;\n color: var(--remove-color, #6c757d);\n font-weight: bold;\n font-size: 14px;\n }\n\n .remove-tag:hover {\n color: var(--remove-hover-color, #dc3545);\n }\n\n .arrow {\n width: 0;\n height: 0;\n border-left: 5px solid transparent;\n border-right: 5px solid transparent;\n border-top: 5px solid var(--arrow-color, #666);\n transition: transform 0.2s;\n }\n\n .arrow.open {\n transform: rotate(180deg);\n }\n\n .dropdown {\n position: fixed;\n z-index: 99999;\n background: var(--dropdown-background, white);\n border: var(--dropdown-border, 1px solid #ccc);\n border-radius: var(--dropdown-border-radius, 4px);\n box-shadow: var(--dropdown-shadow, 0 2px 8px rgba(0, 0, 0, 0.1));\n max-height: 200px;\n overflow-y: auto;\n scroll-behavior: smooth;\n color: #333;\n }\n\n .search-input {\n width: 100%;\n padding: 8px 12px;\n border: none;\n border-bottom: 1px solid #eee;\n font-size: 14px;\n outline: none;\n box-sizing: border-box;\n }\n\n .option {\n padding: 8px 12px;\n cursor: pointer;\n color: var(--option-color, #333);\n transition: background-color 0.2s;\n user-select: none;\n }\n\n .option:hover {\n background-color: var(--option-hover-background, #f8f9fa);\n }\n\n .option.focused {\n background-color: var(--option-focused-background, #007bff);\n color: var(--option-focused-color, white);\n }\n\n .option.selected {\n background-color: var(--option-selected-background, #e3f2fd);\n color: var(--option-selected-color, #1976d2);\n }\n\n .no-options {\n padding: 8px 12px;\n color: var(--no-options-color, #6c757d);\n font-style: italic;\n }\n </style>\n\n <div class=\"select-container\">\n <div class=\"select-trigger\" tabindex=\"-1\">\n <div class=\"selected-content\">\n ${this.multiple && this.selectedOptions.length > 0\n ? this.selectedOptions.map(option => `\n <span class=\"tag\">\n ${option.label}\n <span class=\"remove-tag\" data-value=\"${option.value}\">×</span>\n </span>\n `).join('')\n : `<span>${displayText}</span>`\n }\n </div>\n <div class=\"arrow ${this.isOpen ? 'open' : ''}\"></div>\n </div>\n\n ${this.isOpen ? `\n <div class=\"dropdown\">\n ${this.searchable ? `\n <input\n type=\"text\"\n class=\"search-input\"\n placeholder=\"Search options...\"\n value=\"${this.searchValue}\"\n >\n ` : ''}\n\n ${this.filteredOptions.length > 0\n ? this.filteredOptions.map((option, index) => `\n <div\n class=\"option ${this.selectedOptions.find(selected => selected.value === option.value) ? 'selected' : ''} ${index === this.focusedIndex ? 'focused' : ''}\"\n data-value=\"${option.value}\"\n >\n ${option.label}\n </div>\n `).join('')\n : '<div class=\"no-options\">No options available</div>'\n }\n </div>\n ` : ''}\n </div>\n `;\n\n // Re-focus search input if it was previously focused\n if (wasSearchFocused && this.searchable && this.isOpen) {\n requestAnimationFrame(() => {\n const searchInput = this.shadowRoot.querySelector('.search-input') as HTMLInputElement;\n if (searchInput) {\n searchInput.focus();\n // Restore cursor position to the end\n searchInput.setSelectionRange(searchInput.value.length, searchInput.value.length);\n }\n });\n }\n }\n}\n\n/**\n * Conditionally defines the custom element if in a browser environment.\n */\nconst defineSmartSelect = (tagName: string = 'liwe3-select'): void => {\n if (typeof window !== 'undefined' && !window.customElements.get(tagName)) {\n customElements.define(tagName, SmartSelectElement);\n }\n};\n\n// Auto-register with default tag name\ndefineSmartSelect();\n\nexport { defineSmartSelect };\n"],"names":["SmartSelectElement","name","oldValue","newValue","value","opt","val","option","optionsAttr","e","opts","searchInput","dropdown","options","query","focusedOption","dropdownRect","optionRect","trigger","triggerRect","viewportHeight","viewportWidth","dropdownMaxHeight","dropdownPadding","margin","spaceBelow","spaceAbove","shouldOpenUpward","width","left","top","maxHeight","position","event","newIndex","keydownHandler","target","newFocusedIndex","currentFocused","wasSearchFocused","displayText","index","selected","defineSmartSelect","tagName"],"mappings":"AAUO,MAAMA,UAA2B,YAAY;AAAA,EAUlD,cAAc;AACZ,UAAA,GATF,KAAQ,SAAkB,IAC1B,KAAQ,kBAAkC,CAAA,GAC1C,KAAQ,kBAAkC,CAAA,GAC1C,KAAQ,eAAuB,IAC/B,KAAQ,cAAsB,IAC9B,KAAQ,qBAA8B,IAKpC,KAAK,aAAa,EAAE,MAAM,OAAA,CAAQ,GAG7B,KAAK,aAAa,UAAU,KAC/B,KAAK,aAAa,YAAY,GAAG,GAGnC,KAAK,OAAA,GACL,KAAK,WAAA;AAAA,EACP;AAAA,EAEA,WAAW,qBAA+B;AACxC,WAAO,CAAC,YAAY,cAAc,eAAe,YAAY,SAAS,SAAS;AAAA,EACjF;AAAA,EAEA,yBAAyBC,GAAcC,GAAyBC,GAA+B;AAC7F,IAAID,MAAaC,MACXF,MAAS,cACX,KAAK,kBAAkB,CAAC,GAAG,KAAK,OAAO,IAEzC,KAAK,OAAA;AAAA,EAET;AAAA,EAEA,IAAI,WAAoB;AACtB,WAAO,KAAK,aAAa,UAAU;AAAA,EACrC;AAAA,EAEA,IAAI,SAASG,GAAgB;AAC3B,IAAIA,IACF,KAAK,aAAa,YAAY,EAAE,IAEhC,KAAK,gBAAgB,UAAU;AAAA,EAEnC;AAAA,EAEA,IAAI,aAAsB;AACxB,WAAO,KAAK,aAAa,YAAY;AAAA,EACvC;AAAA,EAEA,IAAI,WAAWA,GAAgB;AAC7B,IAAIA,IACF,KAAK,aAAa,cAAc,EAAE,IAElC,KAAK,gBAAgB,YAAY;AAAA,EAErC;AAAA,EAEA,IAAI,cAAsB;AACxB,WAAO,KAAK,aAAa,aAAa,KAAK;AAAA,EAC7C;AAAA,EAEA,IAAI,YAAYA,GAAe;AAC7B,SAAK,aAAa,eAAeA,CAAK;AAAA,EACxC;AAAA,EAEA,IAAI,WAAoB;AACtB,WAAO,KAAK,aAAa,UAAU;AAAA,EACrC;AAAA,EAEA,IAAI,SAASA,GAAgB;AAC3B,IAAIA,IACF,KAAK,aAAa,YAAY,EAAE,IAEhC,KAAK,gBAAgB,UAAU;AAAA,EAEnC;AAAA,EAEA,IAAI,QAA2B;AAC7B,WAAI,KAAK,WACA,KAAK,gBAAgB,IAAI,CAAAC,MAAOA,EAAI,KAAK,IAE3C,KAAK,gBAAgB,SAAS,IAAI,KAAK,gBAAgB,CAAC,EAAE,QAAQ;AAAA,EAC3E;AAAA,EAEA,IAAI,MAAMC,GAAwB;AAChC,QAAI,KAAK,YAAY,MAAM,QAAQA,CAAG;AACpC,WAAK,kBAAkB,KAAK,QAAQ,OAAO,OAAOA,EAAI,SAASD,EAAI,KAAK,CAAC;AAAA,SACpE;AACL,YAAME,IAAS,KAAK,QAAQ,KAAK,CAAAF,MAAOA,EAAI,UAAUC,CAAG;AACzD,WAAK,kBAAkBC,IAAS,CAACA,CAAM,IAAI,CAAA;AAAA,IAC7C;AACA,SAAK,OAAA;AAAA,EACP;AAAA,EAEA,IAAI,UAA0B;AAC5B,UAAMC,IAAc,KAAK,aAAa,SAAS;AAC/C,QAAIA;AACF,UAAI;AACF,eAAO,KAAK,MAAMA,CAAW;AAAA,MAC/B,SAASC,GAAG;AACV,uBAAQ,MAAM,2BAA2BA,CAAC,GACnC,CAAA;AAAA,MACT;AAEF,WAAO,CAAA;AAAA,EACT;AAAA,EAEA,IAAI,QAAQC,GAAsB;AAChC,SAAK,aAAa,WAAW,KAAK,UAAUA,CAAI,CAAC;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA,EAKA,OAAa;AACX,IAAI,KAAK,aACT,KAAK,SAAS,IACd,KAAK,eAAe,IAChB,KAAK,QAAQ,SAAS,MACxB,KAAK,kBAAkB,CAAC,GAAG,KAAK,OAAO,IAEzC,KAAK,OAAA,GAGL,KAAK,wBAAA,GAGD,KAAK,cACP,sBAAsB,MAAM;AAC1B,YAAMC,IAAc,KAAK,WAAW,cAAc,eAAe;AACjE,MAAIA,KACFA,EAAY,MAAA;AAAA,IAEhB,CAAC,GAGH,KAAK,cAAc,IAAI,YAAY,MAAM,CAAC;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,SAAK,SAAS,IACd,KAAK,eAAe,IACpB,KAAK,cAAc,IAGf,KAAK,cAAc,KAAK,QAAQ,SAAS,MAC3C,KAAK,kBAAkB,CAAC,GAAG,KAAK,OAAO;AAIzC,UAAMC,IAAW,KAAK,WAAW,cAAc,WAAW;AAC1D,IAAIA,MACFA,EAAS,MAAM,MAAM,IACrBA,EAAS,MAAM,OAAO,IACtBA,EAAS,MAAM,QAAQ,IACvBA,EAAS,MAAM,YAAY,KAG7B,KAAK,OAAA,GACL,KAAK,cAAc,IAAI,YAAY,OAAO,CAAC;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA,EAKA,SAAe;AACb,IAAI,KAAK,SACP,KAAK,MAAA,IAEL,KAAK,KAAA;AAAA,EAET;AAAA;AAAA;AAAA;AAAA,EAKA,aAAaR,GAAqB;AAChC,UAAMG,IAAS,KAAK,QAAQ,KAAK,CAAAF,MAAOA,EAAI,UAAUD,CAAK;AAC3D,IAAKG,MAED,KAAK,WACF,KAAK,gBAAgB,KAAK,OAAOF,EAAI,UAAUD,CAAK,KACvD,KAAK,gBAAgB,KAAKG,CAAM,KAGlC,KAAK,kBAAkB,CAACA,CAAM,GAC9B,KAAK,MAAA,IAGP,KAAK,OAAA,GACL,KAAK,cAAc,IAAI,YAAY,UAAU,EAAE,QAAQ,EAAE,OAAO,KAAK,MAAA,EAAM,CAAG,CAAC;AAAA,EACjF;AAAA;AAAA;AAAA;AAAA,EAKA,eAAeH,GAAqB;AAClC,SAAK,kBAAkB,KAAK,gBAAgB,OAAO,CAAAC,MAAOA,EAAI,UAAUD,CAAK,GAC7E,KAAK,OAAA,GACL,KAAK,cAAc,IAAI,YAAY,UAAU,EAAE,QAAQ,EAAE,OAAO,KAAK,MAAA,EAAM,CAAG,CAAC;AAAA,EACjF;AAAA;AAAA;AAAA;AAAA,EAKA,qBAAqC;AACnC,WAAO,CAAC,GAAG,KAAK,eAAe;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,WAAWS,GAA+B;AACxC,SAAK,UAAUA,GACf,KAAK,kBAAkB,CAAC,GAAGA,CAAO,GAClC,KAAK,kBAAkB,CAAA,GACvB,KAAK,OAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAaC,GAAqB;AACxC,SAAK,cAAcA,GACnB,KAAK,kBAAkB,KAAK,QAAQ;AAAA,MAAO,CAAAP,MACzCA,EAAO,MAAM,YAAA,EAAc,SAASO,EAAM,aAAa;AAAA,IAAA,GAEzD,KAAK,eAAe,IACpB,KAAK,OAAA,GACL,KAAK,cAAc,IAAI,YAAY,UAAU,EAAE,QAAQ,EAAE,OAAAA,EAAA,EAAM,CAAG,CAAC;AAAA,EACrE;AAAA;AAAA;AAAA;AAAA,EAKQ,sBAA4B;AAClC,UAAMD,IAAU,KAAK,WAAW,iBAAiB,SAAS;AAG1D,IAAAA,EAAQ,QAAQ,CAAAN,MAAUA,EAAO,UAAU,OAAO,SAAS,CAAC,GAGxD,KAAK,gBAAgB,KAAK,KAAK,eAAeM,EAAQ,UACxDA,EAAQ,KAAK,YAAY,EAAE,UAAU,IAAI,SAAS,GAGpD,KAAK,sBAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA,EAKQ,wBAA8B;AACpC,IAAI,KAAK,eAAe,KAExB,sBAAsB,MAAM;AAC1B,YAAMD,IAAW,KAAK,WAAW,cAAc,WAAW,GACpDG,IAAgB,KAAK,WAAW,cAAc,iBAAiB;AAErE,UAAIH,KAAYG,GAAe;AAC7B,cAAMC,IAAeJ,EAAS,sBAAA,GACxBK,IAAaF,EAAc,sBAAA;AAGjC,QAAIE,EAAW,MAAMD,EAAa,MAChCJ,EAAS,aAAcI,EAAa,MAAMC,EAAW,MAG9CA,EAAW,SAASD,EAAa,WACxCJ,EAAS,aAAcK,EAAW,SAASD,EAAa;AAAA,MAE5D;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKQ,6BAAqG;AAC3G,UAAME,IAAU,KAAK,WAAW,cAAc,iBAAiB;AAC/D,QAAI,CAACA,EAAS,QAAO;AAErB,UAAMC,IAAcD,EAAQ,sBAAA,GACtBE,IAAiB,OAAO,aACxBC,IAAgB,OAAO,YACvBC,IAAoB,KACpBC,IAAkB,IAClBC,IAAS,GAGTC,IAAaL,IAAiBD,EAAY,QAC1CO,IAAaP,EAAY,KAGzBQ,IAAmBF,IAAaH,IAAoBC,KAAmBG,IAAaD,GAGpFG,IAAQT,EAAY,OACpBU,IAAO,KAAK,IAAI,GAAG,KAAK,IAAIV,EAAY,MAAME,IAAgBO,CAAK,CAAC;AAE1E,QAAIE,GACAC;AAEJ,WAAIJ,KAEFI,IAAY,KAAK,IAAIT,GAAmBI,IAAaH,CAAe,GACpEO,IAAMX,EAAY,MAAMY,IAAYP,MAGpCO,IAAY,KAAK,IAAIT,GAAmBG,IAAaF,CAAe,GACpEO,IAAMX,EAAY,SAASK,IAGtB;AAAA,MACL,KAAK,KAAK,IAAI,GAAGM,CAAG;AAAA,MACpB,MAAAD;AAAA,MACA,OAAAD;AAAA,MACA,WAAW,KAAK,IAAI,KAAKG,CAAS;AAAA;AAAA,IAAA;AAAA,EAEtC;AAAA;AAAA;AAAA;AAAA,EAKQ,0BAAgC;AACtC,0BAAsB,MAAM;AAC1B,YAAMnB,IAAW,KAAK,WAAW,cAAc,WAAW;AAC1D,UAAI,CAACA,EAAU;AAEf,YAAMoB,IAAW,KAAK,2BAAA;AACtB,MAAKA,MAGLpB,EAAS,MAAM,MAAM,GAAGoB,EAAS,GAAG,MACpCpB,EAAS,MAAM,OAAO,GAAGoB,EAAS,IAAI,MACtCpB,EAAS,MAAM,QAAQ,GAAGoB,EAAS,KAAK,MACxCpB,EAAS,MAAM,YAAY,GAAGoB,EAAS,SAAS;AAAA,IAClD,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAcC,GAA4B;AAChD,QAAI,MAAK,YAGJ,CAAAA,EAAc;AAGnB,cAFCA,EAAc,sBAAsB,IAE7BA,EAAM,KAAA;AAAA,QACZ,KAAK;AAMH,cALAA,EAAM,eAAA,GACN,KAAK,qBAAqB,IAC1B,aAAa,KAAK,aAAa,GAC/B,KAAK,gBAAgB,OAAO,WAAW,MAAM;AAAE,iBAAK,qBAAqB;AAAA,UAAO,GAAG,GAAG,GAElF,CAAC,KAAK;AACR,iBAAK,KAAA;AAAA,eACA;AAEL,kBAAMtB,IAAc,KAAK,WAAW,cAAc,eAAe;AAGjE,gBAFwB,KAAK,cAAcA,MAAgB,KAAK,WAAW,eAEtD;AACnB,mBAAK,eAAe,GACpBA,EAAY,KAAA,GAEZ,KAAK,MAAA,GACL,KAAK,oBAAA;AACL;AAAA,YACF;AAEA,kBAAMuB,IAAW,KAAK,IAAI,KAAK,eAAe,GAAG,KAAK,gBAAgB,SAAS,CAAC;AAChF,iBAAK,eAAeA,GACpB,KAAK,oBAAA;AAAA,UACP;AACA;AAAA,QAEF,KAAK;AAMH,cALAD,EAAM,eAAA,GACN,KAAK,qBAAqB,IAC1B,aAAa,KAAK,aAAa,GAC/B,KAAK,gBAAgB,OAAO,WAAW,MAAM;AAAE,iBAAK,qBAAqB;AAAA,UAAO,GAAG,GAAG,GAElF,KAAK,QAAQ;AAEf,gBAAI,KAAK,iBAAiB,KAAK,KAAK,YAAY;AAC9C,mBAAK,eAAe,IACpB,KAAK,oBAAA,GACL,sBAAsB,MAAM;AAC1B,sBAAMtB,IAAc,KAAK,WAAW,cAAc,eAAe;AACjE,gBAAIA,MACFA,EAAY,MAAA,GACZA,EAAY,kBAAkBA,EAAY,MAAM,QAAQA,EAAY,MAAM,MAAM;AAAA,cAEpF,CAAC;AACD;AAAA,YACF;AAEA,kBAAMA,IAAc,KAAK,WAAW,cAAc,eAAe;AAGjE,gBAFwB,KAAK,cAAcA,MAAgB,KAAK,WAAW;AAGzE;AAGF,kBAAMuB,IAAW,KAAK,IAAI,KAAK,eAAe,GAAG,EAAE;AACnD,iBAAK,eAAeA,GACpB,KAAK,oBAAA;AAAA,UACP;AACA;AAAA,QAEF,KAAK;AACH,UAAAD,EAAM,eAAA,GACF,KAAK,UAAU,KAAK,gBAAgB,KAAK,KAAK,eAAe,KAAK,gBAAgB,SACpF,KAAK,aAAa,KAAK,gBAAgB,KAAK,YAAY,EAAE,KAAK,IACrD,KAAK,UACf,KAAK,KAAA;AAEP;AAAA,QAEF,KAAK;AACH,UAAAA,EAAM,eAAA,GACN,KAAK,MAAA;AACL;AAAA,QAEF,KAAK;AACH,eAAK,MAAA;AACL;AAAA,MAAA;AAAA,EAEN;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAmB;AAEzB,UAAME,IAAiB,KAAK,cAAc,KAAK,IAAI;AACnD,SAAK,iBAAiB,WAAWA,CAAc,GAC/C,KAAK,WAAW,iBAAiB,WAAWA,CAA+B,GAG3E,KAAK,WAAW,iBAAiB,SAAS,CAAC1B,MAAM;AAC/C,MAAAA,EAAE,gBAAA;AACF,YAAM2B,IAAS3B,EAAE;AAEjB,UAAI2B,EAAO,QAAQ,aAAa,GAAG;AACjC,cAAMhC,IAASgC,EAAO,QAAQ,aAAa,EAAkB,QAAQ;AACrE,QAAIhC,KAAO,KAAK,eAAeA,CAAK;AAAA,MACtC,WAAWgC,EAAO,QAAQ,SAAS,GAAG;AACpC,cAAMhC,IAASgC,EAAO,QAAQ,SAAS,EAAkB,QAAQ;AACjE,QAAIhC,KAAO,KAAK,aAAaA,CAAK;AAAA,MACpC,MAAA,CAAWgC,EAAO,QAAQ,iBAAiB,KACzC,KAAK,OAAA;AAAA,IAET,CAAC,GAGD,KAAK,WAAW,iBAAiB,aAAa,CAAC3B,MAAM;AAEnD,UAAI,KAAK,mBAAoB;AAE7B,YAAM2B,IAAS3B,EAAE;AACjB,UAAI2B,EAAO,QAAQ,SAAS,GAAG;AAC7B,cAAM7B,IAAS6B,EAAO,QAAQ,SAAS,GAEjCC,IADU,MAAM,KAAK,KAAK,WAAW,iBAAiB,SAAS,CAAC,EACtC,QAAQ9B,CAAM;AAG9C,YAAI,KAAK,iBAAiB8B,GAAiB;AAEzC,gBAAMC,IAAiB,KAAK,WAAW,cAAc,iBAAiB;AACtE,UAAIA,KACFA,EAAe,UAAU,OAAO,SAAS,GAI3C/B,EAAO,UAAU,IAAI,SAAS,GAC9B,KAAK,eAAe8B;AAAA,QACtB;AAAA,MACF;AAAA,IACF,CAAC,GAGD,KAAK,WAAW,iBAAiB,cAAc,CAAC5B,MAAM;AAEpD,UAAI,KAAK,mBAAoB;AAG7B,UADeA,EAAE,OACN,QAAQ,WAAW,GAAG;AAC/B,cAAM6B,IAAiB,KAAK,WAAW,cAAc,iBAAiB;AACtE,QAAIA,KACFA,EAAe,UAAU,OAAO,SAAS,GAE3C,KAAK,eAAe;AAAA,MACtB;AAAA,IACF,CAAC,GAGD,KAAK,WAAW,iBAAiB,SAAS,CAAC7B,MAAM;AAC/C,YAAM2B,IAAS3B,EAAE;AACjB,MAAI2B,EAAO,UAAU,SAAS,cAAc,KAC1C,KAAK,aAAaA,EAAO,KAAK;AAAA,IAElC,CAAC,GAGD,SAAS,iBAAiB,SAAS,CAAC3B,MAAM;AACxC,MAAK,KAAK,SAASA,EAAE,MAAc,KACjC,KAAK,MAAA;AAAA,IAET,CAAC,GAGD,OAAO,iBAAiB,UAAU,MAAM;AACtC,MAAI,KAAK,UACP,KAAK,wBAAA;AAAA,IAET,CAAC,GAED,OAAO,iBAAiB,UAAU,MAAM;AACtC,MAAI,KAAK,UACP,KAAK,wBAAA;AAAA,IAET,GAAG,EAAI;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,SAAe;AAErB,IAAI,KAAK,gBAAgB,WAAW,KAAK,KAAK,QAAQ,SAAS,MAC7D,KAAK,kBAAkB,CAAC,GAAG,KAAK,OAAO;AAIzC,UAAM8B,IAAmB,KAAK,WAAW,cAAc,eAAe,MAAM,KAAK,WAAW,eAEtFC,IAAc,KAAK,gBAAgB,SAAS,IAC7C,KAAK,WACJ,GAAG,KAAK,gBAAgB,MAAM,cAC9B,KAAK,gBAAgB,CAAC,EAAE,QAC1B,KAAK;AAET,SAAK,WAAW,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAqJlB,KAAK,YAAY,KAAK,gBAAgB,SAAS,IAC7C,KAAK,gBAAgB,IAAI,CAAAjC,MAAU;AAAA;AAAA,sBAE7BA,EAAO,KAAK;AAAA,2DACyBA,EAAO,KAAK;AAAA;AAAA,iBAEtD,EAAE,KAAK,EAAE,IACV,SAASiC,CAAW,SACxB;AAAA;AAAA,8BAEkB,KAAK,SAAS,SAAS,EAAE;AAAA;AAAA;AAAA,UAG7C,KAAK,SAAS;AAAA;AAAA,cAEV,KAAK,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA,yBAKP,KAAK,WAAW;AAAA;AAAA,gBAEzB,EAAE;AAAA;AAAA,cAEJ,KAAK,gBAAgB,SAAS,IAC5B,KAAK,gBAAgB,IAAI,CAACjC,GAAQkC,MAAU;AAAA;AAAA,oCAExB,KAAK,gBAAgB,KAAK,CAAAC,MAAYA,EAAS,UAAUnC,EAAO,KAAK,IAAI,aAAa,EAAE,IAAIkC,MAAU,KAAK,eAAe,YAAY,EAAE;AAAA,kCAC1IlC,EAAO,KAAK;AAAA;AAAA,sBAExBA,EAAO,KAAK;AAAA;AAAA,iBAEjB,EAAE,KAAK,EAAE,IACV,oDACJ;AAAA;AAAA,YAEA,EAAE;AAAA;AAAA,OAKNgC,KAAoB,KAAK,cAAc,KAAK,UAC9C,sBAAsB,MAAM;AAC1B,YAAM5B,IAAc,KAAK,WAAW,cAAc,eAAe;AACjE,MAAIA,MACFA,EAAY,MAAA,GAEZA,EAAY,kBAAkBA,EAAY,MAAM,QAAQA,EAAY,MAAM,MAAM;AAAA,IAEpF,CAAC;AAAA,EAEL;AACF;AAKA,MAAMgC,IAAoB,CAACC,IAAkB,mBAAyB;AACpE,EAAI,OAAO,SAAW,OAAe,CAAC,OAAO,eAAe,IAAIA,CAAO,KACrE,eAAe,OAAOA,GAAS5C,CAAkB;AAErD;AAGA2C,EAAA;"}
|
|
1
|
+
{"version":3,"file":"SmartSelect.js","sources":["../src/SmartSelect.ts"],"sourcesContent":["/**\n * SmartSelect Web Component\n * A customizable select dropdown with search, multi-select, and keyboard navigation\n */\n\nexport type SelectOption = {\n value: string;\n label: string;\n};\n\nexport class SmartSelectElement extends HTMLElement {\n declare shadowRoot: ShadowRoot;\n private isOpen: boolean = false;\n private selectedOptions: SelectOption[] = [];\n private filteredOptions: SelectOption[] = [];\n private focusedIndex: number = -1;\n private searchValue: string = '';\n private keyboardNavigating: boolean = false;\n private keyboardTimer?: number;\n\n constructor () {\n super();\n this.attachShadow( { mode: 'open' } );\n\n // Make component focusable\n if ( !this.hasAttribute( 'tabindex' ) ) {\n this.setAttribute( 'tabindex', '0' );\n }\n\n this.render();\n this.bindEvents();\n }\n\n static get observedAttributes (): string[] {\n return [ 'multiple', 'searchable', 'placeholder', 'disabled', 'value', 'options' ];\n }\n\n attributeChangedCallback ( name: string, oldValue: string | null, newValue: string | null ): void {\n if ( oldValue !== newValue ) {\n if ( name === 'options' ) {\n this.filteredOptions = [ ...this.options ];\n }\n this.render();\n }\n }\n\n get multiple (): boolean {\n return this.hasAttribute( 'multiple' );\n }\n\n set multiple ( value: boolean ) {\n if ( value ) {\n this.setAttribute( 'multiple', '' );\n } else {\n this.removeAttribute( 'multiple' );\n }\n }\n\n get searchable (): boolean {\n return this.hasAttribute( 'searchable' );\n }\n\n set searchable ( value: boolean ) {\n if ( value ) {\n this.setAttribute( 'searchable', '' );\n } else {\n this.removeAttribute( 'searchable' );\n }\n }\n\n get placeholder (): string {\n return this.getAttribute( 'placeholder' ) || 'Select an option';\n }\n\n set placeholder ( value: string ) {\n this.setAttribute( 'placeholder', value );\n }\n\n get disabled (): boolean {\n return this.hasAttribute( 'disabled' );\n }\n\n set disabled ( value: boolean ) {\n if ( value ) {\n this.setAttribute( 'disabled', '' );\n } else {\n this.removeAttribute( 'disabled' );\n }\n }\n\n get value (): string | string[] {\n if ( this.multiple ) {\n return this.selectedOptions.map( opt => opt.value );\n }\n return this.selectedOptions.length > 0 ? this.selectedOptions[ 0 ].value : '';\n }\n\n set value ( val: string | string[] ) {\n if ( this.multiple && Array.isArray( val ) ) {\n this.selectedOptions = this.options.filter( opt => val.includes( opt.value ) );\n } else {\n const option = this.options.find( opt => opt.value === val );\n this.selectedOptions = option ? [ option ] : [];\n }\n this.render();\n }\n\n get options (): SelectOption[] {\n const optionsAttr = this.getAttribute( 'options' );\n if ( optionsAttr ) {\n try {\n return JSON.parse( optionsAttr );\n } catch ( e ) {\n console.error( 'Invalid options format:', e );\n return [];\n }\n }\n return [];\n }\n\n set options ( opts: SelectOption[] ) {\n this.setAttribute( 'options', JSON.stringify( opts ) );\n }\n\n /**\n * Opens the dropdown\n */\n open (): void {\n if ( this.disabled ) return;\n this.isOpen = true;\n this.focusedIndex = -1;\n if ( this.options.length > 0 ) {\n this.filteredOptions = [ ...this.options ];\n }\n this.render();\n\n // Update dropdown position based on viewport\n this._updateDropdownPosition();\n\n // Focus search input if searchable\n if ( this.searchable ) {\n requestAnimationFrame( () => {\n const searchInput = this.shadowRoot.querySelector( '.search-input' ) as HTMLInputElement;\n if ( searchInput ) {\n searchInput.focus();\n }\n } );\n }\n\n this.dispatchEvent( new CustomEvent( 'open' ) );\n }\n\n /**\n * Closes the dropdown\n */\n close (): void {\n this.isOpen = false;\n this.focusedIndex = -1;\n this.searchValue = '';\n\n // Reset filtered options when closing\n if ( this.searchable && this.options.length > 0 ) {\n this.filteredOptions = [ ...this.options ];\n }\n\n // Clear any inline positioning styles\n const dropdown = this.shadowRoot.querySelector( '.dropdown' ) as HTMLElement;\n if ( dropdown ) {\n dropdown.style.top = '';\n dropdown.style.left = '';\n dropdown.style.width = '';\n dropdown.style.maxHeight = '';\n }\n\n this.render();\n this.dispatchEvent( new CustomEvent( 'close' ) );\n }\n\n /**\n * Toggles the dropdown open/closed state\n */\n toggle (): void {\n if ( this.isOpen ) {\n this.close();\n } else {\n this.open();\n }\n }\n\n /**\n * Selects an option by its value\n */\n selectOption ( value: string ): void {\n const option = this.options.find( opt => opt.value === value );\n if ( !option ) return;\n\n if ( this.multiple ) {\n if ( !this.selectedOptions.find( opt => opt.value === value ) ) {\n this.selectedOptions.push( option );\n }\n } else {\n this.selectedOptions = [ option ];\n this.close();\n }\n\n this.render();\n this.dispatchEvent( new CustomEvent( 'change', { detail: { value: this.value } } ) );\n }\n\n /**\n * Deselects an option by its value\n */\n deselectOption ( value: string ): void {\n this.selectedOptions = this.selectedOptions.filter( opt => opt.value !== value );\n this.render();\n this.dispatchEvent( new CustomEvent( 'change', { detail: { value: this.value } } ) );\n }\n\n /**\n * Returns an array of currently selected options\n */\n getSelectedOptions (): SelectOption[] {\n return [ ...this.selectedOptions ];\n }\n\n /**\n * Sets the options for the select component\n */\n setOptions ( options: SelectOption[] ): void {\n this.options = options;\n this.filteredOptions = [ ...options ];\n this.selectedOptions = [];\n this.render();\n }\n\n /**\n * Handles search functionality\n */\n private handleSearch ( query: string ): void {\n this.searchValue = query;\n this.filteredOptions = this.options.filter( option =>\n option.label.toLowerCase().includes( query.toLowerCase() )\n );\n this.focusedIndex = -1;\n this.render();\n this.dispatchEvent( new CustomEvent( 'search', { detail: { query } } ) );\n }\n\n /**\n * Updates the visual focus state without full re-render\n */\n private updateFocusedOption (): void {\n const options = this.shadowRoot.querySelectorAll( '.option' );\n\n // Remove focused class from all options\n options.forEach( option => option.classList.remove( 'focused' ) );\n\n // Add focused class to current option\n if ( this.focusedIndex >= 0 && this.focusedIndex < options.length ) {\n options[ this.focusedIndex ].classList.add( 'focused' );\n }\n\n this.scrollToFocusedOption();\n }\n\n /**\n * Scrolls the focused option into view\n */\n private scrollToFocusedOption (): void {\n if ( this.focusedIndex < 0 ) return;\n\n requestAnimationFrame( () => {\n const dropdown = this.shadowRoot.querySelector( '.dropdown' ) as HTMLElement;\n const focusedOption = this.shadowRoot.querySelector( '.option.focused' ) as HTMLElement;\n\n if ( dropdown && focusedOption ) {\n const dropdownRect = dropdown.getBoundingClientRect();\n const optionRect = focusedOption.getBoundingClientRect();\n\n // Check if option is above visible area\n if ( optionRect.top < dropdownRect.top ) {\n dropdown.scrollTop -= ( dropdownRect.top - optionRect.top );\n }\n // Check if option is below visible area\n else if ( optionRect.bottom > dropdownRect.bottom ) {\n dropdown.scrollTop += ( optionRect.bottom - dropdownRect.bottom );\n }\n }\n } );\n }\n\n /**\n * Calculates the optimal dropdown position based on viewport constraints\n */\n private _calculateDropdownPosition (): { top: number; left: number; width: number; maxHeight: number; } | null {\n const trigger = this.shadowRoot.querySelector( '.select-trigger' ) as HTMLElement;\n if ( !trigger ) return null;\n\n const triggerRect = trigger.getBoundingClientRect();\n const viewportHeight = window.innerHeight;\n const viewportWidth = window.innerWidth;\n const dropdownMaxHeight = 200;\n const dropdownPadding = 10;\n const margin = 2;\n\n // Calculate available space\n const spaceBelow = viewportHeight - triggerRect.bottom;\n const spaceAbove = triggerRect.top;\n\n // Determine if dropdown should open upward\n const shouldOpenUpward = spaceBelow < dropdownMaxHeight + dropdownPadding && spaceAbove > spaceBelow;\n\n // Calculate dimensions\n const width = triggerRect.width;\n const left = Math.max( 0, Math.min( triggerRect.left, viewportWidth - width ) );\n\n let top: number;\n let maxHeight: number;\n\n if ( shouldOpenUpward ) {\n // Position above the trigger\n maxHeight = Math.min( dropdownMaxHeight, spaceAbove - dropdownPadding );\n top = triggerRect.top - maxHeight - margin;\n } else {\n // Position below the trigger\n maxHeight = Math.min( dropdownMaxHeight, spaceBelow - dropdownPadding );\n top = triggerRect.bottom + margin;\n }\n\n return {\n top: Math.max( 0, top ),\n left,\n width,\n maxHeight: Math.max( 100, maxHeight ) // Ensure minimum height\n };\n }\n\n /**\n * Updates dropdown position using fixed positioning relative to viewport\n */\n private _updateDropdownPosition (): void {\n requestAnimationFrame( () => {\n const dropdown = this.shadowRoot.querySelector( '.dropdown' ) as HTMLElement;\n if ( !dropdown ) return;\n\n const position = this._calculateDropdownPosition();\n if ( !position ) return;\n\n // Apply calculated position as inline styles\n dropdown.style.top = `${ position.top }px`;\n dropdown.style.left = `${ position.left }px`;\n dropdown.style.width = `${ position.width }px`;\n dropdown.style.maxHeight = `${ position.maxHeight }px`;\n } );\n }\n\n /**\n * Handles keyboard navigation\n */\n private handleKeydown ( event: KeyboardEvent ): void {\n if ( this.disabled ) return;\n\n // Prevent double execution if event has already been handled\n if ( ( event as any )._smartSelectHandled ) return;\n ( event as any )._smartSelectHandled = true;\n\n switch ( event.key ) {\n case 'ArrowDown':\n event.preventDefault();\n this.keyboardNavigating = true;\n clearTimeout( this.keyboardTimer );\n this.keyboardTimer = window.setTimeout( () => { this.keyboardNavigating = false; }, 100 );\n\n if ( !this.isOpen ) {\n this.open();\n } else {\n // If searchable and search input is focused, move to first option\n const searchInput = this.shadowRoot.querySelector( '.search-input' ) as HTMLInputElement;\n const isSearchFocused = this.searchable && searchInput === this.shadowRoot.activeElement;\n\n if ( isSearchFocused ) {\n this.focusedIndex = 0;\n searchInput.blur(); // Blur search input to allow normal navigation\n // Focus the component to ensure it receives keyboard events\n this.focus();\n this.updateFocusedOption();\n return;\n }\n // Navigate through options\n const newIndex = Math.min( this.focusedIndex + 1, this.filteredOptions.length - 1 );\n this.focusedIndex = newIndex;\n this.updateFocusedOption();\n }\n break;\n\n case 'ArrowUp':\n event.preventDefault();\n this.keyboardNavigating = true;\n clearTimeout( this.keyboardTimer );\n this.keyboardTimer = window.setTimeout( () => { this.keyboardNavigating = false; }, 100 );\n\n if ( this.isOpen ) {\n // If at first option and searchable, focus search input\n if ( this.focusedIndex === 0 && this.searchable ) {\n this.focusedIndex = -1;\n this.updateFocusedOption();\n requestAnimationFrame( () => {\n const searchInput = this.shadowRoot.querySelector( '.search-input' ) as HTMLInputElement;\n if ( searchInput ) {\n searchInput.focus();\n searchInput.setSelectionRange( searchInput.value.length, searchInput.value.length );\n }\n } );\n return;\n }\n // If searchable and search input is focused, do nothing\n const searchInput = this.shadowRoot.querySelector( '.search-input' ) as HTMLInputElement;\n const isSearchFocused = this.searchable && searchInput === this.shadowRoot.activeElement;\n\n if ( isSearchFocused ) {\n return;\n }\n // Navigate through options\n const newIndex = Math.max( this.focusedIndex - 1, -1 );\n this.focusedIndex = newIndex;\n this.updateFocusedOption();\n }\n break;\n\n case 'Enter':\n event.preventDefault();\n if ( this.isOpen && this.focusedIndex >= 0 && this.focusedIndex < this.filteredOptions.length ) {\n this.selectOption( this.filteredOptions[ this.focusedIndex ].value );\n } else if ( !this.isOpen ) {\n this.open();\n }\n break;\n\n case 'Escape':\n event.preventDefault();\n this.close();\n break;\n\n case 'Tab':\n this.close();\n break;\n }\n }\n\n /**\n * Binds all event listeners\n */\n private bindEvents (): void {\n // Listen for keydown events on both the component and shadow root\n const keydownHandler = this.handleKeydown.bind( this );\n this.addEventListener( 'keydown', keydownHandler );\n this.shadowRoot.addEventListener( 'keydown', keydownHandler as EventListener );\n\n // Use event delegation on the shadow root\n this.shadowRoot.addEventListener( 'click', ( e ) => {\n e.stopPropagation();\n const target = e.target as HTMLElement;\n\n if ( target.closest( '.remove-tag' ) ) {\n const value = ( target.closest( '.remove-tag' ) as HTMLElement ).dataset.value;\n if ( value ) this.deselectOption( value );\n } else if ( target.closest( '.option' ) ) {\n const value = ( target.closest( '.option' ) as HTMLElement ).dataset.value;\n if ( value ) this.selectOption( value );\n } else if ( target.closest( '.select-trigger' ) ) {\n this.toggle();\n }\n } );\n\n // Handle mouse hover on options to update focused index\n this.shadowRoot.addEventListener( 'mouseover', ( e ) => {\n // Don't interfere with keyboard navigation\n if ( this.keyboardNavigating ) return;\n\n const target = e.target as HTMLElement;\n if ( target.closest( '.option' ) ) {\n const option = target.closest( '.option' ) as HTMLElement;\n const options = Array.from( this.shadowRoot.querySelectorAll( '.option' ) );\n const newFocusedIndex = options.indexOf( option );\n\n // Only update if the focused index actually changed\n if ( this.focusedIndex !== newFocusedIndex ) {\n // Remove focused class from current option\n const currentFocused = this.shadowRoot.querySelector( '.option.focused' );\n if ( currentFocused ) {\n currentFocused.classList.remove( 'focused' );\n }\n\n // Add focused class to new option\n option.classList.add( 'focused' );\n this.focusedIndex = newFocusedIndex;\n }\n }\n } );\n\n // Handle mouse leaving dropdown to clear focus\n this.shadowRoot.addEventListener( 'mouseleave', ( e ) => {\n // Don't interfere with keyboard navigation\n if ( this.keyboardNavigating ) return;\n\n const target = e.target as HTMLElement;\n if ( target.closest( '.dropdown' ) ) {\n const currentFocused = this.shadowRoot.querySelector( '.option.focused' );\n if ( currentFocused ) {\n currentFocused.classList.remove( 'focused' );\n }\n this.focusedIndex = -1;\n }\n } );\n\n // Handle search input\n this.shadowRoot.addEventListener( 'input', ( e ) => {\n const target = e.target as HTMLInputElement;\n if ( target.classList.contains( 'search-input' ) ) {\n this.handleSearch( target.value );\n }\n } );\n\n // Close dropdown when clicking outside\n document.addEventListener( 'click', ( e ) => {\n if ( !this.contains( e.target as Node ) ) {\n this.close();\n }\n } );\n\n // Update dropdown position on window resize or scroll\n window.addEventListener( 'resize', () => {\n if ( this.isOpen ) {\n this._updateDropdownPosition();\n }\n } );\n\n window.addEventListener( 'scroll', () => {\n if ( this.isOpen ) {\n this._updateDropdownPosition();\n }\n }, true ); // Use capture to catch all scroll events\n }\n\n /**\n * Renders the component\n */\n private render (): void {\n // Initialize filteredOptions if not set\n if ( this.filteredOptions.length === 0 && this.options.length > 0 ) {\n this.filteredOptions = [ ...this.options ];\n }\n\n // Remember if search input was focused before render\n const wasSearchFocused = this.shadowRoot.querySelector( '.search-input' ) === this.shadowRoot.activeElement;\n\n const displayText = this.selectedOptions.length > 0\n ? ( this.multiple\n ? `${ this.selectedOptions.length } selected`\n : this.selectedOptions[ 0 ].label )\n : this.placeholder;\n\n this.shadowRoot.innerHTML = `\n <style>\n :host {\n display: inline-block;\n position: relative;\n min-width: 200px;\n font-family: var(--font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif);\n font-size: var(--font-size, 14px);\n outline: none;\n }\n\n :host(:focus) .select-trigger {\n border-color: var(--focus-color, #007bff);\n box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);\n }\n\n :host([disabled]) {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .select-container {\n position: relative;\n }\n\n .select-trigger {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--padding, 8px 12px);\n border: var(--border, 1px solid #ccc);\n border-radius: var(--border-radius, 4px);\n background: var(--background, white);\n cursor: pointer;\n min-height: 36px;\n box-sizing: border-box;\n color: #333;\n user-select: none;\n }\n\n .select-trigger:focus {\n outline: none;\n border-color: var(--focus-color, #007bff);\n box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);\n }\n\n .select-trigger[disabled] {\n cursor: not-allowed;\n }\n\n .selected-content {\n display: flex;\n align-items: center;\n gap: 4px;\n flex-wrap: wrap;\n flex: 1;\n }\n\n .tag {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 8px;\n background: var(--tag-background, #e9ecef);\n border-radius: var(--tag-border-radius, 12px);\n font-size: 12px;\n color: var(--tag-color, #495057);\n user-select: none;\n }\n\n .remove-tag {\n cursor: pointer;\n color: var(--remove-color, #6c757d);\n font-weight: bold;\n font-size: 14px;\n }\n\n .remove-tag:hover {\n color: var(--remove-hover-color, #dc3545);\n }\n\n .arrow {\n width: 0;\n height: 0;\n border-left: 5px solid transparent;\n border-right: 5px solid transparent;\n border-top: 5px solid var(--arrow-color, #666);\n transition: transform 0.2s;\n }\n\n .arrow.open {\n transform: rotate(180deg);\n }\n\n .dropdown {\n position: fixed;\n z-index: 99999;\n background: var(--dropdown-background, white);\n border: var(--dropdown-border, 1px solid #ccc);\n border-radius: var(--dropdown-border-radius, 4px);\n box-shadow: var(--dropdown-shadow, 0 2px 8px rgba(0, 0, 0, 0.1));\n max-height: 200px;\n overflow-y: auto;\n scroll-behavior: smooth;\n color: #333;\n }\n\n .search-input {\n width: 100%;\n padding: 8px 12px;\n border: none;\n border-bottom: 1px solid #eee;\n font-size: 14px;\n outline: none;\n box-sizing: border-box;\n }\n\n .option {\n padding: 8px 12px;\n cursor: pointer;\n color: var(--option-color, #333);\n transition: background-color 0.2s;\n user-select: none;\n }\n\n .option:hover {\n background-color: var(--option-hover-background, #f8f9fa);\n }\n\n .option.focused {\n background-color: var(--option-focused-background, #007bff);\n color: var(--option-focused-color, white);\n }\n\n .option.selected {\n background-color: var(--option-selected-background, #e3f2fd);\n color: var(--option-selected-color, #1976d2);\n }\n\n .no-options {\n padding: 8px 12px;\n color: var(--no-options-color, #6c757d);\n font-style: italic;\n }\n </style>\n\n <div class=\"select-container\">\n <div class=\"select-trigger\" tabindex=\"-1\">\n <div class=\"selected-content\">\n ${ this.multiple && this.selectedOptions.length > 0\n ? this.selectedOptions.map( option => `\n <span class=\"tag\">\n ${ option.label }\n <span class=\"remove-tag\" data-value=\"${ option.value }\">×</span>\n </span>\n `).join( '' )\n : `<span>${ displayText }</span>`\n }\n </div>\n <div class=\"arrow ${ this.isOpen ? 'open' : '' }\"></div>\n </div>\n\n ${ this.isOpen ? `\n <div class=\"dropdown\">\n ${ this.searchable ? `\n <input\n type=\"text\"\n class=\"search-input\"\n placeholder=\"Search options...\"\n value=\"${ this.searchValue }\"\n >\n ` : '' }\n\n ${ this.filteredOptions.length > 0\n ? this.filteredOptions.map( ( option, index ) => `\n <div\n class=\"option ${ this.selectedOptions.find( selected => selected.value === option.value ) ? 'selected' : '' } ${ index === this.focusedIndex ? 'focused' : '' }\"\n data-value=\"${ option.value }\"\n >\n ${ option.label }\n </div>\n `).join( '' )\n : '<div class=\"no-options\">No options available</div>'\n }\n </div>\n ` : '' }\n </div>\n `;\n\n // Re-focus search input if it was previously focused\n if ( wasSearchFocused && this.searchable && this.isOpen ) {\n requestAnimationFrame( () => {\n const searchInput = this.shadowRoot.querySelector( '.search-input' ) as HTMLInputElement;\n if ( searchInput ) {\n searchInput.focus();\n // Restore cursor position to the end\n searchInput.setSelectionRange( searchInput.value.length, searchInput.value.length );\n }\n } );\n }\n }\n}\n\n/**\n * Conditionally defines the custom element if in a browser environment.\n */\nconst defineSmartSelect = ( tagName: string = 'liwe3-select' ): void => {\n if ( typeof window !== 'undefined' && !window.customElements.get( tagName ) ) {\n customElements.define( tagName, SmartSelectElement );\n }\n};\n\n// Auto-register with default tag name\ndefineSmartSelect();\n\nexport { defineSmartSelect };\n"],"names":["SmartSelectElement","name","oldValue","newValue","value","opt","val","option","optionsAttr","e","opts","searchInput","dropdown","options","query","focusedOption","dropdownRect","optionRect","trigger","triggerRect","viewportHeight","viewportWidth","dropdownMaxHeight","dropdownPadding","margin","spaceBelow","spaceAbove","shouldOpenUpward","width","left","top","maxHeight","position","event","newIndex","keydownHandler","target","newFocusedIndex","currentFocused","wasSearchFocused","displayText","index","selected","defineSmartSelect","tagName"],"mappings":"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"}
|