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