@smilodon/core 1.4.12 → 1.4.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -15
- package/dist/index.cjs +926 -453
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +926 -453
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/index.umd.js +926 -453
- package/dist/index.umd.js.map +1 -1
- package/dist/index.umd.min.js +1 -1
- package/dist/index.umd.min.js.map +1 -1
- package/dist/types/src/components/enhanced-select.d.ts +5 -0
- package/dist/types/src/config/global-config.d.ts +2 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1351,6 +1351,7 @@ const defaultConfig = {
|
|
|
1351
1351
|
maxSelections: 0,
|
|
1352
1352
|
showRemoveButton: true,
|
|
1353
1353
|
closeOnSelect: true,
|
|
1354
|
+
toggleOnTriggerClick: true,
|
|
1354
1355
|
},
|
|
1355
1356
|
scrollToSelected: {
|
|
1356
1357
|
enabled: true,
|
|
@@ -1883,6 +1884,8 @@ if (!customElements.get('select-option')) {
|
|
|
1883
1884
|
* Enhanced Select Component
|
|
1884
1885
|
* Implements all advanced features: infinite scroll, load more, busy state,
|
|
1885
1886
|
* server-side selection, and full customization
|
|
1887
|
+
*
|
|
1888
|
+
* ✨ Redesigned with formal elegance, refined microinteractions, and polished UX
|
|
1886
1889
|
*/
|
|
1887
1890
|
class EnhancedSelect extends HTMLElement {
|
|
1888
1891
|
get classMap() {
|
|
@@ -1930,6 +1933,8 @@ class EnhancedSelect extends HTMLElement {
|
|
|
1930
1933
|
this._globalStylesObserver = null;
|
|
1931
1934
|
this._globalStylesContainer = null;
|
|
1932
1935
|
this._tracking = { events: [], styles: [], limitations: [] };
|
|
1936
|
+
this._suppressBlurClose = false;
|
|
1937
|
+
this._renderCycleId = 0;
|
|
1933
1938
|
this._shadow = this.attachShadow({ mode: 'open' });
|
|
1934
1939
|
this._uniqueId = `enhanced-select-${Math.random().toString(36).substr(2, 9)}`;
|
|
1935
1940
|
this._rendererHelpers = this._buildRendererHelpers();
|
|
@@ -2004,6 +2009,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2004
2009
|
if (this._boundArrowClick && this._arrowContainer) {
|
|
2005
2010
|
this._arrowContainer.removeEventListener('click', this._boundArrowClick);
|
|
2006
2011
|
}
|
|
2012
|
+
this._renderCycleId += 1;
|
|
2007
2013
|
this._teardownGlobalStylesMirroring();
|
|
2008
2014
|
}
|
|
2009
2015
|
_setGlobalStylesMirroring(enabled) {
|
|
@@ -2107,8 +2113,48 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2107
2113
|
const container = document.createElement('div');
|
|
2108
2114
|
container.className = 'input-container';
|
|
2109
2115
|
container.setAttribute('part', 'button');
|
|
2116
|
+
const inputStyles = this._config.styles.input;
|
|
2117
|
+
if (inputStyles && !this._config.styles.container) {
|
|
2118
|
+
const shellStyleKeys = [
|
|
2119
|
+
'background',
|
|
2120
|
+
'backgroundColor',
|
|
2121
|
+
'border',
|
|
2122
|
+
'borderColor',
|
|
2123
|
+
'borderStyle',
|
|
2124
|
+
'borderWidth',
|
|
2125
|
+
'borderRadius',
|
|
2126
|
+
'boxShadow',
|
|
2127
|
+
'padding',
|
|
2128
|
+
'height',
|
|
2129
|
+
'minHeight',
|
|
2130
|
+
'maxHeight',
|
|
2131
|
+
];
|
|
2132
|
+
for (const key of shellStyleKeys) {
|
|
2133
|
+
const value = inputStyles[key];
|
|
2134
|
+
if (value != null && value !== '') {
|
|
2135
|
+
container.style[key] = value;
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2110
2139
|
return container;
|
|
2111
2140
|
}
|
|
2141
|
+
_syncInputContainerMode() {
|
|
2142
|
+
if (!this._inputContainer || !this._input)
|
|
2143
|
+
return;
|
|
2144
|
+
const isMulti = this._config.selection.mode === 'multi';
|
|
2145
|
+
this._inputContainer.classList.toggle('input-container--multi', isMulti);
|
|
2146
|
+
this._inputContainer.classList.toggle('input-container--single', !isMulti);
|
|
2147
|
+
if (isMulti) {
|
|
2148
|
+
this._input.style.flex = '1 0 var(--select-multi-input-min-width, 96px)';
|
|
2149
|
+
this._input.style.width = 'auto';
|
|
2150
|
+
this._input.style.minWidth = 'var(--select-multi-input-min-width, 96px)';
|
|
2151
|
+
}
|
|
2152
|
+
else {
|
|
2153
|
+
this._input.style.flex = '1 1 auto';
|
|
2154
|
+
this._input.style.width = '100%';
|
|
2155
|
+
this._input.style.minWidth = '0';
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2112
2158
|
_createInput() {
|
|
2113
2159
|
const input = document.createElement('input');
|
|
2114
2160
|
input.setAttribute('part', 'input');
|
|
@@ -2118,6 +2164,35 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2118
2164
|
input.placeholder = this._config.placeholder || 'Select an option...';
|
|
2119
2165
|
input.disabled = !this._config.enabled;
|
|
2120
2166
|
input.readOnly = !this._config.searchable;
|
|
2167
|
+
// Apply a direct inline reset so the input is only the writable text layer,
|
|
2168
|
+
// while the `.input-container` remains the sole visible control shell.
|
|
2169
|
+
input.style.all = 'unset';
|
|
2170
|
+
input.style.display = 'block';
|
|
2171
|
+
input.style.flex = '1 1 auto';
|
|
2172
|
+
input.style.width = '100%';
|
|
2173
|
+
input.style.maxWidth = '100%';
|
|
2174
|
+
input.style.minWidth = '0';
|
|
2175
|
+
input.style.minInlineSize = '0';
|
|
2176
|
+
input.style.minHeight = '0';
|
|
2177
|
+
input.style.padding = '0';
|
|
2178
|
+
input.style.margin = '0';
|
|
2179
|
+
input.style.border = '0';
|
|
2180
|
+
input.style.background = 'transparent';
|
|
2181
|
+
input.style.boxSizing = 'border-box';
|
|
2182
|
+
input.style.outline = 'none';
|
|
2183
|
+
input.style.font = 'inherit';
|
|
2184
|
+
input.style.fontFamily = 'inherit';
|
|
2185
|
+
input.style.lineHeight = 'inherit';
|
|
2186
|
+
input.style.color = 'inherit';
|
|
2187
|
+
input.style.alignSelf = 'center';
|
|
2188
|
+
input.style.appearance = 'none';
|
|
2189
|
+
input.style.webkitAppearance = 'none';
|
|
2190
|
+
input.style.boxShadow = 'none';
|
|
2191
|
+
input.style.borderRadius = '0';
|
|
2192
|
+
input.style.overflow = 'hidden';
|
|
2193
|
+
input.style.textOverflow = 'ellipsis';
|
|
2194
|
+
input.style.whiteSpace = 'nowrap';
|
|
2195
|
+
input.style.cursor = this._config.searchable ? 'text' : 'default';
|
|
2121
2196
|
// Update readonly when input is focused if searchable
|
|
2122
2197
|
input.addEventListener('focus', () => {
|
|
2123
2198
|
if (this._config.searchable) {
|
|
@@ -2128,7 +2203,22 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2128
2203
|
input.className += ' ' + this._config.styles.classNames.input;
|
|
2129
2204
|
}
|
|
2130
2205
|
if (this._config.styles.input) {
|
|
2131
|
-
|
|
2206
|
+
const inputStyles = { ...this._config.styles.input };
|
|
2207
|
+
// Route shell-like styling to .input-container so the control appears as
|
|
2208
|
+
// a single styled input instead of two visually separate layers.
|
|
2209
|
+
delete inputStyles.background;
|
|
2210
|
+
delete inputStyles.backgroundColor;
|
|
2211
|
+
delete inputStyles.border;
|
|
2212
|
+
delete inputStyles.borderColor;
|
|
2213
|
+
delete inputStyles.borderStyle;
|
|
2214
|
+
delete inputStyles.borderWidth;
|
|
2215
|
+
delete inputStyles.borderRadius;
|
|
2216
|
+
delete inputStyles.boxShadow;
|
|
2217
|
+
delete inputStyles.padding;
|
|
2218
|
+
delete inputStyles.height;
|
|
2219
|
+
delete inputStyles.minHeight;
|
|
2220
|
+
delete inputStyles.maxHeight;
|
|
2221
|
+
Object.assign(input.style, inputStyles);
|
|
2132
2222
|
}
|
|
2133
2223
|
input.setAttribute('role', 'combobox');
|
|
2134
2224
|
input.setAttribute('aria-expanded', 'false');
|
|
@@ -2172,7 +2262,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2172
2262
|
container.className = 'dropdown-arrow-container';
|
|
2173
2263
|
container.innerHTML = `
|
|
2174
2264
|
<svg class="dropdown-arrow" part="arrow" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2175
|
-
<path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="
|
|
2265
|
+
<path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
2176
2266
|
</svg>
|
|
2177
2267
|
`;
|
|
2178
2268
|
return container;
|
|
@@ -2185,7 +2275,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2185
2275
|
const icon = document.createElement('span');
|
|
2186
2276
|
icon.className = 'clear-control-icon';
|
|
2187
2277
|
icon.setAttribute('part', 'clear-icon');
|
|
2188
|
-
icon.
|
|
2278
|
+
icon.innerHTML = `<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 4L4 12M4 4L12 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
|
|
2189
2279
|
button.setAttribute('aria-label', this._config.clearControl.ariaLabel || 'Clear selection and search');
|
|
2190
2280
|
button.appendChild(icon);
|
|
2191
2281
|
this._clearControlIcon = icon;
|
|
@@ -2211,18 +2301,229 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2211
2301
|
this._dropdown.id = listboxId;
|
|
2212
2302
|
this._input.setAttribute('aria-controls', listboxId);
|
|
2213
2303
|
this._input.setAttribute('aria-owns', listboxId);
|
|
2304
|
+
this._syncInputContainerMode();
|
|
2214
2305
|
this._syncClearControlState();
|
|
2215
2306
|
}
|
|
2216
2307
|
_initializeStyles() {
|
|
2217
2308
|
const style = document.createElement('style');
|
|
2218
2309
|
style.textContent = `
|
|
2310
|
+
/* ═══════════════════════════════════════════════════════════════════════════
|
|
2311
|
+
ELEGANT SELECT COMPONENT — Refined Design System
|
|
2312
|
+
Formal aesthetics with sophisticated microinteractions
|
|
2313
|
+
═══════════════════════════════════════════════════════════════════════════ */
|
|
2314
|
+
|
|
2219
2315
|
:host {
|
|
2316
|
+
--select-primary: #1a1a2e;
|
|
2317
|
+
--select-primary-light: #16213e;
|
|
2318
|
+
--select-accent: #0f3460;
|
|
2319
|
+
--select-accent-hover: #e94560;
|
|
2320
|
+
--select-surface: #ffffff;
|
|
2321
|
+
--select-surface-elevated: #fafbfc;
|
|
2322
|
+
--select-border: #e1e5eb;
|
|
2323
|
+
--select-border-focus: #0f3460;
|
|
2324
|
+
--select-text: #1a1a2e;
|
|
2325
|
+
--select-text-muted: #6b7280;
|
|
2326
|
+
--select-text-placeholder: #9ca3af;
|
|
2327
|
+
--select-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.04);
|
|
2328
|
+
--select-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08);
|
|
2329
|
+
--select-shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.12);
|
|
2330
|
+
--select-shadow-focus: 0 0 0 3px rgba(15, 52, 96, 0.12);
|
|
2331
|
+
--select-radius-sm: 6px;
|
|
2332
|
+
--select-radius-md: 10px;
|
|
2333
|
+
--select-radius-lg: 14px;
|
|
2334
|
+
--select-transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
2335
|
+
--select-transition-smooth: 250ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
2336
|
+
--select-transition-bounce: 350ms cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
2337
|
+
--select-badge-animation: badgeEnter 300ms cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
|
|
2338
|
+
--select-badge-enter-from-opacity: 0;
|
|
2339
|
+
--select-badge-enter-from-transform: scale(0.8) translateY(-4px);
|
|
2340
|
+
--select-badge-enter-to-opacity: 1;
|
|
2341
|
+
--select-badge-enter-to-transform: scale(1) translateY(0);
|
|
2342
|
+
--select-badge-hover-transform: scale(1.02);
|
|
2343
|
+
--select-badge-letter-spacing: 0.01em;
|
|
2344
|
+
--select-badge-hover-shadow: var(--select-shadow-md), inset 0 1px 0 rgba(255, 255, 255, 0.15);
|
|
2345
|
+
--select-badge-remove-focus-outline: 2px solid rgba(255, 255, 255, 0.5);
|
|
2346
|
+
--select-badge-remove-focus-offset: 1px;
|
|
2347
|
+
--select-badge-remove-hover-transform: scale(1.15) rotate(90deg);
|
|
2348
|
+
--select-badge-remove-active-transform: scale(0.95) rotate(90deg);
|
|
2349
|
+
--select-input-hover-border: var(--select-border-focus);
|
|
2350
|
+
--select-input-hover-shadow: var(--select-shadow-sm), 0 0 0 1px rgba(15, 52, 96, 0.05);
|
|
2351
|
+
--select-input-font-weight: 450;
|
|
2352
|
+
--select-input-letter-spacing: 0.01em;
|
|
2353
|
+
--select-input-disabled-opacity: 0.6;
|
|
2354
|
+
--select-separator-opacity: 0.6;
|
|
2355
|
+
--select-separator-active-opacity: 1;
|
|
2356
|
+
--select-separator-dark-bg: linear-gradient(
|
|
2357
|
+
to bottom,
|
|
2358
|
+
transparent 0%,
|
|
2359
|
+
var(--select-border) 20%,
|
|
2360
|
+
var(--select-border) 80%,
|
|
2361
|
+
transparent 100%
|
|
2362
|
+
);
|
|
2363
|
+
--select-arrow-open-transform: rotate(180deg);
|
|
2364
|
+
--select-clear-button-hover-transform: translateY(-50%) scale(1.1);
|
|
2365
|
+
--select-clear-button-active-transform: translateY(-50%) scale(0.95);
|
|
2366
|
+
--select-clear-button-focus-offset: 2px;
|
|
2367
|
+
--select-clear-icon-size: 14px;
|
|
2368
|
+
--select-dropdown-top: calc(100% + 6px);
|
|
2369
|
+
--select-dropdown-animation: dropdownEnter 200ms cubic-bezier(0.4, 0, 0.2, 1) forwards;
|
|
2370
|
+
--select-dropdown-enter-from-opacity: 0;
|
|
2371
|
+
--select-dropdown-enter-from-transform: translateY(-8px) scale(0.98);
|
|
2372
|
+
--select-dropdown-enter-to-opacity: 1;
|
|
2373
|
+
--select-dropdown-enter-to-transform: translateY(0) scale(1);
|
|
2374
|
+
--select-dropdown-scroll-behavior: smooth;
|
|
2375
|
+
--select-dropdown-transform-origin: top center;
|
|
2376
|
+
--select-scrollbar-width: 6px;
|
|
2377
|
+
--select-scrollbar-thumb-radius: 3px;
|
|
2378
|
+
--select-option-hover-transform: translateX(2px);
|
|
2379
|
+
--select-option-font-weight: 450;
|
|
2380
|
+
--select-option-margin: 2px 0;
|
|
2381
|
+
--select-option-active-outline-offset: -2px;
|
|
2382
|
+
--select-option-selected-active-outline-offset: -2px;
|
|
2383
|
+
--select-option-selected-indicator-width: 3px;
|
|
2384
|
+
--select-option-selected-indicator-height: 60%;
|
|
2385
|
+
--select-option-selected-indicator-bg: var(--select-accent);
|
|
2386
|
+
--select-option-selected-indicator-radius: 0 2px 2px 0;
|
|
2387
|
+
--select-option-selected-indicator-left: 0;
|
|
2388
|
+
--select-option-selected-indicator-top: 50%;
|
|
2389
|
+
--select-option-selected-indicator-transform: translateY(-50%);
|
|
2390
|
+
--select-option-selected-indicator-animation: selectedIndicator 200ms cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
|
|
2391
|
+
--select-option-selected-indicator-from-height: 0;
|
|
2392
|
+
--select-option-selected-indicator-to-height: 60%;
|
|
2393
|
+
--select-option-selected-indicator-from-opacity: 0;
|
|
2394
|
+
--select-option-selected-indicator-to-opacity: 1;
|
|
2395
|
+
--select-option-pressed-transform: translateX(2px) scale(0.99);
|
|
2396
|
+
--select-option-selected-pressed-transform: scale(0.99);
|
|
2397
|
+
--select-button-hover-transform: translateY(-1px);
|
|
2398
|
+
--select-button-active-transform: translateY(0) scale(0.98);
|
|
2399
|
+
--select-button-font-weight: 550;
|
|
2400
|
+
--select-button-letter-spacing: 0.02em;
|
|
2401
|
+
--select-spinner-animation: spin 0.8s cubic-bezier(0.4, 0, 0.2, 1) infinite;
|
|
2402
|
+
--select-searching-spinner-active-color: var(--select-accent);
|
|
2403
|
+
--select-badge-remove-margin-left: 2px;
|
|
2404
|
+
--select-badge-remove-radius: 50%;
|
|
2405
|
+
--select-badge-remove-font-weight: 600;
|
|
2406
|
+
--select-empty-gap: 8px;
|
|
2407
|
+
--select-searching-gap: 8px;
|
|
2408
|
+
--select-searching-spinner-size: 24px;
|
|
2409
|
+
--select-searching-spinner-border: 2px solid var(--select-border);
|
|
2410
|
+
--select-searching-spinner-animation: var(--select-spinner-animation);
|
|
2411
|
+
--select-group-header-separator-margin-top: 8px;
|
|
2412
|
+
--select-group-header-separator-padding-top: 14px;
|
|
2413
|
+
--select-group-header-separator-border-top: 1px solid var(--select-border);
|
|
2414
|
+
--select-reduced-motion-duration: 0.01ms;
|
|
2415
|
+
--select-reduced-motion-iteration-count: 1;
|
|
2416
|
+
--select-high-contrast-border-width: 2px;
|
|
2417
|
+
--select-high-contrast-indicator-width: 4px;
|
|
2418
|
+
--select-high-contrast-focus-outline-width: 3px;
|
|
2419
|
+
--select-high-contrast-focus-outline-color: Highlight;
|
|
2420
|
+
--select-touch-target-min-height: 44px;
|
|
2421
|
+
--select-badge-dark-bg: linear-gradient(135deg, var(--select-accent) 0%, #4f46e5 100%);
|
|
2422
|
+
|
|
2220
2423
|
display: block;
|
|
2221
2424
|
position: relative;
|
|
2222
2425
|
width: var(--select-width, 100%);
|
|
2223
2426
|
height: var(--select-height, auto);
|
|
2427
|
+
font-family: var(--select-font-family, 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif);
|
|
2428
|
+
}
|
|
2429
|
+
|
|
2430
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
2431
|
+
Selection Badges — Refined pill design with elegant interactions
|
|
2432
|
+
───────────────────────────────────────────────────────────────────────── */
|
|
2433
|
+
|
|
2434
|
+
.selection-badge {
|
|
2435
|
+
display: inline-flex;
|
|
2436
|
+
align-items: center;
|
|
2437
|
+
gap: var(--select-badge-gap, 6px);
|
|
2438
|
+
flex: 0 1 auto;
|
|
2439
|
+
min-height: var(--select-badge-min-height, 26px);
|
|
2440
|
+
padding: var(--select-badge-padding, 4px 10px 4px 12px);
|
|
2441
|
+
margin: var(--select-badge-margin, 3px);
|
|
2442
|
+
background: var(--select-badge-bg, linear-gradient(135deg, var(--select-accent) 0%, var(--select-primary-light) 100%));
|
|
2443
|
+
color: var(--select-badge-color, #ffffff);
|
|
2444
|
+
border: var(--select-badge-border, none);
|
|
2445
|
+
border-radius: var(--select-badge-border-radius, 999px);
|
|
2446
|
+
box-shadow: var(--select-badge-shadow, var(--select-shadow-sm), inset 0 1px 0 rgba(255, 255, 255, 0.1));
|
|
2447
|
+
box-sizing: border-box;
|
|
2448
|
+
font-size: var(--select-badge-font-size, 13px);
|
|
2449
|
+
font-weight: var(--select-badge-font-weight, 500);
|
|
2450
|
+
line-height: var(--select-badge-line-height, 1.2);
|
|
2451
|
+
letter-spacing: var(--select-badge-letter-spacing);
|
|
2452
|
+
max-width: var(--select-badge-max-width, 100%);
|
|
2453
|
+
overflow: hidden;
|
|
2454
|
+
transform: scale(1);
|
|
2455
|
+
transition:
|
|
2456
|
+
transform var(--select-transition-bounce),
|
|
2457
|
+
box-shadow var(--select-transition-fast),
|
|
2458
|
+
background var(--select-transition-fast);
|
|
2459
|
+
animation: var(--select-badge-animation);
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2462
|
+
@keyframes badgeEnter {
|
|
2463
|
+
0% {
|
|
2464
|
+
opacity: var(--select-badge-enter-from-opacity);
|
|
2465
|
+
transform: var(--select-badge-enter-from-transform);
|
|
2466
|
+
}
|
|
2467
|
+
100% {
|
|
2468
|
+
opacity: var(--select-badge-enter-to-opacity);
|
|
2469
|
+
transform: var(--select-badge-enter-to-transform);
|
|
2470
|
+
}
|
|
2224
2471
|
}
|
|
2225
2472
|
|
|
2473
|
+
.selection-badge:hover {
|
|
2474
|
+
box-shadow: var(--select-badge-hover-shadow);
|
|
2475
|
+
transform: var(--select-badge-hover-transform);
|
|
2476
|
+
}
|
|
2477
|
+
|
|
2478
|
+
.selection-badge-label {
|
|
2479
|
+
display: block;
|
|
2480
|
+
min-width: 0;
|
|
2481
|
+
overflow: hidden;
|
|
2482
|
+
text-overflow: ellipsis;
|
|
2483
|
+
white-space: nowrap;
|
|
2484
|
+
line-height: var(--select-badge-line-height, 1.2);
|
|
2485
|
+
}
|
|
2486
|
+
|
|
2487
|
+
.badge-remove {
|
|
2488
|
+
display: inline-flex;
|
|
2489
|
+
align-items: center;
|
|
2490
|
+
justify-content: center;
|
|
2491
|
+
width: var(--select-badge-remove-size, 18px);
|
|
2492
|
+
height: var(--select-badge-remove-size, 18px);
|
|
2493
|
+
padding: 0;
|
|
2494
|
+
margin-left: var(--select-badge-remove-margin-left);
|
|
2495
|
+
background: var(--select-badge-remove-bg, rgba(255, 255, 255, 0.2));
|
|
2496
|
+
border: var(--select-badge-remove-border, none);
|
|
2497
|
+
border-radius: var(--select-badge-remove-radius);
|
|
2498
|
+
color: var(--select-badge-remove-color, #ffffff);
|
|
2499
|
+
font-size: var(--select-badge-remove-font-size, 11px);
|
|
2500
|
+
font-weight: var(--select-badge-remove-font-weight);
|
|
2501
|
+
line-height: 1;
|
|
2502
|
+
cursor: pointer;
|
|
2503
|
+
flex: 0 0 auto;
|
|
2504
|
+
transition:
|
|
2505
|
+
background var(--select-transition-fast),
|
|
2506
|
+
transform var(--select-transition-bounce);
|
|
2507
|
+
}
|
|
2508
|
+
|
|
2509
|
+
.badge-remove:hover {
|
|
2510
|
+
background: var(--select-badge-remove-hover-bg, rgba(233, 69, 96, 0.9));
|
|
2511
|
+
transform: var(--select-badge-remove-hover-transform);
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2514
|
+
.badge-remove:focus-visible {
|
|
2515
|
+
outline: var(--select-badge-remove-focus-outline);
|
|
2516
|
+
outline-offset: var(--select-badge-remove-focus-offset);
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2519
|
+
.badge-remove:active {
|
|
2520
|
+
transform: var(--select-badge-remove-active-transform);
|
|
2521
|
+
}
|
|
2522
|
+
|
|
2523
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
2524
|
+
Input Container — Sophisticated control shell
|
|
2525
|
+
───────────��───────────────────────────────────────────────────────────── */
|
|
2526
|
+
|
|
2226
2527
|
.select-container {
|
|
2227
2528
|
position: relative;
|
|
2228
2529
|
width: 100%;
|
|
@@ -2233,102 +2534,143 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2233
2534
|
width: 100%;
|
|
2234
2535
|
display: flex;
|
|
2235
2536
|
align-items: center;
|
|
2236
|
-
flex-wrap:
|
|
2537
|
+
flex-wrap: nowrap;
|
|
2237
2538
|
gap: var(--select-input-gap, 6px);
|
|
2238
|
-
padding: var(--select-input-padding,
|
|
2539
|
+
padding: var(--select-input-padding, 10px 52px 10px 14px);
|
|
2239
2540
|
height: var(--select-input-height, auto);
|
|
2240
|
-
min-height: var(--select-input-min-height,
|
|
2541
|
+
min-height: var(--select-input-min-height, 48px);
|
|
2241
2542
|
max-height: var(--select-input-max-height, 160px);
|
|
2242
2543
|
overflow-y: var(--select-input-overflow-y, auto);
|
|
2243
|
-
align-content:
|
|
2244
|
-
background: var(--select-input-bg, var(--select-
|
|
2245
|
-
border: var(--select-input-border,
|
|
2246
|
-
border-radius: var(--select-input-border-radius,
|
|
2544
|
+
align-content: center;
|
|
2545
|
+
background: var(--select-input-bg, var(--select-surface));
|
|
2546
|
+
border: var(--select-input-border, 1.5px solid var(--select-border));
|
|
2547
|
+
border-radius: var(--select-input-border-radius, var(--select-radius-md));
|
|
2548
|
+
box-shadow: var(--select-shadow-sm);
|
|
2247
2549
|
box-sizing: border-box;
|
|
2248
|
-
transition:
|
|
2550
|
+
transition:
|
|
2551
|
+
border-color var(--select-transition-fast),
|
|
2552
|
+
box-shadow var(--select-transition-smooth),
|
|
2553
|
+
transform var(--select-transition-fast);
|
|
2554
|
+
}
|
|
2555
|
+
|
|
2556
|
+
.input-container:hover {
|
|
2557
|
+
border-color: var(--select-input-hover-border);
|
|
2558
|
+
box-shadow: var(--select-input-hover-shadow);
|
|
2559
|
+
}
|
|
2560
|
+
|
|
2561
|
+
.input-container.input-container--single {
|
|
2562
|
+
flex-wrap: nowrap;
|
|
2563
|
+
align-content: center;
|
|
2564
|
+
}
|
|
2565
|
+
|
|
2566
|
+
.input-container.input-container--multi {
|
|
2567
|
+
flex-wrap: wrap;
|
|
2568
|
+
align-content: flex-start;
|
|
2249
2569
|
}
|
|
2250
2570
|
|
|
2251
2571
|
.input-container:focus-within {
|
|
2252
|
-
border-color: var(--select-input-focus-border, var(--select-border-focus
|
|
2253
|
-
box-shadow: var(--select-
|
|
2572
|
+
border-color: var(--select-input-focus-border, var(--select-border-focus));
|
|
2573
|
+
box-shadow: var(--select-shadow-focus), var(--select-shadow-sm);
|
|
2254
2574
|
}
|
|
2255
2575
|
|
|
2256
|
-
/*
|
|
2576
|
+
/* Elegant separator line before arrow */
|
|
2257
2577
|
.input-container::after {
|
|
2258
2578
|
content: '';
|
|
2259
2579
|
position: absolute;
|
|
2260
2580
|
top: 50%;
|
|
2261
|
-
right: var(--select-separator-position,
|
|
2581
|
+
right: var(--select-separator-position, 42px);
|
|
2262
2582
|
transform: translateY(-50%);
|
|
2263
|
-
width: var(--select-separator-width,
|
|
2264
|
-
height: var(--select-separator-height,
|
|
2265
|
-
background: var(--select-separator-bg,
|
|
2583
|
+
width: var(--select-separator-width, 1px);
|
|
2584
|
+
height: var(--select-separator-height, 50%);
|
|
2585
|
+
background: var(--select-separator-bg, linear-gradient(
|
|
2266
2586
|
to bottom,
|
|
2267
2587
|
transparent 0%,
|
|
2268
|
-
|
|
2269
|
-
|
|
2588
|
+
var(--select-border) 20%,
|
|
2589
|
+
var(--select-border) 80%,
|
|
2270
2590
|
transparent 100%
|
|
2271
|
-
))
|
|
2591
|
+
));
|
|
2272
2592
|
pointer-events: none;
|
|
2273
2593
|
z-index: 1;
|
|
2594
|
+
opacity: var(--select-separator-opacity);
|
|
2595
|
+
transition: opacity var(--select-transition-fast);
|
|
2274
2596
|
}
|
|
2275
2597
|
|
|
2598
|
+
.input-container:hover::after,
|
|
2599
|
+
.input-container:focus-within::after {
|
|
2600
|
+
opacity: var(--select-separator-active-opacity);
|
|
2601
|
+
}
|
|
2602
|
+
|
|
2603
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
2604
|
+
Dropdown Arrow — Smooth rotation with refined styling
|
|
2605
|
+
───────────────────────────────────────────────────────────────────────── */
|
|
2606
|
+
|
|
2276
2607
|
.dropdown-arrow-container {
|
|
2277
2608
|
position: absolute;
|
|
2278
2609
|
top: 0;
|
|
2279
2610
|
right: 0;
|
|
2280
2611
|
bottom: 0;
|
|
2281
|
-
width: var(--select-arrow-width,
|
|
2282
|
-
/* allow explicit height override even though container normally stretches */
|
|
2612
|
+
width: var(--select-arrow-width, 42px);
|
|
2283
2613
|
height: var(--select-arrow-height, auto);
|
|
2284
2614
|
display: flex;
|
|
2285
2615
|
align-items: center;
|
|
2286
2616
|
justify-content: center;
|
|
2287
2617
|
cursor: pointer;
|
|
2288
|
-
|
|
2289
|
-
border-radius: var(--select-arrow-border-radius, 0 4px 4px 0);
|
|
2618
|
+
border-radius: var(--select-arrow-border-radius, 0 var(--select-radius-md) var(--select-radius-md) 0);
|
|
2290
2619
|
z-index: 2;
|
|
2620
|
+
transition: background-color var(--select-transition-fast);
|
|
2291
2621
|
}
|
|
2292
2622
|
|
|
2293
2623
|
.input-container.has-clear-control {
|
|
2294
|
-
padding: var(--select-input-padding-with-clear,
|
|
2624
|
+
padding: var(--select-input-padding-with-clear, 10px 84px 10px 14px);
|
|
2295
2625
|
}
|
|
2296
2626
|
|
|
2297
2627
|
.input-container.has-clear-control::after {
|
|
2298
|
-
right: var(--select-separator-position-with-clear,
|
|
2628
|
+
right: var(--select-separator-position-with-clear, 74px);
|
|
2299
2629
|
}
|
|
2300
2630
|
|
|
2301
2631
|
.dropdown-arrow-container.with-clear-control {
|
|
2302
|
-
right: var(--select-arrow-right-with-clear,
|
|
2632
|
+
right: var(--select-arrow-right-with-clear, 34px);
|
|
2303
2633
|
}
|
|
2304
2634
|
|
|
2635
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
2636
|
+
Clear Control — Minimal and elegant dismiss button
|
|
2637
|
+
───────────────────────────────────────────────────────────────────────── */
|
|
2638
|
+
|
|
2305
2639
|
.clear-control-button {
|
|
2306
2640
|
position: absolute;
|
|
2307
2641
|
top: 50%;
|
|
2308
|
-
right: var(--select-clear-button-right,
|
|
2642
|
+
right: var(--select-clear-button-right, 8px);
|
|
2309
2643
|
transform: translateY(-50%);
|
|
2310
|
-
width: var(--select-clear-button-size,
|
|
2311
|
-
height: var(--select-clear-button-size,
|
|
2644
|
+
width: var(--select-clear-button-size, 26px);
|
|
2645
|
+
height: var(--select-clear-button-size, 26px);
|
|
2312
2646
|
border: var(--select-clear-button-border, none);
|
|
2313
|
-
border-radius: var(--select-clear-button-radius,
|
|
2647
|
+
border-radius: var(--select-clear-button-radius, 50%);
|
|
2314
2648
|
background: var(--select-clear-button-bg, transparent);
|
|
2315
|
-
color: var(--select-clear-button-color,
|
|
2649
|
+
color: var(--select-clear-button-color, var(--select-text-muted));
|
|
2316
2650
|
display: inline-flex;
|
|
2317
2651
|
align-items: center;
|
|
2318
2652
|
justify-content: center;
|
|
2319
2653
|
cursor: pointer;
|
|
2320
2654
|
z-index: 3;
|
|
2321
|
-
transition:
|
|
2655
|
+
transition:
|
|
2656
|
+
background var(--select-transition-fast),
|
|
2657
|
+
color var(--select-transition-fast),
|
|
2658
|
+
transform var(--select-transition-bounce);
|
|
2322
2659
|
}
|
|
2323
2660
|
|
|
2324
2661
|
.clear-control-button:hover {
|
|
2325
|
-
background: var(--select-clear-button-hover-bg, rgba(
|
|
2326
|
-
color: var(--select-clear-button-hover-color,
|
|
2662
|
+
background: var(--select-clear-button-hover-bg, rgba(233, 69, 96, 0.1));
|
|
2663
|
+
color: var(--select-clear-button-hover-color, var(--select-accent-hover));
|
|
2664
|
+
transform: var(--select-clear-button-hover-transform);
|
|
2665
|
+
}
|
|
2666
|
+
|
|
2667
|
+
.clear-control-button:active {
|
|
2668
|
+
transform: var(--select-clear-button-active-transform);
|
|
2327
2669
|
}
|
|
2328
2670
|
|
|
2329
2671
|
.clear-control-button:focus-visible {
|
|
2330
|
-
outline: var(--select-clear-button-focus-outline, 2px solid
|
|
2331
|
-
outline-offset:
|
|
2672
|
+
outline: var(--select-clear-button-focus-outline, 2px solid var(--select-border-focus));
|
|
2673
|
+
outline-offset: var(--select-clear-button-focus-offset);
|
|
2332
2674
|
}
|
|
2333
2675
|
|
|
2334
2676
|
.clear-control-button[hidden] {
|
|
@@ -2336,260 +2678,332 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2336
2678
|
}
|
|
2337
2679
|
|
|
2338
2680
|
.clear-control-icon {
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
font-weight: var(--select-clear-icon-weight, 500);
|
|
2681
|
+
width: var(--select-clear-icon-size);
|
|
2682
|
+
height: var(--select-clear-icon-size);
|
|
2342
2683
|
pointer-events: none;
|
|
2343
2684
|
}
|
|
2344
2685
|
|
|
2345
|
-
.
|
|
2346
|
-
|
|
2686
|
+
.clear-control-icon svg {
|
|
2687
|
+
width: 100%;
|
|
2688
|
+
height: 100%;
|
|
2347
2689
|
}
|
|
2348
2690
|
|
|
2349
|
-
.dropdown-arrow:hover {
|
|
2350
|
-
|
|
2351
|
-
color: var(--select-arrow-hover, var(--select-arrow-hover-color, #667eea));
|
|
2691
|
+
.dropdown-arrow-container:hover {
|
|
2692
|
+
background-color: var(--select-arrow-hover-bg, rgba(15, 52, 96, 0.05));
|
|
2352
2693
|
}
|
|
2353
2694
|
|
|
2354
2695
|
.dropdown-arrow {
|
|
2355
|
-
width: var(--select-arrow-
|
|
2356
|
-
height: var(--select-arrow-
|
|
2357
|
-
color: var(--select-arrow-color,
|
|
2358
|
-
transition:
|
|
2359
|
-
|
|
2696
|
+
width: var(--select-arrow-size, 18px);
|
|
2697
|
+
height: var(--select-arrow-size, 18px);
|
|
2698
|
+
color: var(--select-arrow-color, var(--select-text-muted));
|
|
2699
|
+
transition:
|
|
2700
|
+
transform var(--select-transition-smooth),
|
|
2701
|
+
color var(--select-transition-fast);
|
|
2702
|
+
transform-origin: center;
|
|
2360
2703
|
}
|
|
2361
2704
|
|
|
2362
2705
|
.dropdown-arrow path {
|
|
2363
|
-
stroke-width: var(--select-arrow-stroke-width,
|
|
2706
|
+
stroke-width: var(--select-arrow-stroke-width, 1.5);
|
|
2364
2707
|
}
|
|
2365
2708
|
|
|
2366
2709
|
.dropdown-arrow-container:hover .dropdown-arrow {
|
|
2367
|
-
color: var(--select-arrow-hover-color,
|
|
2710
|
+
color: var(--select-arrow-hover-color, var(--select-accent));
|
|
2368
2711
|
}
|
|
2369
2712
|
|
|
2370
2713
|
.dropdown-arrow.open {
|
|
2371
|
-
transform:
|
|
2714
|
+
transform: var(--select-arrow-open-transform);
|
|
2372
2715
|
}
|
|
2373
2716
|
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2717
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
2718
|
+
Input Field — Clean text entry
|
|
2719
|
+
───────────────────────────────────────────────────────────────────────── */
|
|
2720
|
+
|
|
2721
|
+
.input-container > .select-input,
|
|
2722
|
+
input.select-input,
|
|
2723
|
+
.select-input[type="text"] {
|
|
2724
|
+
display: block;
|
|
2725
|
+
flex: 1 1 auto;
|
|
2726
|
+
width: var(--select-input-width, 100%);
|
|
2727
|
+
max-width: 100%;
|
|
2728
|
+
min-width: var(--select-input-min-width, 0);
|
|
2729
|
+
min-inline-size: 0;
|
|
2730
|
+
min-height: 0;
|
|
2731
|
+
padding: var(--select-input-field-padding, 0) !important;
|
|
2732
|
+
margin: 0 !important;
|
|
2733
|
+
border: 0 !important;
|
|
2734
|
+
font-size: var(--select-input-font-size, 15px);
|
|
2381
2735
|
line-height: var(--select-input-line-height, 1.5);
|
|
2382
|
-
color: var(--select-input-color, var(--select-text
|
|
2383
|
-
background: transparent;
|
|
2736
|
+
color: var(--select-input-color, var(--select-text));
|
|
2737
|
+
background: transparent !important;
|
|
2384
2738
|
box-sizing: border-box;
|
|
2385
|
-
outline: none;
|
|
2386
|
-
font-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
.selection-badge {
|
|
2394
|
-
display: inline-flex;
|
|
2395
|
-
align-items: center;
|
|
2396
|
-
gap: var(--select-badge-gap, 4px);
|
|
2397
|
-
padding: var(--select-badge-padding, 4px 8px);
|
|
2398
|
-
margin: var(--select-badge-margin, 2px);
|
|
2399
|
-
background: var(--select-badge-bg, #667eea);
|
|
2400
|
-
color: var(--select-badge-color, white);
|
|
2401
|
-
border-radius: var(--select-badge-border-radius, 4px);
|
|
2402
|
-
font-size: var(--select-badge-font-size, 13px);
|
|
2403
|
-
line-height: 1;
|
|
2404
|
-
max-width: var(--select-badge-max-width, 100%);
|
|
2405
|
-
white-space: nowrap;
|
|
2739
|
+
outline: none !important;
|
|
2740
|
+
font-weight: var(--select-input-font-weight);
|
|
2741
|
+
letter-spacing: var(--select-input-letter-spacing);
|
|
2742
|
+
align-self: center;
|
|
2743
|
+
appearance: none !important;
|
|
2744
|
+
-webkit-appearance: none !important;
|
|
2745
|
+
box-shadow: none !important;
|
|
2746
|
+
border-radius: 0 !important;
|
|
2406
2747
|
overflow: hidden;
|
|
2407
2748
|
text-overflow: ellipsis;
|
|
2749
|
+
white-space: nowrap;
|
|
2408
2750
|
}
|
|
2409
2751
|
|
|
2410
|
-
.
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
justify-content: center;
|
|
2414
|
-
width: var(--select-badge-remove-size, 16px);
|
|
2415
|
-
height: var(--select-badge-remove-size, 16px);
|
|
2416
|
-
padding: 0;
|
|
2417
|
-
margin-left: 4px;
|
|
2418
|
-
background: var(--select-badge-remove-bg, rgba(255, 255, 255, 0.3));
|
|
2419
|
-
border: none;
|
|
2420
|
-
border-radius: 50%;
|
|
2421
|
-
color: var(--select-badge-remove-color, white);
|
|
2422
|
-
font-size: var(--select-badge-remove-font-size, 16px);
|
|
2423
|
-
line-height: 1;
|
|
2424
|
-
cursor: pointer;
|
|
2425
|
-
transition: background 0.2s;
|
|
2752
|
+
.select-input::placeholder {
|
|
2753
|
+
color: var(--select-input-placeholder-color, var(--select-text-placeholder));
|
|
2754
|
+
font-weight: 400;
|
|
2426
2755
|
}
|
|
2427
|
-
|
|
2428
|
-
.
|
|
2429
|
-
|
|
2756
|
+
|
|
2757
|
+
.input-container.input-container--multi > .select-input,
|
|
2758
|
+
.input-container.input-container--multi > input.select-input,
|
|
2759
|
+
.input-container.input-container--multi > .select-input[type="text"] {
|
|
2760
|
+
width: auto;
|
|
2761
|
+
min-width: var(--select-multi-input-min-width, 96px);
|
|
2762
|
+
flex: 1 0 var(--select-multi-input-min-width, 96px);
|
|
2430
2763
|
}
|
|
2431
2764
|
|
|
2432
|
-
.
|
|
2433
|
-
|
|
2434
|
-
|
|
2765
|
+
.input-container.input-container--single > .select-input,
|
|
2766
|
+
.input-container.input-container--single > input.select-input,
|
|
2767
|
+
.input-container.input-container--single > .select-input[type="text"] {
|
|
2768
|
+
width: var(--select-input-width, 100%);
|
|
2769
|
+
min-width: 0;
|
|
2770
|
+
flex: 1 1 auto;
|
|
2435
2771
|
}
|
|
2436
2772
|
|
|
2437
2773
|
.select-input:disabled {
|
|
2438
2774
|
background-color: var(--select-disabled-bg, #f5f5f5);
|
|
2439
2775
|
cursor: not-allowed;
|
|
2776
|
+
opacity: var(--select-input-disabled-opacity);
|
|
2440
2777
|
}
|
|
2441
2778
|
|
|
2779
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
2780
|
+
Dropdown Panel — Elegant floating container
|
|
2781
|
+
───────────────────────────────────────────────────────────────────────── */
|
|
2782
|
+
|
|
2442
2783
|
.select-dropdown {
|
|
2443
2784
|
position: absolute;
|
|
2444
|
-
scroll-behavior:
|
|
2445
|
-
top:
|
|
2785
|
+
scroll-behavior: var(--select-dropdown-scroll-behavior);
|
|
2786
|
+
top: var(--select-dropdown-top);
|
|
2446
2787
|
left: 0;
|
|
2447
2788
|
right: 0;
|
|
2448
|
-
|
|
2449
|
-
max-height: var(--select-dropdown-max-height, 300px);
|
|
2789
|
+
max-height: var(--select-dropdown-max-height, 320px);
|
|
2450
2790
|
overflow: hidden;
|
|
2451
|
-
background: var(--select-dropdown-bg, var(--select-
|
|
2452
|
-
border: 1px solid var(--select-dropdown-border,
|
|
2453
|
-
border-radius: var(--select-dropdown-border-radius,
|
|
2454
|
-
box-shadow: var(--select-dropdown-shadow,
|
|
2791
|
+
background: var(--select-dropdown-bg, var(--select-surface));
|
|
2792
|
+
border: 1px solid var(--select-dropdown-border, var(--select-border));
|
|
2793
|
+
border-radius: var(--select-dropdown-border-radius, var(--select-radius-lg));
|
|
2794
|
+
box-shadow: var(--select-dropdown-shadow, var(--select-shadow-lg));
|
|
2455
2795
|
z-index: var(--select-dropdown-z-index, 1000);
|
|
2796
|
+
transform-origin: var(--select-dropdown-transform-origin);
|
|
2797
|
+
animation: var(--select-dropdown-animation);
|
|
2798
|
+
}
|
|
2799
|
+
|
|
2800
|
+
@keyframes dropdownEnter {
|
|
2801
|
+
0% {
|
|
2802
|
+
opacity: var(--select-dropdown-enter-from-opacity);
|
|
2803
|
+
transform: var(--select-dropdown-enter-from-transform);
|
|
2804
|
+
}
|
|
2805
|
+
100% {
|
|
2806
|
+
opacity: var(--select-dropdown-enter-to-opacity);
|
|
2807
|
+
transform: var(--select-dropdown-enter-to-transform);
|
|
2808
|
+
}
|
|
2456
2809
|
}
|
|
2457
2810
|
|
|
2458
2811
|
.options-container {
|
|
2459
2812
|
position: relative;
|
|
2460
|
-
max-height: var(--select-options-max-height,
|
|
2813
|
+
max-height: var(--select-options-max-height, 320px);
|
|
2461
2814
|
overflow: auto;
|
|
2462
|
-
|
|
2463
|
-
background: var(--select-options-bg, var(--select-
|
|
2815
|
+
padding: var(--select-options-padding, 6px);
|
|
2816
|
+
background: var(--select-options-bg, var(--select-surface));
|
|
2817
|
+
|
|
2818
|
+
/* Custom scrollbar styling */
|
|
2819
|
+
scrollbar-width: thin;
|
|
2820
|
+
scrollbar-color: var(--select-border) transparent;
|
|
2821
|
+
}
|
|
2822
|
+
|
|
2823
|
+
.options-container::-webkit-scrollbar {
|
|
2824
|
+
width: var(--select-scrollbar-width);
|
|
2825
|
+
}
|
|
2826
|
+
|
|
2827
|
+
.options-container::-webkit-scrollbar-track {
|
|
2828
|
+
background: transparent;
|
|
2829
|
+
}
|
|
2830
|
+
|
|
2831
|
+
.options-container::-webkit-scrollbar-thumb {
|
|
2832
|
+
background: var(--select-border);
|
|
2833
|
+
border-radius: var(--select-scrollbar-thumb-radius);
|
|
2464
2834
|
}
|
|
2835
|
+
|
|
2836
|
+
.options-container::-webkit-scrollbar-thumb:hover {
|
|
2837
|
+
background: var(--select-text-muted);
|
|
2838
|
+
}
|
|
2839
|
+
|
|
2840
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
2841
|
+
Group Headers — Refined section dividers
|
|
2842
|
+
───────────────────────────────────────────────────────────────────────── */
|
|
2465
2843
|
|
|
2466
2844
|
.group-header {
|
|
2467
|
-
padding: var(--select-group-header-padding,
|
|
2845
|
+
padding: var(--select-group-header-padding, 10px 12px 6px);
|
|
2468
2846
|
font-weight: var(--select-group-header-weight, 600);
|
|
2469
|
-
color: var(--select-group-header-color,
|
|
2470
|
-
background-color: var(--select-group-header-bg,
|
|
2847
|
+
color: var(--select-group-header-color, var(--select-text-muted));
|
|
2848
|
+
background-color: var(--select-group-header-bg, var(--select-surface));
|
|
2471
2849
|
text-align: var(--select-group-header-text-align, left);
|
|
2472
|
-
font-size: var(--select-group-header-font-size,
|
|
2850
|
+
font-size: var(--select-group-header-font-size, 11px);
|
|
2473
2851
|
text-transform: var(--select-group-header-text-transform, uppercase);
|
|
2474
|
-
letter-spacing: var(--select-group-header-letter-spacing, 0.
|
|
2852
|
+
letter-spacing: var(--select-group-header-letter-spacing, 0.08em);
|
|
2475
2853
|
position: sticky;
|
|
2476
2854
|
top: 0;
|
|
2477
2855
|
z-index: 1;
|
|
2478
|
-
border-bottom: var(--select-group-header-border-bottom,
|
|
2856
|
+
border-bottom: var(--select-group-header-border-bottom, none);
|
|
2857
|
+
}
|
|
2858
|
+
|
|
2859
|
+
.group-header:not(:first-child) {
|
|
2860
|
+
margin-top: var(--select-group-header-separator-margin-top);
|
|
2861
|
+
padding-top: var(--select-group-header-separator-padding-top);
|
|
2862
|
+
border-top: var(--select-group-header-separator-border-top);
|
|
2479
2863
|
}
|
|
2480
2864
|
|
|
2865
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
2866
|
+
Options — Elegant hover and selection states
|
|
2867
|
+
───────────────────────────────────────────────────────────────────────── */
|
|
2868
|
+
|
|
2481
2869
|
.option {
|
|
2482
|
-
|
|
2870
|
+
position: relative;
|
|
2871
|
+
padding: var(--select-option-padding, 10px 14px);
|
|
2483
2872
|
cursor: pointer;
|
|
2484
|
-
color: var(--select-option-color, var(--select-text
|
|
2485
|
-
background: var(--select-option-bg,
|
|
2486
|
-
transition:
|
|
2873
|
+
color: var(--select-option-color, var(--select-text));
|
|
2874
|
+
background: var(--select-option-bg, transparent);
|
|
2875
|
+
transition:
|
|
2876
|
+
background var(--select-transition-fast),
|
|
2877
|
+
color var(--select-transition-fast),
|
|
2878
|
+
transform var(--select-transition-fast),
|
|
2879
|
+
box-shadow var(--select-transition-fast);
|
|
2487
2880
|
user-select: none;
|
|
2488
2881
|
font-size: var(--select-option-font-size, 14px);
|
|
2882
|
+
font-weight: var(--select-option-font-weight);
|
|
2489
2883
|
line-height: var(--select-option-line-height, 1.5);
|
|
2490
|
-
border: var(--select-option-border,
|
|
2491
|
-
|
|
2492
|
-
border-radius: var(--select-option-border-radius, 0);
|
|
2493
|
-
box-shadow: var(--select-option-shadow, none);
|
|
2494
|
-
transform: var(--select-option-transform, none);
|
|
2884
|
+
border-radius: var(--select-option-border-radius, var(--select-radius-sm));
|
|
2885
|
+
margin: var(--select-option-margin);
|
|
2495
2886
|
}
|
|
2496
2887
|
|
|
2497
2888
|
.option:hover {
|
|
2498
|
-
background: var(--select-option-hover-bg,
|
|
2499
|
-
color: var(--select-option-hover-color,
|
|
2889
|
+
background: var(--select-option-hover-bg, var(--select-surface-elevated));
|
|
2890
|
+
color: var(--select-option-hover-color, var(--select-text));
|
|
2891
|
+
transform: var(--select-option-hover-transform);
|
|
2500
2892
|
}
|
|
2501
2893
|
|
|
2502
2894
|
.option.selected {
|
|
2503
|
-
background: var(--select-option-selected-bg,
|
|
2504
|
-
color: var(--select-option-selected-color,
|
|
2505
|
-
font-weight: var(--select-option-selected-weight,
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2895
|
+
background: var(--select-option-selected-bg, linear-gradient(135deg, rgba(15, 52, 96, 0.08) 0%, rgba(15, 52, 96, 0.04) 100%));
|
|
2896
|
+
color: var(--select-option-selected-color, var(--select-accent));
|
|
2897
|
+
font-weight: var(--select-option-selected-weight, 550);
|
|
2898
|
+
}
|
|
2899
|
+
|
|
2900
|
+
.option.selected::before {
|
|
2901
|
+
content: '';
|
|
2902
|
+
position: absolute;
|
|
2903
|
+
left: var(--select-option-selected-indicator-left);
|
|
2904
|
+
top: var(--select-option-selected-indicator-top);
|
|
2905
|
+
transform: var(--select-option-selected-indicator-transform);
|
|
2906
|
+
width: var(--select-option-selected-indicator-width);
|
|
2907
|
+
height: var(--select-option-selected-indicator-height);
|
|
2908
|
+
background: var(--select-option-selected-indicator-bg);
|
|
2909
|
+
border-radius: var(--select-option-selected-indicator-radius);
|
|
2910
|
+
animation: var(--select-option-selected-indicator-animation);
|
|
2911
|
+
}
|
|
2912
|
+
|
|
2913
|
+
@keyframes selectedIndicator {
|
|
2914
|
+
0% {
|
|
2915
|
+
height: var(--select-option-selected-indicator-from-height);
|
|
2916
|
+
opacity: var(--select-option-selected-indicator-from-opacity);
|
|
2917
|
+
}
|
|
2918
|
+
100% {
|
|
2919
|
+
height: var(--select-option-selected-indicator-to-height);
|
|
2920
|
+
opacity: var(--select-option-selected-indicator-to-opacity);
|
|
2921
|
+
}
|
|
2511
2922
|
}
|
|
2512
2923
|
|
|
2513
2924
|
.option.selected:hover {
|
|
2514
|
-
background: var(--select-option-selected-hover-bg,
|
|
2515
|
-
color: var(--select-option-selected-hover-color, var(--select-option-selected-color, #4338ca));
|
|
2516
|
-
border: var(--select-option-selected-hover-border, var(--select-option-selected-border, var(--select-option-border, none)));
|
|
2517
|
-
border-bottom: var(--select-option-selected-hover-border-bottom, var(--select-option-selected-border-bottom, var(--select-option-border-bottom, none)));
|
|
2518
|
-
box-shadow: var(--select-option-selected-hover-shadow, var(--select-option-selected-shadow, var(--select-option-shadow, none)));
|
|
2519
|
-
transform: var(--select-option-selected-hover-transform, var(--select-option-selected-transform, var(--select-option-transform, none)));
|
|
2925
|
+
background: var(--select-option-selected-hover-bg, linear-gradient(135deg, rgba(15, 52, 96, 0.12) 0%, rgba(15, 52, 96, 0.06) 100%));
|
|
2520
2926
|
}
|
|
2521
2927
|
|
|
2522
2928
|
.option.active:not(.selected) {
|
|
2523
|
-
background: var(--select-option-active-bg,
|
|
2524
|
-
|
|
2525
|
-
outline: var(--select-option-active-outline
|
|
2526
|
-
outline-offset: -2px;
|
|
2929
|
+
background: var(--select-option-active-bg, var(--select-surface-elevated));
|
|
2930
|
+
outline: var(--select-option-active-outline, 2px solid rgba(15, 52, 96, 0.3));
|
|
2931
|
+
outline-offset: var(--select-option-active-outline-offset);
|
|
2527
2932
|
}
|
|
2528
2933
|
|
|
2529
2934
|
.option.selected.active {
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
border: var(--select-option-selected-active-border, var(--select-option-selected-border, var(--select-option-border, none)));
|
|
2533
|
-
border-bottom: var(--select-option-selected-active-border-bottom, var(--select-option-selected-border-bottom, var(--select-option-border-bottom, none)));
|
|
2534
|
-
box-shadow: var(--select-option-selected-active-shadow, var(--select-option-selected-shadow, var(--select-option-shadow, none)));
|
|
2535
|
-
transform: var(--select-option-selected-active-transform, var(--select-option-selected-transform, var(--select-option-transform, none)));
|
|
2536
|
-
outline: var(--select-option-selected-active-outline, var(--select-option-active-outline, 2px solid rgba(99, 102, 241, 0.45)));
|
|
2537
|
-
outline-offset: -2px;
|
|
2935
|
+
outline: var(--select-option-selected-active-outline, 2px solid rgba(15, 52, 96, 0.4));
|
|
2936
|
+
outline-offset: var(--select-option-selected-active-outline-offset);
|
|
2538
2937
|
}
|
|
2539
2938
|
|
|
2540
2939
|
.option:active:not(.selected) {
|
|
2541
|
-
background: var(--select-option-pressed-bg,
|
|
2940
|
+
background: var(--select-option-pressed-bg, rgba(15, 52, 96, 0.08));
|
|
2941
|
+
transform: var(--select-option-pressed-transform);
|
|
2542
2942
|
}
|
|
2543
2943
|
|
|
2544
2944
|
.option.selected:active {
|
|
2545
|
-
|
|
2945
|
+
transform: var(--select-option-selected-pressed-transform);
|
|
2546
2946
|
}
|
|
2547
2947
|
|
|
2948
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
2949
|
+
Load More & Busy States — Refined feedback indicators
|
|
2950
|
+
───────────────────────────────────────────────────────────────────────── */
|
|
2951
|
+
|
|
2548
2952
|
.load-more-container {
|
|
2549
2953
|
padding: var(--select-load-more-padding, 12px);
|
|
2550
2954
|
text-align: center;
|
|
2551
|
-
border-top: var(--select-divider-border, 1px solid #e0e0e0);
|
|
2552
|
-
background: var(--select-load-more-bg, white);
|
|
2553
2955
|
}
|
|
2554
2956
|
|
|
2555
2957
|
.load-more-button {
|
|
2556
|
-
padding: var(--select-button-padding,
|
|
2557
|
-
border: var(--select-button-border,
|
|
2558
|
-
background: var(--select-button-bg,
|
|
2559
|
-
color: var(--select-button-color,
|
|
2560
|
-
border-radius: var(--select-button-border-radius,
|
|
2958
|
+
padding: var(--select-button-padding, 10px 20px);
|
|
2959
|
+
border: var(--select-button-border, 1.5px solid var(--select-border));
|
|
2960
|
+
background: var(--select-button-bg, transparent);
|
|
2961
|
+
color: var(--select-button-color, var(--select-accent));
|
|
2962
|
+
border-radius: var(--select-button-border-radius, var(--select-radius-md));
|
|
2561
2963
|
cursor: pointer;
|
|
2562
|
-
font-size: var(--select-button-font-size,
|
|
2563
|
-
font-
|
|
2564
|
-
|
|
2964
|
+
font-size: var(--select-button-font-size, 13px);
|
|
2965
|
+
font-weight: var(--select-button-font-weight);
|
|
2966
|
+
letter-spacing: var(--select-button-letter-spacing);
|
|
2967
|
+
transition:
|
|
2968
|
+
background var(--select-transition-fast),
|
|
2969
|
+
border-color var(--select-transition-fast),
|
|
2970
|
+
color var(--select-transition-fast),
|
|
2971
|
+
transform var(--select-transition-bounce);
|
|
2565
2972
|
}
|
|
2566
2973
|
|
|
2567
2974
|
.load-more-button:hover {
|
|
2568
|
-
background: var(--select-button-hover-bg,
|
|
2975
|
+
background: var(--select-button-hover-bg, var(--select-accent));
|
|
2976
|
+
border-color: var(--select-accent);
|
|
2569
2977
|
color: var(--select-button-hover-color, white);
|
|
2978
|
+
transform: var(--select-button-hover-transform);
|
|
2979
|
+
}
|
|
2980
|
+
|
|
2981
|
+
.load-more-button:active {
|
|
2982
|
+
transform: var(--select-button-active-transform);
|
|
2570
2983
|
}
|
|
2571
2984
|
|
|
2572
2985
|
.load-more-button:disabled {
|
|
2573
2986
|
opacity: var(--select-button-disabled-opacity, 0.5);
|
|
2574
2987
|
cursor: not-allowed;
|
|
2988
|
+
transform: none;
|
|
2575
2989
|
}
|
|
2576
2990
|
|
|
2577
2991
|
.busy-bucket {
|
|
2578
|
-
padding: var(--select-busy-padding,
|
|
2992
|
+
padding: var(--select-busy-padding, 20px);
|
|
2579
2993
|
text-align: center;
|
|
2580
|
-
color: var(--select-busy-color,
|
|
2581
|
-
background: var(--select-busy-bg,
|
|
2582
|
-
font-size: var(--select-busy-font-size,
|
|
2994
|
+
color: var(--select-busy-color, var(--select-text-muted));
|
|
2995
|
+
background: var(--select-busy-bg, transparent);
|
|
2996
|
+
font-size: var(--select-busy-font-size, 13px);
|
|
2583
2997
|
}
|
|
2584
2998
|
|
|
2585
2999
|
.spinner {
|
|
2586
3000
|
display: inline-block;
|
|
2587
|
-
width: var(--select-spinner-size,
|
|
2588
|
-
height: var(--select-spinner-size,
|
|
2589
|
-
border: var(--select-spinner-border, 2px solid
|
|
2590
|
-
border-top-color: var(--select-spinner-active-color,
|
|
3001
|
+
width: var(--select-spinner-size, 22px);
|
|
3002
|
+
height: var(--select-spinner-size, 22px);
|
|
3003
|
+
border: var(--select-spinner-border, 2px solid var(--select-border));
|
|
3004
|
+
border-top-color: var(--select-spinner-active-color, var(--select-accent));
|
|
2591
3005
|
border-radius: 50%;
|
|
2592
|
-
animation:
|
|
3006
|
+
animation: var(--select-spinner-animation);
|
|
2593
3007
|
}
|
|
2594
3008
|
|
|
2595
3009
|
@keyframes spin {
|
|
@@ -2597,61 +3011,97 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2597
3011
|
}
|
|
2598
3012
|
|
|
2599
3013
|
.empty-state {
|
|
2600
|
-
padding: var(--select-empty-padding, 24px);
|
|
3014
|
+
padding: var(--select-empty-padding, 32px 24px);
|
|
2601
3015
|
text-align: center;
|
|
2602
|
-
color: var(--select-empty-color,
|
|
3016
|
+
color: var(--select-empty-color, var(--select-text-muted));
|
|
2603
3017
|
font-size: var(--select-empty-font-size, 14px);
|
|
2604
|
-
background: var(--select-empty-bg,
|
|
3018
|
+
background: var(--select-empty-bg, transparent);
|
|
2605
3019
|
display: flex;
|
|
2606
3020
|
flex-direction: column;
|
|
2607
3021
|
align-items: center;
|
|
2608
3022
|
justify-content: center;
|
|
2609
|
-
gap:
|
|
2610
|
-
min-height: var(--select-empty-min-height, 72px);
|
|
3023
|
+
gap: var(--select-empty-gap);
|
|
2611
3024
|
}
|
|
2612
3025
|
|
|
2613
3026
|
.searching-state {
|
|
2614
|
-
padding: var(--select-searching-padding, 24px);
|
|
3027
|
+
padding: var(--select-searching-padding, 32px 24px);
|
|
2615
3028
|
text-align: center;
|
|
2616
|
-
color: var(--select-searching-color,
|
|
3029
|
+
color: var(--select-searching-color, var(--select-accent));
|
|
2617
3030
|
font-size: var(--select-searching-font-size, 14px);
|
|
2618
|
-
|
|
2619
|
-
background: var(--select-searching-bg, white);
|
|
2620
|
-
animation: pulse 1.5s ease-in-out infinite;
|
|
3031
|
+
background: var(--select-searching-bg, transparent);
|
|
2621
3032
|
display: flex;
|
|
2622
3033
|
flex-direction: column;
|
|
2623
3034
|
align-items: center;
|
|
2624
3035
|
justify-content: center;
|
|
2625
|
-
gap:
|
|
2626
|
-
min-height: var(--select-searching-min-height, 72px);
|
|
3036
|
+
gap: var(--select-searching-gap);
|
|
2627
3037
|
}
|
|
2628
3038
|
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
3039
|
+
.searching-state::before {
|
|
3040
|
+
content: '';
|
|
3041
|
+
width: var(--select-searching-spinner-size);
|
|
3042
|
+
height: var(--select-searching-spinner-size);
|
|
3043
|
+
border: var(--select-searching-spinner-border);
|
|
3044
|
+
border-top-color: var(--select-searching-spinner-active-color);
|
|
3045
|
+
border-radius: 50%;
|
|
3046
|
+
animation: var(--select-searching-spinner-animation);
|
|
2632
3047
|
}
|
|
2633
3048
|
|
|
2634
|
-
/*
|
|
3049
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
3050
|
+
Error States — Clear visual feedback
|
|
3051
|
+
───────────────────────────────────────────────────────────────────────── */
|
|
3052
|
+
|
|
2635
3053
|
.select-input[aria-invalid="true"] {
|
|
2636
|
-
border-color: var(--select-error-border, #
|
|
3054
|
+
border-color: var(--select-error-border, #e94560);
|
|
2637
3055
|
}
|
|
2638
3056
|
|
|
2639
3057
|
.select-input[aria-invalid="true"]:focus {
|
|
2640
|
-
border-color: var(--select-error-border, #
|
|
2641
|
-
box-shadow: 0 0 0
|
|
2642
|
-
outline-color: var(--select-error-border, #dc2626);
|
|
3058
|
+
border-color: var(--select-error-border, #e94560);
|
|
3059
|
+
box-shadow: 0 0 0 3px var(--select-error-shadow, rgba(233, 69, 96, 0.15));
|
|
2643
3060
|
}
|
|
2644
3061
|
|
|
2645
|
-
/*
|
|
3062
|
+
/* ──────────────────────────────────────────────────��──────────────────────
|
|
3063
|
+
Accessibility — Reduced motion & High contrast
|
|
3064
|
+
───────────────────────────────────────────────────────────────────────── */
|
|
3065
|
+
|
|
2646
3066
|
@media (prefers-reduced-motion: reduce) {
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
3067
|
+
*,
|
|
3068
|
+
*::before,
|
|
3069
|
+
*::after {
|
|
3070
|
+
animation-duration: var(--select-reduced-motion-duration) !important;
|
|
3071
|
+
animation-iteration-count: var(--select-reduced-motion-iteration-count) !important;
|
|
3072
|
+
transition-duration: var(--select-reduced-motion-duration) !important;
|
|
3073
|
+
}
|
|
3074
|
+
|
|
3075
|
+
.dropdown-arrow.open {
|
|
3076
|
+
transform: var(--select-arrow-open-transform);
|
|
2651
3077
|
}
|
|
2652
3078
|
}
|
|
2653
3079
|
|
|
2654
|
-
|
|
3080
|
+
@media (prefers-contrast: high) {
|
|
3081
|
+
.select-input:focus {
|
|
3082
|
+
outline-width: var(--select-high-contrast-focus-outline-width);
|
|
3083
|
+
outline-color: var(--select-high-contrast-focus-outline-color);
|
|
3084
|
+
}
|
|
3085
|
+
|
|
3086
|
+
.input-container {
|
|
3087
|
+
border-width: var(--select-high-contrast-border-width);
|
|
3088
|
+
}
|
|
3089
|
+
|
|
3090
|
+
.option.selected::before {
|
|
3091
|
+
width: var(--select-high-contrast-indicator-width);
|
|
3092
|
+
}
|
|
3093
|
+
}
|
|
3094
|
+
|
|
3095
|
+
/* Touch targets (WCAG 2.5.5) */
|
|
3096
|
+
.load-more-button,
|
|
3097
|
+
select-option {
|
|
3098
|
+
min-height: var(--select-touch-target-min-height);
|
|
3099
|
+
}
|
|
3100
|
+
|
|
3101
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
3102
|
+
Dark Mode — Elegant dark theme
|
|
3103
|
+
───────────────────────────────────────────────────────────────────────── */
|
|
3104
|
+
|
|
2655
3105
|
:host(.dark-mode),
|
|
2656
3106
|
:host([dark-mode]),
|
|
2657
3107
|
:host([darkmode]),
|
|
@@ -2663,139 +3113,129 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2663
3113
|
:host-context([darkmode]),
|
|
2664
3114
|
:host-context([data-theme="dark"]),
|
|
2665
3115
|
:host-context([theme="dark"]) {
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
--select-
|
|
2669
|
-
--select-
|
|
2670
|
-
--select-
|
|
2671
|
-
--select-
|
|
2672
|
-
--select-
|
|
2673
|
-
--select-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
color: var(--select-dark-text, #f9fafb);
|
|
2682
|
-
}
|
|
2683
|
-
|
|
2684
|
-
.select-input::placeholder {
|
|
2685
|
-
color: var(--select-dark-placeholder, #6b7280);
|
|
2686
|
-
}
|
|
2687
|
-
|
|
2688
|
-
.select-dropdown {
|
|
2689
|
-
background: var(--select-dark-dropdown-bg, #1f2937);
|
|
2690
|
-
border-color: var(--select-dark-dropdown-border, #4b5563);
|
|
2691
|
-
}
|
|
3116
|
+
--select-primary: #e5e5e5;
|
|
3117
|
+
--select-primary-light: #2a2a3e;
|
|
3118
|
+
--select-accent: #6366f1;
|
|
3119
|
+
--select-accent-hover: #f43f5e;
|
|
3120
|
+
--select-surface: #1a1a2e;
|
|
3121
|
+
--select-surface-elevated: #252540;
|
|
3122
|
+
--select-border: #3f3f5a;
|
|
3123
|
+
--select-border-focus: #6366f1;
|
|
3124
|
+
--select-text: #f5f5f5;
|
|
3125
|
+
--select-text-muted: #9ca3af;
|
|
3126
|
+
--select-text-placeholder: #6b7280;
|
|
3127
|
+
--select-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.2);
|
|
3128
|
+
--select-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.3);
|
|
3129
|
+
--select-shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.4);
|
|
3130
|
+
--select-shadow-focus: 0 0 0 3px rgba(99, 102, 241, 0.25);
|
|
2692
3131
|
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
}
|
|
2701
|
-
|
|
2702
|
-
.option:hover {
|
|
2703
|
-
background: var(--select-dark-option-hover-bg, #374151);
|
|
2704
|
-
color: var(--select-dark-option-hover-color, #f9fafb);
|
|
2705
|
-
}
|
|
2706
|
-
|
|
2707
|
-
.option.selected {
|
|
2708
|
-
background: var(--select-dark-option-selected-bg, #3730a3);
|
|
2709
|
-
color: var(--select-dark-option-selected-text, #e0e7ff);
|
|
2710
|
-
border: var(--select-dark-option-selected-border, var(--select-option-selected-border, var(--select-option-border, none)));
|
|
2711
|
-
border-bottom: var(--select-dark-option-selected-border-bottom, var(--select-option-selected-border-bottom, var(--select-option-border-bottom, none)));
|
|
2712
|
-
box-shadow: var(--select-dark-option-selected-shadow, var(--select-option-selected-shadow, var(--select-option-shadow, none)));
|
|
2713
|
-
transform: var(--select-dark-option-selected-transform, var(--select-option-selected-transform, var(--select-option-transform, none)));
|
|
2714
|
-
}
|
|
2715
|
-
|
|
2716
|
-
.option.selected:hover {
|
|
2717
|
-
background: var(--select-dark-option-selected-hover-bg, var(--select-dark-option-selected-bg, #3730a3));
|
|
2718
|
-
color: var(--select-dark-option-selected-hover-color, var(--select-dark-option-selected-text, #e0e7ff));
|
|
2719
|
-
border: var(--select-dark-option-selected-hover-border, var(--select-dark-option-selected-border, var(--select-option-selected-hover-border, var(--select-option-selected-border, var(--select-option-border, none)))));
|
|
2720
|
-
border-bottom: var(--select-dark-option-selected-hover-border-bottom, var(--select-dark-option-selected-border-bottom, var(--select-option-selected-hover-border-bottom, var(--select-option-selected-border-bottom, var(--select-option-border-bottom, none)))));
|
|
2721
|
-
box-shadow: var(--select-dark-option-selected-hover-shadow, var(--select-dark-option-selected-shadow, var(--select-option-selected-hover-shadow, var(--select-option-selected-shadow, var(--select-option-shadow, none)))));
|
|
2722
|
-
transform: var(--select-dark-option-selected-hover-transform, var(--select-dark-option-selected-transform, var(--select-option-selected-hover-transform, var(--select-option-selected-transform, var(--select-option-transform, none)))));
|
|
2723
|
-
}
|
|
2724
|
-
|
|
2725
|
-
.option.active:not(.selected) {
|
|
2726
|
-
background-color: var(--select-dark-option-active-bg, #374151);
|
|
2727
|
-
color: var(--select-dark-option-active-color, #f9fafb);
|
|
2728
|
-
outline: var(--select-dark-option-active-outline, 2px solid rgba(129, 140, 248, 0.55));
|
|
2729
|
-
}
|
|
2730
|
-
|
|
2731
|
-
/* Group header in dark mode */
|
|
2732
|
-
.group-header {
|
|
2733
|
-
color: var(--select-dark-group-header-color, var(--select-group-header-color, #6b7280));
|
|
2734
|
-
background-color: var(--select-dark-group-header-bg, var(--select-group-header-bg, #374151));
|
|
2735
|
-
}
|
|
3132
|
+
--select-option-bg: transparent;
|
|
3133
|
+
--select-option-color: var(--select-text);
|
|
3134
|
+
--select-option-hover-bg: var(--select-surface-elevated);
|
|
3135
|
+
--select-option-hover-color: var(--select-text);
|
|
3136
|
+
--select-option-selected-bg: linear-gradient(135deg, rgba(99, 102, 241, 0.15) 0%, rgba(99, 102, 241, 0.08) 100%);
|
|
3137
|
+
--select-option-selected-color: #a5b4fc;
|
|
3138
|
+
}
|
|
2736
3139
|
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
3140
|
+
:host(.dark-mode) .input-container,
|
|
3141
|
+
:host([dark-mode]) .input-container,
|
|
3142
|
+
:host([darkmode]) .input-container,
|
|
3143
|
+
:host([data-theme="dark"]) .input-container,
|
|
3144
|
+
:host([theme="dark"]) .input-container,
|
|
3145
|
+
:host-context(.dark-mode) .input-container,
|
|
3146
|
+
:host-context(.dark) .input-container,
|
|
3147
|
+
:host-context([dark-mode]) .input-container,
|
|
3148
|
+
:host-context([darkmode]) .input-container,
|
|
3149
|
+
:host-context([data-theme="dark"]) .input-container,
|
|
3150
|
+
:host-context([theme="dark"]) .input-container {
|
|
3151
|
+
background: var(--select-surface);
|
|
3152
|
+
border-color: var(--select-border);
|
|
3153
|
+
}
|
|
2747
3154
|
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
3155
|
+
:host(.dark-mode) .input-container::after,
|
|
3156
|
+
:host([dark-mode]) .input-container::after,
|
|
3157
|
+
:host([darkmode]) .input-container::after,
|
|
3158
|
+
:host([data-theme="dark"]) .input-container::after,
|
|
3159
|
+
:host([theme="dark"]) .input-container::after,
|
|
3160
|
+
:host-context(.dark-mode) .input-container::after,
|
|
3161
|
+
:host-context(.dark) .input-container::after,
|
|
3162
|
+
:host-context([dark-mode]) .input-container::after,
|
|
3163
|
+
:host-context([darkmode]) .input-container::after,
|
|
3164
|
+
:host-context([data-theme="dark"]) .input-container::after,
|
|
3165
|
+
:host-context([theme="dark"]) .input-container::after {
|
|
3166
|
+
background: var(--select-separator-dark-bg);
|
|
3167
|
+
}
|
|
2752
3168
|
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
3169
|
+
:host(.dark-mode) .select-dropdown,
|
|
3170
|
+
:host([dark-mode]) .select-dropdown,
|
|
3171
|
+
:host([darkmode]) .select-dropdown,
|
|
3172
|
+
:host([data-theme="dark"]) .select-dropdown,
|
|
3173
|
+
:host([theme="dark"]) .select-dropdown,
|
|
3174
|
+
:host-context(.dark-mode) .select-dropdown,
|
|
3175
|
+
:host-context(.dark) .select-dropdown,
|
|
3176
|
+
:host-context([dark-mode]) .select-dropdown,
|
|
3177
|
+
:host-context([darkmode]) .select-dropdown,
|
|
3178
|
+
:host-context([data-theme="dark"]) .select-dropdown,
|
|
3179
|
+
:host-context([theme="dark"]) .select-dropdown {
|
|
3180
|
+
background: var(--select-surface);
|
|
3181
|
+
border-color: var(--select-border);
|
|
3182
|
+
}
|
|
2757
3183
|
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
3184
|
+
:host(.dark-mode) .options-container,
|
|
3185
|
+
:host([dark-mode]) .options-container,
|
|
3186
|
+
:host([darkmode]) .options-container,
|
|
3187
|
+
:host([data-theme="dark"]) .options-container,
|
|
3188
|
+
:host([theme="dark"]) .options-container,
|
|
3189
|
+
:host-context(.dark-mode) .options-container,
|
|
3190
|
+
:host-context(.dark) .options-container,
|
|
3191
|
+
:host-context([dark-mode]) .options-container,
|
|
3192
|
+
:host-context([darkmode]) .options-container,
|
|
3193
|
+
:host-context([data-theme="dark"]) .options-container,
|
|
3194
|
+
:host-context([theme="dark"]) .options-container {
|
|
3195
|
+
background: var(--select-surface);
|
|
3196
|
+
scrollbar-color: var(--select-border) transparent;
|
|
3197
|
+
}
|
|
2767
3198
|
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
}
|
|
3199
|
+
:host(.dark-mode) .selection-badge,
|
|
3200
|
+
:host([dark-mode]) .selection-badge,
|
|
3201
|
+
:host([darkmode]) .selection-badge,
|
|
3202
|
+
:host([data-theme="dark"]) .selection-badge,
|
|
3203
|
+
:host([theme="dark"]) .selection-badge,
|
|
3204
|
+
:host-context(.dark-mode) .selection-badge,
|
|
3205
|
+
:host-context(.dark) .selection-badge,
|
|
3206
|
+
:host-context([dark-mode]) .selection-badge,
|
|
3207
|
+
:host-context([darkmode]) .selection-badge,
|
|
3208
|
+
:host-context([data-theme="dark"]) .selection-badge,
|
|
3209
|
+
:host-context([theme="dark"]) .selection-badge {
|
|
3210
|
+
background: var(--select-badge-dark-bg);
|
|
2781
3211
|
}
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
3212
|
+
|
|
3213
|
+
:host(.dark-mode) .group-header:not(:first-child),
|
|
3214
|
+
:host([dark-mode]) .group-header:not(:first-child),
|
|
3215
|
+
:host([darkmode]) .group-header:not(:first-child),
|
|
3216
|
+
:host([data-theme="dark"]) .group-header:not(:first-child),
|
|
3217
|
+
:host([theme="dark"]) .group-header:not(:first-child),
|
|
3218
|
+
:host-context(.dark-mode) .group-header:not(:first-child),
|
|
3219
|
+
:host-context(.dark) .group-header:not(:first-child),
|
|
3220
|
+
:host-context([dark-mode]) .group-header:not(:first-child),
|
|
3221
|
+
:host-context([darkmode]) .group-header:not(:first-child),
|
|
3222
|
+
:host-context([data-theme="dark"]) .group-header:not(:first-child),
|
|
3223
|
+
:host-context([theme="dark"]) .group-header:not(:first-child) {
|
|
3224
|
+
border-top-color: var(--select-border);
|
|
2793
3225
|
}
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
.
|
|
2797
|
-
|
|
2798
|
-
|
|
3226
|
+
|
|
3227
|
+
:host(.dark-mode) .option.selected::before,
|
|
3228
|
+
:host([dark-mode]) .option.selected::before,
|
|
3229
|
+
:host([darkmode]) .option.selected::before,
|
|
3230
|
+
:host([data-theme="dark"]) .option.selected::before,
|
|
3231
|
+
:host([theme="dark"]) .option.selected::before,
|
|
3232
|
+
:host-context(.dark-mode) .option.selected::before,
|
|
3233
|
+
:host-context(.dark) .option.selected::before,
|
|
3234
|
+
:host-context([dark-mode]) .option.selected::before,
|
|
3235
|
+
:host-context([darkmode]) .option.selected::before,
|
|
3236
|
+
:host-context([data-theme="dark"]) .option.selected::before,
|
|
3237
|
+
:host-context([theme="dark"]) .option.selected::before {
|
|
3238
|
+
background: var(--select-accent);
|
|
2799
3239
|
}
|
|
2800
3240
|
`;
|
|
2801
3241
|
// Insert as first child to ensure styles are processed first
|
|
@@ -2815,10 +3255,10 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2815
3255
|
// delegate to the existing open/close helpers so we don't accidentally
|
|
2816
3256
|
// drift out of sync with the logic in those methods (focus, events,
|
|
2817
3257
|
// scroll-to-selected, etc.)
|
|
2818
|
-
if (this._state.isOpen) {
|
|
3258
|
+
if (this._state.isOpen && this._config.selection.toggleOnTriggerClick !== false) {
|
|
2819
3259
|
this._handleClose();
|
|
2820
3260
|
}
|
|
2821
|
-
else {
|
|
3261
|
+
else if (!this._state.isOpen) {
|
|
2822
3262
|
this._handleOpen();
|
|
2823
3263
|
}
|
|
2824
3264
|
};
|
|
@@ -2841,7 +3281,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2841
3281
|
this._inputContainer.addEventListener('pointerdown', (e) => {
|
|
2842
3282
|
// Prevent propagation to document click listener but do NOT preventDefault.
|
|
2843
3283
|
// Allow default so browser events (click) on newly opened options still fire.
|
|
2844
|
-
e.stopPropagation();
|
|
3284
|
+
// e.stopPropagation(); // BUG: By stopping propagation here, the document click listener doesn't see it, which is fine for not closing it. But be very careful.
|
|
2845
3285
|
const target = e.target;
|
|
2846
3286
|
if (!this._config.enabled)
|
|
2847
3287
|
return;
|
|
@@ -2849,21 +3289,23 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2849
3289
|
return;
|
|
2850
3290
|
if (target && target.closest('.clear-control-button'))
|
|
2851
3291
|
return;
|
|
3292
|
+
// If we clicked the container, but not the input itself, we must prevent default
|
|
3293
|
+
// otherwise the browser moves focus from our input to the body, immediately triggering blur.
|
|
3294
|
+
if (target && !target.matches('.select-input')) {
|
|
3295
|
+
e.preventDefault();
|
|
3296
|
+
}
|
|
3297
|
+
const clickedInput = Boolean(target && target.matches('.select-input'));
|
|
3298
|
+
if (this._state.isOpen &&
|
|
3299
|
+
this._config.selection.toggleOnTriggerClick !== false &&
|
|
3300
|
+
!(clickedInput && this._config.searchable)) {
|
|
3301
|
+
this._handleClose();
|
|
3302
|
+
return;
|
|
3303
|
+
}
|
|
2852
3304
|
const wasClosed = !this._state.isOpen;
|
|
2853
3305
|
if (wasClosed) {
|
|
2854
3306
|
this._handleOpen();
|
|
2855
3307
|
}
|
|
2856
|
-
|
|
2857
|
-
// Keep open while interacting directly with the input so users can
|
|
2858
|
-
// place cursor/type without accidental collapse.
|
|
2859
|
-
if (target === this._input) {
|
|
2860
|
-
this._input.focus();
|
|
2861
|
-
return;
|
|
2862
|
-
}
|
|
2863
|
-
// clicking other parts of the input container while open toggles close
|
|
2864
|
-
this._handleClose();
|
|
2865
|
-
}
|
|
2866
|
-
// Focus the input (do not prevent default behavior)
|
|
3308
|
+
// Focus the input (do not prevent default behavior for input itself)
|
|
2867
3309
|
this._input.focus();
|
|
2868
3310
|
// If we just opened the dropdown, transfer pointer capture to the
|
|
2869
3311
|
// options container so the subsequent pointerup lands there instead of
|
|
@@ -2905,7 +3347,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2905
3347
|
return;
|
|
2906
3348
|
}
|
|
2907
3349
|
this._handleClose();
|
|
2908
|
-
},
|
|
3350
|
+
}, 150);
|
|
2909
3351
|
});
|
|
2910
3352
|
// Input search
|
|
2911
3353
|
this._input.addEventListener('input', (e) => {
|
|
@@ -2922,7 +3364,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2922
3364
|
this._suppressBlurClose = true;
|
|
2923
3365
|
setTimeout(() => {
|
|
2924
3366
|
this._suppressBlurClose = false;
|
|
2925
|
-
},
|
|
3367
|
+
}, 150); // Increased timeout to ensure click finishes before blur checks
|
|
2926
3368
|
});
|
|
2927
3369
|
// Delegated click listener for improved event handling (robust across shadow DOM)
|
|
2928
3370
|
const handleOptionEvent = (e) => {
|
|
@@ -2953,9 +3395,6 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2953
3395
|
}
|
|
2954
3396
|
};
|
|
2955
3397
|
this._optionsContainer.addEventListener('click', handleOptionEvent);
|
|
2956
|
-
// also watch pointerup to catch cases where the pointerdown started outside
|
|
2957
|
-
// (e.g. on the input) and the click never fires
|
|
2958
|
-
this._optionsContainer.addEventListener('pointerup', handleOptionEvent);
|
|
2959
3398
|
// Keyboard navigation
|
|
2960
3399
|
this._input.addEventListener('keydown', (e) => this._handleKeydown(e));
|
|
2961
3400
|
// Click outside to close — robust detection across shadow DOM and custom renderers
|
|
@@ -3053,17 +3492,6 @@ class EnhancedSelect extends HTMLElement {
|
|
|
3053
3492
|
this._dropdown.style.display = 'block';
|
|
3054
3493
|
this._input.setAttribute('aria-expanded', 'true');
|
|
3055
3494
|
this._updateArrowRotation();
|
|
3056
|
-
// Clear search query when opening to show all options
|
|
3057
|
-
// This ensures we can scroll to selected item
|
|
3058
|
-
if (this._config.searchable) {
|
|
3059
|
-
this._state.searchQuery = '';
|
|
3060
|
-
// Don't clear input value if it represents selection
|
|
3061
|
-
// But if we want to search, we might want to clear it?
|
|
3062
|
-
// Standard behavior: input keeps value (label), but dropdown shows all options
|
|
3063
|
-
// until user types.
|
|
3064
|
-
// However, our filtering logic uses _state.searchQuery.
|
|
3065
|
-
// So clearing it here resets the filter.
|
|
3066
|
-
}
|
|
3067
3495
|
// Render options when opening
|
|
3068
3496
|
this._renderOptions();
|
|
3069
3497
|
this._setInitialActiveOption();
|
|
@@ -3309,24 +3737,24 @@ class EnhancedSelect extends HTMLElement {
|
|
|
3309
3737
|
}
|
|
3310
3738
|
}
|
|
3311
3739
|
_setActive(index) {
|
|
3312
|
-
const options = Array.from(this._optionsContainer.children);
|
|
3313
3740
|
// Clear previous active state
|
|
3314
|
-
if (this._state.activeIndex >= 0
|
|
3315
|
-
const prevOption =
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
prevOption.setActive
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3741
|
+
if (this._state.activeIndex >= 0) {
|
|
3742
|
+
const prevOption = this._getOptionElementByIndex(this._state.activeIndex);
|
|
3743
|
+
if (prevOption) {
|
|
3744
|
+
// Check if it's a custom SelectOption or a lightweight DOM element
|
|
3745
|
+
if ('setActive' in prevOption && typeof prevOption.setActive === 'function') {
|
|
3746
|
+
prevOption.setActive(false);
|
|
3747
|
+
}
|
|
3748
|
+
else {
|
|
3749
|
+
// Lightweight option - remove active class
|
|
3750
|
+
prevOption.classList.remove('smilodon-option--active');
|
|
3751
|
+
}
|
|
3324
3752
|
}
|
|
3325
3753
|
}
|
|
3326
3754
|
this._state.activeIndex = index;
|
|
3327
3755
|
// Set new active state
|
|
3328
|
-
|
|
3329
|
-
|
|
3756
|
+
const option = this._getOptionElementByIndex(index);
|
|
3757
|
+
if (option) {
|
|
3330
3758
|
// Check if it's a custom SelectOption or a lightweight DOM element
|
|
3331
3759
|
if ('setActive' in option && typeof option.setActive === 'function') {
|
|
3332
3760
|
option.setActive(true);
|
|
@@ -3334,13 +3762,13 @@ class EnhancedSelect extends HTMLElement {
|
|
|
3334
3762
|
else {
|
|
3335
3763
|
// Lightweight option - add active class
|
|
3336
3764
|
option.classList.add('smilodon-option--active');
|
|
3337
|
-
option.setAttribute('aria-selected', 'true');
|
|
3338
3765
|
}
|
|
3339
3766
|
if (typeof option.scrollIntoView === 'function') {
|
|
3767
|
+
// Don't scroll wildly when just opening with pre-selections
|
|
3340
3768
|
option.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
|
3341
3769
|
}
|
|
3342
3770
|
// Announce position for screen readers
|
|
3343
|
-
const total =
|
|
3771
|
+
const total = this._state.loadedItems.length;
|
|
3344
3772
|
this._announce(`Item ${index + 1} of ${total}`);
|
|
3345
3773
|
// Update aria-activedescendant using the actual option id when available
|
|
3346
3774
|
const optionId = option.id || `${this._uniqueId}-option-${index}`;
|
|
@@ -3463,9 +3891,9 @@ class EnhancedSelect extends HTMLElement {
|
|
|
3463
3891
|
// FIX: Do not rely on this._optionsContainer.children[index] because filtering changes the children
|
|
3464
3892
|
// Instead, use the index to update state directly
|
|
3465
3893
|
const item = this._state.loadedItems[index];
|
|
3466
|
-
|
|
3467
|
-
if (!item)
|
|
3894
|
+
if (!item) {
|
|
3468
3895
|
return;
|
|
3896
|
+
}
|
|
3469
3897
|
const isCurrentlySelected = this._state.selectedIndices.has(index);
|
|
3470
3898
|
// Keep active/focus styling aligned with the most recently interacted option.
|
|
3471
3899
|
// Without this, a previously selected item may retain active classes/styles
|
|
@@ -3476,7 +3904,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
3476
3904
|
const wasSelected = this._state.selectedIndices.has(index);
|
|
3477
3905
|
this._state.selectedIndices.clear();
|
|
3478
3906
|
this._state.selectedItems.clear();
|
|
3479
|
-
if (!wasSelected) {
|
|
3907
|
+
if (!wasSelected || !this._config.selection.allowDeselect) {
|
|
3480
3908
|
// Select this option
|
|
3481
3909
|
this._state.selectedIndices.add(index);
|
|
3482
3910
|
this._state.selectedItems.set(index, item);
|
|
@@ -3538,16 +3966,34 @@ class EnhancedSelect extends HTMLElement {
|
|
|
3538
3966
|
});
|
|
3539
3967
|
}
|
|
3540
3968
|
_handleOptionRemove(index) {
|
|
3969
|
+
const item = this._state.selectedItems.get(index);
|
|
3541
3970
|
const option = this._getOptionElementByIndex(index);
|
|
3542
|
-
if (!option)
|
|
3543
|
-
return;
|
|
3544
3971
|
this._state.selectedIndices.delete(index);
|
|
3545
3972
|
this._state.selectedItems.delete(index);
|
|
3546
|
-
option.setSelected
|
|
3973
|
+
if (option && 'setSelected' in option && typeof option.setSelected === 'function') {
|
|
3974
|
+
option.setSelected(false);
|
|
3975
|
+
}
|
|
3976
|
+
else if (option) {
|
|
3977
|
+
option.classList.remove('selected', 'sm-selected', 'smilodon-option--selected');
|
|
3978
|
+
option.setAttribute('aria-selected', 'false');
|
|
3979
|
+
const stateTokens = (option.dataset.smState || '')
|
|
3980
|
+
.split(' ')
|
|
3981
|
+
.map(token => token.trim())
|
|
3982
|
+
.filter(token => token && token !== 'selected');
|
|
3983
|
+
if (stateTokens.length > 0) {
|
|
3984
|
+
option.dataset.smState = stateTokens.join(' ');
|
|
3985
|
+
}
|
|
3986
|
+
else {
|
|
3987
|
+
delete option.dataset.smState;
|
|
3988
|
+
}
|
|
3989
|
+
}
|
|
3547
3990
|
this._updateInputDisplay();
|
|
3991
|
+
this._renderOptions();
|
|
3548
3992
|
this._emitChange();
|
|
3549
|
-
const config = option.getConfig
|
|
3550
|
-
|
|
3993
|
+
const config = option && 'getConfig' in option && typeof option.getConfig === 'function'
|
|
3994
|
+
? option.getConfig()
|
|
3995
|
+
: undefined;
|
|
3996
|
+
this._emit('remove', { item: config?.item ?? item, index });
|
|
3551
3997
|
}
|
|
3552
3998
|
_updateInputDisplay() {
|
|
3553
3999
|
const selectedItems = Array.from(this._state.selectedItems.values());
|
|
@@ -3575,22 +4021,29 @@ class EnhancedSelect extends HTMLElement {
|
|
|
3575
4021
|
const badge = document.createElement('span');
|
|
3576
4022
|
badge.className = 'selection-badge';
|
|
3577
4023
|
badge.setAttribute('part', 'chip');
|
|
3578
|
-
|
|
4024
|
+
const badgeLabel = document.createElement('span');
|
|
4025
|
+
badgeLabel.className = 'selection-badge-label';
|
|
4026
|
+
badgeLabel.textContent = getLabel(item);
|
|
4027
|
+
badge.appendChild(badgeLabel);
|
|
3579
4028
|
// Add remove button to badge
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
4029
|
+
if (this._config.selection.showRemoveButton !== false) {
|
|
4030
|
+
const removeBtn = document.createElement('button');
|
|
4031
|
+
removeBtn.type = 'button';
|
|
4032
|
+
removeBtn.className = 'badge-remove';
|
|
4033
|
+
removeBtn.setAttribute('part', 'chip-remove');
|
|
4034
|
+
removeBtn.innerHTML = '×';
|
|
4035
|
+
removeBtn.setAttribute('aria-label', `Remove ${getLabel(item)}`);
|
|
4036
|
+
removeBtn.addEventListener('pointerdown', (e) => {
|
|
4037
|
+
e.stopPropagation();
|
|
4038
|
+
e.preventDefault();
|
|
4039
|
+
});
|
|
4040
|
+
removeBtn.addEventListener('click', (e) => {
|
|
4041
|
+
e.stopPropagation();
|
|
4042
|
+
e.preventDefault();
|
|
4043
|
+
this._handleOptionRemove(index);
|
|
4044
|
+
});
|
|
4045
|
+
badge.appendChild(removeBtn);
|
|
4046
|
+
}
|
|
3594
4047
|
this._inputContainer.insertBefore(badge, this._input);
|
|
3595
4048
|
});
|
|
3596
4049
|
}
|
|
@@ -3642,10 +4095,12 @@ class EnhancedSelect extends HTMLElement {
|
|
|
3642
4095
|
const option = this._getOptionElementByIndex(targetIndex);
|
|
3643
4096
|
if (option) {
|
|
3644
4097
|
// Use smooth scrolling with center alignment for better UX
|
|
3645
|
-
option.scrollIntoView
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
4098
|
+
if (typeof option.scrollIntoView === 'function') {
|
|
4099
|
+
option.scrollIntoView({
|
|
4100
|
+
block: this._config.scrollToSelected.block || 'center',
|
|
4101
|
+
behavior: 'smooth',
|
|
4102
|
+
});
|
|
4103
|
+
}
|
|
3649
4104
|
// Also set it as active for keyboard navigation
|
|
3650
4105
|
this._setActive(targetIndex);
|
|
3651
4106
|
}
|
|
@@ -3857,7 +4312,6 @@ class EnhancedSelect extends HTMLElement {
|
|
|
3857
4312
|
const getValue = this._config.serverSide.getValueFromItem || ((item) => item?.value ?? item);
|
|
3858
4313
|
const selectedValues = selectedItems.map(getValue);
|
|
3859
4314
|
const selectedIndices = Array.from(this._state.selectedIndices);
|
|
3860
|
-
// Debug: log change payload
|
|
3861
4315
|
this._emit('change', { selectedItems, selectedValues, selectedIndices });
|
|
3862
4316
|
this._config.callbacks.onChange?.(selectedItems, selectedValues);
|
|
3863
4317
|
}
|
|
@@ -4061,7 +4515,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
4061
4515
|
this._clearControl.setAttribute('aria-label', this._config.clearControl.ariaLabel || 'Clear selection and search');
|
|
4062
4516
|
}
|
|
4063
4517
|
if (this._clearControlIcon) {
|
|
4064
|
-
this._clearControlIcon.
|
|
4518
|
+
this._clearControlIcon.innerHTML = `<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 4L4 12M4 4L12 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
|
|
4065
4519
|
}
|
|
4066
4520
|
if (this._dropdown) {
|
|
4067
4521
|
if (this._config.selection.mode === 'multi') {
|
|
@@ -4071,6 +4525,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
4071
4525
|
this._dropdown.removeAttribute('aria-multiselectable');
|
|
4072
4526
|
}
|
|
4073
4527
|
}
|
|
4528
|
+
this._syncInputContainerMode();
|
|
4074
4529
|
// Re-initialize observers in case infinite scroll was enabled/disabled
|
|
4075
4530
|
this._initializeObservers();
|
|
4076
4531
|
this._syncClearControlState();
|
|
@@ -4176,6 +4631,8 @@ class EnhancedSelect extends HTMLElement {
|
|
|
4176
4631
|
* Render options based on current state
|
|
4177
4632
|
*/
|
|
4178
4633
|
_renderOptions() {
|
|
4634
|
+
this._renderCycleId += 1;
|
|
4635
|
+
const renderCycleId = this._renderCycleId;
|
|
4179
4636
|
// Cleanup observer
|
|
4180
4637
|
if (this._loadMoreTrigger && this._intersectionObserver) {
|
|
4181
4638
|
this._intersectionObserver.unobserve(this._loadMoreTrigger);
|
|
@@ -4240,23 +4697,21 @@ class EnhancedSelect extends HTMLElement {
|
|
|
4240
4697
|
}
|
|
4241
4698
|
else {
|
|
4242
4699
|
// Normal rendering (flat list or filtered)
|
|
4243
|
-
|
|
4700
|
+
const filteredIndices = [];
|
|
4244
4701
|
this._state.loadedItems.forEach((item, index) => {
|
|
4245
|
-
// Apply filter if query exists
|
|
4246
4702
|
if (query) {
|
|
4247
4703
|
try {
|
|
4248
4704
|
const label = String(getLabel(item)).toLowerCase();
|
|
4249
4705
|
if (!label.includes(query))
|
|
4250
4706
|
return;
|
|
4251
4707
|
}
|
|
4252
|
-
catch (
|
|
4708
|
+
catch (_e) {
|
|
4253
4709
|
return;
|
|
4254
4710
|
}
|
|
4255
4711
|
}
|
|
4256
|
-
|
|
4257
|
-
this._renderSingleOption(item, index, getValue, getLabel);
|
|
4712
|
+
filteredIndices.push(index);
|
|
4258
4713
|
});
|
|
4259
|
-
if (
|
|
4714
|
+
if (filteredIndices.length === 0 && !this._state.isBusy) {
|
|
4260
4715
|
const empty = document.createElement('div');
|
|
4261
4716
|
empty.setAttribute('part', 'no-results');
|
|
4262
4717
|
empty.className = 'empty-state';
|
|
@@ -4268,6 +4723,54 @@ class EnhancedSelect extends HTMLElement {
|
|
|
4268
4723
|
}
|
|
4269
4724
|
this._optionsContainer.appendChild(empty);
|
|
4270
4725
|
}
|
|
4726
|
+
else {
|
|
4727
|
+
const shouldIncrementalRender = this._config.virtualize !== false
|
|
4728
|
+
&& this._state.groupedItems.length === 0
|
|
4729
|
+
&& filteredIndices.length > 300;
|
|
4730
|
+
if (shouldIncrementalRender) {
|
|
4731
|
+
const chunkSize = 80;
|
|
4732
|
+
let cursor = 0;
|
|
4733
|
+
let maxRenderTarget = 0;
|
|
4734
|
+
if (this._state.selectedIndices.size > 0 && this._config.scrollToSelected.enabled) {
|
|
4735
|
+
const indices = Array.from(this._state.selectedIndices).sort((a, b) => a - b);
|
|
4736
|
+
const targetIndex = this._config.scrollToSelected.multiSelectTarget === 'first' ? indices[0] : indices[indices.length - 1];
|
|
4737
|
+
const filteredPos = filteredIndices.indexOf(targetIndex);
|
|
4738
|
+
if (filteredPos !== -1) {
|
|
4739
|
+
maxRenderTarget = filteredPos + 20; // Ensure we render up to the selection
|
|
4740
|
+
}
|
|
4741
|
+
}
|
|
4742
|
+
const renderChunk = () => {
|
|
4743
|
+
if (renderCycleId !== this._renderCycleId)
|
|
4744
|
+
return;
|
|
4745
|
+
const fragment = document.createDocumentFragment();
|
|
4746
|
+
const chunkEnd = Math.min(Math.max(cursor + chunkSize, maxRenderTarget), filteredIndices.length);
|
|
4747
|
+
maxRenderTarget = 0; // Reset after fast-forwarding
|
|
4748
|
+
for (; cursor < chunkEnd; cursor += 1) {
|
|
4749
|
+
const itemIndex = filteredIndices[cursor];
|
|
4750
|
+
const item = this._state.loadedItems[itemIndex];
|
|
4751
|
+
this._renderSingleOption(item, itemIndex, getValue, getLabel, fragment);
|
|
4752
|
+
}
|
|
4753
|
+
this._optionsContainer.appendChild(fragment);
|
|
4754
|
+
if (cursor < filteredIndices.length) {
|
|
4755
|
+
requestAnimationFrame(renderChunk);
|
|
4756
|
+
}
|
|
4757
|
+
else {
|
|
4758
|
+
if (renderCycleId !== this._renderCycleId)
|
|
4759
|
+
return;
|
|
4760
|
+
if (!this._state.isBusy && (this._config.loadMore.enabled || this._config.infiniteScroll.enabled) && this._state.loadedItems.length > 0) {
|
|
4761
|
+
this._addLoadMoreTrigger();
|
|
4762
|
+
}
|
|
4763
|
+
this._finalizePerfMarks();
|
|
4764
|
+
}
|
|
4765
|
+
};
|
|
4766
|
+
renderChunk();
|
|
4767
|
+
return;
|
|
4768
|
+
}
|
|
4769
|
+
filteredIndices.forEach((itemIndex) => {
|
|
4770
|
+
const item = this._state.loadedItems[itemIndex];
|
|
4771
|
+
this._renderSingleOption(item, itemIndex, getValue, getLabel);
|
|
4772
|
+
});
|
|
4773
|
+
}
|
|
4271
4774
|
}
|
|
4272
4775
|
// Append Busy Indicator if busy
|
|
4273
4776
|
if (this._state.isBusy && this._config.busyBucket.enabled) {
|
|
@@ -4292,7 +4795,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
4292
4795
|
}
|
|
4293
4796
|
this._finalizePerfMarks();
|
|
4294
4797
|
}
|
|
4295
|
-
_renderSingleOption(item, index, getValue, getLabel) {
|
|
4798
|
+
_renderSingleOption(item, index, getValue, getLabel, targetContainer = this._optionsContainer) {
|
|
4296
4799
|
const isSelected = this._state.selectedIndices.has(index);
|
|
4297
4800
|
const isDisabled = Boolean(item?.disabled);
|
|
4298
4801
|
const optionId = `${this._uniqueId}-option-${index}`;
|
|
@@ -4308,7 +4811,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
4308
4811
|
disabled: isDisabled,
|
|
4309
4812
|
id: optionId,
|
|
4310
4813
|
});
|
|
4311
|
-
|
|
4814
|
+
targetContainer.appendChild(optionElement);
|
|
4312
4815
|
return;
|
|
4313
4816
|
}
|
|
4314
4817
|
const option = new SelectOption({
|
|
@@ -4337,20 +4840,16 @@ class EnhancedSelect extends HTMLElement {
|
|
|
4337
4840
|
option.dataset.smValue = String(val);
|
|
4338
4841
|
}
|
|
4339
4842
|
option.id = option.id || optionId;
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
shiftKey: mouseEvent.shiftKey,
|
|
4345
|
-
toggleKey: mouseEvent.ctrlKey || mouseEvent.metaKey,
|
|
4346
|
-
});
|
|
4347
|
-
});
|
|
4843
|
+
// Do NOT bind a native click listener here for SelectOption elements.
|
|
4844
|
+
// Like custom rendered options, they are fully handled by the `handleOptionEvent` delegator
|
|
4845
|
+
// on `this._optionsContainer` (line 1221).
|
|
4846
|
+
// Adding this listener causes the double-click toggle bug since both fire on selection!
|
|
4348
4847
|
option.addEventListener('optionRemove', (event) => {
|
|
4349
4848
|
const detail = event.detail;
|
|
4350
4849
|
const targetIndex = detail?.index ?? index;
|
|
4351
4850
|
this._handleOptionRemove(targetIndex);
|
|
4352
4851
|
});
|
|
4353
|
-
|
|
4852
|
+
targetContainer.appendChild(option);
|
|
4354
4853
|
}
|
|
4355
4854
|
_normalizeCustomOptionElement(element, meta) {
|
|
4356
4855
|
const optionEl = element instanceof HTMLElement ? element : document.createElement('div');
|
|
@@ -4437,35 +4936,9 @@ class EnhancedSelect extends HTMLElement {
|
|
|
4437
4936
|
optionEl.tabIndex = -1;
|
|
4438
4937
|
}
|
|
4439
4938
|
if (!this._customOptionBoundElements.has(optionEl)) {
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
if (current.getAttribute('aria-disabled') === 'true')
|
|
4444
|
-
return;
|
|
4445
|
-
const parsedIndex = Number(current.dataset.index);
|
|
4446
|
-
if (!Number.isFinite(parsedIndex))
|
|
4447
|
-
return;
|
|
4448
|
-
const mouseEvent = e;
|
|
4449
|
-
this._selectOption(parsedIndex, {
|
|
4450
|
-
shiftKey: mouseEvent.shiftKey,
|
|
4451
|
-
toggleKey: mouseEvent.ctrlKey || mouseEvent.metaKey,
|
|
4452
|
-
});
|
|
4453
|
-
});
|
|
4454
|
-
optionEl.addEventListener('keydown', (e) => {
|
|
4455
|
-
if (e.key !== 'Enter' && e.key !== ' ')
|
|
4456
|
-
return;
|
|
4457
|
-
const current = e.currentTarget;
|
|
4458
|
-
if (current.getAttribute('aria-disabled') === 'true')
|
|
4459
|
-
return;
|
|
4460
|
-
const parsedIndex = Number(current.dataset.index);
|
|
4461
|
-
if (!Number.isFinite(parsedIndex))
|
|
4462
|
-
return;
|
|
4463
|
-
e.preventDefault();
|
|
4464
|
-
this._selectOption(parsedIndex, {
|
|
4465
|
-
shiftKey: e.shiftKey,
|
|
4466
|
-
toggleKey: e.ctrlKey || e.metaKey,
|
|
4467
|
-
});
|
|
4468
|
-
});
|
|
4939
|
+
// Intentionally NOT binding native option click listeners for custom options!
|
|
4940
|
+
// All option interactions are globally handled by _optionsContainer.addEventListener('click', handleOptionEvent);
|
|
4941
|
+
// Re-attaching here causes the double-click toggle bug if a child component fails to stopPropagation.
|
|
4469
4942
|
this._customOptionBoundElements.add(optionEl);
|
|
4470
4943
|
}
|
|
4471
4944
|
return optionEl;
|