@smilodon/core 1.4.11 → 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/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,
@@ -1401,6 +1402,18 @@ const defaultConfig = {
1401
1402
  icon: '×',
1402
1403
  },
1403
1404
  callbacks: {},
1405
+ tracking: {
1406
+ enabled: false,
1407
+ events: true,
1408
+ styling: true,
1409
+ limitations: true,
1410
+ emitDiagnostics: false,
1411
+ maxEntries: 200,
1412
+ },
1413
+ limitations: {
1414
+ policies: {},
1415
+ autoMitigateRuntimeModeSwitch: true,
1416
+ },
1404
1417
  enabled: true,
1405
1418
  searchable: false,
1406
1419
  placeholder: 'Select an option...',
@@ -1869,6 +1882,8 @@ if (!customElements.get('select-option')) {
1869
1882
  * Enhanced Select Component
1870
1883
  * Implements all advanced features: infinite scroll, load more, busy state,
1871
1884
  * server-side selection, and full customization
1885
+ *
1886
+ * ✨ Redesigned with formal elegance, refined microinteractions, and polished UX
1872
1887
  */
1873
1888
  class EnhancedSelect extends HTMLElement {
1874
1889
  get classMap() {
@@ -1877,6 +1892,10 @@ class EnhancedSelect extends HTMLElement {
1877
1892
  set classMap(map) {
1878
1893
  this._classMap = map;
1879
1894
  this._setGlobalStylesMirroring(Boolean(this._optionRenderer || map || this._groupHeaderRenderer));
1895
+ this._track('style', 'classMapChanged', {
1896
+ hasClassMap: Boolean(map),
1897
+ keys: map ? Object.keys(map) : [],
1898
+ });
1880
1899
  if (!this.isConnected)
1881
1900
  return;
1882
1901
  this._renderOptions();
@@ -1892,6 +1911,7 @@ class EnhancedSelect extends HTMLElement {
1892
1911
  set groupHeaderRenderer(renderer) {
1893
1912
  this._groupHeaderRenderer = renderer;
1894
1913
  this._setGlobalStylesMirroring(Boolean(this._optionRenderer || this._classMap || renderer));
1914
+ this._track('style', 'groupHeaderRendererChanged', { enabled: Boolean(renderer) });
1895
1915
  if (!this.isConnected)
1896
1916
  return;
1897
1917
  this._renderOptions();
@@ -1910,6 +1930,9 @@ class EnhancedSelect extends HTMLElement {
1910
1930
  this._mirrorGlobalStylesForCustomOptions = false;
1911
1931
  this._globalStylesObserver = null;
1912
1932
  this._globalStylesContainer = null;
1933
+ this._tracking = { events: [], styles: [], limitations: [] };
1934
+ this._suppressBlurClose = false;
1935
+ this._renderCycleId = 0;
1913
1936
  this._shadow = this.attachShadow({ mode: 'open' });
1914
1937
  this._uniqueId = `enhanced-select-${Math.random().toString(36).substr(2, 9)}`;
1915
1938
  this._rendererHelpers = this._buildRendererHelpers();
@@ -1984,6 +2007,7 @@ class EnhancedSelect extends HTMLElement {
1984
2007
  if (this._boundArrowClick && this._arrowContainer) {
1985
2008
  this._arrowContainer.removeEventListener('click', this._boundArrowClick);
1986
2009
  }
2010
+ this._renderCycleId += 1;
1987
2011
  this._teardownGlobalStylesMirroring();
1988
2012
  }
1989
2013
  _setGlobalStylesMirroring(enabled) {
@@ -1994,6 +2018,7 @@ class EnhancedSelect extends HTMLElement {
1994
2018
  return;
1995
2019
  }
1996
2020
  this._mirrorGlobalStylesForCustomOptions = enabled;
2021
+ this._track('style', 'globalStylesMirroringChanged', { enabled });
1997
2022
  if (enabled) {
1998
2023
  this._setupGlobalStylesMirroring();
1999
2024
  }
@@ -2086,8 +2111,48 @@ class EnhancedSelect extends HTMLElement {
2086
2111
  const container = document.createElement('div');
2087
2112
  container.className = 'input-container';
2088
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
+ }
2089
2137
  return container;
2090
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
+ }
2091
2156
  _createInput() {
2092
2157
  const input = document.createElement('input');
2093
2158
  input.setAttribute('part', 'input');
@@ -2097,6 +2162,35 @@ class EnhancedSelect extends HTMLElement {
2097
2162
  input.placeholder = this._config.placeholder || 'Select an option...';
2098
2163
  input.disabled = !this._config.enabled;
2099
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';
2100
2194
  // Update readonly when input is focused if searchable
2101
2195
  input.addEventListener('focus', () => {
2102
2196
  if (this._config.searchable) {
@@ -2107,7 +2201,22 @@ class EnhancedSelect extends HTMLElement {
2107
2201
  input.className += ' ' + this._config.styles.classNames.input;
2108
2202
  }
2109
2203
  if (this._config.styles.input) {
2110
- Object.assign(input.style, this._config.styles.input);
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);
2111
2220
  }
2112
2221
  input.setAttribute('role', 'combobox');
2113
2222
  input.setAttribute('aria-expanded', 'false');
@@ -2151,7 +2260,7 @@ class EnhancedSelect extends HTMLElement {
2151
2260
  container.className = 'dropdown-arrow-container';
2152
2261
  container.innerHTML = `
2153
2262
  <svg class="dropdown-arrow" part="arrow" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
2154
- <path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
2263
+ <path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
2155
2264
  </svg>
2156
2265
  `;
2157
2266
  return container;
@@ -2164,7 +2273,7 @@ class EnhancedSelect extends HTMLElement {
2164
2273
  const icon = document.createElement('span');
2165
2274
  icon.className = 'clear-control-icon';
2166
2275
  icon.setAttribute('part', 'clear-icon');
2167
- icon.textContent = this._config.clearControl.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>`;
2168
2277
  button.setAttribute('aria-label', this._config.clearControl.ariaLabel || 'Clear selection and search');
2169
2278
  button.appendChild(icon);
2170
2279
  this._clearControlIcon = icon;
@@ -2190,18 +2299,229 @@ class EnhancedSelect extends HTMLElement {
2190
2299
  this._dropdown.id = listboxId;
2191
2300
  this._input.setAttribute('aria-controls', listboxId);
2192
2301
  this._input.setAttribute('aria-owns', listboxId);
2302
+ this._syncInputContainerMode();
2193
2303
  this._syncClearControlState();
2194
2304
  }
2195
2305
  _initializeStyles() {
2196
2306
  const style = document.createElement('style');
2197
2307
  style.textContent = `
2308
+ /* ═══════════════════════════════════════════════════════════════════════════
2309
+ ELEGANT SELECT COMPONENT — Refined Design System
2310
+ Formal aesthetics with sophisticated microinteractions
2311
+ ═══════════════════════════════════════════════════════════════════════════ */
2312
+
2198
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
+
2199
2421
  display: block;
2200
2422
  position: relative;
2201
2423
  width: var(--select-width, 100%);
2202
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
+ }
2203
2469
  }
2204
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
+
2205
2525
  .select-container {
2206
2526
  position: relative;
2207
2527
  width: 100%;
@@ -2212,102 +2532,143 @@ class EnhancedSelect extends HTMLElement {
2212
2532
  width: 100%;
2213
2533
  display: flex;
2214
2534
  align-items: center;
2215
- flex-wrap: wrap;
2535
+ flex-wrap: nowrap;
2216
2536
  gap: var(--select-input-gap, 6px);
2217
- padding: var(--select-input-padding, 6px 52px 6px 8px);
2537
+ padding: var(--select-input-padding, 10px 52px 10px 14px);
2218
2538
  height: var(--select-input-height, auto);
2219
- min-height: var(--select-input-min-height, 44px);
2539
+ min-height: var(--select-input-min-height, 48px);
2220
2540
  max-height: var(--select-input-max-height, 160px);
2221
2541
  overflow-y: var(--select-input-overflow-y, auto);
2222
- align-content: flex-start;
2223
- background: var(--select-input-bg, var(--select-bg, white));
2224
- border: var(--select-input-border, 1px solid var(--select-border-color, #d1d5db));
2225
- border-radius: var(--select-input-border-radius, 6px);
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);
2226
2547
  box-sizing: border-box;
2227
- transition: all 0.2s ease;
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;
2228
2567
  }
2229
2568
 
2230
2569
  .input-container:focus-within {
2231
- border-color: var(--select-input-focus-border, var(--select-border-focus-color, #667eea));
2232
- box-shadow: var(--select-input-focus-shadow, 0 0 0 3px rgba(102, 126, 234, 0.1));
2570
+ border-color: var(--select-input-focus-border, var(--select-border-focus));
2571
+ box-shadow: var(--select-shadow-focus), var(--select-shadow-sm);
2233
2572
  }
2234
2573
 
2235
- /* Gradient separator before arrow */
2574
+ /* Elegant separator line before arrow */
2236
2575
  .input-container::after {
2237
2576
  content: '';
2238
2577
  position: absolute;
2239
2578
  top: 50%;
2240
- right: var(--select-separator-position, var(--select-seperator-position, 40px));
2579
+ right: var(--select-separator-position, 42px);
2241
2580
  transform: translateY(-50%);
2242
- width: var(--select-separator-width, var(--select-seperator-width, 1px));
2243
- height: var(--select-separator-height, var(--select-seperator-height, 60%));
2244
- background: var(--select-separator-bg, var(--select-seperator-bg, var(--select-separator-gradient, var(--select-seperator-gradient, linear-gradient(
2581
+ width: var(--select-separator-width, 1px);
2582
+ height: var(--select-separator-height, 50%);
2583
+ background: var(--select-separator-bg, linear-gradient(
2245
2584
  to bottom,
2246
2585
  transparent 0%,
2247
- rgba(0, 0, 0, 0.1) 20%,
2248
- rgba(0, 0, 0, 0.1) 80%,
2586
+ var(--select-border) 20%,
2587
+ var(--select-border) 80%,
2249
2588
  transparent 100%
2250
- ))));
2589
+ ));
2251
2590
  pointer-events: none;
2252
2591
  z-index: 1;
2592
+ opacity: var(--select-separator-opacity);
2593
+ transition: opacity var(--select-transition-fast);
2594
+ }
2595
+
2596
+ .input-container:hover::after,
2597
+ .input-container:focus-within::after {
2598
+ opacity: var(--select-separator-active-opacity);
2253
2599
  }
2254
2600
 
2601
+ /* ─────────────────────────────────────────────────────────────────────────
2602
+ Dropdown Arrow — Smooth rotation with refined styling
2603
+ ───────────────────────────────────────────────────────────────────────── */
2604
+
2255
2605
  .dropdown-arrow-container {
2256
2606
  position: absolute;
2257
2607
  top: 0;
2258
2608
  right: 0;
2259
2609
  bottom: 0;
2260
- width: var(--select-arrow-width, 40px);
2261
- /* allow explicit height override even though container normally stretches */
2610
+ width: var(--select-arrow-width, 42px);
2262
2611
  height: var(--select-arrow-height, auto);
2263
2612
  display: flex;
2264
2613
  align-items: center;
2265
2614
  justify-content: center;
2266
2615
  cursor: pointer;
2267
- transition: background-color 0.2s ease;
2268
- 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);
2269
2617
  z-index: 2;
2618
+ transition: background-color var(--select-transition-fast);
2270
2619
  }
2271
2620
 
2272
2621
  .input-container.has-clear-control {
2273
- padding: var(--select-input-padding-with-clear, 6px 84px 6px 8px);
2622
+ padding: var(--select-input-padding-with-clear, 10px 84px 10px 14px);
2274
2623
  }
2275
2624
 
2276
2625
  .input-container.has-clear-control::after {
2277
- right: var(--select-separator-position-with-clear, var(--select-seperator-position-with-clear, 72px));
2626
+ right: var(--select-separator-position-with-clear, 74px);
2278
2627
  }
2279
2628
 
2280
2629
  .dropdown-arrow-container.with-clear-control {
2281
- right: var(--select-arrow-right-with-clear, 32px);
2630
+ right: var(--select-arrow-right-with-clear, 34px);
2282
2631
  }
2283
2632
 
2633
+ /* ─────────────────────────────────────────────────────────────────────────
2634
+ Clear Control — Minimal and elegant dismiss button
2635
+ ───────────────────────────────────────────────────────────────────────── */
2636
+
2284
2637
  .clear-control-button {
2285
2638
  position: absolute;
2286
2639
  top: 50%;
2287
- right: var(--select-clear-button-right, 6px);
2640
+ right: var(--select-clear-button-right, 8px);
2288
2641
  transform: translateY(-50%);
2289
- width: var(--select-clear-button-size, 24px);
2290
- height: var(--select-clear-button-size, 24px);
2642
+ width: var(--select-clear-button-size, 26px);
2643
+ height: var(--select-clear-button-size, 26px);
2291
2644
  border: var(--select-clear-button-border, none);
2292
- border-radius: var(--select-clear-button-radius, 999px);
2645
+ border-radius: var(--select-clear-button-radius, 50%);
2293
2646
  background: var(--select-clear-button-bg, transparent);
2294
- color: var(--select-clear-button-color, #6b7280);
2647
+ color: var(--select-clear-button-color, var(--select-text-muted));
2295
2648
  display: inline-flex;
2296
2649
  align-items: center;
2297
2650
  justify-content: center;
2298
2651
  cursor: pointer;
2299
2652
  z-index: 3;
2300
- transition: var(--select-clear-button-transition, all 0.2s ease);
2653
+ transition:
2654
+ background var(--select-transition-fast),
2655
+ color var(--select-transition-fast),
2656
+ transform var(--select-transition-bounce);
2301
2657
  }
2302
2658
 
2303
2659
  .clear-control-button:hover {
2304
- background: var(--select-clear-button-hover-bg, rgba(0, 0, 0, 0.06));
2305
- color: var(--select-clear-button-hover-color, #111827);
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);
2306
2667
  }
2307
2668
 
2308
2669
  .clear-control-button:focus-visible {
2309
- outline: var(--select-clear-button-focus-outline, 2px solid rgba(102, 126, 234, 0.55));
2310
- outline-offset: 1px;
2670
+ outline: var(--select-clear-button-focus-outline, 2px solid var(--select-border-focus));
2671
+ outline-offset: var(--select-clear-button-focus-offset);
2311
2672
  }
2312
2673
 
2313
2674
  .clear-control-button[hidden] {
@@ -2315,260 +2676,332 @@ class EnhancedSelect extends HTMLElement {
2315
2676
  }
2316
2677
 
2317
2678
  .clear-control-icon {
2318
- font-size: var(--select-clear-icon-size, 16px);
2319
- line-height: 1;
2320
- font-weight: var(--select-clear-icon-weight, 500);
2679
+ width: var(--select-clear-icon-size);
2680
+ height: var(--select-clear-icon-size);
2321
2681
  pointer-events: none;
2322
2682
  }
2323
2683
 
2324
- .dropdown-arrow-container:hover {
2325
- background-color: var(--select-arrow-hover-bg, rgba(102, 126, 234, 0.08));
2684
+ .clear-control-icon svg {
2685
+ width: 100%;
2686
+ height: 100%;
2326
2687
  }
2327
2688
 
2328
- .dropdown-arrow:hover {
2329
- /* legacy alias --select-arrow-hover for icon color */
2330
- 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));
2331
2691
  }
2332
2692
 
2333
2693
  .dropdown-arrow {
2334
- width: var(--select-arrow-width, var(--select-arrow-size, 16px));
2335
- height: var(--select-arrow-height, var(--select-arrow-size, 16px));
2336
- color: var(--select-arrow-color, #667eea);
2337
- transition: transform 0.2s ease, color 0.2s ease;
2338
- transform: translateY(0);
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;
2339
2701
  }
2340
2702
 
2341
2703
  .dropdown-arrow path {
2342
- stroke-width: var(--select-arrow-stroke-width, 2);
2704
+ stroke-width: var(--select-arrow-stroke-width, 1.5);
2343
2705
  }
2344
2706
 
2345
2707
  .dropdown-arrow-container:hover .dropdown-arrow {
2346
- color: var(--select-arrow-hover-color, #667eea);
2708
+ color: var(--select-arrow-hover-color, var(--select-accent));
2347
2709
  }
2348
2710
 
2349
2711
  .dropdown-arrow.open {
2350
- transform: rotate(180deg);
2712
+ transform: var(--select-arrow-open-transform);
2351
2713
  }
2352
2714
 
2353
- .select-input {
2354
- flex: 1;
2355
- width: var(--select-input-width, auto);
2356
- min-width: var(--select-input-min-width, 120px);
2357
- padding: var(--select-input-field-padding, 4px);
2358
- border: none;
2359
- font-size: var(--select-input-font-size, 14px);
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);
2360
2733
  line-height: var(--select-input-line-height, 1.5);
2361
- color: var(--select-input-color, var(--select-text-color, #1f2937));
2362
- background: transparent;
2734
+ color: var(--select-input-color, var(--select-text));
2735
+ background: transparent !important;
2363
2736
  box-sizing: border-box;
2364
- outline: none;
2365
- font-family: var(--select-font-family, inherit);
2366
- }
2367
-
2368
- .select-input::placeholder {
2369
- color: var(--select-input-placeholder-color, var(--select-placeholder-color, #9ca3af));
2370
- }
2371
-
2372
- .selection-badge {
2373
- display: inline-flex;
2374
- align-items: center;
2375
- gap: var(--select-badge-gap, 4px);
2376
- padding: var(--select-badge-padding, 4px 8px);
2377
- margin: var(--select-badge-margin, 2px);
2378
- background: var(--select-badge-bg, #667eea);
2379
- color: var(--select-badge-color, white);
2380
- border-radius: var(--select-badge-border-radius, 4px);
2381
- font-size: var(--select-badge-font-size, 13px);
2382
- line-height: 1;
2383
- max-width: var(--select-badge-max-width, 100%);
2384
- 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;
2385
2745
  overflow: hidden;
2386
2746
  text-overflow: ellipsis;
2747
+ white-space: nowrap;
2387
2748
  }
2388
2749
 
2389
- .badge-remove {
2390
- display: inline-flex;
2391
- align-items: center;
2392
- justify-content: center;
2393
- width: var(--select-badge-remove-size, 16px);
2394
- height: var(--select-badge-remove-size, 16px);
2395
- padding: 0;
2396
- margin-left: 4px;
2397
- background: var(--select-badge-remove-bg, rgba(255, 255, 255, 0.3));
2398
- border: none;
2399
- border-radius: 50%;
2400
- color: var(--select-badge-remove-color, white);
2401
- font-size: var(--select-badge-remove-font-size, 16px);
2402
- line-height: 1;
2403
- cursor: pointer;
2404
- transition: background 0.2s;
2750
+ .select-input::placeholder {
2751
+ color: var(--select-input-placeholder-color, var(--select-text-placeholder));
2752
+ font-weight: 400;
2405
2753
  }
2406
-
2407
- .badge-remove:hover {
2408
- background: var(--select-badge-remove-hover-bg, rgba(255, 255, 255, 0.5));
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);
2409
2761
  }
2410
2762
 
2411
- .badge-remove:focus-visible {
2412
- outline: 2px solid var(--select-badge-remove-focus-outline, rgba(255, 255, 255, 0.8));
2413
- outline-offset: 2px;
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;
2414
2769
  }
2415
2770
 
2416
2771
  .select-input:disabled {
2417
2772
  background-color: var(--select-disabled-bg, #f5f5f5);
2418
2773
  cursor: not-allowed;
2774
+ opacity: var(--select-input-disabled-opacity);
2419
2775
  }
2420
2776
 
2777
+ /* ─────────────────────────────────────────────────────────────────────────
2778
+ Dropdown Panel — Elegant floating container
2779
+ ───────────────────────────────────────────────────────────────────────── */
2780
+
2421
2781
  .select-dropdown {
2422
2782
  position: absolute;
2423
- scroll-behavior: smooth;
2424
- top: 100%;
2783
+ scroll-behavior: var(--select-dropdown-scroll-behavior);
2784
+ top: var(--select-dropdown-top);
2425
2785
  left: 0;
2426
2786
  right: 0;
2427
- margin-top: var(--select-dropdown-margin-top, 4px);
2428
- max-height: var(--select-dropdown-max-height, 300px);
2787
+ max-height: var(--select-dropdown-max-height, 320px);
2429
2788
  overflow: hidden;
2430
- background: var(--select-dropdown-bg, var(--select-bg, white));
2431
- border: 1px solid var(--select-dropdown-border, #ccc);
2432
- border-radius: var(--select-dropdown-border-radius, 4px);
2433
- box-shadow: var(--select-dropdown-shadow, 0 4px 6px rgba(0,0,0,0.1));
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));
2434
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
+ }
2435
2807
  }
2436
2808
 
2437
2809
  .options-container {
2438
2810
  position: relative;
2439
- max-height: var(--select-options-max-height, 300px);
2811
+ max-height: var(--select-options-max-height, 320px);
2440
2812
  overflow: auto;
2441
- transition: opacity 0.2s ease-in-out;
2442
- background: var(--select-options-bg, var(--select-dropdown-bg, var(--select-bg, white)));
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);
2443
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
+ ───────────────────────────────────────────────────────────────────────── */
2444
2841
 
2445
2842
  .group-header {
2446
- padding: var(--select-group-header-padding, 8px 12px);
2843
+ padding: var(--select-group-header-padding, 10px 12px 6px);
2447
2844
  font-weight: var(--select-group-header-weight, 600);
2448
- color: var(--select-group-header-color, #6b7280);
2449
- background-color: var(--select-group-header-bg, #f3f4f6);
2845
+ color: var(--select-group-header-color, var(--select-text-muted));
2846
+ background-color: var(--select-group-header-bg, var(--select-surface));
2450
2847
  text-align: var(--select-group-header-text-align, left);
2451
- font-size: var(--select-group-header-font-size, 12px);
2848
+ font-size: var(--select-group-header-font-size, 11px);
2452
2849
  text-transform: var(--select-group-header-text-transform, uppercase);
2453
- letter-spacing: var(--select-group-header-letter-spacing, 0.05em);
2850
+ letter-spacing: var(--select-group-header-letter-spacing, 0.08em);
2454
2851
  position: sticky;
2455
2852
  top: 0;
2456
2853
  z-index: 1;
2457
- border-bottom: var(--select-group-header-border-bottom, 1px solid #e5e7eb);
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);
2458
2861
  }
2459
2862
 
2863
+ /* ─────────────────────────────────────────────────────────────────────────
2864
+ Options — Elegant hover and selection states
2865
+ ───────────────────────────────────────────────────────────────────────── */
2866
+
2460
2867
  .option {
2461
- padding: var(--select-option-padding, 8px 12px);
2868
+ position: relative;
2869
+ padding: var(--select-option-padding, 10px 14px);
2462
2870
  cursor: pointer;
2463
- color: var(--select-option-color, var(--select-text-color, #1f2937));
2464
- background: var(--select-option-bg, var(--select-dropdown-bg, var(--select-bg, white)));
2465
- transition: var(--select-option-transition, background-color 0.15s ease);
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);
2466
2878
  user-select: none;
2467
2879
  font-size: var(--select-option-font-size, 14px);
2880
+ font-weight: var(--select-option-font-weight);
2468
2881
  line-height: var(--select-option-line-height, 1.5);
2469
- border: var(--select-option-border, none);
2470
- border-bottom: var(--select-option-border-bottom, none);
2471
- border-radius: var(--select-option-border-radius, 0);
2472
- box-shadow: var(--select-option-shadow, none);
2473
- transform: var(--select-option-transform, none);
2882
+ border-radius: var(--select-option-border-radius, var(--select-radius-sm));
2883
+ margin: var(--select-option-margin);
2474
2884
  }
2475
2885
 
2476
2886
  .option:hover {
2477
- background: var(--select-option-hover-bg, #f3f4f6);
2478
- color: var(--select-option-hover-color, #1f2937);
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);
2479
2890
  }
2480
2891
 
2481
2892
  .option.selected {
2482
- background: var(--select-option-selected-bg, #e0e7ff);
2483
- color: var(--select-option-selected-color, #4338ca);
2484
- font-weight: var(--select-option-selected-weight, 500);
2485
- border: var(--select-option-selected-border, var(--select-option-border, none));
2486
- border-bottom: var(--select-option-selected-border-bottom, var(--select-option-border-bottom, none));
2487
- border-radius: var(--select-option-selected-border-radius, var(--select-option-border-radius, 0));
2488
- box-shadow: var(--select-option-selected-shadow, var(--select-option-shadow, none));
2489
- transform: var(--select-option-selected-transform, var(--select-option-transform, none));
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
+ }
2490
2920
  }
2491
2921
 
2492
2922
  .option.selected:hover {
2493
- background: var(--select-option-selected-hover-bg, var(--select-option-selected-bg, #e0e7ff));
2494
- color: var(--select-option-selected-hover-color, var(--select-option-selected-color, #4338ca));
2495
- border: var(--select-option-selected-hover-border, var(--select-option-selected-border, var(--select-option-border, none)));
2496
- border-bottom: var(--select-option-selected-hover-border-bottom, var(--select-option-selected-border-bottom, var(--select-option-border-bottom, none)));
2497
- box-shadow: var(--select-option-selected-hover-shadow, var(--select-option-selected-shadow, var(--select-option-shadow, none)));
2498
- 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%));
2499
2924
  }
2500
2925
 
2501
2926
  .option.active:not(.selected) {
2502
- background: var(--select-option-active-bg, #f3f4f6);
2503
- color: var(--select-option-active-color, #1f2937);
2504
- outline: var(--select-option-active-outline, 2px solid rgba(99, 102, 241, 0.45));
2505
- 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);
2506
2930
  }
2507
2931
 
2508
2932
  .option.selected.active {
2509
- background: var(--select-option-selected-active-bg, var(--select-option-selected-bg, #e0e7ff));
2510
- color: var(--select-option-selected-active-color, var(--select-option-selected-color, #4338ca));
2511
- border: var(--select-option-selected-active-border, var(--select-option-selected-border, var(--select-option-border, none)));
2512
- border-bottom: var(--select-option-selected-active-border-bottom, var(--select-option-selected-border-bottom, var(--select-option-border-bottom, none)));
2513
- box-shadow: var(--select-option-selected-active-shadow, var(--select-option-selected-shadow, var(--select-option-shadow, none)));
2514
- transform: var(--select-option-selected-active-transform, var(--select-option-selected-transform, var(--select-option-transform, none)));
2515
- outline: var(--select-option-selected-active-outline, var(--select-option-active-outline, 2px solid rgba(99, 102, 241, 0.45)));
2516
- 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);
2517
2935
  }
2518
2936
 
2519
2937
  .option:active:not(.selected) {
2520
- background: var(--select-option-pressed-bg, #e5e7eb);
2938
+ background: var(--select-option-pressed-bg, rgba(15, 52, 96, 0.08));
2939
+ transform: var(--select-option-pressed-transform);
2521
2940
  }
2522
2941
 
2523
2942
  .option.selected:active {
2524
- background: var(--select-option-selected-pressed-bg, var(--select-option-selected-hover-bg, var(--select-option-selected-bg, #e0e7ff)));
2943
+ transform: var(--select-option-selected-pressed-transform);
2525
2944
  }
2526
2945
 
2946
+ /* ─────────────────────────────────────────────────────────────────────────
2947
+ Load More & Busy States — Refined feedback indicators
2948
+ ───────────────────────────────────────────────────────────────────────── */
2949
+
2527
2950
  .load-more-container {
2528
2951
  padding: var(--select-load-more-padding, 12px);
2529
2952
  text-align: center;
2530
- border-top: var(--select-divider-border, 1px solid #e0e0e0);
2531
- background: var(--select-load-more-bg, white);
2532
2953
  }
2533
2954
 
2534
2955
  .load-more-button {
2535
- padding: var(--select-button-padding, 8px 16px);
2536
- border: var(--select-button-border, 1px solid #1976d2);
2537
- background: var(--select-button-bg, white);
2538
- color: var(--select-button-color, #1976d2);
2539
- border-radius: var(--select-button-border-radius, 4px);
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));
2540
2961
  cursor: pointer;
2541
- font-size: var(--select-button-font-size, 14px);
2542
- font-family: var(--select-font-family, inherit);
2543
- transition: all 0.2s ease;
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);
2544
2970
  }
2545
2971
 
2546
2972
  .load-more-button:hover {
2547
- background: var(--select-button-hover-bg, #1976d2);
2973
+ background: var(--select-button-hover-bg, var(--select-accent));
2974
+ border-color: var(--select-accent);
2548
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);
2549
2981
  }
2550
2982
 
2551
2983
  .load-more-button:disabled {
2552
2984
  opacity: var(--select-button-disabled-opacity, 0.5);
2553
2985
  cursor: not-allowed;
2986
+ transform: none;
2554
2987
  }
2555
2988
 
2556
2989
  .busy-bucket {
2557
- padding: var(--select-busy-padding, 16px);
2990
+ padding: var(--select-busy-padding, 20px);
2558
2991
  text-align: center;
2559
- color: var(--select-busy-color, #666);
2560
- background: var(--select-busy-bg, white);
2561
- font-size: var(--select-busy-font-size, 14px);
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);
2562
2995
  }
2563
2996
 
2564
2997
  .spinner {
2565
2998
  display: inline-block;
2566
- width: var(--select-spinner-size, 20px);
2567
- height: var(--select-spinner-size, 20px);
2568
- border: var(--select-spinner-border, 2px solid #ccc);
2569
- border-top-color: var(--select-spinner-active-color, #1976d2);
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));
2570
3003
  border-radius: 50%;
2571
- animation: spin 0.6s linear infinite;
3004
+ animation: var(--select-spinner-animation);
2572
3005
  }
2573
3006
 
2574
3007
  @keyframes spin {
@@ -2576,61 +3009,97 @@ class EnhancedSelect extends HTMLElement {
2576
3009
  }
2577
3010
 
2578
3011
  .empty-state {
2579
- padding: var(--select-empty-padding, 24px);
3012
+ padding: var(--select-empty-padding, 32px 24px);
2580
3013
  text-align: center;
2581
- color: var(--select-empty-color, #6b7280);
3014
+ color: var(--select-empty-color, var(--select-text-muted));
2582
3015
  font-size: var(--select-empty-font-size, 14px);
2583
- background: var(--select-empty-bg, white);
3016
+ background: var(--select-empty-bg, transparent);
2584
3017
  display: flex;
2585
3018
  flex-direction: column;
2586
3019
  align-items: center;
2587
3020
  justify-content: center;
2588
- gap: 6px;
2589
- min-height: var(--select-empty-min-height, 72px);
3021
+ gap: var(--select-empty-gap);
2590
3022
  }
2591
3023
 
2592
3024
  .searching-state {
2593
- padding: var(--select-searching-padding, 24px);
3025
+ padding: var(--select-searching-padding, 32px 24px);
2594
3026
  text-align: center;
2595
- color: var(--select-searching-color, #667eea);
3027
+ color: var(--select-searching-color, var(--select-accent));
2596
3028
  font-size: var(--select-searching-font-size, 14px);
2597
- font-style: italic;
2598
- background: var(--select-searching-bg, white);
2599
- animation: pulse 1.5s ease-in-out infinite;
3029
+ background: var(--select-searching-bg, transparent);
2600
3030
  display: flex;
2601
3031
  flex-direction: column;
2602
3032
  align-items: center;
2603
3033
  justify-content: center;
2604
- gap: 6px;
2605
- min-height: var(--select-searching-min-height, 72px);
3034
+ gap: var(--select-searching-gap);
2606
3035
  }
2607
3036
 
2608
- @keyframes pulse {
2609
- 0%, 100% { opacity: 1; }
2610
- 50% { opacity: 0.5; }
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);
2611
3045
  }
2612
3046
 
2613
- /* Error states */
3047
+ /* ─────────────────────────────────────────────────────────────────────────
3048
+ Error States — Clear visual feedback
3049
+ ───────────────────────────────────────────────────────────────────────── */
3050
+
2614
3051
  .select-input[aria-invalid="true"] {
2615
- border-color: var(--select-error-border, #dc2626);
3052
+ border-color: var(--select-error-border, #e94560);
2616
3053
  }
2617
3054
 
2618
3055
  .select-input[aria-invalid="true"]:focus {
2619
- border-color: var(--select-error-border, #dc2626);
2620
- box-shadow: 0 0 0 2px var(--select-error-shadow, rgba(220, 38, 38, 0.1));
2621
- 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));
2622
3058
  }
2623
3059
 
2624
- /* Accessibility: Reduced motion */
3060
+ /* ──────────────────────────────────────────────────��──────────────────────
3061
+ Accessibility — Reduced motion & High contrast
3062
+ ───────────────────────────────────────────────────────────────────────── */
3063
+
2625
3064
  @media (prefers-reduced-motion: reduce) {
2626
- * {
2627
- animation-duration: 0.01ms !important;
2628
- animation-iteration-count: 1 !important;
2629
- transition-duration: 0.01ms !important;
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);
2630
3075
  }
2631
3076
  }
2632
3077
 
2633
- /* Dark mode - Opt-in via class, data attribute, or ancestor context */
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
+
2634
3103
  :host(.dark-mode),
2635
3104
  :host([dark-mode]),
2636
3105
  :host([darkmode]),
@@ -2642,139 +3111,129 @@ class EnhancedSelect extends HTMLElement {
2642
3111
  :host-context([darkmode]),
2643
3112
  :host-context([data-theme="dark"]),
2644
3113
  :host-context([theme="dark"]) {
2645
- /* map dark tokens to base option tokens so nested <select-option>
2646
- components also pick up dark mode via inherited CSS variables */
2647
- --select-option-bg: var(--select-dark-option-bg, #1f2937);
2648
- --select-option-color: var(--select-dark-option-color, #f9fafb);
2649
- --select-option-hover-bg: var(--select-dark-option-hover-bg, #374151);
2650
- --select-option-hover-color: var(--select-dark-option-hover-color, #f9fafb);
2651
- --select-option-selected-bg: var(--select-dark-option-selected-bg, #3730a3);
2652
- --select-option-selected-color: var(--select-dark-option-selected-text, #e0e7ff);
2653
-
2654
- .input-container {
2655
- background: var(--select-dark-bg, #1f2937);
2656
- border-color: var(--select-dark-border, #4b5563);
2657
- }
2658
-
2659
- .select-input {
2660
- color: var(--select-dark-text, #f9fafb);
2661
- }
2662
-
2663
- .select-input::placeholder {
2664
- color: var(--select-dark-placeholder, #6b7280);
2665
- }
2666
-
2667
- .select-dropdown {
2668
- background: var(--select-dark-dropdown-bg, #1f2937);
2669
- border-color: var(--select-dark-dropdown-border, #4b5563);
2670
- }
2671
-
2672
- .options-container {
2673
- background: var(--select-dark-options-bg, #1f2937);
2674
- }
2675
-
2676
- .option {
2677
- color: var(--select-dark-option-color, #f9fafb);
2678
- background: var(--select-dark-option-bg, #1f2937);
2679
- }
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);
2680
3129
 
2681
- .option:hover {
2682
- background: var(--select-dark-option-hover-bg, #374151);
2683
- color: var(--select-dark-option-hover-color, #f9fafb);
2684
- }
2685
-
2686
- .option.selected {
2687
- background: var(--select-dark-option-selected-bg, #3730a3);
2688
- color: var(--select-dark-option-selected-text, #e0e7ff);
2689
- border: var(--select-dark-option-selected-border, var(--select-option-selected-border, var(--select-option-border, none)));
2690
- border-bottom: var(--select-dark-option-selected-border-bottom, var(--select-option-selected-border-bottom, var(--select-option-border-bottom, none)));
2691
- box-shadow: var(--select-dark-option-selected-shadow, var(--select-option-selected-shadow, var(--select-option-shadow, none)));
2692
- transform: var(--select-dark-option-selected-transform, var(--select-option-selected-transform, var(--select-option-transform, none)));
2693
- }
2694
-
2695
- .option.selected:hover {
2696
- background: var(--select-dark-option-selected-hover-bg, var(--select-dark-option-selected-bg, #3730a3));
2697
- color: var(--select-dark-option-selected-hover-color, var(--select-dark-option-selected-text, #e0e7ff));
2698
- 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)))));
2699
- 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)))));
2700
- 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)))));
2701
- 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)))));
2702
- }
2703
-
2704
- .option.active:not(.selected) {
2705
- background-color: var(--select-dark-option-active-bg, #374151);
2706
- color: var(--select-dark-option-active-color, #f9fafb);
2707
- outline: var(--select-dark-option-active-outline, 2px solid rgba(129, 140, 248, 0.55));
2708
- }
2709
-
2710
- /* Group header in dark mode */
2711
- .group-header {
2712
- color: var(--select-dark-group-header-color, var(--select-group-header-color, #6b7280));
2713
- background-color: var(--select-dark-group-header-bg, var(--select-group-header-bg, #374151));
2714
- }
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
+ }
2715
3137
 
2716
- .option.selected.active {
2717
- background-color: var(--select-dark-option-selected-active-bg, var(--select-dark-option-selected-bg, #3730a3));
2718
- color: var(--select-dark-option-selected-active-color, var(--select-dark-option-selected-text, #e0e7ff));
2719
- border: var(--select-dark-option-selected-active-border, var(--select-dark-option-selected-border, var(--select-option-selected-active-border, var(--select-option-selected-border, var(--select-option-border, none)))));
2720
- border-bottom: var(--select-dark-option-selected-active-border-bottom, var(--select-dark-option-selected-border-bottom, var(--select-option-selected-active-border-bottom, var(--select-option-selected-border-bottom, var(--select-option-border-bottom, none)))));
2721
- box-shadow: var(--select-dark-option-selected-active-shadow, var(--select-dark-option-selected-shadow, var(--select-option-selected-active-shadow, var(--select-option-selected-shadow, var(--select-option-shadow, none)))));
2722
- transform: var(--select-dark-option-selected-active-transform, var(--select-dark-option-selected-transform, var(--select-option-selected-active-transform, var(--select-option-selected-transform, var(--select-option-transform, none)))));
2723
- outline: var(--select-dark-option-selected-active-outline, var(--select-dark-option-active-outline, var(--select-option-selected-active-outline, var(--select-option-active-outline, 2px solid rgba(129, 140, 248, 0.55)))));
2724
- outline-offset: -2px;
2725
- }
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
+ }
2726
3152
 
2727
- .selection-badge {
2728
- background: var(--select-dark-badge-bg, #4f46e5);
2729
- color: var(--select-dark-badge-color, #eef2ff);
2730
- }
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
+ }
2731
3166
 
2732
- .badge-remove {
2733
- background: var(--select-dark-badge-remove-bg, rgba(255, 255, 255, 0.15));
2734
- color: var(--select-dark-badge-remove-color, #e5e7eb);
2735
- }
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
+ }
2736
3181
 
2737
- .badge-remove:hover {
2738
- background: var(--select-dark-badge-remove-hover-bg, rgba(255, 255, 255, 0.3));
2739
- }
2740
-
2741
- .busy-bucket,
2742
- .empty-state {
2743
- color: var(--select-dark-busy-color, #9ca3af);
2744
- background: var(--select-dark-empty-bg, #111827);
2745
- }
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
+ }
2746
3196
 
2747
- .searching-state {
2748
- background: var(--select-dark-searching-bg, #111827);
2749
- }
2750
-
2751
- .input-container::after {
2752
- background: linear-gradient(
2753
- to bottom,
2754
- transparent 0%,
2755
- rgba(255, 255, 255, 0.1) 20%,
2756
- rgba(255, 255, 255, 0.1) 80%,
2757
- transparent 100%
2758
- );
2759
- }
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);
2760
3209
  }
2761
-
2762
- /* Accessibility: High contrast mode */
2763
- @media (prefers-contrast: high) {
2764
- .select-input:focus {
2765
- outline-width: 3px;
2766
- outline-color: Highlight;
2767
- }
2768
-
2769
- .select-input {
2770
- border-width: 2px;
2771
- }
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);
2772
3223
  }
2773
-
2774
- /* Touch targets (WCAG 2.5.5) */
2775
- .load-more-button,
2776
- select-option {
2777
- min-height: 44px;
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);
2778
3237
  }
2779
3238
  `;
2780
3239
  // Insert as first child to ensure styles are processed first
@@ -2794,10 +3253,10 @@ class EnhancedSelect extends HTMLElement {
2794
3253
  // delegate to the existing open/close helpers so we don't accidentally
2795
3254
  // drift out of sync with the logic in those methods (focus, events,
2796
3255
  // scroll-to-selected, etc.)
2797
- if (this._state.isOpen) {
3256
+ if (this._state.isOpen && this._config.selection.toggleOnTriggerClick !== false) {
2798
3257
  this._handleClose();
2799
3258
  }
2800
- else {
3259
+ else if (!this._state.isOpen) {
2801
3260
  this._handleOpen();
2802
3261
  }
2803
3262
  };
@@ -2820,7 +3279,7 @@ class EnhancedSelect extends HTMLElement {
2820
3279
  this._inputContainer.addEventListener('pointerdown', (e) => {
2821
3280
  // Prevent propagation to document click listener but do NOT preventDefault.
2822
3281
  // Allow default so browser events (click) on newly opened options still fire.
2823
- 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.
2824
3283
  const target = e.target;
2825
3284
  if (!this._config.enabled)
2826
3285
  return;
@@ -2828,21 +3287,23 @@ class EnhancedSelect extends HTMLElement {
2828
3287
  return;
2829
3288
  if (target && target.closest('.clear-control-button'))
2830
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
+ }
2831
3302
  const wasClosed = !this._state.isOpen;
2832
3303
  if (wasClosed) {
2833
3304
  this._handleOpen();
2834
3305
  }
2835
- else {
2836
- // Keep open while interacting directly with the input so users can
2837
- // place cursor/type without accidental collapse.
2838
- if (target === this._input) {
2839
- this._input.focus();
2840
- return;
2841
- }
2842
- // clicking other parts of the input container while open toggles close
2843
- this._handleClose();
2844
- }
2845
- // Focus the input (do not prevent default behavior)
3306
+ // Focus the input (do not prevent default behavior for input itself)
2846
3307
  this._input.focus();
2847
3308
  // If we just opened the dropdown, transfer pointer capture to the
2848
3309
  // options container so the subsequent pointerup lands there instead of
@@ -2884,7 +3345,7 @@ class EnhancedSelect extends HTMLElement {
2884
3345
  return;
2885
3346
  }
2886
3347
  this._handleClose();
2887
- }, 0);
3348
+ }, 150);
2888
3349
  });
2889
3350
  // Input search
2890
3351
  this._input.addEventListener('input', (e) => {
@@ -2901,7 +3362,7 @@ class EnhancedSelect extends HTMLElement {
2901
3362
  this._suppressBlurClose = true;
2902
3363
  setTimeout(() => {
2903
3364
  this._suppressBlurClose = false;
2904
- }, 0);
3365
+ }, 150); // Increased timeout to ensure click finishes before blur checks
2905
3366
  });
2906
3367
  // Delegated click listener for improved event handling (robust across shadow DOM)
2907
3368
  const handleOptionEvent = (e) => {
@@ -2932,9 +3393,6 @@ class EnhancedSelect extends HTMLElement {
2932
3393
  }
2933
3394
  };
2934
3395
  this._optionsContainer.addEventListener('click', handleOptionEvent);
2935
- // also watch pointerup to catch cases where the pointerdown started outside
2936
- // (e.g. on the input) and the click never fires
2937
- this._optionsContainer.addEventListener('pointerup', handleOptionEvent);
2938
3396
  // Keyboard navigation
2939
3397
  this._input.addEventListener('keydown', (e) => this._handleKeydown(e));
2940
3398
  // Click outside to close — robust detection across shadow DOM and custom renderers
@@ -3032,17 +3490,6 @@ class EnhancedSelect extends HTMLElement {
3032
3490
  this._dropdown.style.display = 'block';
3033
3491
  this._input.setAttribute('aria-expanded', 'true');
3034
3492
  this._updateArrowRotation();
3035
- // Clear search query when opening to show all options
3036
- // This ensures we can scroll to selected item
3037
- if (this._config.searchable) {
3038
- this._state.searchQuery = '';
3039
- // Don't clear input value if it represents selection
3040
- // But if we want to search, we might want to clear it?
3041
- // Standard behavior: input keeps value (label), but dropdown shows all options
3042
- // until user types.
3043
- // However, our filtering logic uses _state.searchQuery.
3044
- // So clearing it here resets the filter.
3045
- }
3046
3493
  // Render options when opening
3047
3494
  this._renderOptions();
3048
3495
  this._setInitialActiveOption();
@@ -3288,24 +3735,24 @@ class EnhancedSelect extends HTMLElement {
3288
3735
  }
3289
3736
  }
3290
3737
  _setActive(index) {
3291
- const options = Array.from(this._optionsContainer.children);
3292
3738
  // Clear previous active state
3293
- if (this._state.activeIndex >= 0 && options[this._state.activeIndex]) {
3294
- const prevOption = options[this._state.activeIndex];
3295
- // Check if it's a custom SelectOption or a lightweight DOM element
3296
- if ('setActive' in prevOption && typeof prevOption.setActive === 'function') {
3297
- prevOption.setActive(false);
3298
- }
3299
- else {
3300
- // Lightweight option - remove active class
3301
- prevOption.classList.remove('smilodon-option--active');
3302
- prevOption.setAttribute('aria-selected', 'false');
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
+ }
3303
3750
  }
3304
3751
  }
3305
3752
  this._state.activeIndex = index;
3306
3753
  // Set new active state
3307
- if (options[index]) {
3308
- const option = options[index];
3754
+ const option = this._getOptionElementByIndex(index);
3755
+ if (option) {
3309
3756
  // Check if it's a custom SelectOption or a lightweight DOM element
3310
3757
  if ('setActive' in option && typeof option.setActive === 'function') {
3311
3758
  option.setActive(true);
@@ -3313,13 +3760,13 @@ class EnhancedSelect extends HTMLElement {
3313
3760
  else {
3314
3761
  // Lightweight option - add active class
3315
3762
  option.classList.add('smilodon-option--active');
3316
- option.setAttribute('aria-selected', 'true');
3317
3763
  }
3318
3764
  if (typeof option.scrollIntoView === 'function') {
3765
+ // Don't scroll wildly when just opening with pre-selections
3319
3766
  option.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
3320
3767
  }
3321
3768
  // Announce position for screen readers
3322
- const total = options.length;
3769
+ const total = this._state.loadedItems.length;
3323
3770
  this._announce(`Item ${index + 1} of ${total}`);
3324
3771
  // Update aria-activedescendant using the actual option id when available
3325
3772
  const optionId = option.id || `${this._uniqueId}-option-${index}`;
@@ -3442,9 +3889,9 @@ class EnhancedSelect extends HTMLElement {
3442
3889
  // FIX: Do not rely on this._optionsContainer.children[index] because filtering changes the children
3443
3890
  // Instead, use the index to update state directly
3444
3891
  const item = this._state.loadedItems[index];
3445
- // Debug: log selection attempt
3446
- if (!item)
3892
+ if (!item) {
3447
3893
  return;
3894
+ }
3448
3895
  const isCurrentlySelected = this._state.selectedIndices.has(index);
3449
3896
  // Keep active/focus styling aligned with the most recently interacted option.
3450
3897
  // Without this, a previously selected item may retain active classes/styles
@@ -3455,7 +3902,7 @@ class EnhancedSelect extends HTMLElement {
3455
3902
  const wasSelected = this._state.selectedIndices.has(index);
3456
3903
  this._state.selectedIndices.clear();
3457
3904
  this._state.selectedItems.clear();
3458
- if (!wasSelected) {
3905
+ if (!wasSelected || !this._config.selection.allowDeselect) {
3459
3906
  // Select this option
3460
3907
  this._state.selectedIndices.add(index);
3461
3908
  this._state.selectedItems.set(index, item);
@@ -3517,16 +3964,34 @@ class EnhancedSelect extends HTMLElement {
3517
3964
  });
3518
3965
  }
3519
3966
  _handleOptionRemove(index) {
3967
+ const item = this._state.selectedItems.get(index);
3520
3968
  const option = this._getOptionElementByIndex(index);
3521
- if (!option)
3522
- return;
3523
3969
  this._state.selectedIndices.delete(index);
3524
3970
  this._state.selectedItems.delete(index);
3525
- option.setSelected(false);
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
+ }
3526
3988
  this._updateInputDisplay();
3989
+ this._renderOptions();
3527
3990
  this._emitChange();
3528
- const config = option.getConfig();
3529
- this._emit('remove', { item: config.item, index });
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 });
3530
3995
  }
3531
3996
  _updateInputDisplay() {
3532
3997
  const selectedItems = Array.from(this._state.selectedItems.values());
@@ -3554,22 +4019,29 @@ class EnhancedSelect extends HTMLElement {
3554
4019
  const badge = document.createElement('span');
3555
4020
  badge.className = 'selection-badge';
3556
4021
  badge.setAttribute('part', 'chip');
3557
- badge.textContent = getLabel(item);
4022
+ const badgeLabel = document.createElement('span');
4023
+ badgeLabel.className = 'selection-badge-label';
4024
+ badgeLabel.textContent = getLabel(item);
4025
+ badge.appendChild(badgeLabel);
3558
4026
  // Add remove button to badge
3559
- const removeBtn = document.createElement('button');
3560
- removeBtn.className = 'badge-remove';
3561
- removeBtn.setAttribute('part', 'chip-remove');
3562
- removeBtn.innerHTML = '×';
3563
- removeBtn.setAttribute('aria-label', `Remove ${getLabel(item)}`);
3564
- removeBtn.addEventListener('click', (e) => {
3565
- e.stopPropagation();
3566
- this._state.selectedIndices.delete(index);
3567
- this._state.selectedItems.delete(index);
3568
- this._updateInputDisplay();
3569
- this._renderOptions();
3570
- this._emitChange();
3571
- });
3572
- badge.appendChild(removeBtn);
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
+ }
3573
4045
  this._inputContainer.insertBefore(badge, this._input);
3574
4046
  });
3575
4047
  }
@@ -3621,10 +4093,12 @@ class EnhancedSelect extends HTMLElement {
3621
4093
  const option = this._getOptionElementByIndex(targetIndex);
3622
4094
  if (option) {
3623
4095
  // Use smooth scrolling with center alignment for better UX
3624
- option.scrollIntoView({
3625
- block: this._config.scrollToSelected.block || 'center',
3626
- behavior: 'smooth',
3627
- });
4096
+ if (typeof option.scrollIntoView === 'function') {
4097
+ option.scrollIntoView({
4098
+ block: this._config.scrollToSelected.block || 'center',
4099
+ behavior: 'smooth',
4100
+ });
4101
+ }
3628
4102
  // Also set it as active for keyboard navigation
3629
4103
  this._setActive(targetIndex);
3630
4104
  }
@@ -3674,13 +4148,168 @@ class EnhancedSelect extends HTMLElement {
3674
4148
  }
3675
4149
  _emit(name, detail) {
3676
4150
  this.dispatchEvent(new CustomEvent(name, { detail, bubbles: true, composed: true }));
4151
+ if (name !== 'diagnostic') {
4152
+ this._track('event', String(name), detail);
4153
+ }
4154
+ }
4155
+ _track(source, name, detail) {
4156
+ const cfg = this._config.tracking;
4157
+ if (!cfg?.enabled)
4158
+ return;
4159
+ if (source === 'event' && !cfg.events)
4160
+ return;
4161
+ if (source === 'style' && !cfg.styling)
4162
+ return;
4163
+ if (source === 'limitation' && !cfg.limitations)
4164
+ return;
4165
+ const entry = {
4166
+ timestamp: Date.now(),
4167
+ source,
4168
+ name,
4169
+ detail,
4170
+ };
4171
+ const bucket = source === 'event'
4172
+ ? this._tracking.events
4173
+ : source === 'style'
4174
+ ? this._tracking.styles
4175
+ : this._tracking.limitations;
4176
+ bucket.push(entry);
4177
+ const maxEntries = Math.max(10, cfg.maxEntries || 200);
4178
+ if (bucket.length > maxEntries) {
4179
+ bucket.splice(0, bucket.length - maxEntries);
4180
+ }
4181
+ if (cfg.emitDiagnostics) {
4182
+ this.dispatchEvent(new CustomEvent('diagnostic', {
4183
+ detail: entry,
4184
+ bubbles: true,
4185
+ composed: true,
4186
+ }));
4187
+ }
4188
+ }
4189
+ _getKnownLimitationDefinitions() {
4190
+ return [
4191
+ {
4192
+ id: 'variableItemHeight',
4193
+ title: 'Variable item height',
4194
+ description: 'Virtualization assumes fixed or estimated item heights; fully dynamic heights are not yet supported.',
4195
+ workaround: 'Use consistent item heights or set estimatedItemHeight to your dominant row size.',
4196
+ },
4197
+ {
4198
+ id: 'builtInFetchPaginationApi',
4199
+ title: 'Built-in fetch/pagination API',
4200
+ description: 'Core does not include a built-in fetchUrl/searchUrl pagination transport.',
4201
+ workaround: 'Use onSearch/onLoadMore callbacks and update data via setItems().',
4202
+ },
4203
+ {
4204
+ id: 'virtualizationOverheadSmallLists',
4205
+ title: 'Virtualization overhead for small lists',
4206
+ description: 'Virtualization can add slight overhead on very small lists.',
4207
+ workaround: 'Disable virtualization for tiny datasets when micro-latency is critical.',
4208
+ },
4209
+ {
4210
+ id: 'runtimeModeSwitching',
4211
+ title: 'Runtime single/multi mode switching',
4212
+ description: 'Switching between single and multi mode can require state reset for consistency.',
4213
+ workaround: 'Enable autoMitigateRuntimeModeSwitch or recreate/reset component state when toggling modes.',
4214
+ },
4215
+ {
4216
+ id: 'legacyBrowserSupport',
4217
+ title: 'Legacy browser support',
4218
+ description: 'Official support targets modern evergreen browsers.',
4219
+ },
4220
+ {
4221
+ id: 'webkitArchLinux',
4222
+ title: 'Playwright WebKit on Arch-based Linux',
4223
+ description: 'Native WebKit Playwright bundle depends on unavailable legacy system libraries on Arch-based distros.',
4224
+ workaround: 'Run WebKit E2E tests via Playwright Docker image.',
4225
+ },
4226
+ ];
4227
+ }
4228
+ _evaluateLimitationStatus(id) {
4229
+ const policyMode = this._config.limitations?.policies?.[id]?.mode ?? 'default';
4230
+ if (policyMode === 'suppress')
4231
+ return 'suppressed';
4232
+ if (id === 'runtimeModeSwitching' && this._config.limitations?.autoMitigateRuntimeModeSwitch) {
4233
+ return 'mitigated';
4234
+ }
4235
+ return 'active';
4236
+ }
4237
+ getKnownLimitations() {
4238
+ return this._getKnownLimitationDefinitions().map((limitation) => {
4239
+ const mode = this._config.limitations?.policies?.[limitation.id]?.mode ?? 'default';
4240
+ return {
4241
+ ...limitation,
4242
+ mode,
4243
+ status: this._evaluateLimitationStatus(limitation.id),
4244
+ };
4245
+ });
4246
+ }
4247
+ setLimitationPolicies(policies) {
4248
+ const next = {
4249
+ ...(this._config.limitations?.policies || {}),
4250
+ ...policies,
4251
+ };
4252
+ this.updateConfig({
4253
+ limitations: {
4254
+ ...(this._config.limitations || { autoMitigateRuntimeModeSwitch: true, policies: {} }),
4255
+ policies: next,
4256
+ },
4257
+ });
4258
+ this._track('limitation', 'policiesUpdated', { policies: next });
4259
+ }
4260
+ getTrackingSnapshot() {
4261
+ return {
4262
+ events: [...this._tracking.events],
4263
+ styles: [...this._tracking.styles],
4264
+ limitations: [...this._tracking.limitations],
4265
+ };
4266
+ }
4267
+ clearTracking(source) {
4268
+ if (!source || source === 'all') {
4269
+ this._tracking.events = [];
4270
+ this._tracking.styles = [];
4271
+ this._tracking.limitations = [];
4272
+ return;
4273
+ }
4274
+ if (source === 'event')
4275
+ this._tracking.events = [];
4276
+ if (source === 'style')
4277
+ this._tracking.styles = [];
4278
+ if (source === 'limitation')
4279
+ this._tracking.limitations = [];
4280
+ }
4281
+ getCapabilities() {
4282
+ return {
4283
+ styling: {
4284
+ classMap: true,
4285
+ optionRenderer: true,
4286
+ groupHeaderRenderer: true,
4287
+ cssCustomProperties: true,
4288
+ shadowParts: true,
4289
+ globalStyleMirroring: true,
4290
+ },
4291
+ events: {
4292
+ emitted: ['select', 'open', 'close', 'search', 'change', 'loadMore', 'remove', 'clear', 'error', 'diagnostic'],
4293
+ diagnosticEvent: true,
4294
+ },
4295
+ functionality: {
4296
+ multiSelect: true,
4297
+ searchable: true,
4298
+ infiniteScroll: true,
4299
+ loadMore: true,
4300
+ clearControl: true,
4301
+ groupedItems: true,
4302
+ serverSideSelection: true,
4303
+ runtimeModeSwitchMitigation: Boolean(this._config.limitations?.autoMitigateRuntimeModeSwitch),
4304
+ },
4305
+ limitations: this.getKnownLimitations(),
4306
+ };
3677
4307
  }
3678
4308
  _emitChange() {
3679
4309
  const selectedItems = Array.from(this._state.selectedItems.values());
3680
4310
  const getValue = this._config.serverSide.getValueFromItem || ((item) => item?.value ?? item);
3681
4311
  const selectedValues = selectedItems.map(getValue);
3682
4312
  const selectedIndices = Array.from(this._state.selectedIndices);
3683
- // Debug: log change payload
3684
4313
  this._emit('change', { selectedItems, selectedValues, selectedIndices });
3685
4314
  this._config.callbacks.onChange?.(selectedItems, selectedValues);
3686
4315
  }
@@ -3691,6 +4320,7 @@ class EnhancedSelect extends HTMLElement {
3691
4320
  set optionRenderer(renderer) {
3692
4321
  this._optionRenderer = renderer;
3693
4322
  this._setGlobalStylesMirroring(Boolean(renderer || this._classMap));
4323
+ this._track('style', 'optionRendererChanged', { enabled: Boolean(renderer) });
3694
4324
  this._renderOptions();
3695
4325
  }
3696
4326
  /**
@@ -3855,7 +4485,22 @@ class EnhancedSelect extends HTMLElement {
3855
4485
  * Update component configuration
3856
4486
  */
3857
4487
  updateConfig(config) {
3858
- this._config = selectConfig.mergeWithComponentConfig(config);
4488
+ const previousMode = this._config.selection.mode;
4489
+ this._config = this._mergeConfig(this._config, config);
4490
+ if (previousMode !== this._config.selection.mode &&
4491
+ this._config.limitations?.autoMitigateRuntimeModeSwitch) {
4492
+ this.clear();
4493
+ this._track('limitation', 'runtimeModeSwitchMitigated', {
4494
+ from: previousMode,
4495
+ to: this._config.selection.mode,
4496
+ });
4497
+ }
4498
+ else if (previousMode !== this._config.selection.mode) {
4499
+ this._track('limitation', 'runtimeModeSwitchDetected', {
4500
+ from: previousMode,
4501
+ to: this._config.selection.mode,
4502
+ });
4503
+ }
3859
4504
  // Update input state based on new config
3860
4505
  if (this._input) {
3861
4506
  this._input.readOnly = !this._config.searchable;
@@ -3868,7 +4513,7 @@ class EnhancedSelect extends HTMLElement {
3868
4513
  this._clearControl.setAttribute('aria-label', this._config.clearControl.ariaLabel || 'Clear selection and search');
3869
4514
  }
3870
4515
  if (this._clearControlIcon) {
3871
- this._clearControlIcon.textContent = this._config.clearControl.icon || '×';
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>`;
3872
4517
  }
3873
4518
  if (this._dropdown) {
3874
4519
  if (this._config.selection.mode === 'multi') {
@@ -3878,11 +4523,28 @@ class EnhancedSelect extends HTMLElement {
3878
4523
  this._dropdown.removeAttribute('aria-multiselectable');
3879
4524
  }
3880
4525
  }
4526
+ this._syncInputContainerMode();
3881
4527
  // Re-initialize observers in case infinite scroll was enabled/disabled
3882
4528
  this._initializeObservers();
3883
4529
  this._syncClearControlState();
3884
4530
  this._renderOptions();
3885
4531
  }
4532
+ _mergeConfig(target, source) {
4533
+ const result = { ...target };
4534
+ for (const key in source) {
4535
+ if (!Object.prototype.hasOwnProperty.call(source, key))
4536
+ continue;
4537
+ const sourceValue = source[key];
4538
+ const targetValue = result[key];
4539
+ if (sourceValue && typeof sourceValue === 'object' && !Array.isArray(sourceValue)) {
4540
+ result[key] = this._mergeConfig(targetValue && typeof targetValue === 'object' ? targetValue : {}, sourceValue);
4541
+ }
4542
+ else {
4543
+ result[key] = sourceValue;
4544
+ }
4545
+ }
4546
+ return result;
4547
+ }
3886
4548
  _handleClearControlClick() {
3887
4549
  const shouldClearSelection = this._config.clearControl.clearSelection !== false;
3888
4550
  const shouldClearSearch = this._config.clearControl.clearSearch !== false;
@@ -3967,6 +4629,8 @@ class EnhancedSelect extends HTMLElement {
3967
4629
  * Render options based on current state
3968
4630
  */
3969
4631
  _renderOptions() {
4632
+ this._renderCycleId += 1;
4633
+ const renderCycleId = this._renderCycleId;
3970
4634
  // Cleanup observer
3971
4635
  if (this._loadMoreTrigger && this._intersectionObserver) {
3972
4636
  this._intersectionObserver.unobserve(this._loadMoreTrigger);
@@ -4031,23 +4695,21 @@ class EnhancedSelect extends HTMLElement {
4031
4695
  }
4032
4696
  else {
4033
4697
  // Normal rendering (flat list or filtered)
4034
- let hasRenderedItems = false;
4698
+ const filteredIndices = [];
4035
4699
  this._state.loadedItems.forEach((item, index) => {
4036
- // Apply filter if query exists
4037
4700
  if (query) {
4038
4701
  try {
4039
4702
  const label = String(getLabel(item)).toLowerCase();
4040
4703
  if (!label.includes(query))
4041
4704
  return;
4042
4705
  }
4043
- catch (e) {
4706
+ catch (_e) {
4044
4707
  return;
4045
4708
  }
4046
4709
  }
4047
- hasRenderedItems = true;
4048
- this._renderSingleOption(item, index, getValue, getLabel);
4710
+ filteredIndices.push(index);
4049
4711
  });
4050
- if (!hasRenderedItems && !this._state.isBusy) {
4712
+ if (filteredIndices.length === 0 && !this._state.isBusy) {
4051
4713
  const empty = document.createElement('div');
4052
4714
  empty.setAttribute('part', 'no-results');
4053
4715
  empty.className = 'empty-state';
@@ -4059,6 +4721,54 @@ class EnhancedSelect extends HTMLElement {
4059
4721
  }
4060
4722
  this._optionsContainer.appendChild(empty);
4061
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
+ }
4062
4772
  }
4063
4773
  // Append Busy Indicator if busy
4064
4774
  if (this._state.isBusy && this._config.busyBucket.enabled) {
@@ -4083,7 +4793,7 @@ class EnhancedSelect extends HTMLElement {
4083
4793
  }
4084
4794
  this._finalizePerfMarks();
4085
4795
  }
4086
- _renderSingleOption(item, index, getValue, getLabel) {
4796
+ _renderSingleOption(item, index, getValue, getLabel, targetContainer = this._optionsContainer) {
4087
4797
  const isSelected = this._state.selectedIndices.has(index);
4088
4798
  const isDisabled = Boolean(item?.disabled);
4089
4799
  const optionId = `${this._uniqueId}-option-${index}`;
@@ -4099,7 +4809,7 @@ class EnhancedSelect extends HTMLElement {
4099
4809
  disabled: isDisabled,
4100
4810
  id: optionId,
4101
4811
  });
4102
- this._optionsContainer.appendChild(optionElement);
4812
+ targetContainer.appendChild(optionElement);
4103
4813
  return;
4104
4814
  }
4105
4815
  const option = new SelectOption({
@@ -4128,20 +4838,16 @@ class EnhancedSelect extends HTMLElement {
4128
4838
  option.dataset.smValue = String(val);
4129
4839
  }
4130
4840
  option.id = option.id || optionId;
4131
- option.addEventListener('click', (e) => {
4132
- e.stopPropagation(); // Prevent duplicate handling by delegation
4133
- const mouseEvent = e;
4134
- this._selectOption(index, {
4135
- shiftKey: mouseEvent.shiftKey,
4136
- toggleKey: mouseEvent.ctrlKey || mouseEvent.metaKey,
4137
- });
4138
- });
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!
4139
4845
  option.addEventListener('optionRemove', (event) => {
4140
4846
  const detail = event.detail;
4141
4847
  const targetIndex = detail?.index ?? index;
4142
4848
  this._handleOptionRemove(targetIndex);
4143
4849
  });
4144
- this._optionsContainer.appendChild(option);
4850
+ targetContainer.appendChild(option);
4145
4851
  }
4146
4852
  _normalizeCustomOptionElement(element, meta) {
4147
4853
  const optionEl = element instanceof HTMLElement ? element : document.createElement('div');
@@ -4228,35 +4934,9 @@ class EnhancedSelect extends HTMLElement {
4228
4934
  optionEl.tabIndex = -1;
4229
4935
  }
4230
4936
  if (!this._customOptionBoundElements.has(optionEl)) {
4231
- optionEl.addEventListener('click', (e) => {
4232
- e.stopPropagation();
4233
- const current = e.currentTarget;
4234
- if (current.getAttribute('aria-disabled') === 'true')
4235
- return;
4236
- const parsedIndex = Number(current.dataset.index);
4237
- if (!Number.isFinite(parsedIndex))
4238
- return;
4239
- const mouseEvent = e;
4240
- this._selectOption(parsedIndex, {
4241
- shiftKey: mouseEvent.shiftKey,
4242
- toggleKey: mouseEvent.ctrlKey || mouseEvent.metaKey,
4243
- });
4244
- });
4245
- optionEl.addEventListener('keydown', (e) => {
4246
- if (e.key !== 'Enter' && e.key !== ' ')
4247
- return;
4248
- const current = e.currentTarget;
4249
- if (current.getAttribute('aria-disabled') === 'true')
4250
- return;
4251
- const parsedIndex = Number(current.dataset.index);
4252
- if (!Number.isFinite(parsedIndex))
4253
- return;
4254
- e.preventDefault();
4255
- this._selectOption(parsedIndex, {
4256
- shiftKey: e.shiftKey,
4257
- toggleKey: e.ctrlKey || e.metaKey,
4258
- });
4259
- });
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.
4260
4940
  this._customOptionBoundElements.add(optionEl);
4261
4941
  }
4262
4942
  return optionEl;