@smilodon/core 1.4.12 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1350,7 +1350,20 @@ const defaultConfig = {
1350
1350
  allowDeselect: false,
1351
1351
  maxSelections: 0,
1352
1352
  showRemoveButton: true,
1353
+ removeButtonIcon: '×',
1353
1354
  closeOnSelect: true,
1355
+ toggleOnTriggerClick: true,
1356
+ },
1357
+ direction: 'ltr',
1358
+ dropdownPlacement: {
1359
+ mode: 'bottom',
1360
+ },
1361
+ multiSelectDisplay: {
1362
+ mode: 'wrap',
1363
+ maxHeight: '160px',
1364
+ overflowX: 'hidden',
1365
+ overflowY: 'auto',
1366
+ dragScroll: true,
1354
1367
  },
1355
1368
  scrollToSelected: {
1356
1369
  enabled: true,
@@ -1512,6 +1525,8 @@ class SelectOption extends HTMLElement {
1512
1525
  :host {
1513
1526
  display: block;
1514
1527
  position: relative;
1528
+ font: inherit;
1529
+ color: inherit;
1515
1530
  }
1516
1531
 
1517
1532
  /* Allow authors to style selected state from outside the shadow root
@@ -1543,7 +1558,7 @@ class SelectOption extends HTMLElement {
1543
1558
  display: flex;
1544
1559
  align-items: center;
1545
1560
  justify-content: space-between;
1546
- padding: var(--select-option-padding, 8px 12px);
1561
+ padding: var(--select-option-padding, 10px 14px);
1547
1562
  cursor: pointer;
1548
1563
  user-select: none;
1549
1564
  color: var(--select-option-color, var(--select-text-color, #1f2937));
@@ -1551,13 +1566,19 @@ class SelectOption extends HTMLElement {
1551
1566
  transition: var(--select-option-transition, background-color 0.2s ease);
1552
1567
  border: var(--select-option-border, none);
1553
1568
  border-bottom: var(--select-option-border-bottom, none);
1554
- border-radius: var(--select-option-border-radius, 0);
1569
+ border-radius: var(--select-option-border-radius, var(--select-radius-sm, 6px));
1555
1570
  box-shadow: var(--select-option-shadow, none);
1556
1571
  transform: var(--select-option-transform, none);
1572
+ font: inherit;
1557
1573
  }
1558
1574
 
1559
1575
  .option-container:hover {
1560
1576
  background: var(--select-option-hover-bg, #f0f0f0);
1577
+ color: var(--select-option-hover-color, var(--select-option-color, var(--select-text-color, #1f2937)));
1578
+ border: var(--select-option-hover-border, var(--select-option-border, none));
1579
+ border-bottom: var(--select-option-hover-border-bottom, var(--select-option-border-bottom, none));
1580
+ box-shadow: var(--select-option-hover-shadow, var(--select-option-shadow, none));
1581
+ transform: var(--select-option-hover-transform, var(--select-option-transform, none));
1561
1582
  }
1562
1583
 
1563
1584
  .option-container.selected {
@@ -1580,13 +1601,22 @@ class SelectOption extends HTMLElement {
1580
1601
  }
1581
1602
 
1582
1603
  .option-container.active {
1583
- outline: 2px solid var(--select-option-active-outline, #1976d2);
1584
- outline-offset: -2px;
1604
+ background: var(--select-option-active-bg, var(--select-option-hover-bg, #f0f0f0));
1605
+ color: var(--select-option-active-color, var(--select-option-hover-color, var(--select-option-color, var(--select-text-color, #1f2937))));
1606
+ border: var(--select-option-active-border, var(--select-option-hover-border, var(--select-option-border, none)));
1607
+ box-shadow: var(--select-option-active-shadow, var(--select-option-shadow, none));
1608
+ transform: var(--select-option-active-transform, var(--select-option-transform, none));
1609
+ outline: var(--select-option-active-outline, 2px solid #1976d2);
1610
+ outline-offset: var(--select-option-active-outline-offset, -2px);
1585
1611
  }
1586
1612
 
1587
1613
  .option-container.disabled {
1588
- opacity: 0.5;
1589
- cursor: not-allowed;
1614
+ background: var(--select-option-disabled-bg, var(--select-option-bg, var(--select-dropdown-bg, var(--select-bg, white))));
1615
+ color: var(--select-option-disabled-color, var(--select-option-color, var(--select-text-color, #1f2937)));
1616
+ border: var(--select-option-disabled-border, var(--select-option-border, none));
1617
+ border-bottom: var(--select-option-disabled-border-bottom, var(--select-option-border-bottom, none));
1618
+ opacity: var(--select-option-disabled-opacity, 0.5);
1619
+ cursor: var(--select-option-disabled-cursor, not-allowed);
1590
1620
  pointer-events: none;
1591
1621
  }
1592
1622
 
@@ -1603,6 +1633,12 @@ class SelectOption extends HTMLElement {
1603
1633
  color: var(--select-checkmark-color, currentColor);
1604
1634
  }
1605
1635
 
1636
+ :host([dir="rtl"]) .checkmark-icon,
1637
+ :host-context([dir="rtl"]) .checkmark-icon {
1638
+ margin-left: 0;
1639
+ margin-right: var(--select-checkmark-margin-left, 8px);
1640
+ }
1641
+
1606
1642
  :host([aria-selected="true"]) .checkmark-icon,
1607
1643
  .option-container.selected .checkmark-icon {
1608
1644
  display: inline-flex;
@@ -1610,20 +1646,49 @@ class SelectOption extends HTMLElement {
1610
1646
 
1611
1647
  .remove-button {
1612
1648
  margin-left: 8px;
1613
- padding: 2px 6px;
1649
+ width: var(--select-badge-remove-size, 18px);
1650
+ height: var(--select-badge-remove-size, 18px);
1651
+ min-width: var(--select-badge-remove-min-width, var(--select-badge-remove-size, 18px));
1652
+ min-height: var(--select-badge-remove-min-height, var(--select-badge-remove-size, 18px));
1653
+ padding: 0;
1614
1654
  border: none;
1615
- background-color: var(--select-remove-btn-bg, transparent);
1616
- color: var(--select-remove-btn-color, #666);
1655
+ background-color: var(--select-badge-remove-bg, rgba(255, 255, 255, 0.2));
1656
+ color: var(--select-badge-remove-color, currentColor);
1617
1657
  cursor: pointer;
1618
- border-radius: 3px;
1619
- font-size: 16px;
1658
+ border-radius: var(--select-badge-remove-radius, 50%);
1659
+ font-size: var(--select-badge-remove-font-size, 0.7333em);
1660
+ font-weight: var(--select-badge-remove-font-weight, 600);
1620
1661
  line-height: 1;
1662
+ display: inline-flex;
1663
+ align-items: center;
1664
+ justify-content: center;
1621
1665
  transition: all 0.2s ease;
1622
1666
  }
1667
+
1668
+ :host([dir="rtl"]) .remove-button,
1669
+ :host-context([dir="rtl"]) .remove-button {
1670
+ margin-left: 0;
1671
+ margin-right: 8px;
1672
+ }
1673
+
1674
+ .remove-button-icon {
1675
+ display: inline-flex;
1676
+ align-items: center;
1677
+ justify-content: center;
1678
+ width: var(--select-badge-remove-icon-size, 10px);
1679
+ height: var(--select-badge-remove-icon-size, 10px);
1680
+ pointer-events: none;
1681
+ }
1682
+
1683
+ .remove-button-icon svg {
1684
+ width: 100%;
1685
+ height: 100%;
1686
+ display: block;
1687
+ }
1623
1688
 
1624
1689
  .remove-button:hover {
1625
- background-color: var(--select-remove-btn-hover-bg, #ffebee);
1626
- color: var(--select-remove-btn-hover-color, #c62828);
1690
+ background-color: var(--select-badge-remove-hover-bg, #ffebee);
1691
+ color: var(--select-badge-remove-hover-color, #c62828);
1627
1692
  }
1628
1693
 
1629
1694
  .remove-button:focus {
@@ -1710,10 +1775,22 @@ class SelectOption extends HTMLElement {
1710
1775
  if (showRemoveButton && selected) {
1711
1776
  this._removeButton = document.createElement('button');
1712
1777
  this._removeButton.className = 'remove-button';
1713
- this._removeButton.innerHTML = '×';
1714
1778
  this._removeButton.setAttribute('part', 'chip-remove');
1715
1779
  this._removeButton.setAttribute('aria-label', 'Remove option');
1716
1780
  this._removeButton.setAttribute('type', 'button');
1781
+ const removeIcon = document.createElement('span');
1782
+ removeIcon.className = 'remove-button-icon';
1783
+ removeIcon.setAttribute('part', 'chip-remove-icon');
1784
+ const iconMarkup = this._config.removeButtonIcon && this._config.removeButtonIcon.trim()
1785
+ ? this._config.removeButtonIcon
1786
+ : '×';
1787
+ if (iconMarkup.trim().startsWith('<')) {
1788
+ removeIcon.innerHTML = iconMarkup;
1789
+ }
1790
+ else {
1791
+ removeIcon.textContent = iconMarkup;
1792
+ }
1793
+ this._removeButton.appendChild(removeIcon);
1717
1794
  this._container.appendChild(this._removeButton);
1718
1795
  }
1719
1796
  // Set ARIA attributes and State attributes on Host
@@ -1883,6 +1960,8 @@ if (!customElements.get('select-option')) {
1883
1960
  * Enhanced Select Component
1884
1961
  * Implements all advanced features: infinite scroll, load more, busy state,
1885
1962
  * server-side selection, and full customization
1963
+ *
1964
+ * ✨ Redesigned with formal elegance, refined microinteractions, and polished UX
1886
1965
  */
1887
1966
  class EnhancedSelect extends HTMLElement {
1888
1967
  get classMap() {
@@ -1930,6 +2009,18 @@ class EnhancedSelect extends HTMLElement {
1930
2009
  this._globalStylesObserver = null;
1931
2010
  this._globalStylesContainer = null;
1932
2011
  this._tracking = { events: [], styles: [], limitations: [] };
2012
+ this._suppressBlurClose = false;
2013
+ this._renderCycleId = 0;
2014
+ this._suppressNextOpenClick = false;
2015
+ this._resolvedDropdownPlacement = 'bottom';
2016
+ this._multiScrollDrag = {
2017
+ active: false,
2018
+ moved: false,
2019
+ pointerId: -1,
2020
+ startX: 0,
2021
+ startScrollLeft: 0,
2022
+ };
2023
+ this._liftedAncestors = [];
1933
2024
  this._shadow = this.attachShadow({ mode: 'open' });
1934
2025
  this._uniqueId = `enhanced-select-${Math.random().toString(36).substr(2, 9)}`;
1935
2026
  this._rendererHelpers = this._buildRendererHelpers();
@@ -1957,6 +2048,7 @@ class EnhancedSelect extends HTMLElement {
1957
2048
  // Create DOM structure
1958
2049
  this._container = this._createContainer();
1959
2050
  this._inputContainer = this._createInputContainer();
2051
+ this._inputContent = this._createInputContent();
1960
2052
  this._input = this._createInput();
1961
2053
  this._arrowContainer = this._createArrowContainer();
1962
2054
  this._clearControl = this._createClearControl();
@@ -1965,6 +2057,8 @@ class EnhancedSelect extends HTMLElement {
1965
2057
  this._liveRegion = this._createLiveRegion();
1966
2058
  // Initialize styles BEFORE assembling DOM (order matters in shadow DOM)
1967
2059
  this._initializeStyles();
2060
+ this._syncStyleConfigVariables();
2061
+ this._syncDirectionConfig();
1968
2062
  this._assembleDOM();
1969
2063
  this._attachEventListeners();
1970
2064
  this._initializeObservers();
@@ -2000,10 +2094,12 @@ class EnhancedSelect extends HTMLElement {
2000
2094
  clearTimeout(this._typeTimeout);
2001
2095
  if (this._searchTimeout)
2002
2096
  clearTimeout(this._searchTimeout);
2097
+ this._endMultiScrollDrag();
2003
2098
  // Cleanup arrow click listener
2004
2099
  if (this._boundArrowClick && this._arrowContainer) {
2005
2100
  this._arrowContainer.removeEventListener('click', this._boundArrowClick);
2006
2101
  }
2102
+ this._renderCycleId += 1;
2007
2103
  this._teardownGlobalStylesMirroring();
2008
2104
  }
2009
2105
  _setGlobalStylesMirroring(enabled) {
@@ -2107,8 +2203,360 @@ class EnhancedSelect extends HTMLElement {
2107
2203
  const container = document.createElement('div');
2108
2204
  container.className = 'input-container';
2109
2205
  container.setAttribute('part', 'button');
2206
+ const inputStyles = this._config.styles.input;
2207
+ if (inputStyles && !this._config.styles.container) {
2208
+ const shellStyleKeys = [
2209
+ 'background',
2210
+ 'backgroundColor',
2211
+ 'border',
2212
+ 'borderColor',
2213
+ 'borderStyle',
2214
+ 'borderWidth',
2215
+ 'borderRadius',
2216
+ 'boxShadow',
2217
+ 'padding',
2218
+ 'height',
2219
+ 'minHeight',
2220
+ 'maxHeight',
2221
+ ];
2222
+ for (const key of shellStyleKeys) {
2223
+ const value = inputStyles[key];
2224
+ if (value != null && value !== '') {
2225
+ container.style[key] = value;
2226
+ }
2227
+ }
2228
+ }
2110
2229
  return container;
2111
2230
  }
2231
+ _createInputContent() {
2232
+ const content = document.createElement('div');
2233
+ content.className = 'input-content';
2234
+ return content;
2235
+ }
2236
+ _setCssVariable(name, value) {
2237
+ if (value == null || value === '') {
2238
+ this.style.removeProperty(name);
2239
+ return;
2240
+ }
2241
+ this.style.setProperty(name, String(value));
2242
+ }
2243
+ _applyStyleVariableMap(styleConfig, variableMap) {
2244
+ Object.entries(variableMap).forEach(([styleKey, variableName]) => {
2245
+ this._setCssVariable(variableName, styleConfig?.[styleKey]);
2246
+ });
2247
+ }
2248
+ _syncStyleConfigVariables() {
2249
+ const styles = this._config.styles;
2250
+ this._applyStyleVariableMap(styles.option, {
2251
+ background: '--select-option-bg',
2252
+ backgroundColor: '--select-option-bg',
2253
+ color: '--select-option-color',
2254
+ border: '--select-option-border',
2255
+ borderBottom: '--select-option-border-bottom',
2256
+ borderRadius: '--select-option-border-radius',
2257
+ boxShadow: '--select-option-shadow',
2258
+ transform: '--select-option-transform',
2259
+ padding: '--select-option-padding',
2260
+ margin: '--select-option-margin',
2261
+ fontSize: '--select-option-font-size',
2262
+ fontWeight: '--select-option-font-weight',
2263
+ lineHeight: '--select-option-line-height',
2264
+ textAlign: '--select-option-text-align',
2265
+ });
2266
+ this._applyStyleVariableMap(styles.selectedOption, {
2267
+ background: '--select-option-selected-bg',
2268
+ backgroundColor: '--select-option-selected-bg',
2269
+ color: '--select-option-selected-color',
2270
+ border: '--select-option-selected-border',
2271
+ borderBottom: '--select-option-selected-border-bottom',
2272
+ borderRadius: '--select-option-selected-border-radius',
2273
+ boxShadow: '--select-option-selected-shadow',
2274
+ transform: '--select-option-selected-transform',
2275
+ fontWeight: '--select-option-selected-weight',
2276
+ });
2277
+ this._applyStyleVariableMap(styles.hoverOption, {
2278
+ background: '--select-option-hover-bg',
2279
+ backgroundColor: '--select-option-hover-bg',
2280
+ color: '--select-option-hover-color',
2281
+ border: '--select-option-hover-border',
2282
+ borderBottom: '--select-option-hover-border-bottom',
2283
+ boxShadow: '--select-option-hover-shadow',
2284
+ transform: '--select-option-hover-transform',
2285
+ });
2286
+ this._applyStyleVariableMap(styles.activeOption, {
2287
+ background: '--select-option-active-bg',
2288
+ backgroundColor: '--select-option-active-bg',
2289
+ color: '--select-option-active-color',
2290
+ border: '--select-option-active-border',
2291
+ outline: '--select-option-active-outline',
2292
+ outlineOffset: '--select-option-active-outline-offset',
2293
+ boxShadow: '--select-option-active-shadow',
2294
+ transform: '--select-option-active-transform',
2295
+ });
2296
+ this._applyStyleVariableMap(styles.disabledOption, {
2297
+ background: '--select-option-disabled-bg',
2298
+ backgroundColor: '--select-option-disabled-bg',
2299
+ color: '--select-option-disabled-color',
2300
+ border: '--select-option-disabled-border',
2301
+ borderBottom: '--select-option-disabled-border-bottom',
2302
+ opacity: '--select-option-disabled-opacity',
2303
+ cursor: '--select-option-disabled-cursor',
2304
+ });
2305
+ this._applyStyleVariableMap(styles.badge, {
2306
+ width: '--select-badge-width',
2307
+ minWidth: '--select-badge-min-width',
2308
+ maxWidth: '--select-badge-max-width',
2309
+ height: '--select-badge-height',
2310
+ minHeight: '--select-badge-min-height',
2311
+ padding: '--select-badge-padding',
2312
+ margin: '--select-badge-margin',
2313
+ gap: '--select-badge-gap',
2314
+ background: '--select-badge-bg',
2315
+ backgroundColor: '--select-badge-bg',
2316
+ color: '--select-badge-color',
2317
+ border: '--select-badge-border',
2318
+ borderRadius: '--select-badge-border-radius',
2319
+ boxShadow: '--select-badge-shadow',
2320
+ fontSize: '--select-badge-font-size',
2321
+ fontWeight: '--select-badge-font-weight',
2322
+ lineHeight: '--select-badge-line-height',
2323
+ letterSpacing: '--select-badge-letter-spacing',
2324
+ });
2325
+ this._applyStyleVariableMap(styles.badgeHover, {
2326
+ background: '--select-badge-hover-bg',
2327
+ backgroundColor: '--select-badge-hover-bg',
2328
+ color: '--select-badge-hover-color',
2329
+ border: '--select-badge-hover-border',
2330
+ boxShadow: '--select-badge-hover-shadow',
2331
+ transform: '--select-badge-hover-transform',
2332
+ });
2333
+ this._applyStyleVariableMap(styles.badgeActive, {
2334
+ background: '--select-badge-active-bg',
2335
+ backgroundColor: '--select-badge-active-bg',
2336
+ color: '--select-badge-active-color',
2337
+ border: '--select-badge-active-border',
2338
+ boxShadow: '--select-badge-active-shadow',
2339
+ transform: '--select-badge-active-transform',
2340
+ });
2341
+ this._applyStyleVariableMap(styles.badgeLabel, {
2342
+ color: '--select-badge-label-color',
2343
+ fontSize: '--select-badge-label-font-size',
2344
+ fontWeight: '--select-badge-label-font-weight',
2345
+ lineHeight: '--select-badge-label-line-height',
2346
+ letterSpacing: '--select-badge-label-letter-spacing',
2347
+ textAlign: '--select-badge-label-text-align',
2348
+ });
2349
+ this._applyStyleVariableMap(styles.badgeRemove, {
2350
+ width: '--select-badge-remove-size',
2351
+ height: '--select-badge-remove-size',
2352
+ minWidth: '--select-badge-remove-min-width',
2353
+ minHeight: '--select-badge-remove-min-height',
2354
+ marginLeft: '--select-badge-remove-margin-left',
2355
+ background: '--select-badge-remove-bg',
2356
+ backgroundColor: '--select-badge-remove-bg',
2357
+ border: '--select-badge-remove-border',
2358
+ borderRadius: '--select-badge-remove-radius',
2359
+ color: '--select-badge-remove-color',
2360
+ fontSize: '--select-badge-remove-font-size',
2361
+ fontWeight: '--select-badge-remove-font-weight',
2362
+ });
2363
+ this._applyStyleVariableMap(styles.badgeRemoveHover, {
2364
+ background: '--select-badge-remove-hover-bg',
2365
+ backgroundColor: '--select-badge-remove-hover-bg',
2366
+ color: '--select-badge-remove-hover-color',
2367
+ border: '--select-badge-remove-hover-border',
2368
+ boxShadow: '--select-badge-remove-hover-shadow',
2369
+ transform: '--select-badge-remove-hover-transform',
2370
+ });
2371
+ this._applyStyleVariableMap(styles.badgeRemoveActive, {
2372
+ background: '--select-badge-remove-active-bg',
2373
+ backgroundColor: '--select-badge-remove-active-bg',
2374
+ color: '--select-badge-remove-active-color',
2375
+ border: '--select-badge-remove-active-border',
2376
+ boxShadow: '--select-badge-remove-active-shadow',
2377
+ transform: '--select-badge-remove-active-transform',
2378
+ });
2379
+ this._applyStyleVariableMap(styles.groupHeader, {
2380
+ padding: '--select-group-header-padding',
2381
+ margin: '--select-group-header-margin',
2382
+ color: '--select-group-header-color',
2383
+ background: '--select-group-header-bg',
2384
+ backgroundColor: '--select-group-header-bg',
2385
+ border: '--select-group-header-border',
2386
+ borderBottom: '--select-group-header-border-bottom',
2387
+ borderRadius: '--select-group-header-border-radius',
2388
+ boxShadow: '--select-group-header-shadow',
2389
+ textAlign: '--select-group-header-text-align',
2390
+ fontSize: '--select-group-header-font-size',
2391
+ fontWeight: '--select-group-header-weight',
2392
+ textTransform: '--select-group-header-text-transform',
2393
+ letterSpacing: '--select-group-header-letter-spacing',
2394
+ });
2395
+ }
2396
+ _resolveDropdownPlacement() {
2397
+ const placementMode = this._config.dropdownPlacement?.mode ?? 'bottom';
2398
+ if (placementMode === 'bottom' || placementMode === 'top') {
2399
+ return placementMode;
2400
+ }
2401
+ const hostRect = this._container.getBoundingClientRect();
2402
+ const availableBelow = window.innerHeight - hostRect.bottom;
2403
+ const computedDropdown = window.getComputedStyle(this._dropdown);
2404
+ const maxHeight = Number.parseFloat(computedDropdown.maxHeight || '0');
2405
+ const desiredHeight = Number.isFinite(maxHeight) && maxHeight > 0
2406
+ ? Math.min(this._dropdown.scrollHeight, maxHeight)
2407
+ : this._dropdown.scrollHeight;
2408
+ return availableBelow >= desiredHeight ? 'bottom' : 'top';
2409
+ }
2410
+ _syncDropdownPlacement() {
2411
+ if (!this._dropdown)
2412
+ return;
2413
+ this._resolvedDropdownPlacement = this._resolveDropdownPlacement();
2414
+ this._dropdown.setAttribute('data-placement', this._resolvedDropdownPlacement);
2415
+ }
2416
+ _syncDirectionConfig() {
2417
+ this.setAttribute('dir', this._config.direction ?? 'ltr');
2418
+ }
2419
+ _setIconContent(target, markup, fallback) {
2420
+ const content = markup && markup.trim() ? markup : fallback;
2421
+ target.innerHTML = '';
2422
+ if (content.trim().startsWith('<')) {
2423
+ target.innerHTML = content;
2424
+ return;
2425
+ }
2426
+ target.textContent = content;
2427
+ }
2428
+ _syncInputContainerMode() {
2429
+ if (!this._inputContainer || !this._input)
2430
+ return;
2431
+ const isMulti = this._config.selection.mode === 'multi';
2432
+ this._inputContainer.classList.toggle('input-container--multi', isMulti);
2433
+ this._inputContainer.classList.toggle('input-container--single', !isMulti);
2434
+ if (isMulti) {
2435
+ this._input.style.flex = '1 0 var(--select-multi-input-min-width, 96px)';
2436
+ this._input.style.width = 'auto';
2437
+ this._input.style.minWidth = 'var(--select-multi-input-min-width, 96px)';
2438
+ }
2439
+ else {
2440
+ this._input.style.flex = '1 1 auto';
2441
+ this._input.style.width = '100%';
2442
+ this._input.style.minWidth = '0';
2443
+ }
2444
+ this._syncMultiSelectDisplayConfig();
2445
+ }
2446
+ _syncMultiSelectDisplayConfig() {
2447
+ if (!this._inputContainer || !this._input)
2448
+ return;
2449
+ const isMulti = this._config.selection.mode === 'multi';
2450
+ const mode = this._config.multiSelectDisplay?.mode ?? 'wrap';
2451
+ const dragEnabled = this._config.multiSelectDisplay?.dragScroll !== false;
2452
+ if (!isMulti) {
2453
+ this._inputContainer.removeAttribute('data-multi-scroll-mode');
2454
+ this._inputContainer.removeAttribute('data-drag-scroll');
2455
+ this._inputContainer.classList.remove('is-dragging-scroll');
2456
+ this._inputContainer.style.removeProperty('--select-multi-input-max-height');
2457
+ this._inputContainer.style.removeProperty('--select-multi-input-overflow-x');
2458
+ this._inputContainer.style.removeProperty('--select-multi-input-overflow-y');
2459
+ this._inputContainer.style.removeProperty('--select-multi-input-flex-wrap');
2460
+ this._inputContainer.style.removeProperty('--select-multi-input-align-content');
2461
+ return;
2462
+ }
2463
+ const maxHeight = this._config.multiSelectDisplay?.maxHeight ?? '160px';
2464
+ const overflowX = this._config.multiSelectDisplay?.overflowX ?? (mode === 'horizontal' ? 'auto' : 'hidden');
2465
+ const overflowY = this._config.multiSelectDisplay?.overflowY ?? (mode === 'horizontal' ? 'hidden' : 'auto');
2466
+ const flexWrap = mode === 'horizontal' ? 'nowrap' : 'wrap';
2467
+ const alignContent = mode === 'horizontal' ? 'center' : 'flex-start';
2468
+ this._inputContainer.setAttribute('data-multi-scroll-mode', mode);
2469
+ this._inputContainer.setAttribute('data-drag-scroll', String(dragEnabled && mode === 'horizontal'));
2470
+ this._inputContainer.style.setProperty('--select-multi-input-max-height', maxHeight);
2471
+ this._inputContainer.style.setProperty('--select-multi-input-overflow-x', overflowX);
2472
+ this._inputContainer.style.setProperty('--select-multi-input-overflow-y', overflowY);
2473
+ this._inputContainer.style.setProperty('--select-multi-input-flex-wrap', flexWrap);
2474
+ this._inputContainer.style.setProperty('--select-multi-input-align-content', alignContent);
2475
+ }
2476
+ _canUseHorizontalMultiScroll(target) {
2477
+ if (this._config.selection.mode !== 'multi')
2478
+ return false;
2479
+ if ((this._config.multiSelectDisplay?.mode ?? 'wrap') !== 'horizontal')
2480
+ return false;
2481
+ if (this._config.multiSelectDisplay?.dragScroll === false)
2482
+ return false;
2483
+ if (!target)
2484
+ return true;
2485
+ if (target.closest('.dropdown-arrow-container, .clear-control-button, .badge-remove'))
2486
+ return false;
2487
+ if (target.matches('.select-input'))
2488
+ return false;
2489
+ return true;
2490
+ }
2491
+ _isScrollableMultiSelectMode() {
2492
+ if (this._config.selection.mode !== 'multi')
2493
+ return false;
2494
+ const mode = this._config.multiSelectDisplay?.mode ?? 'wrap';
2495
+ return mode === 'vertical' || mode === 'horizontal';
2496
+ }
2497
+ _isPointerOnInputScrollbar(event) {
2498
+ if (!this._isScrollableMultiSelectMode())
2499
+ return false;
2500
+ const rect = this._inputContent.getBoundingClientRect();
2501
+ const verticalScrollbarWidth = this._inputContent.offsetWidth - this._inputContent.clientWidth;
2502
+ const horizontalScrollbarHeight = this._inputContent.offsetHeight - this._inputContent.clientHeight;
2503
+ if (verticalScrollbarWidth > 0
2504
+ && event.clientX >= rect.right - verticalScrollbarWidth
2505
+ && event.clientX <= rect.right
2506
+ && event.clientY >= rect.top
2507
+ && event.clientY <= rect.bottom) {
2508
+ return true;
2509
+ }
2510
+ if (horizontalScrollbarHeight > 0
2511
+ && event.clientY >= rect.bottom - horizontalScrollbarHeight
2512
+ && event.clientY <= rect.bottom
2513
+ && event.clientX >= rect.left
2514
+ && event.clientX <= rect.right) {
2515
+ return true;
2516
+ }
2517
+ return false;
2518
+ }
2519
+ _beginMultiScrollDrag(e) {
2520
+ const scrollHost = this._inputContent;
2521
+ this._multiScrollDrag.active = true;
2522
+ this._multiScrollDrag.moved = false;
2523
+ this._multiScrollDrag.pointerId = e.pointerId;
2524
+ this._multiScrollDrag.startX = e.clientX;
2525
+ this._multiScrollDrag.startScrollLeft = scrollHost.scrollLeft;
2526
+ this._inputContainer.classList.add('is-dragging-scroll');
2527
+ try {
2528
+ scrollHost.setPointerCapture(e.pointerId);
2529
+ }
2530
+ catch (_error) {
2531
+ // ignore pointer capture issues
2532
+ }
2533
+ }
2534
+ _updateMultiScrollDrag(e) {
2535
+ if (!this._multiScrollDrag.active || this._multiScrollDrag.pointerId !== e.pointerId)
2536
+ return;
2537
+ const deltaX = e.clientX - this._multiScrollDrag.startX;
2538
+ if (Math.abs(deltaX) > 3) {
2539
+ this._multiScrollDrag.moved = true;
2540
+ }
2541
+ this._inputContent.scrollLeft = this._multiScrollDrag.startScrollLeft - deltaX;
2542
+ }
2543
+ _endMultiScrollDrag(pointerId) {
2544
+ if (!this._multiScrollDrag.active)
2545
+ return;
2546
+ if (pointerId != null && this._multiScrollDrag.pointerId !== pointerId)
2547
+ return;
2548
+ try {
2549
+ if (this._multiScrollDrag.pointerId >= 0) {
2550
+ this._inputContent.releasePointerCapture(this._multiScrollDrag.pointerId);
2551
+ }
2552
+ }
2553
+ catch (_error) {
2554
+ // ignore pointer capture issues
2555
+ }
2556
+ this._multiScrollDrag.active = false;
2557
+ this._multiScrollDrag.pointerId = -1;
2558
+ this._inputContainer.classList.remove('is-dragging-scroll');
2559
+ }
2112
2560
  _createInput() {
2113
2561
  const input = document.createElement('input');
2114
2562
  input.setAttribute('part', 'input');
@@ -2118,6 +2566,35 @@ class EnhancedSelect extends HTMLElement {
2118
2566
  input.placeholder = this._config.placeholder || 'Select an option...';
2119
2567
  input.disabled = !this._config.enabled;
2120
2568
  input.readOnly = !this._config.searchable;
2569
+ // Apply a direct inline reset so the input is only the writable text layer,
2570
+ // while the `.input-container` remains the sole visible control shell.
2571
+ input.style.all = 'unset';
2572
+ input.style.display = 'block';
2573
+ input.style.flex = '1 1 auto';
2574
+ input.style.width = '100%';
2575
+ input.style.maxWidth = '100%';
2576
+ input.style.minWidth = '0';
2577
+ input.style.minInlineSize = '0';
2578
+ input.style.minHeight = '0';
2579
+ input.style.padding = '0';
2580
+ input.style.margin = '0';
2581
+ input.style.border = '0';
2582
+ input.style.background = 'transparent';
2583
+ input.style.boxSizing = 'border-box';
2584
+ input.style.outline = 'none';
2585
+ input.style.font = 'inherit';
2586
+ input.style.fontFamily = 'inherit';
2587
+ input.style.lineHeight = 'inherit';
2588
+ input.style.color = 'inherit';
2589
+ input.style.alignSelf = 'center';
2590
+ input.style.appearance = 'none';
2591
+ input.style.webkitAppearance = 'none';
2592
+ input.style.boxShadow = 'none';
2593
+ input.style.borderRadius = '0';
2594
+ input.style.overflow = 'hidden';
2595
+ input.style.textOverflow = 'ellipsis';
2596
+ input.style.whiteSpace = 'nowrap';
2597
+ input.style.cursor = this._config.searchable ? 'text' : 'default';
2121
2598
  // Update readonly when input is focused if searchable
2122
2599
  input.addEventListener('focus', () => {
2123
2600
  if (this._config.searchable) {
@@ -2128,7 +2605,22 @@ class EnhancedSelect extends HTMLElement {
2128
2605
  input.className += ' ' + this._config.styles.classNames.input;
2129
2606
  }
2130
2607
  if (this._config.styles.input) {
2131
- Object.assign(input.style, this._config.styles.input);
2608
+ const inputStyles = { ...this._config.styles.input };
2609
+ // Route shell-like styling to .input-container so the control appears as
2610
+ // a single styled input instead of two visually separate layers.
2611
+ delete inputStyles.background;
2612
+ delete inputStyles.backgroundColor;
2613
+ delete inputStyles.border;
2614
+ delete inputStyles.borderColor;
2615
+ delete inputStyles.borderStyle;
2616
+ delete inputStyles.borderWidth;
2617
+ delete inputStyles.borderRadius;
2618
+ delete inputStyles.boxShadow;
2619
+ delete inputStyles.padding;
2620
+ delete inputStyles.height;
2621
+ delete inputStyles.minHeight;
2622
+ delete inputStyles.maxHeight;
2623
+ Object.assign(input.style, inputStyles);
2132
2624
  }
2133
2625
  input.setAttribute('role', 'combobox');
2134
2626
  input.setAttribute('aria-expanded', 'false');
@@ -2172,7 +2664,7 @@ class EnhancedSelect extends HTMLElement {
2172
2664
  container.className = 'dropdown-arrow-container';
2173
2665
  container.innerHTML = `
2174
2666
  <svg class="dropdown-arrow" part="arrow" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
2175
- <path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
2667
+ <path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
2176
2668
  </svg>
2177
2669
  `;
2178
2670
  return container;
@@ -2185,14 +2677,15 @@ class EnhancedSelect extends HTMLElement {
2185
2677
  const icon = document.createElement('span');
2186
2678
  icon.className = 'clear-control-icon';
2187
2679
  icon.setAttribute('part', 'clear-icon');
2188
- icon.textContent = this._config.clearControl.icon || '×';
2680
+ icon.innerHTML = `<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 4L4 12M4 4L12 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
2189
2681
  button.setAttribute('aria-label', this._config.clearControl.ariaLabel || 'Clear selection and search');
2190
2682
  button.appendChild(icon);
2191
2683
  this._clearControlIcon = icon;
2192
2684
  return button;
2193
2685
  }
2194
2686
  _assembleDOM() {
2195
- this._inputContainer.appendChild(this._input);
2687
+ this._inputContent.appendChild(this._input);
2688
+ this._inputContainer.appendChild(this._inputContent);
2196
2689
  if (this._clearControl) {
2197
2690
  this._inputContainer.appendChild(this._clearControl);
2198
2691
  }
@@ -2211,18 +2704,324 @@ class EnhancedSelect extends HTMLElement {
2211
2704
  this._dropdown.id = listboxId;
2212
2705
  this._input.setAttribute('aria-controls', listboxId);
2213
2706
  this._input.setAttribute('aria-owns', listboxId);
2707
+ this._syncInputContainerMode();
2214
2708
  this._syncClearControlState();
2215
2709
  }
2216
2710
  _initializeStyles() {
2217
2711
  const style = document.createElement('style');
2218
2712
  style.textContent = `
2713
+ /* ═══════════════════════════════════════════════════════════════════════════
2714
+ ELEGANT SELECT COMPONENT — Refined Design System
2715
+ Formal aesthetics with sophisticated microinteractions
2716
+ ═══════════════════════════════════════════════════════════════════════════ */
2717
+
2219
2718
  :host {
2719
+ --select-primary: #1a1a2e;
2720
+ --select-primary-light: #16213e;
2721
+ --select-accent: #0f3460;
2722
+ --select-accent-hover: #e94560;
2723
+ --select-surface: #ffffff;
2724
+ --select-surface-elevated: #fafbfc;
2725
+ --select-border: #e1e5eb;
2726
+ --select-border-focus: #0f3460;
2727
+ --select-text: #1a1a2e;
2728
+ --select-text-muted: #6b7280;
2729
+ --select-text-placeholder: #9ca3af;
2730
+ --select-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.04);
2731
+ --select-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08);
2732
+ --select-shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.12);
2733
+ --select-shadow-focus: 0 0 0 3px rgba(15, 52, 96, 0.12);
2734
+ --select-radius-sm: 6px;
2735
+ --select-radius-md: 10px;
2736
+ --select-radius-lg: 14px;
2737
+ --select-transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
2738
+ --select-transition-smooth: 250ms cubic-bezier(0.4, 0, 0.2, 1);
2739
+ --select-transition-bounce: 350ms cubic-bezier(0.34, 1.56, 0.64, 1);
2740
+ --select-badge-animation: badgeEnter 300ms cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
2741
+ --select-badge-enter-from-opacity: 0;
2742
+ --select-badge-enter-from-transform: scale(0.8) translateY(-4px);
2743
+ --select-badge-enter-to-opacity: 1;
2744
+ --select-badge-enter-to-transform: scale(1) translateY(0);
2745
+ --select-badge-active-transform: scale(0.98);
2746
+ --select-badge-hover-transform: scale(1.02);
2747
+ --select-badge-letter-spacing: 0.01em;
2748
+ --select-badge-hover-shadow: var(--select-shadow-md), inset 0 1px 0 rgba(255, 255, 255, 0.15);
2749
+ --select-badge-remove-icon-size: 10px;
2750
+ --select-badge-remove-focus-outline: 2px solid rgba(255, 255, 255, 0.5);
2751
+ --select-badge-remove-focus-offset: 1px;
2752
+ --select-badge-remove-hover-transform: scale(1.15) rotate(90deg);
2753
+ --select-badge-remove-active-transform: scale(0.95) rotate(90deg);
2754
+ --select-input-hover-border: var(--select-border-focus);
2755
+ --select-input-hover-shadow: var(--select-shadow-sm), 0 0 0 1px rgba(15, 52, 96, 0.05);
2756
+ --select-input-font-weight: 450;
2757
+ --select-input-letter-spacing: 0.01em;
2758
+ --select-input-disabled-opacity: 0.6;
2759
+ --select-input-overflow-x: hidden;
2760
+ --select-input-align-items: center;
2761
+ --select-input-align-content: center;
2762
+ --select-input-align-self: center;
2763
+ --select-multi-input-max-height: 160px;
2764
+ --select-multi-input-overflow-x: hidden;
2765
+ --select-multi-input-overflow-y: auto;
2766
+ --select-multi-input-flex-wrap: wrap;
2767
+ --select-multi-input-align-content: flex-start;
2768
+ --select-multi-input-horizontal-input-flex: 0 0 var(--select-multi-input-min-width, 96px);
2769
+ --select-multi-input-horizontal-cursor: grab;
2770
+ --select-multi-input-horizontal-active-cursor: grabbing;
2771
+ --select-multi-separator-inset-block: 10px;
2772
+ --select-multi-action-surface-bg: var(--select-input-bg, var(--select-surface));
2773
+ --select-multi-action-divider: 1px solid var(--select-border);
2774
+ --select-separator-opacity: 0.6;
2775
+ --select-separator-active-opacity: 1;
2776
+ --select-separator-position: var(--select-arrow-width, 42px);
2777
+ --select-separator-position-with-clear: calc(var(--select-arrow-right-with-clear, 34px) + var(--select-arrow-width, 42px));
2778
+ --select-separator-dark-bg: linear-gradient(
2779
+ to bottom,
2780
+ transparent 0%,
2781
+ var(--select-border) 20%,
2782
+ var(--select-border) 80%,
2783
+ transparent 100%
2784
+ );
2785
+ --select-arrow-open-transform: rotate(180deg);
2786
+ --select-clear-button-hover-transform: translateY(-50%) scale(1.1);
2787
+ --select-clear-button-active-transform: translateY(-50%) scale(0.95);
2788
+ --select-clear-button-focus-offset: 2px;
2789
+ --select-clear-icon-size: 14px;
2790
+ --select-dropdown-top: calc(100% + 6px);
2791
+ --select-dropdown-bottom: calc(100% + 6px);
2792
+ --select-dropdown-animation: dropdownEnter 200ms cubic-bezier(0.4, 0, 0.2, 1) forwards;
2793
+ --select-dropdown-enter-from-opacity: 0;
2794
+ --select-dropdown-enter-from-transform: translateY(-8px) scale(0.98);
2795
+ --select-dropdown-enter-to-opacity: 1;
2796
+ --select-dropdown-enter-to-transform: translateY(0) scale(1);
2797
+ --select-dropdown-top-transform-origin: bottom center;
2798
+ --select-dropdown-top-enter-from-transform: translateY(8px) scale(0.98);
2799
+ --select-dropdown-padding: 6px;
2800
+ --select-dropdown-scroll-behavior: smooth;
2801
+ --select-dropdown-transform-origin: top center;
2802
+ --select-scrollbar-width: 6px;
2803
+ --select-scrollbar-thumb-radius: 3px;
2804
+ --select-option-hover-transform: translateX(2px);
2805
+ --select-option-font-weight: 450;
2806
+ --select-option-margin: 2px 0;
2807
+ --select-option-disabled-opacity: 0.5;
2808
+ --select-option-disabled-cursor: not-allowed;
2809
+ --select-option-active-outline-offset: -2px;
2810
+ --select-option-selected-active-outline-offset: -2px;
2811
+ --select-option-selected-indicator-width: 3px;
2812
+ --select-option-selected-indicator-height: 60%;
2813
+ --select-option-selected-indicator-bg: var(--select-accent);
2814
+ --select-option-selected-indicator-radius: 0 2px 2px 0;
2815
+ --select-option-selected-indicator-left: 0;
2816
+ --select-option-selected-indicator-top: 50%;
2817
+ --select-option-selected-indicator-transform: translateY(-50%);
2818
+ --select-option-selected-indicator-animation: selectedIndicator 200ms cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
2819
+ --select-option-selected-indicator-from-height: 0;
2820
+ --select-option-selected-indicator-to-height: 60%;
2821
+ --select-option-selected-indicator-from-opacity: 0;
2822
+ --select-option-selected-indicator-to-opacity: 1;
2823
+ --select-option-pressed-transform: translateX(2px) scale(0.99);
2824
+ --select-option-selected-pressed-transform: scale(0.99);
2825
+ --select-button-hover-transform: translateY(-1px);
2826
+ --select-button-active-transform: translateY(0) scale(0.98);
2827
+ --select-button-font-weight: 550;
2828
+ --select-button-letter-spacing: 0.02em;
2829
+ --select-spinner-animation: spin 0.8s cubic-bezier(0.4, 0, 0.2, 1) infinite;
2830
+ --select-searching-spinner-active-color: var(--select-accent);
2831
+ --select-badge-remove-margin-left: 2px;
2832
+ --select-badge-remove-radius: 50%;
2833
+ --select-badge-remove-font-weight: 600;
2834
+ --select-empty-gap: 8px;
2835
+ --select-searching-gap: 8px;
2836
+ --select-searching-spinner-size: 24px;
2837
+ --select-searching-spinner-border: 2px solid var(--select-border);
2838
+ --select-searching-spinner-animation: var(--select-spinner-animation);
2839
+ --select-group-header-separator-margin-top: 8px;
2840
+ --select-group-header-separator-padding-top: 14px;
2841
+ --select-group-header-separator-border-top: 1px solid var(--select-border);
2842
+ --select-reduced-motion-duration: 0.01ms;
2843
+ --select-reduced-motion-iteration-count: 1;
2844
+ --select-high-contrast-border-width: 2px;
2845
+ --select-high-contrast-indicator-width: 4px;
2846
+ --select-high-contrast-focus-outline-width: 3px;
2847
+ --select-high-contrast-focus-outline-color: Highlight;
2848
+ --select-touch-target-min-height: 44px;
2849
+ --select-badge-dark-bg: linear-gradient(135deg, var(--select-accent) 0%, #4f46e5 100%);
2850
+ --select-host-z-index: auto;
2851
+ --select-host-open-z-index: var(--select-dropdown-z-index, 1000);
2852
+ --select-ancestor-open-z-index: var(--select-host-open-z-index, var(--select-dropdown-z-index, 1000));
2853
+
2220
2854
  display: block;
2221
2855
  position: relative;
2856
+ z-index: var(--select-host-z-index, auto);
2222
2857
  width: var(--select-width, 100%);
2223
2858
  height: var(--select-height, auto);
2859
+ font-family: var(--select-font-family, inherit);
2860
+ font-size: var(--select-font-size, inherit);
2861
+ line-height: var(--select-line-height, inherit);
2862
+ color: var(--select-color, inherit);
2863
+ }
2864
+
2865
+ :host([data-open="true"]) {
2866
+ z-index: var(--select-host-open-z-index, var(--select-dropdown-z-index, 1000));
2867
+ }
2868
+
2869
+ :host([dir="ltr"]) {
2870
+ direction: ltr;
2871
+ }
2872
+
2873
+ :host([dir="rtl"]) {
2874
+ direction: rtl;
2875
+ }
2876
+
2877
+ /* ─────────────────────────────────────────────────────────────────────────
2878
+ Selection Badges — Refined pill design with elegant interactions
2879
+ ───────────────────────────────────────────────────────────────────────── */
2880
+
2881
+ .selection-badge {
2882
+ display: inline-flex;
2883
+ align-items: center;
2884
+ gap: var(--select-badge-gap, 6px);
2885
+ flex: 0 1 auto;
2886
+ width: var(--select-badge-width, auto);
2887
+ min-width: var(--select-badge-min-width, 0);
2888
+ height: var(--select-badge-height, auto);
2889
+ min-height: var(--select-badge-min-height, 26px);
2890
+ padding: var(--select-badge-padding, 4px 10px 4px 12px);
2891
+ margin: var(--select-badge-margin, 3px);
2892
+ background: var(--select-badge-bg, linear-gradient(135deg, var(--select-accent) 0%, var(--select-primary-light) 100%));
2893
+ color: var(--select-badge-color, #ffffff);
2894
+ border: var(--select-badge-border, none);
2895
+ border-radius: var(--select-badge-border-radius, 999px);
2896
+ box-shadow: var(--select-badge-shadow, var(--select-shadow-sm), inset 0 1px 0 rgba(255, 255, 255, 0.1));
2897
+ box-sizing: border-box;
2898
+ font-size: var(--select-badge-font-size, 0.8667em);
2899
+ font-weight: var(--select-badge-font-weight, 500);
2900
+ line-height: var(--select-badge-line-height, 1.2);
2901
+ letter-spacing: var(--select-badge-letter-spacing);
2902
+ max-width: var(--select-badge-max-width, 100%);
2903
+ overflow: hidden;
2904
+ transform: scale(1);
2905
+ transition:
2906
+ transform var(--select-transition-bounce),
2907
+ box-shadow var(--select-transition-fast),
2908
+ background var(--select-transition-fast);
2909
+ animation: var(--select-badge-animation);
2910
+ }
2911
+
2912
+ @keyframes badgeEnter {
2913
+ 0% {
2914
+ opacity: var(--select-badge-enter-from-opacity);
2915
+ transform: var(--select-badge-enter-from-transform);
2916
+ }
2917
+ 100% {
2918
+ opacity: var(--select-badge-enter-to-opacity);
2919
+ transform: var(--select-badge-enter-to-transform);
2920
+ }
2224
2921
  }
2225
2922
 
2923
+ .selection-badge:hover {
2924
+ background: var(--select-badge-hover-bg, var(--select-badge-bg, linear-gradient(135deg, var(--select-accent) 0%, var(--select-primary-light) 100%)));
2925
+ color: var(--select-badge-hover-color, var(--select-badge-color, #ffffff));
2926
+ border: var(--select-badge-hover-border, var(--select-badge-border, none));
2927
+ box-shadow: var(--select-badge-hover-shadow);
2928
+ transform: var(--select-badge-hover-transform);
2929
+ }
2930
+
2931
+ .selection-badge:active {
2932
+ background: var(--select-badge-active-bg, var(--select-badge-hover-bg, var(--select-badge-bg, linear-gradient(135deg, var(--select-accent) 0%, var(--select-primary-light) 100%))));
2933
+ color: var(--select-badge-active-color, var(--select-badge-hover-color, var(--select-badge-color, #ffffff)));
2934
+ border: var(--select-badge-active-border, var(--select-badge-hover-border, var(--select-badge-border, none)));
2935
+ box-shadow: var(--select-badge-active-shadow, var(--select-badge-hover-shadow));
2936
+ transform: var(--select-badge-active-transform);
2937
+ }
2938
+
2939
+ .selection-badge-label {
2940
+ display: block;
2941
+ min-width: 0;
2942
+ overflow: hidden;
2943
+ text-overflow: ellipsis;
2944
+ white-space: nowrap;
2945
+ color: var(--select-badge-label-color, inherit);
2946
+ font-size: var(--select-badge-label-font-size, inherit);
2947
+ font-weight: var(--select-badge-label-font-weight, inherit);
2948
+ line-height: var(--select-badge-label-line-height, var(--select-badge-line-height, 1.2));
2949
+ letter-spacing: var(--select-badge-label-letter-spacing, inherit);
2950
+ text-align: var(--select-badge-label-text-align, start);
2951
+ }
2952
+
2953
+ .badge-remove {
2954
+ display: inline-flex;
2955
+ align-items: center;
2956
+ justify-content: center;
2957
+ width: var(--select-badge-remove-size, 18px);
2958
+ height: var(--select-badge-remove-size, 18px);
2959
+ min-width: var(--select-badge-remove-min-width, var(--select-badge-remove-size, 18px));
2960
+ min-height: var(--select-badge-remove-min-height, var(--select-badge-remove-size, 18px));
2961
+ padding: 0;
2962
+ margin-left: var(--select-badge-remove-margin-left);
2963
+ background: var(--select-badge-remove-bg, rgba(255, 255, 255, 0.2));
2964
+ border: var(--select-badge-remove-border, none);
2965
+ border-radius: var(--select-badge-remove-radius);
2966
+ color: var(--select-badge-remove-color, #ffffff);
2967
+ font-size: var(--select-badge-remove-font-size, 0.7333em);
2968
+ font-weight: var(--select-badge-remove-font-weight);
2969
+ line-height: 1;
2970
+ cursor: pointer;
2971
+ flex: 0 0 auto;
2972
+ transition:
2973
+ background var(--select-transition-fast),
2974
+ color var(--select-transition-fast),
2975
+ border-color var(--select-transition-fast),
2976
+ box-shadow var(--select-transition-fast),
2977
+ transform var(--select-transition-bounce);
2978
+ }
2979
+
2980
+ .badge-remove-icon {
2981
+ display: inline-flex;
2982
+ align-items: center;
2983
+ justify-content: center;
2984
+ width: var(--select-badge-remove-icon-size, 10px);
2985
+ height: var(--select-badge-remove-icon-size, 10px);
2986
+ pointer-events: none;
2987
+ }
2988
+
2989
+ .badge-remove-icon svg {
2990
+ width: 100%;
2991
+ height: 100%;
2992
+ display: block;
2993
+ }
2994
+
2995
+ .badge-remove:hover {
2996
+ background: var(--select-badge-remove-hover-bg, rgba(233, 69, 96, 0.9));
2997
+ color: var(--select-badge-remove-hover-color, var(--select-badge-remove-color, #ffffff));
2998
+ border: var(--select-badge-remove-hover-border, var(--select-badge-remove-border, none));
2999
+ box-shadow: var(--select-badge-remove-hover-shadow, none);
3000
+ transform: var(--select-badge-remove-hover-transform);
3001
+ }
3002
+
3003
+ .badge-remove:focus-visible {
3004
+ outline: var(--select-badge-remove-focus-outline);
3005
+ outline-offset: var(--select-badge-remove-focus-offset);
3006
+ }
3007
+
3008
+ .badge-remove:active {
3009
+ background: var(--select-badge-remove-active-bg, var(--select-badge-remove-hover-bg, rgba(233, 69, 96, 0.9)));
3010
+ color: var(--select-badge-remove-active-color, var(--select-badge-remove-hover-color, var(--select-badge-remove-color, #ffffff)));
3011
+ border: var(--select-badge-remove-active-border, var(--select-badge-remove-hover-border, var(--select-badge-remove-border, none)));
3012
+ box-shadow: var(--select-badge-remove-active-shadow, var(--select-badge-remove-hover-shadow, none));
3013
+ transform: var(--select-badge-remove-active-transform);
3014
+ }
3015
+
3016
+ :host([dir="rtl"]) .badge-remove {
3017
+ margin-left: 0;
3018
+ margin-right: var(--select-badge-remove-margin-left);
3019
+ }
3020
+
3021
+ /* ─────────────────────────────────────────────────────────────────────────
3022
+ Input Container — Sophisticated control shell
3023
+ ───────────��───────────────────────────────────────────────────────────── */
3024
+
2226
3025
  .select-container {
2227
3026
  position: relative;
2228
3027
  width: 100%;
@@ -2232,364 +3031,644 @@ class EnhancedSelect extends HTMLElement {
2232
3031
  position: relative;
2233
3032
  width: 100%;
2234
3033
  display: flex;
2235
- align-items: center;
2236
- flex-wrap: wrap;
3034
+ align-items: var(--select-input-align-items, center);
3035
+ flex-wrap: nowrap;
3036
+ justify-content: var(--select-input-justify-content, flex-start);
2237
3037
  gap: var(--select-input-gap, 6px);
2238
- padding: var(--select-input-padding, 6px 52px 6px 8px);
3038
+ padding: var(--select-input-padding, 10px 52px 10px 14px);
2239
3039
  height: var(--select-input-height, auto);
2240
- min-height: var(--select-input-min-height, 44px);
3040
+ min-height: var(--select-input-min-height, 48px);
2241
3041
  max-height: var(--select-input-max-height, 160px);
2242
- overflow-y: var(--select-input-overflow-y, auto);
2243
- align-content: flex-start;
2244
- background: var(--select-input-bg, var(--select-bg, white));
2245
- border: var(--select-input-border, 1px solid var(--select-border-color, #d1d5db));
2246
- border-radius: var(--select-input-border-radius, 6px);
3042
+ overflow: hidden;
3043
+ align-content: var(--select-input-align-content, center);
3044
+ text-align: var(--select-input-text-align, start);
3045
+ background: var(--select-input-bg, var(--select-surface));
3046
+ border: var(--select-input-border, 1.5px solid var(--select-border));
3047
+ border-radius: var(--select-input-border-radius, var(--select-radius-md));
3048
+ box-shadow: var(--select-shadow-sm);
3049
+ box-sizing: border-box;
3050
+ transition:
3051
+ border-color var(--select-transition-fast),
3052
+ box-shadow var(--select-transition-smooth),
3053
+ transform var(--select-transition-fast);
3054
+ }
3055
+
3056
+ .input-content {
3057
+ position: relative;
3058
+ display: flex;
3059
+ flex: 1 1 auto;
3060
+ width: 100%;
3061
+ min-width: 0;
3062
+ min-height: 0;
3063
+ align-self: stretch;
3064
+ align-items: var(--select-input-align-items, center);
3065
+ align-content: var(--select-input-align-content, center);
3066
+ justify-content: var(--select-input-justify-content, flex-start);
3067
+ flex-wrap: nowrap;
3068
+ gap: var(--select-input-gap, 6px);
3069
+ overflow-x: var(--select-input-overflow-x, hidden);
3070
+ overflow-y: var(--select-input-overflow-y, hidden);
3071
+ scrollbar-width: thin;
2247
3072
  box-sizing: border-box;
2248
- transition: all 0.2s ease;
3073
+ }
3074
+
3075
+ .input-container:hover {
3076
+ border-color: var(--select-input-hover-border);
3077
+ box-shadow: var(--select-input-hover-shadow);
3078
+ }
3079
+
3080
+ .input-container.input-container--single {
3081
+ flex-wrap: nowrap;
3082
+ align-content: center;
3083
+ }
3084
+
3085
+ .input-container.input-container--multi {
3086
+ max-height: var(--select-multi-input-max-height, var(--select-input-max-height, 160px));
3087
+ }
3088
+
3089
+ .input-container.input-container--multi .input-content {
3090
+ flex-wrap: var(--select-multi-input-flex-wrap, wrap);
3091
+ align-content: var(--select-multi-input-align-content, flex-start);
3092
+ overflow-x: var(--select-multi-input-overflow-x, hidden);
3093
+ overflow-y: var(--select-multi-input-overflow-y, auto);
3094
+ }
3095
+
3096
+ .input-container.input-container--multi::after {
3097
+ top: var(--select-multi-separator-inset-block, 10px);
3098
+ bottom: var(--select-multi-separator-inset-block, 10px);
3099
+ height: auto;
3100
+ transform: none;
3101
+ }
3102
+
3103
+ .input-container.input-container--multi[data-multi-scroll-mode="horizontal"] {
3104
+ align-items: stretch;
3105
+ }
3106
+
3107
+ .input-container.input-container--multi[data-multi-scroll-mode="horizontal"] .input-content {
3108
+ cursor: var(--select-multi-input-horizontal-cursor, grab);
3109
+ overflow-y: hidden;
3110
+ overscroll-behavior-x: contain;
3111
+ }
3112
+
3113
+ .input-container.input-container--multi[data-multi-scroll-mode="horizontal"]::after {
3114
+ display: none;
3115
+ }
3116
+
3117
+ .input-container.input-container--multi[data-multi-scroll-mode="horizontal"].is-dragging-scroll .input-content {
3118
+ cursor: var(--select-multi-input-horizontal-active-cursor, grabbing);
3119
+ user-select: none;
3120
+ }
3121
+
3122
+ .input-container.input-container--multi[data-multi-scroll-mode="horizontal"] .input-content .selection-badge {
3123
+ flex: 0 0 auto;
3124
+ }
3125
+
3126
+ .input-container.input-container--multi[data-multi-scroll-mode="horizontal"] .input-content > .select-input,
3127
+ .input-container.input-container--multi[data-multi-scroll-mode="horizontal"] .input-content > input.select-input,
3128
+ .input-container.input-container--multi[data-multi-scroll-mode="horizontal"] .input-content > .select-input[type="text"] {
3129
+ flex: var(--select-multi-input-horizontal-input-flex, 0 0 var(--select-multi-input-min-width, 96px));
3130
+ }
3131
+
3132
+ .input-container.input-container--multi[data-multi-scroll-mode="horizontal"] .dropdown-arrow-container {
3133
+ background: var(--select-multi-action-surface-bg, var(--select-input-bg, var(--select-surface)));
3134
+ border-left: var(--select-multi-action-divider, 1px solid var(--select-border));
3135
+ z-index: 4;
3136
+ }
3137
+
3138
+ .input-container.input-container--multi[data-multi-scroll-mode="horizontal"] .dropdown-arrow-container.with-clear-control {
3139
+ right: var(--select-arrow-right-with-clear, 34px);
3140
+ }
3141
+
3142
+ :host([dir="rtl"]) .input-container.input-container--multi[data-multi-scroll-mode="horizontal"] .dropdown-arrow-container {
3143
+ border-left: none;
3144
+ border-right: var(--select-multi-action-divider, 1px solid var(--select-border));
3145
+ }
3146
+
3147
+ :host([dir="rtl"]) .input-container.input-container--multi[data-multi-scroll-mode="horizontal"] .dropdown-arrow-container.with-clear-control {
3148
+ right: auto;
3149
+ left: var(--select-arrow-right-with-clear, 34px);
3150
+ }
3151
+
3152
+ .input-container.input-container--multi[data-multi-scroll-mode="horizontal"] .clear-control-button {
3153
+ background: var(--select-multi-action-surface-bg, var(--select-input-bg, var(--select-surface)));
3154
+ z-index: 4;
2249
3155
  }
2250
3156
 
2251
3157
  .input-container:focus-within {
2252
- border-color: var(--select-input-focus-border, var(--select-border-focus-color, #667eea));
2253
- box-shadow: var(--select-input-focus-shadow, 0 0 0 3px rgba(102, 126, 234, 0.1));
3158
+ border-color: var(--select-input-focus-border, var(--select-border-focus));
3159
+ box-shadow: var(--select-shadow-focus), var(--select-shadow-sm);
2254
3160
  }
2255
3161
 
2256
- /* Gradient separator before arrow */
3162
+ /* Elegant separator line before arrow */
2257
3163
  .input-container::after {
2258
3164
  content: '';
2259
3165
  position: absolute;
2260
3166
  top: 50%;
2261
- right: var(--select-separator-position, var(--select-seperator-position, 40px));
3167
+ right: var(--select-separator-position, var(--select-arrow-width, 42px));
2262
3168
  transform: translateY(-50%);
2263
- width: var(--select-separator-width, var(--select-seperator-width, 1px));
2264
- height: var(--select-separator-height, var(--select-seperator-height, 60%));
2265
- background: var(--select-separator-bg, var(--select-seperator-bg, var(--select-separator-gradient, var(--select-seperator-gradient, linear-gradient(
3169
+ width: var(--select-separator-width, 1px);
3170
+ height: var(--select-separator-height, 50%);
3171
+ background: var(--select-separator-bg, linear-gradient(
2266
3172
  to bottom,
2267
3173
  transparent 0%,
2268
- rgba(0, 0, 0, 0.1) 20%,
2269
- rgba(0, 0, 0, 0.1) 80%,
3174
+ var(--select-border) 20%,
3175
+ var(--select-border) 80%,
2270
3176
  transparent 100%
2271
- ))));
3177
+ ));
2272
3178
  pointer-events: none;
2273
3179
  z-index: 1;
3180
+ opacity: var(--select-separator-opacity);
3181
+ transition: opacity var(--select-transition-fast);
3182
+ }
3183
+
3184
+ .input-container:hover::after,
3185
+ .input-container:focus-within::after {
3186
+ opacity: var(--select-separator-active-opacity);
3187
+ }
3188
+
3189
+ :host([dir="rtl"]) .input-container::after {
3190
+ right: auto;
3191
+ left: var(--select-separator-position, var(--select-arrow-width, 42px));
2274
3192
  }
2275
3193
 
3194
+ /* ─────────────────────────────────────────────────────────────────────────
3195
+ Dropdown Arrow — Smooth rotation with refined styling
3196
+ ───────────────────────────────────────────────────────────────────────── */
3197
+
2276
3198
  .dropdown-arrow-container {
2277
3199
  position: absolute;
2278
3200
  top: 0;
2279
3201
  right: 0;
2280
3202
  bottom: 0;
2281
- width: var(--select-arrow-width, 40px);
2282
- /* allow explicit height override even though container normally stretches */
3203
+ width: var(--select-arrow-width, 42px);
2283
3204
  height: var(--select-arrow-height, auto);
2284
3205
  display: flex;
2285
3206
  align-items: center;
2286
3207
  justify-content: center;
2287
3208
  cursor: pointer;
2288
- transition: background-color 0.2s ease;
2289
- border-radius: var(--select-arrow-border-radius, 0 4px 4px 0);
3209
+ border-radius: var(--select-arrow-border-radius, 0 var(--select-radius-md) var(--select-radius-md) 0);
2290
3210
  z-index: 2;
3211
+ transition: background-color var(--select-transition-fast);
2291
3212
  }
2292
3213
 
2293
3214
  .input-container.has-clear-control {
2294
- padding: var(--select-input-padding-with-clear, 6px 84px 6px 8px);
3215
+ padding: var(--select-input-padding-with-clear, 10px 84px 10px 14px);
2295
3216
  }
2296
3217
 
2297
3218
  .input-container.has-clear-control::after {
2298
- right: var(--select-separator-position-with-clear, var(--select-seperator-position-with-clear, 72px));
3219
+ right: var(--select-separator-position-with-clear, calc(var(--select-arrow-right-with-clear, 34px) + var(--select-arrow-width, 42px)));
2299
3220
  }
2300
3221
 
2301
3222
  .dropdown-arrow-container.with-clear-control {
2302
- right: var(--select-arrow-right-with-clear, 32px);
3223
+ right: var(--select-arrow-right-with-clear, 34px);
3224
+ }
3225
+
3226
+ :host([dir="rtl"]) .dropdown-arrow-container {
3227
+ right: auto;
3228
+ left: 0;
3229
+ border-radius: var(--select-arrow-border-radius-rtl, var(--select-radius-md) 0 0 var(--select-radius-md));
3230
+ }
3231
+
3232
+ :host([dir="rtl"]) .input-container.has-clear-control::after {
3233
+ right: auto;
3234
+ left: var(--select-separator-position-with-clear, calc(var(--select-arrow-right-with-clear, 34px) + var(--select-arrow-width, 42px)));
2303
3235
  }
2304
3236
 
3237
+ :host([dir="rtl"]) .dropdown-arrow-container.with-clear-control {
3238
+ right: auto;
3239
+ left: var(--select-arrow-right-with-clear, 34px);
3240
+ }
3241
+
3242
+ /* ─────────────────────────────────────────────────────────────────────────
3243
+ Clear Control — Minimal and elegant dismiss button
3244
+ ───────────────────────────────────────────────────────────────────────── */
3245
+
2305
3246
  .clear-control-button {
2306
3247
  position: absolute;
2307
3248
  top: 50%;
2308
- right: var(--select-clear-button-right, 6px);
3249
+ right: var(--select-clear-button-right, 8px);
2309
3250
  transform: translateY(-50%);
2310
- width: var(--select-clear-button-size, 24px);
2311
- height: var(--select-clear-button-size, 24px);
3251
+ width: var(--select-clear-button-size, 26px);
3252
+ height: var(--select-clear-button-size, 26px);
2312
3253
  border: var(--select-clear-button-border, none);
2313
- border-radius: var(--select-clear-button-radius, 999px);
3254
+ border-radius: var(--select-clear-button-radius, 50%);
2314
3255
  background: var(--select-clear-button-bg, transparent);
2315
- color: var(--select-clear-button-color, #6b7280);
3256
+ color: var(--select-clear-button-color, var(--select-text-muted));
2316
3257
  display: inline-flex;
2317
3258
  align-items: center;
2318
3259
  justify-content: center;
2319
3260
  cursor: pointer;
2320
3261
  z-index: 3;
2321
- transition: var(--select-clear-button-transition, all 0.2s ease);
3262
+ transition:
3263
+ background var(--select-transition-fast),
3264
+ color var(--select-transition-fast),
3265
+ transform var(--select-transition-bounce);
2322
3266
  }
2323
3267
 
2324
3268
  .clear-control-button:hover {
2325
- background: var(--select-clear-button-hover-bg, rgba(0, 0, 0, 0.06));
2326
- color: var(--select-clear-button-hover-color, #111827);
3269
+ background: var(--select-clear-button-hover-bg, rgba(233, 69, 96, 0.1));
3270
+ color: var(--select-clear-button-hover-color, var(--select-accent-hover));
3271
+ transform: var(--select-clear-button-hover-transform);
3272
+ }
3273
+
3274
+ .clear-control-button:active {
3275
+ transform: var(--select-clear-button-active-transform);
2327
3276
  }
2328
3277
 
2329
3278
  .clear-control-button:focus-visible {
2330
- outline: var(--select-clear-button-focus-outline, 2px solid rgba(102, 126, 234, 0.55));
2331
- outline-offset: 1px;
3279
+ outline: var(--select-clear-button-focus-outline, 2px solid var(--select-border-focus));
3280
+ outline-offset: var(--select-clear-button-focus-offset);
2332
3281
  }
2333
3282
 
2334
3283
  .clear-control-button[hidden] {
2335
3284
  display: none;
2336
3285
  }
2337
3286
 
3287
+ :host([dir="rtl"]) .clear-control-button {
3288
+ right: auto;
3289
+ left: var(--select-clear-button-right, 8px);
3290
+ }
3291
+
2338
3292
  .clear-control-icon {
2339
- font-size: var(--select-clear-icon-size, 16px);
2340
- line-height: 1;
2341
- font-weight: var(--select-clear-icon-weight, 500);
3293
+ width: var(--select-clear-icon-size);
3294
+ height: var(--select-clear-icon-size);
2342
3295
  pointer-events: none;
2343
3296
  }
2344
3297
 
2345
- .dropdown-arrow-container:hover {
2346
- background-color: var(--select-arrow-hover-bg, rgba(102, 126, 234, 0.08));
3298
+ .clear-control-icon svg {
3299
+ width: 100%;
3300
+ height: 100%;
2347
3301
  }
2348
3302
 
2349
- .dropdown-arrow:hover {
2350
- /* legacy alias --select-arrow-hover for icon color */
2351
- color: var(--select-arrow-hover, var(--select-arrow-hover-color, #667eea));
3303
+ .dropdown-arrow-container:hover {
3304
+ background-color: var(--select-arrow-hover-bg, rgba(15, 52, 96, 0.05));
2352
3305
  }
2353
3306
 
2354
3307
  .dropdown-arrow {
2355
- width: var(--select-arrow-width, var(--select-arrow-size, 16px));
2356
- height: var(--select-arrow-height, var(--select-arrow-size, 16px));
2357
- color: var(--select-arrow-color, #667eea);
2358
- transition: transform 0.2s ease, color 0.2s ease;
2359
- transform: translateY(0);
3308
+ width: var(--select-arrow-size, 18px);
3309
+ height: var(--select-arrow-size, 18px);
3310
+ color: var(--select-arrow-color, var(--select-text-muted));
3311
+ transition:
3312
+ transform var(--select-transition-smooth),
3313
+ color var(--select-transition-fast);
3314
+ transform-origin: center;
2360
3315
  }
2361
3316
 
2362
3317
  .dropdown-arrow path {
2363
- stroke-width: var(--select-arrow-stroke-width, 2);
3318
+ stroke-width: var(--select-arrow-stroke-width, 1.5);
2364
3319
  }
2365
3320
 
2366
3321
  .dropdown-arrow-container:hover .dropdown-arrow {
2367
- color: var(--select-arrow-hover-color, #667eea);
3322
+ color: var(--select-arrow-hover-color, var(--select-accent));
2368
3323
  }
2369
3324
 
2370
3325
  .dropdown-arrow.open {
2371
- transform: rotate(180deg);
3326
+ transform: var(--select-arrow-open-transform);
2372
3327
  }
2373
3328
 
2374
- .select-input {
2375
- flex: 1;
2376
- width: var(--select-input-width, auto);
2377
- min-width: var(--select-input-min-width, 120px);
2378
- padding: var(--select-input-field-padding, 4px);
2379
- border: none;
2380
- font-size: var(--select-input-font-size, 14px);
3329
+ /* ─────────────────────────────────────────────────────────────────────────
3330
+ Input Field — Clean text entry
3331
+ ───────────────────────────────────────────────────────────────────────── */
3332
+
3333
+ .input-container > .select-input,
3334
+ input.select-input,
3335
+ .select-input[type="text"] {
3336
+ display: block;
3337
+ flex: 1 1 auto;
3338
+ width: var(--select-input-width, 100%);
3339
+ max-width: 100%;
3340
+ min-width: var(--select-input-min-width, 0);
3341
+ min-inline-size: 0;
3342
+ min-height: 0;
3343
+ padding: var(--select-input-field-padding, 0) !important;
3344
+ margin: 0 !important;
3345
+ border: 0 !important;
3346
+ font-size: var(--select-input-font-size, inherit);
2381
3347
  line-height: var(--select-input-line-height, 1.5);
2382
- color: var(--select-input-color, var(--select-text-color, #1f2937));
2383
- background: transparent;
3348
+ color: var(--select-input-color, var(--select-text));
3349
+ background: transparent !important;
2384
3350
  box-sizing: border-box;
2385
- outline: none;
2386
- font-family: var(--select-font-family, inherit);
2387
- }
2388
-
2389
- .select-input::placeholder {
2390
- color: var(--select-input-placeholder-color, var(--select-placeholder-color, #9ca3af));
2391
- }
2392
-
2393
- .selection-badge {
2394
- display: inline-flex;
2395
- align-items: center;
2396
- gap: var(--select-badge-gap, 4px);
2397
- padding: var(--select-badge-padding, 4px 8px);
2398
- margin: var(--select-badge-margin, 2px);
2399
- background: var(--select-badge-bg, #667eea);
2400
- color: var(--select-badge-color, white);
2401
- border-radius: var(--select-badge-border-radius, 4px);
2402
- font-size: var(--select-badge-font-size, 13px);
2403
- line-height: 1;
2404
- max-width: var(--select-badge-max-width, 100%);
2405
- white-space: nowrap;
3351
+ outline: none !important;
3352
+ font-weight: var(--select-input-font-weight);
3353
+ letter-spacing: var(--select-input-letter-spacing);
3354
+ font-family: var(--select-input-font-family, inherit);
3355
+ font-style: var(--select-input-font-style, normal);
3356
+ align-self: var(--select-input-align-self, center);
3357
+ appearance: none !important;
3358
+ -webkit-appearance: none !important;
3359
+ box-shadow: none !important;
3360
+ border-radius: 0 !important;
2406
3361
  overflow: hidden;
2407
3362
  text-overflow: ellipsis;
3363
+ text-align: var(--select-input-text-align, inherit);
3364
+ white-space: nowrap;
2408
3365
  }
2409
3366
 
2410
- .badge-remove {
2411
- display: inline-flex;
2412
- align-items: center;
2413
- justify-content: center;
2414
- width: var(--select-badge-remove-size, 16px);
2415
- height: var(--select-badge-remove-size, 16px);
2416
- padding: 0;
2417
- margin-left: 4px;
2418
- background: var(--select-badge-remove-bg, rgba(255, 255, 255, 0.3));
2419
- border: none;
2420
- border-radius: 50%;
2421
- color: var(--select-badge-remove-color, white);
2422
- font-size: var(--select-badge-remove-font-size, 16px);
2423
- line-height: 1;
2424
- cursor: pointer;
2425
- transition: background 0.2s;
3367
+ .select-input::placeholder {
3368
+ color: var(--select-input-placeholder-color, var(--select-text-placeholder));
3369
+ font-weight: 400;
2426
3370
  }
2427
-
2428
- .badge-remove:hover {
2429
- background: var(--select-badge-remove-hover-bg, rgba(255, 255, 255, 0.5));
3371
+
3372
+ :host([dir="rtl"]) .input-container {
3373
+ padding: var(--select-input-padding-rtl, 10px 14px 10px 52px);
3374
+ }
3375
+
3376
+ :host([dir="rtl"]) .input-container.has-clear-control {
3377
+ padding: var(--select-input-padding-with-clear-rtl, 10px 14px 10px 84px);
2430
3378
  }
2431
3379
 
2432
- .badge-remove:focus-visible {
2433
- outline: 2px solid var(--select-badge-remove-focus-outline, rgba(255, 255, 255, 0.8));
2434
- outline-offset: 2px;
3380
+ .input-container.input-container--multi > .select-input,
3381
+ .input-container.input-container--multi > input.select-input,
3382
+ .input-container.input-container--multi > .select-input[type="text"] {
3383
+ width: auto;
3384
+ min-width: var(--select-multi-input-min-width, 96px);
3385
+ flex: 1 0 var(--select-multi-input-min-width, 96px);
3386
+ }
3387
+
3388
+ .input-container.input-container--single > .select-input,
3389
+ .input-container.input-container--single > input.select-input,
3390
+ .input-container.input-container--single > .select-input[type="text"] {
3391
+ width: var(--select-input-width, 100%);
3392
+ min-width: 0;
3393
+ flex: 1 1 auto;
2435
3394
  }
2436
3395
 
2437
3396
  .select-input:disabled {
2438
3397
  background-color: var(--select-disabled-bg, #f5f5f5);
2439
3398
  cursor: not-allowed;
3399
+ opacity: var(--select-input-disabled-opacity);
2440
3400
  }
2441
3401
 
3402
+ /* ─────────────────────────────────────────────────────────────────────────
3403
+ Dropdown Panel — Elegant floating container
3404
+ ───────────────────────────────────────────────────────────────────────── */
3405
+
2442
3406
  .select-dropdown {
2443
3407
  position: absolute;
2444
- scroll-behavior: smooth;
2445
- top: 100%;
3408
+ scroll-behavior: var(--select-dropdown-scroll-behavior);
3409
+ top: var(--select-dropdown-top);
3410
+ bottom: auto;
2446
3411
  left: 0;
2447
3412
  right: 0;
2448
- margin-top: var(--select-dropdown-margin-top, 4px);
2449
- max-height: var(--select-dropdown-max-height, 300px);
3413
+ max-height: var(--select-dropdown-max-height, 320px);
2450
3414
  overflow: hidden;
2451
- background: var(--select-dropdown-bg, var(--select-bg, white));
2452
- border: 1px solid var(--select-dropdown-border, #ccc);
2453
- border-radius: var(--select-dropdown-border-radius, 4px);
2454
- box-shadow: var(--select-dropdown-shadow, 0 4px 6px rgba(0,0,0,0.1));
3415
+ box-sizing: border-box;
3416
+ background: var(--select-dropdown-bg, var(--select-surface));
3417
+ border: 1px solid var(--select-dropdown-border, var(--select-border));
3418
+ border-radius: var(--select-dropdown-border-radius, var(--select-radius-lg));
3419
+ box-shadow: var(--select-dropdown-shadow, var(--select-shadow-lg));
2455
3420
  z-index: var(--select-dropdown-z-index, 1000);
3421
+ transform-origin: var(--select-dropdown-transform-origin);
3422
+ animation: var(--select-dropdown-animation);
3423
+ }
3424
+
3425
+ .select-dropdown[data-placement="top"] {
3426
+ top: auto;
3427
+ bottom: var(--select-dropdown-bottom, calc(100% + 6px));
3428
+ transform-origin: var(--select-dropdown-top-transform-origin, bottom center);
3429
+ }
3430
+
3431
+ .select-dropdown[data-placement="top"] {
3432
+ --select-dropdown-enter-from-transform: var(--select-dropdown-top-enter-from-transform, translateY(8px) scale(0.98));
3433
+ }
3434
+
3435
+ @keyframes dropdownEnter {
3436
+ 0% {
3437
+ opacity: var(--select-dropdown-enter-from-opacity);
3438
+ transform: var(--select-dropdown-enter-from-transform);
3439
+ }
3440
+ 100% {
3441
+ opacity: var(--select-dropdown-enter-to-opacity);
3442
+ transform: var(--select-dropdown-enter-to-transform);
3443
+ }
2456
3444
  }
2457
3445
 
2458
3446
  .options-container {
2459
3447
  position: relative;
2460
- max-height: var(--select-options-max-height, 300px);
3448
+ max-height: var(--select-options-max-height, 320px);
2461
3449
  overflow: auto;
2462
- transition: opacity 0.2s ease-in-out;
2463
- background: var(--select-options-bg, var(--select-dropdown-bg, var(--select-bg, white)));
3450
+ box-sizing: border-box;
3451
+ padding: var(--select-options-padding, var(--select-dropdown-padding, 6px));
3452
+ background: var(--select-options-bg, var(--select-dropdown-bg, var(--select-surface)));
3453
+
3454
+ /* Custom scrollbar styling */
3455
+ scrollbar-width: thin;
3456
+ scrollbar-color: var(--select-border) transparent;
2464
3457
  }
3458
+
3459
+ .options-container::-webkit-scrollbar {
3460
+ width: var(--select-scrollbar-width);
3461
+ }
3462
+
3463
+ .options-container::-webkit-scrollbar-track {
3464
+ background: transparent;
3465
+ }
3466
+
3467
+ .options-container::-webkit-scrollbar-thumb {
3468
+ background: var(--select-border);
3469
+ border-radius: var(--select-scrollbar-thumb-radius);
3470
+ }
3471
+
3472
+ .options-container::-webkit-scrollbar-thumb:hover {
3473
+ background: var(--select-text-muted);
3474
+ }
3475
+
3476
+ /* ─────────────────────────────────────────────────────────────────────────
3477
+ Group Headers — Refined section dividers
3478
+ ───────────────────────────────────────────────────────────────────────── */
2465
3479
 
2466
3480
  .group-header {
2467
- padding: var(--select-group-header-padding, 8px 12px);
3481
+ padding: var(--select-group-header-padding, 10px 12px 6px);
3482
+ margin: var(--select-group-header-margin, 0);
2468
3483
  font-weight: var(--select-group-header-weight, 600);
2469
- color: var(--select-group-header-color, #6b7280);
2470
- background-color: var(--select-group-header-bg, #f3f4f6);
3484
+ color: var(--select-group-header-color, var(--select-text-muted));
3485
+ background-color: var(--select-group-header-bg, var(--select-surface));
2471
3486
  text-align: var(--select-group-header-text-align, left);
2472
- font-size: var(--select-group-header-font-size, 12px);
3487
+ font-size: var(--select-group-header-font-size, 0.7333em);
2473
3488
  text-transform: var(--select-group-header-text-transform, uppercase);
2474
- letter-spacing: var(--select-group-header-letter-spacing, 0.05em);
3489
+ letter-spacing: var(--select-group-header-letter-spacing, 0.08em);
2475
3490
  position: sticky;
2476
3491
  top: 0;
2477
3492
  z-index: 1;
2478
- border-bottom: var(--select-group-header-border-bottom, 1px solid #e5e7eb);
3493
+ border: var(--select-group-header-border, none);
3494
+ border-bottom: var(--select-group-header-border-bottom, none);
3495
+ border-radius: var(--select-group-header-border-radius, 0);
3496
+ box-shadow: var(--select-group-header-shadow, none);
3497
+ }
3498
+
3499
+ .group-header:not(:first-child) {
3500
+ margin-top: var(--select-group-header-separator-margin-top);
3501
+ padding-top: var(--select-group-header-separator-padding-top);
3502
+ border-top: var(--select-group-header-separator-border-top);
2479
3503
  }
2480
3504
 
3505
+ /* ─────────────────────────────────────────────────────────────────────────
3506
+ Options — Elegant hover and selection states
3507
+ ───────────────────────────────────────────────────────────────────────── */
3508
+
2481
3509
  .option {
2482
- padding: var(--select-option-padding, 8px 12px);
3510
+ position: relative;
3511
+ padding: var(--select-option-padding, 10px 14px);
2483
3512
  cursor: pointer;
2484
- color: var(--select-option-color, var(--select-text-color, #1f2937));
2485
- background: var(--select-option-bg, var(--select-dropdown-bg, var(--select-bg, white)));
2486
- transition: var(--select-option-transition, background-color 0.15s ease);
3513
+ color: var(--select-option-color, var(--select-text));
3514
+ background: var(--select-option-bg, transparent);
3515
+ border: var(--select-option-border, none);
3516
+ text-align: var(--select-option-text-align, start);
3517
+ transition:
3518
+ background var(--select-transition-fast),
3519
+ color var(--select-transition-fast),
3520
+ border-color var(--select-transition-fast),
3521
+ transform var(--select-transition-fast),
3522
+ box-shadow var(--select-transition-fast);
2487
3523
  user-select: none;
2488
- font-size: var(--select-option-font-size, 14px);
3524
+ font-size: var(--select-option-font-size, inherit);
3525
+ font-weight: var(--select-option-font-weight);
2489
3526
  line-height: var(--select-option-line-height, 1.5);
2490
- border: var(--select-option-border, none);
2491
- border-bottom: var(--select-option-border-bottom, none);
2492
- border-radius: var(--select-option-border-radius, 0);
2493
- box-shadow: var(--select-option-shadow, none);
2494
- transform: var(--select-option-transform, none);
3527
+ border-radius: var(--select-option-border-radius, var(--select-radius-sm));
3528
+ margin: var(--select-option-margin);
2495
3529
  }
2496
3530
 
2497
3531
  .option:hover {
2498
- background: var(--select-option-hover-bg, #f3f4f6);
2499
- color: var(--select-option-hover-color, #1f2937);
3532
+ background: var(--select-option-hover-bg, var(--select-surface-elevated));
3533
+ border: var(--select-option-hover-border, var(--select-option-border, none));
3534
+ color: var(--select-option-hover-color, var(--select-text));
3535
+ transform: var(--select-option-hover-transform);
2500
3536
  }
2501
3537
 
2502
3538
  .option.selected {
2503
- background: var(--select-option-selected-bg, #e0e7ff);
2504
- color: var(--select-option-selected-color, #4338ca);
2505
- font-weight: var(--select-option-selected-weight, 500);
3539
+ background: var(--select-option-selected-bg, linear-gradient(135deg, rgba(15, 52, 96, 0.08) 0%, rgba(15, 52, 96, 0.04) 100%));
2506
3540
  border: var(--select-option-selected-border, var(--select-option-border, none));
2507
- border-bottom: var(--select-option-selected-border-bottom, var(--select-option-border-bottom, none));
2508
- border-radius: var(--select-option-selected-border-radius, var(--select-option-border-radius, 0));
2509
- box-shadow: var(--select-option-selected-shadow, var(--select-option-shadow, none));
2510
- transform: var(--select-option-selected-transform, var(--select-option-transform, none));
3541
+ color: var(--select-option-selected-color, var(--select-accent));
3542
+ font-weight: var(--select-option-selected-weight, 550);
3543
+ }
3544
+
3545
+ .option.selected::before {
3546
+ content: '';
3547
+ position: absolute;
3548
+ left: var(--select-option-selected-indicator-left);
3549
+ top: var(--select-option-selected-indicator-top);
3550
+ transform: var(--select-option-selected-indicator-transform);
3551
+ width: var(--select-option-selected-indicator-width);
3552
+ height: var(--select-option-selected-indicator-height);
3553
+ background: var(--select-option-selected-indicator-bg);
3554
+ border-radius: var(--select-option-selected-indicator-radius);
3555
+ animation: var(--select-option-selected-indicator-animation);
3556
+ }
3557
+
3558
+ :host([dir="rtl"]) .option.selected::before {
3559
+ left: auto;
3560
+ right: var(--select-option-selected-indicator-right, var(--select-option-selected-indicator-left));
3561
+ border-radius: var(--select-option-selected-indicator-radius-rtl, 2px 0 0 2px);
3562
+ }
3563
+
3564
+ @keyframes selectedIndicator {
3565
+ 0% {
3566
+ height: var(--select-option-selected-indicator-from-height);
3567
+ opacity: var(--select-option-selected-indicator-from-opacity);
3568
+ }
3569
+ 100% {
3570
+ height: var(--select-option-selected-indicator-to-height);
3571
+ opacity: var(--select-option-selected-indicator-to-opacity);
3572
+ }
2511
3573
  }
2512
3574
 
2513
3575
  .option.selected:hover {
2514
- background: var(--select-option-selected-hover-bg, var(--select-option-selected-bg, #e0e7ff));
2515
- color: var(--select-option-selected-hover-color, var(--select-option-selected-color, #4338ca));
2516
- border: var(--select-option-selected-hover-border, var(--select-option-selected-border, var(--select-option-border, none)));
2517
- border-bottom: var(--select-option-selected-hover-border-bottom, var(--select-option-selected-border-bottom, var(--select-option-border-bottom, none)));
2518
- box-shadow: var(--select-option-selected-hover-shadow, var(--select-option-selected-shadow, var(--select-option-shadow, none)));
2519
- transform: var(--select-option-selected-hover-transform, var(--select-option-selected-transform, var(--select-option-transform, none)));
3576
+ background: var(--select-option-selected-hover-bg, var(--select-option-selected-bg, linear-gradient(135deg, rgba(15, 52, 96, 0.12) 0%, rgba(15, 52, 96, 0.06) 100%)));
3577
+ border: var(--select-option-selected-hover-border, var(--select-option-selected-border, var(--select-option-hover-border, var(--select-option-border, none))));
3578
+ color: var(--select-option-selected-hover-color, var(--select-option-selected-color, var(--select-accent)));
2520
3579
  }
2521
3580
 
2522
3581
  .option.active:not(.selected) {
2523
- background: var(--select-option-active-bg, #f3f4f6);
2524
- color: var(--select-option-active-color, #1f2937);
2525
- outline: var(--select-option-active-outline, 2px solid rgba(99, 102, 241, 0.45));
2526
- outline-offset: -2px;
3582
+ background: var(--select-option-active-bg, var(--select-surface-elevated));
3583
+ color: var(--select-option-active-color, var(--select-option-hover-color, var(--select-option-color, var(--select-text))));
3584
+ border: var(--select-option-active-border, var(--select-option-hover-border, var(--select-option-border, none)));
3585
+ outline: var(--select-option-active-outline, 2px solid rgba(15, 52, 96, 0.3));
3586
+ outline-offset: var(--select-option-active-outline-offset);
3587
+ box-shadow: var(--select-option-active-shadow, none);
3588
+ transform: var(--select-option-active-transform, none);
2527
3589
  }
2528
3590
 
2529
3591
  .option.selected.active {
2530
- background: var(--select-option-selected-active-bg, var(--select-option-selected-bg, #e0e7ff));
2531
- color: var(--select-option-selected-active-color, var(--select-option-selected-color, #4338ca));
2532
- border: var(--select-option-selected-active-border, var(--select-option-selected-border, var(--select-option-border, none)));
2533
- border-bottom: var(--select-option-selected-active-border-bottom, var(--select-option-selected-border-bottom, var(--select-option-border-bottom, none)));
2534
- box-shadow: var(--select-option-selected-active-shadow, var(--select-option-selected-shadow, var(--select-option-shadow, none)));
2535
- transform: var(--select-option-selected-active-transform, var(--select-option-selected-transform, var(--select-option-transform, none)));
2536
- outline: var(--select-option-selected-active-outline, var(--select-option-active-outline, 2px solid rgba(99, 102, 241, 0.45)));
2537
- outline-offset: -2px;
3592
+ outline: var(--select-option-selected-active-outline, 2px solid rgba(15, 52, 96, 0.4));
3593
+ outline-offset: var(--select-option-selected-active-outline-offset);
3594
+ }
3595
+
3596
+ .option.disabled {
3597
+ background: var(--select-option-disabled-bg, var(--select-option-bg, transparent));
3598
+ color: var(--select-option-disabled-color, var(--select-text-muted));
3599
+ border: var(--select-option-disabled-border, var(--select-option-border, none));
3600
+ opacity: var(--select-option-disabled-opacity, 0.5);
3601
+ cursor: var(--select-option-disabled-cursor, not-allowed);
2538
3602
  }
2539
3603
 
2540
3604
  .option:active:not(.selected) {
2541
- background: var(--select-option-pressed-bg, #e5e7eb);
3605
+ background: var(--select-option-pressed-bg, rgba(15, 52, 96, 0.08));
3606
+ transform: var(--select-option-pressed-transform);
2542
3607
  }
2543
3608
 
2544
3609
  .option.selected:active {
2545
- background: var(--select-option-selected-pressed-bg, var(--select-option-selected-hover-bg, var(--select-option-selected-bg, #e0e7ff)));
3610
+ transform: var(--select-option-selected-pressed-transform);
2546
3611
  }
2547
3612
 
3613
+ /* ─────────────────────────────────────────────────────────────────────────
3614
+ Load More & Busy States — Refined feedback indicators
3615
+ ───────────────────────────────────────────────────────────────────────── */
3616
+
2548
3617
  .load-more-container {
2549
3618
  padding: var(--select-load-more-padding, 12px);
2550
3619
  text-align: center;
2551
- border-top: var(--select-divider-border, 1px solid #e0e0e0);
2552
- background: var(--select-load-more-bg, white);
2553
3620
  }
2554
3621
 
2555
3622
  .load-more-button {
2556
- padding: var(--select-button-padding, 8px 16px);
2557
- border: var(--select-button-border, 1px solid #1976d2);
2558
- background: var(--select-button-bg, white);
2559
- color: var(--select-button-color, #1976d2);
2560
- border-radius: var(--select-button-border-radius, 4px);
3623
+ padding: var(--select-button-padding, 10px 20px);
3624
+ border: var(--select-button-border, 1.5px solid var(--select-border));
3625
+ background: var(--select-button-bg, transparent);
3626
+ color: var(--select-button-color, var(--select-accent));
3627
+ border-radius: var(--select-button-border-radius, var(--select-radius-md));
2561
3628
  cursor: pointer;
2562
- font-size: var(--select-button-font-size, 14px);
2563
- font-family: var(--select-font-family, inherit);
2564
- transition: all 0.2s ease;
3629
+ font-size: var(--select-button-font-size, 0.8667em);
3630
+ font-weight: var(--select-button-font-weight);
3631
+ letter-spacing: var(--select-button-letter-spacing);
3632
+ transition:
3633
+ background var(--select-transition-fast),
3634
+ border-color var(--select-transition-fast),
3635
+ color var(--select-transition-fast),
3636
+ transform var(--select-transition-bounce);
2565
3637
  }
2566
3638
 
2567
3639
  .load-more-button:hover {
2568
- background: var(--select-button-hover-bg, #1976d2);
3640
+ background: var(--select-button-hover-bg, var(--select-accent));
3641
+ border-color: var(--select-accent);
2569
3642
  color: var(--select-button-hover-color, white);
3643
+ transform: var(--select-button-hover-transform);
3644
+ }
3645
+
3646
+ .load-more-button:active {
3647
+ transform: var(--select-button-active-transform);
2570
3648
  }
2571
3649
 
2572
3650
  .load-more-button:disabled {
2573
3651
  opacity: var(--select-button-disabled-opacity, 0.5);
2574
3652
  cursor: not-allowed;
3653
+ transform: none;
2575
3654
  }
2576
3655
 
2577
3656
  .busy-bucket {
2578
- padding: var(--select-busy-padding, 16px);
3657
+ padding: var(--select-busy-padding, 20px);
2579
3658
  text-align: center;
2580
- color: var(--select-busy-color, #666);
2581
- background: var(--select-busy-bg, white);
2582
- font-size: var(--select-busy-font-size, 14px);
3659
+ color: var(--select-busy-color, var(--select-text-muted));
3660
+ background: var(--select-busy-bg, transparent);
3661
+ font-size: var(--select-busy-font-size, 0.8667em);
2583
3662
  }
2584
3663
 
2585
3664
  .spinner {
2586
3665
  display: inline-block;
2587
- width: var(--select-spinner-size, 20px);
2588
- height: var(--select-spinner-size, 20px);
2589
- border: var(--select-spinner-border, 2px solid #ccc);
2590
- border-top-color: var(--select-spinner-active-color, #1976d2);
3666
+ width: var(--select-spinner-size, 22px);
3667
+ height: var(--select-spinner-size, 22px);
3668
+ border: var(--select-spinner-border, 2px solid var(--select-border));
3669
+ border-top-color: var(--select-spinner-active-color, var(--select-accent));
2591
3670
  border-radius: 50%;
2592
- animation: spin 0.6s linear infinite;
3671
+ animation: var(--select-spinner-animation);
2593
3672
  }
2594
3673
 
2595
3674
  @keyframes spin {
@@ -2597,61 +3676,97 @@ class EnhancedSelect extends HTMLElement {
2597
3676
  }
2598
3677
 
2599
3678
  .empty-state {
2600
- padding: var(--select-empty-padding, 24px);
3679
+ padding: var(--select-empty-padding, 32px 24px);
2601
3680
  text-align: center;
2602
- color: var(--select-empty-color, #6b7280);
2603
- font-size: var(--select-empty-font-size, 14px);
2604
- background: var(--select-empty-bg, white);
3681
+ color: var(--select-empty-color, var(--select-text-muted));
3682
+ font-size: var(--select-empty-font-size, 0.9333em);
3683
+ background: var(--select-empty-bg, transparent);
2605
3684
  display: flex;
2606
3685
  flex-direction: column;
2607
3686
  align-items: center;
2608
3687
  justify-content: center;
2609
- gap: 6px;
2610
- min-height: var(--select-empty-min-height, 72px);
3688
+ gap: var(--select-empty-gap);
2611
3689
  }
2612
3690
 
2613
3691
  .searching-state {
2614
- padding: var(--select-searching-padding, 24px);
3692
+ padding: var(--select-searching-padding, 32px 24px);
2615
3693
  text-align: center;
2616
- color: var(--select-searching-color, #667eea);
2617
- font-size: var(--select-searching-font-size, 14px);
2618
- font-style: italic;
2619
- background: var(--select-searching-bg, white);
2620
- animation: pulse 1.5s ease-in-out infinite;
3694
+ color: var(--select-searching-color, var(--select-accent));
3695
+ font-size: var(--select-searching-font-size, 0.9333em);
3696
+ background: var(--select-searching-bg, transparent);
2621
3697
  display: flex;
2622
3698
  flex-direction: column;
2623
3699
  align-items: center;
2624
3700
  justify-content: center;
2625
- gap: 6px;
2626
- min-height: var(--select-searching-min-height, 72px);
3701
+ gap: var(--select-searching-gap);
2627
3702
  }
2628
3703
 
2629
- @keyframes pulse {
2630
- 0%, 100% { opacity: 1; }
2631
- 50% { opacity: 0.5; }
3704
+ .searching-state::before {
3705
+ content: '';
3706
+ width: var(--select-searching-spinner-size);
3707
+ height: var(--select-searching-spinner-size);
3708
+ border: var(--select-searching-spinner-border);
3709
+ border-top-color: var(--select-searching-spinner-active-color);
3710
+ border-radius: 50%;
3711
+ animation: var(--select-searching-spinner-animation);
2632
3712
  }
2633
3713
 
2634
- /* Error states */
3714
+ /* ─────────────────────────────────────────────────────────────────────────
3715
+ Error States — Clear visual feedback
3716
+ ───────────────────────────────────────────────────────────────────────── */
3717
+
2635
3718
  .select-input[aria-invalid="true"] {
2636
- border-color: var(--select-error-border, #dc2626);
3719
+ border-color: var(--select-error-border, #e94560);
2637
3720
  }
2638
3721
 
2639
3722
  .select-input[aria-invalid="true"]:focus {
2640
- border-color: var(--select-error-border, #dc2626);
2641
- box-shadow: 0 0 0 2px var(--select-error-shadow, rgba(220, 38, 38, 0.1));
2642
- outline-color: var(--select-error-border, #dc2626);
3723
+ border-color: var(--select-error-border, #e94560);
3724
+ box-shadow: 0 0 0 3px var(--select-error-shadow, rgba(233, 69, 96, 0.15));
2643
3725
  }
2644
3726
 
2645
- /* Accessibility: Reduced motion */
3727
+ /* ──────────────────────────────────────────────────��──────────────────────
3728
+ Accessibility — Reduced motion & High contrast
3729
+ ───────────────────────────────────────────────────────────────────────── */
3730
+
2646
3731
  @media (prefers-reduced-motion: reduce) {
2647
- * {
2648
- animation-duration: 0.01ms !important;
2649
- animation-iteration-count: 1 !important;
2650
- transition-duration: 0.01ms !important;
3732
+ *,
3733
+ *::before,
3734
+ *::after {
3735
+ animation-duration: var(--select-reduced-motion-duration) !important;
3736
+ animation-iteration-count: var(--select-reduced-motion-iteration-count) !important;
3737
+ transition-duration: var(--select-reduced-motion-duration) !important;
3738
+ }
3739
+
3740
+ .dropdown-arrow.open {
3741
+ transform: var(--select-arrow-open-transform);
3742
+ }
3743
+ }
3744
+
3745
+ @media (prefers-contrast: high) {
3746
+ .select-input:focus {
3747
+ outline-width: var(--select-high-contrast-focus-outline-width);
3748
+ outline-color: var(--select-high-contrast-focus-outline-color);
3749
+ }
3750
+
3751
+ .input-container {
3752
+ border-width: var(--select-high-contrast-border-width);
2651
3753
  }
3754
+
3755
+ .option.selected::before {
3756
+ width: var(--select-high-contrast-indicator-width);
3757
+ }
3758
+ }
3759
+
3760
+ /* Touch targets (WCAG 2.5.5) */
3761
+ .load-more-button,
3762
+ select-option {
3763
+ min-height: var(--select-touch-target-min-height);
2652
3764
  }
2653
3765
 
2654
- /* Dark mode - Opt-in via class, data attribute, or ancestor context */
3766
+ /* ─────────────────────────────────────────────────────────────────────────
3767
+ Dark Mode — Elegant dark theme
3768
+ ───────────────────────────────────────────────────────────────────────── */
3769
+
2655
3770
  :host(.dark-mode),
2656
3771
  :host([dark-mode]),
2657
3772
  :host([darkmode]),
@@ -2663,139 +3778,133 @@ class EnhancedSelect extends HTMLElement {
2663
3778
  :host-context([darkmode]),
2664
3779
  :host-context([data-theme="dark"]),
2665
3780
  :host-context([theme="dark"]) {
2666
- /* map dark tokens to base option tokens so nested <select-option>
2667
- components also pick up dark mode via inherited CSS variables */
2668
- --select-option-bg: var(--select-dark-option-bg, #1f2937);
2669
- --select-option-color: var(--select-dark-option-color, #f9fafb);
2670
- --select-option-hover-bg: var(--select-dark-option-hover-bg, #374151);
2671
- --select-option-hover-color: var(--select-dark-option-hover-color, #f9fafb);
2672
- --select-option-selected-bg: var(--select-dark-option-selected-bg, #3730a3);
2673
- --select-option-selected-color: var(--select-dark-option-selected-text, #e0e7ff);
2674
-
2675
- .input-container {
2676
- background: var(--select-dark-bg, #1f2937);
2677
- border-color: var(--select-dark-border, #4b5563);
2678
- }
2679
-
2680
- .select-input {
2681
- color: var(--select-dark-text, #f9fafb);
2682
- }
2683
-
2684
- .select-input::placeholder {
2685
- color: var(--select-dark-placeholder, #6b7280);
2686
- }
2687
-
2688
- .select-dropdown {
2689
- background: var(--select-dark-dropdown-bg, #1f2937);
2690
- border-color: var(--select-dark-dropdown-border, #4b5563);
2691
- }
2692
-
2693
- .options-container {
2694
- background: var(--select-dark-options-bg, #1f2937);
2695
- }
2696
-
2697
- .option {
2698
- color: var(--select-dark-option-color, #f9fafb);
2699
- background: var(--select-dark-option-bg, #1f2937);
2700
- }
2701
-
2702
- .option:hover {
2703
- background: var(--select-dark-option-hover-bg, #374151);
2704
- color: var(--select-dark-option-hover-color, #f9fafb);
2705
- }
2706
-
2707
- .option.selected {
2708
- background: var(--select-dark-option-selected-bg, #3730a3);
2709
- color: var(--select-dark-option-selected-text, #e0e7ff);
2710
- border: var(--select-dark-option-selected-border, var(--select-option-selected-border, var(--select-option-border, none)));
2711
- border-bottom: var(--select-dark-option-selected-border-bottom, var(--select-option-selected-border-bottom, var(--select-option-border-bottom, none)));
2712
- box-shadow: var(--select-dark-option-selected-shadow, var(--select-option-selected-shadow, var(--select-option-shadow, none)));
2713
- transform: var(--select-dark-option-selected-transform, var(--select-option-selected-transform, var(--select-option-transform, none)));
2714
- }
3781
+ --select-primary: #e5e5e5;
3782
+ --select-primary-light: #2a2a3e;
3783
+ --select-accent: #6366f1;
3784
+ --select-accent-hover: #f43f5e;
3785
+ --select-surface: var(--select-dark-bg, #1a1a2e);
3786
+ --select-surface-elevated: #252540;
3787
+ --select-border: var(--select-dark-border, #3f3f5a);
3788
+ --select-border-focus: #6366f1;
3789
+ --select-text: var(--select-dark-text, #f5f5f5);
3790
+ --select-text-muted: #9ca3af;
3791
+ --select-text-placeholder: #6b7280;
3792
+ --select-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.2);
3793
+ --select-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.3);
3794
+ --select-shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.4);
3795
+ --select-shadow-focus: 0 0 0 3px rgba(99, 102, 241, 0.25);
2715
3796
 
2716
- .option.selected:hover {
2717
- background: var(--select-dark-option-selected-hover-bg, var(--select-dark-option-selected-bg, #3730a3));
2718
- color: var(--select-dark-option-selected-hover-color, var(--select-dark-option-selected-text, #e0e7ff));
2719
- border: var(--select-dark-option-selected-hover-border, var(--select-dark-option-selected-border, var(--select-option-selected-hover-border, var(--select-option-selected-border, var(--select-option-border, none)))));
2720
- border-bottom: var(--select-dark-option-selected-hover-border-bottom, var(--select-dark-option-selected-border-bottom, var(--select-option-selected-hover-border-bottom, var(--select-option-selected-border-bottom, var(--select-option-border-bottom, none)))));
2721
- box-shadow: var(--select-dark-option-selected-hover-shadow, var(--select-dark-option-selected-shadow, var(--select-option-selected-hover-shadow, var(--select-option-selected-shadow, var(--select-option-shadow, none)))));
2722
- transform: var(--select-dark-option-selected-hover-transform, var(--select-dark-option-selected-transform, var(--select-option-selected-hover-transform, var(--select-option-selected-transform, var(--select-option-transform, none)))));
2723
- }
3797
+ --select-dropdown-bg: var(--select-dark-dropdown-bg, var(--select-surface));
2724
3798
 
2725
- .option.active:not(.selected) {
2726
- background-color: var(--select-dark-option-active-bg, #374151);
2727
- color: var(--select-dark-option-active-color, #f9fafb);
2728
- outline: var(--select-dark-option-active-outline, 2px solid rgba(129, 140, 248, 0.55));
2729
- }
2730
-
2731
- /* Group header in dark mode */
2732
- .group-header {
2733
- color: var(--select-dark-group-header-color, var(--select-group-header-color, #6b7280));
2734
- background-color: var(--select-dark-group-header-bg, var(--select-group-header-bg, #374151));
2735
- }
3799
+ --select-option-bg: var(--select-dark-option-bg, transparent);
3800
+ --select-option-color: var(--select-dark-option-color, var(--select-text));
3801
+ --select-option-hover-bg: var(--select-dark-option-hover-bg, var(--select-surface-elevated));
3802
+ --select-option-hover-color: var(--select-dark-option-hover-color, var(--select-text));
3803
+ --select-option-selected-bg: var(--select-dark-option-selected-bg, linear-gradient(135deg, rgba(99, 102, 241, 0.15) 0%, rgba(99, 102, 241, 0.08) 100%));
3804
+ --select-option-selected-color: var(--select-dark-option-selected-color, #a5b4fc);
3805
+ --select-option-selected-hover-bg: var(--select-dark-option-selected-hover-bg, var(--select-option-selected-bg));
3806
+ --select-option-selected-hover-color: var(--select-dark-option-selected-hover-color, var(--select-option-selected-color));
3807
+ }
2736
3808
 
2737
- .option.selected.active {
2738
- background-color: var(--select-dark-option-selected-active-bg, var(--select-dark-option-selected-bg, #3730a3));
2739
- color: var(--select-dark-option-selected-active-color, var(--select-dark-option-selected-text, #e0e7ff));
2740
- 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)))));
2741
- 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)))));
2742
- 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)))));
2743
- 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)))));
2744
- 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)))));
2745
- outline-offset: -2px;
2746
- }
3809
+ :host(.dark-mode) .input-container,
3810
+ :host([dark-mode]) .input-container,
3811
+ :host([darkmode]) .input-container,
3812
+ :host([data-theme="dark"]) .input-container,
3813
+ :host([theme="dark"]) .input-container,
3814
+ :host-context(.dark-mode) .input-container,
3815
+ :host-context(.dark) .input-container,
3816
+ :host-context([dark-mode]) .input-container,
3817
+ :host-context([darkmode]) .input-container,
3818
+ :host-context([data-theme="dark"]) .input-container,
3819
+ :host-context([theme="dark"]) .input-container {
3820
+ background: var(--select-surface);
3821
+ border-color: var(--select-border);
3822
+ }
2747
3823
 
2748
- .selection-badge {
2749
- background: var(--select-dark-badge-bg, #4f46e5);
2750
- color: var(--select-dark-badge-color, #eef2ff);
2751
- }
3824
+ :host(.dark-mode) .input-container::after,
3825
+ :host([dark-mode]) .input-container::after,
3826
+ :host([darkmode]) .input-container::after,
3827
+ :host([data-theme="dark"]) .input-container::after,
3828
+ :host([theme="dark"]) .input-container::after,
3829
+ :host-context(.dark-mode) .input-container::after,
3830
+ :host-context(.dark) .input-container::after,
3831
+ :host-context([dark-mode]) .input-container::after,
3832
+ :host-context([darkmode]) .input-container::after,
3833
+ :host-context([data-theme="dark"]) .input-container::after,
3834
+ :host-context([theme="dark"]) .input-container::after {
3835
+ background: var(--select-separator-dark-bg);
3836
+ }
2752
3837
 
2753
- .badge-remove {
2754
- background: var(--select-dark-badge-remove-bg, rgba(255, 255, 255, 0.15));
2755
- color: var(--select-dark-badge-remove-color, #e5e7eb);
2756
- }
3838
+ :host(.dark-mode) .select-dropdown,
3839
+ :host([dark-mode]) .select-dropdown,
3840
+ :host([darkmode]) .select-dropdown,
3841
+ :host([data-theme="dark"]) .select-dropdown,
3842
+ :host([theme="dark"]) .select-dropdown,
3843
+ :host-context(.dark-mode) .select-dropdown,
3844
+ :host-context(.dark) .select-dropdown,
3845
+ :host-context([dark-mode]) .select-dropdown,
3846
+ :host-context([darkmode]) .select-dropdown,
3847
+ :host-context([data-theme="dark"]) .select-dropdown,
3848
+ :host-context([theme="dark"]) .select-dropdown {
3849
+ background: var(--select-dropdown-bg, var(--select-surface));
3850
+ border-color: var(--select-border);
3851
+ }
2757
3852
 
2758
- .badge-remove:hover {
2759
- background: var(--select-dark-badge-remove-hover-bg, rgba(255, 255, 255, 0.3));
2760
- }
2761
-
2762
- .busy-bucket,
2763
- .empty-state {
2764
- color: var(--select-dark-busy-color, #9ca3af);
2765
- background: var(--select-dark-empty-bg, #111827);
2766
- }
3853
+ :host(.dark-mode) .options-container,
3854
+ :host([dark-mode]) .options-container,
3855
+ :host([darkmode]) .options-container,
3856
+ :host([data-theme="dark"]) .options-container,
3857
+ :host([theme="dark"]) .options-container,
3858
+ :host-context(.dark-mode) .options-container,
3859
+ :host-context(.dark) .options-container,
3860
+ :host-context([dark-mode]) .options-container,
3861
+ :host-context([darkmode]) .options-container,
3862
+ :host-context([data-theme="dark"]) .options-container,
3863
+ :host-context([theme="dark"]) .options-container {
3864
+ background: var(--select-dropdown-bg, var(--select-surface));
3865
+ scrollbar-color: var(--select-border) transparent;
3866
+ }
2767
3867
 
2768
- .searching-state {
2769
- background: var(--select-dark-searching-bg, #111827);
2770
- }
2771
-
2772
- .input-container::after {
2773
- background: linear-gradient(
2774
- to bottom,
2775
- transparent 0%,
2776
- rgba(255, 255, 255, 0.1) 20%,
2777
- rgba(255, 255, 255, 0.1) 80%,
2778
- transparent 100%
2779
- );
2780
- }
3868
+ :host(.dark-mode) .selection-badge,
3869
+ :host([dark-mode]) .selection-badge,
3870
+ :host([darkmode]) .selection-badge,
3871
+ :host([data-theme="dark"]) .selection-badge,
3872
+ :host([theme="dark"]) .selection-badge,
3873
+ :host-context(.dark-mode) .selection-badge,
3874
+ :host-context(.dark) .selection-badge,
3875
+ :host-context([dark-mode]) .selection-badge,
3876
+ :host-context([darkmode]) .selection-badge,
3877
+ :host-context([data-theme="dark"]) .selection-badge,
3878
+ :host-context([theme="dark"]) .selection-badge {
3879
+ background: var(--select-badge-dark-bg);
2781
3880
  }
2782
-
2783
- /* Accessibility: High contrast mode */
2784
- @media (prefers-contrast: high) {
2785
- .select-input:focus {
2786
- outline-width: 3px;
2787
- outline-color: Highlight;
2788
- }
2789
-
2790
- .select-input {
2791
- border-width: 2px;
2792
- }
3881
+
3882
+ :host(.dark-mode) .group-header:not(:first-child),
3883
+ :host([dark-mode]) .group-header:not(:first-child),
3884
+ :host([darkmode]) .group-header:not(:first-child),
3885
+ :host([data-theme="dark"]) .group-header:not(:first-child),
3886
+ :host([theme="dark"]) .group-header:not(:first-child),
3887
+ :host-context(.dark-mode) .group-header:not(:first-child),
3888
+ :host-context(.dark) .group-header:not(:first-child),
3889
+ :host-context([dark-mode]) .group-header:not(:first-child),
3890
+ :host-context([darkmode]) .group-header:not(:first-child),
3891
+ :host-context([data-theme="dark"]) .group-header:not(:first-child),
3892
+ :host-context([theme="dark"]) .group-header:not(:first-child) {
3893
+ border-top-color: var(--select-border);
2793
3894
  }
2794
-
2795
- /* Touch targets (WCAG 2.5.5) */
2796
- .load-more-button,
2797
- select-option {
2798
- min-height: 44px;
3895
+
3896
+ :host(.dark-mode) .option.selected::before,
3897
+ :host([dark-mode]) .option.selected::before,
3898
+ :host([darkmode]) .option.selected::before,
3899
+ :host([data-theme="dark"]) .option.selected::before,
3900
+ :host([theme="dark"]) .option.selected::before,
3901
+ :host-context(.dark-mode) .option.selected::before,
3902
+ :host-context(.dark) .option.selected::before,
3903
+ :host-context([dark-mode]) .option.selected::before,
3904
+ :host-context([darkmode]) .option.selected::before,
3905
+ :host-context([data-theme="dark"]) .option.selected::before,
3906
+ :host-context([theme="dark"]) .option.selected::before {
3907
+ background: var(--select-accent);
2799
3908
  }
2800
3909
  `;
2801
3910
  // Insert as first child to ensure styles are processed first
@@ -2815,10 +3924,10 @@ class EnhancedSelect extends HTMLElement {
2815
3924
  // delegate to the existing open/close helpers so we don't accidentally
2816
3925
  // drift out of sync with the logic in those methods (focus, events,
2817
3926
  // scroll-to-selected, etc.)
2818
- if (this._state.isOpen) {
3927
+ if (this._state.isOpen && this._config.selection.toggleOnTriggerClick !== false) {
2819
3928
  this._handleClose();
2820
3929
  }
2821
- else {
3930
+ else if (!this._state.isOpen) {
2822
3931
  this._handleOpen();
2823
3932
  }
2824
3933
  };
@@ -2841,7 +3950,7 @@ class EnhancedSelect extends HTMLElement {
2841
3950
  this._inputContainer.addEventListener('pointerdown', (e) => {
2842
3951
  // Prevent propagation to document click listener but do NOT preventDefault.
2843
3952
  // Allow default so browser events (click) on newly opened options still fire.
2844
- e.stopPropagation();
3953
+ // e.stopPropagation(); // BUG: By stopping propagation here, the document click listener doesn't see it, which is fine for not closing it. But be very careful.
2845
3954
  const target = e.target;
2846
3955
  if (!this._config.enabled)
2847
3956
  return;
@@ -2849,21 +3958,38 @@ class EnhancedSelect extends HTMLElement {
2849
3958
  return;
2850
3959
  if (target && target.closest('.clear-control-button'))
2851
3960
  return;
3961
+ if (this._isPointerOnInputScrollbar(e)) {
3962
+ this._suppressNextOpenClick = true;
3963
+ return;
3964
+ }
3965
+ const isHorizontalMultiMode = this._config.selection.mode === 'multi'
3966
+ && (this._config.multiSelectDisplay?.mode ?? 'wrap') === 'horizontal';
3967
+ const enableHorizontalDrag = this._canUseHorizontalMultiScroll(target);
3968
+ if (enableHorizontalDrag && e.button === 0) {
3969
+ this._beginMultiScrollDrag(e);
3970
+ this._suppressNextOpenClick = false;
3971
+ e.preventDefault();
3972
+ return;
3973
+ }
3974
+ // If we clicked the container, but not the input itself, we must prevent default
3975
+ // otherwise the browser moves focus from our input to the body, immediately triggering blur.
3976
+ if (target && !target.matches('.select-input')) {
3977
+ e.preventDefault();
3978
+ }
3979
+ const clickedInput = Boolean(target && target.matches('.select-input'));
3980
+ if (this._state.isOpen &&
3981
+ this._config.selection.toggleOnTriggerClick !== false &&
3982
+ !isHorizontalMultiMode &&
3983
+ !enableHorizontalDrag &&
3984
+ !(clickedInput && this._config.searchable)) {
3985
+ this._handleClose();
3986
+ return;
3987
+ }
2852
3988
  const wasClosed = !this._state.isOpen;
2853
3989
  if (wasClosed) {
2854
3990
  this._handleOpen();
2855
3991
  }
2856
- else {
2857
- // Keep open while interacting directly with the input so users can
2858
- // place cursor/type without accidental collapse.
2859
- if (target === this._input) {
2860
- this._input.focus();
2861
- return;
2862
- }
2863
- // clicking other parts of the input container while open toggles close
2864
- this._handleClose();
2865
- }
2866
- // Focus the input (do not prevent default behavior)
3992
+ // Focus the input (do not prevent default behavior for input itself)
2867
3993
  this._input.focus();
2868
3994
  // If we just opened the dropdown, transfer pointer capture to the
2869
3995
  // options container so the subsequent pointerup lands there instead of
@@ -2877,6 +4003,47 @@ class EnhancedSelect extends HTMLElement {
2877
4003
  }
2878
4004
  }
2879
4005
  });
4006
+ this._inputContainer.addEventListener('click', (e) => {
4007
+ const target = e.target;
4008
+ const isHorizontalMultiMode = this._config.selection.mode === 'multi'
4009
+ && (this._config.multiSelectDisplay?.mode ?? 'wrap') === 'horizontal';
4010
+ if (!this._config.enabled || !isHorizontalMultiMode) {
4011
+ this._suppressNextOpenClick = false;
4012
+ this._multiScrollDrag.moved = false;
4013
+ return;
4014
+ }
4015
+ if (target && target.closest('.dropdown-arrow-container, .clear-control-button')) {
4016
+ this._suppressNextOpenClick = false;
4017
+ this._multiScrollDrag.moved = false;
4018
+ return;
4019
+ }
4020
+ if (this._suppressNextOpenClick || this._multiScrollDrag.moved || this._isPointerOnInputScrollbar(e)) {
4021
+ this._suppressNextOpenClick = false;
4022
+ this._multiScrollDrag.moved = false;
4023
+ return;
4024
+ }
4025
+ this._suppressNextOpenClick = false;
4026
+ this._multiScrollDrag.moved = false;
4027
+ if (!this._state.isOpen) {
4028
+ this._handleOpen();
4029
+ }
4030
+ this._input.focus();
4031
+ });
4032
+ this._inputContent.addEventListener('pointermove', (e) => {
4033
+ this._updateMultiScrollDrag(e);
4034
+ });
4035
+ this._inputContent.addEventListener('pointerup', (e) => {
4036
+ this._endMultiScrollDrag(e.pointerId);
4037
+ });
4038
+ this._inputContent.addEventListener('pointercancel', (e) => {
4039
+ this._endMultiScrollDrag(e.pointerId);
4040
+ });
4041
+ this._inputContent.addEventListener('wheel', (e) => {
4042
+ if (this._config.selection.mode === 'multi'
4043
+ && (this._config.multiSelectDisplay?.mode ?? 'wrap') === 'horizontal') {
4044
+ e.preventDefault();
4045
+ }
4046
+ }, { passive: false });
2880
4047
  // Input container click - prevent event from reaching document listener
2881
4048
  this._container.addEventListener('click', (e) => {
2882
4049
  e.stopPropagation();
@@ -2905,7 +4072,7 @@ class EnhancedSelect extends HTMLElement {
2905
4072
  return;
2906
4073
  }
2907
4074
  this._handleClose();
2908
- }, 0);
4075
+ }, 150);
2909
4076
  });
2910
4077
  // Input search
2911
4078
  this._input.addEventListener('input', (e) => {
@@ -2922,7 +4089,7 @@ class EnhancedSelect extends HTMLElement {
2922
4089
  this._suppressBlurClose = true;
2923
4090
  setTimeout(() => {
2924
4091
  this._suppressBlurClose = false;
2925
- }, 0);
4092
+ }, 150); // Increased timeout to ensure click finishes before blur checks
2926
4093
  });
2927
4094
  // Delegated click listener for improved event handling (robust across shadow DOM)
2928
4095
  const handleOptionEvent = (e) => {
@@ -2953,9 +4120,6 @@ class EnhancedSelect extends HTMLElement {
2953
4120
  }
2954
4121
  };
2955
4122
  this._optionsContainer.addEventListener('click', handleOptionEvent);
2956
- // also watch pointerup to catch cases where the pointerdown started outside
2957
- // (e.g. on the input) and the click never fires
2958
- this._optionsContainer.addEventListener('pointerup', handleOptionEvent);
2959
4123
  // Keyboard navigation
2960
4124
  this._input.addEventListener('keydown', (e) => this._handleKeydown(e));
2961
4125
  // Click outside to close — robust detection across shadow DOM and custom renderers
@@ -3050,22 +4214,14 @@ class EnhancedSelect extends HTMLElement {
3050
4214
  this._input.focus();
3051
4215
  this._markOpenStart();
3052
4216
  this._state.isOpen = true;
4217
+ this.setAttribute('data-open', 'true');
4218
+ this._liftStackingAncestors();
3053
4219
  this._dropdown.style.display = 'block';
3054
4220
  this._input.setAttribute('aria-expanded', 'true');
3055
4221
  this._updateArrowRotation();
3056
- // Clear search query when opening to show all options
3057
- // This ensures we can scroll to selected item
3058
- if (this._config.searchable) {
3059
- this._state.searchQuery = '';
3060
- // Don't clear input value if it represents selection
3061
- // But if we want to search, we might want to clear it?
3062
- // Standard behavior: input keeps value (label), but dropdown shows all options
3063
- // until user types.
3064
- // However, our filtering logic uses _state.searchQuery.
3065
- // So clearing it here resets the filter.
3066
- }
3067
4222
  // Render options when opening
3068
4223
  this._renderOptions();
4224
+ this._syncDropdownPlacement();
3069
4225
  this._setInitialActiveOption();
3070
4226
  this._emit('open', {});
3071
4227
  this._config.callbacks.onOpen?.();
@@ -3085,7 +4241,10 @@ class EnhancedSelect extends HTMLElement {
3085
4241
  if (!this._state.isOpen)
3086
4242
  return;
3087
4243
  this._state.isOpen = false;
4244
+ this.removeAttribute('data-open');
4245
+ this._restoreLiftedAncestors();
3088
4246
  this._dropdown.style.display = 'none';
4247
+ this._dropdown.setAttribute('data-placement', this._resolvedDropdownPlacement);
3089
4248
  this._input.setAttribute('aria-expanded', 'false');
3090
4249
  this._input.removeAttribute('aria-activedescendant');
3091
4250
  this._updateArrowRotation();
@@ -3095,14 +4254,76 @@ class EnhancedSelect extends HTMLElement {
3095
4254
  }
3096
4255
  _updateDropdownVisibility() {
3097
4256
  if (this._state.isOpen) {
4257
+ this.setAttribute('data-open', 'true');
4258
+ this._liftStackingAncestors();
3098
4259
  this._dropdown.style.display = 'block';
4260
+ this._syncDropdownPlacement();
3099
4261
  this._input.setAttribute('aria-expanded', 'true');
3100
4262
  }
3101
4263
  else {
4264
+ this.removeAttribute('data-open');
4265
+ this._restoreLiftedAncestors();
3102
4266
  this._dropdown.style.display = 'none';
3103
4267
  this._input.setAttribute('aria-expanded', 'false');
3104
4268
  }
3105
4269
  }
4270
+ _liftStackingAncestors() {
4271
+ this._restoreLiftedAncestors();
4272
+ let ancestor = this.parentElement;
4273
+ while (ancestor && ancestor !== document.body) {
4274
+ if (this._createsStackingContext(ancestor)) {
4275
+ const hadMarker = ancestor.hasAttribute('data-smilodon-open-ancestor');
4276
+ this._liftedAncestors.push({
4277
+ element: ancestor,
4278
+ position: ancestor.style.position,
4279
+ zIndex: ancestor.style.zIndex,
4280
+ hadMarker,
4281
+ });
4282
+ if (getComputedStyle(ancestor).position === 'static') {
4283
+ ancestor.style.position = 'relative';
4284
+ }
4285
+ ancestor.style.zIndex = 'var(--select-ancestor-open-z-index, var(--select-host-open-z-index, var(--select-dropdown-z-index, 1000)))';
4286
+ ancestor.setAttribute('data-smilodon-open-ancestor', 'true');
4287
+ }
4288
+ ancestor = ancestor.parentElement;
4289
+ }
4290
+ }
4291
+ _restoreLiftedAncestors() {
4292
+ for (const lifted of this._liftedAncestors) {
4293
+ lifted.element.style.position = lifted.position;
4294
+ lifted.element.style.zIndex = lifted.zIndex;
4295
+ if (!lifted.hadMarker) {
4296
+ lifted.element.removeAttribute('data-smilodon-open-ancestor');
4297
+ }
4298
+ }
4299
+ this._liftedAncestors = [];
4300
+ }
4301
+ _createsStackingContext(element) {
4302
+ const style = getComputedStyle(element);
4303
+ if (style.position === 'fixed' || style.position === 'sticky')
4304
+ return true;
4305
+ if (style.zIndex !== 'auto' && style.position !== 'static')
4306
+ return true;
4307
+ if (style.opacity !== '1')
4308
+ return true;
4309
+ if (style.transform !== 'none')
4310
+ return true;
4311
+ if (style.filter !== 'none')
4312
+ return true;
4313
+ if (style.backdropFilter && style.backdropFilter !== 'none')
4314
+ return true;
4315
+ if (style.perspective !== 'none')
4316
+ return true;
4317
+ if (style.mixBlendMode !== 'normal')
4318
+ return true;
4319
+ if (style.isolation === 'isolate')
4320
+ return true;
4321
+ if (style.contain.includes('paint') || style.contain.includes('layout'))
4322
+ return true;
4323
+ if (style.willChange.includes('transform') || style.willChange.includes('opacity') || style.willChange.includes('filter'))
4324
+ return true;
4325
+ return false;
4326
+ }
3106
4327
  _updateArrowRotation() {
3107
4328
  if (this._arrowContainer) {
3108
4329
  const arrow = this._arrowContainer.querySelector('.dropdown-arrow');
@@ -3309,24 +4530,24 @@ class EnhancedSelect extends HTMLElement {
3309
4530
  }
3310
4531
  }
3311
4532
  _setActive(index) {
3312
- const options = Array.from(this._optionsContainer.children);
3313
4533
  // Clear previous active state
3314
- if (this._state.activeIndex >= 0 && options[this._state.activeIndex]) {
3315
- const prevOption = options[this._state.activeIndex];
3316
- // Check if it's a custom SelectOption or a lightweight DOM element
3317
- if ('setActive' in prevOption && typeof prevOption.setActive === 'function') {
3318
- prevOption.setActive(false);
3319
- }
3320
- else {
3321
- // Lightweight option - remove active class
3322
- prevOption.classList.remove('smilodon-option--active');
3323
- prevOption.setAttribute('aria-selected', 'false');
4534
+ if (this._state.activeIndex >= 0) {
4535
+ const prevOption = this._getOptionElementByIndex(this._state.activeIndex);
4536
+ if (prevOption) {
4537
+ // Check if it's a custom SelectOption or a lightweight DOM element
4538
+ if ('setActive' in prevOption && typeof prevOption.setActive === 'function') {
4539
+ prevOption.setActive(false);
4540
+ }
4541
+ else {
4542
+ // Lightweight option - remove active class
4543
+ prevOption.classList.remove('smilodon-option--active');
4544
+ }
3324
4545
  }
3325
4546
  }
3326
4547
  this._state.activeIndex = index;
3327
4548
  // Set new active state
3328
- if (options[index]) {
3329
- const option = options[index];
4549
+ const option = this._getOptionElementByIndex(index);
4550
+ if (option) {
3330
4551
  // Check if it's a custom SelectOption or a lightweight DOM element
3331
4552
  if ('setActive' in option && typeof option.setActive === 'function') {
3332
4553
  option.setActive(true);
@@ -3334,13 +4555,13 @@ class EnhancedSelect extends HTMLElement {
3334
4555
  else {
3335
4556
  // Lightweight option - add active class
3336
4557
  option.classList.add('smilodon-option--active');
3337
- option.setAttribute('aria-selected', 'true');
3338
4558
  }
3339
4559
  if (typeof option.scrollIntoView === 'function') {
4560
+ // Don't scroll wildly when just opening with pre-selections
3340
4561
  option.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
3341
4562
  }
3342
4563
  // Announce position for screen readers
3343
- const total = options.length;
4564
+ const total = this._state.loadedItems.length;
3344
4565
  this._announce(`Item ${index + 1} of ${total}`);
3345
4566
  // Update aria-activedescendant using the actual option id when available
3346
4567
  const optionId = option.id || `${this._uniqueId}-option-${index}`;
@@ -3463,9 +4684,9 @@ class EnhancedSelect extends HTMLElement {
3463
4684
  // FIX: Do not rely on this._optionsContainer.children[index] because filtering changes the children
3464
4685
  // Instead, use the index to update state directly
3465
4686
  const item = this._state.loadedItems[index];
3466
- // Debug: log selection attempt
3467
- if (!item)
4687
+ if (!item) {
3468
4688
  return;
4689
+ }
3469
4690
  const isCurrentlySelected = this._state.selectedIndices.has(index);
3470
4691
  // Keep active/focus styling aligned with the most recently interacted option.
3471
4692
  // Without this, a previously selected item may retain active classes/styles
@@ -3476,7 +4697,7 @@ class EnhancedSelect extends HTMLElement {
3476
4697
  const wasSelected = this._state.selectedIndices.has(index);
3477
4698
  this._state.selectedIndices.clear();
3478
4699
  this._state.selectedItems.clear();
3479
- if (!wasSelected) {
4700
+ if (!wasSelected || !this._config.selection.allowDeselect) {
3480
4701
  // Select this option
3481
4702
  this._state.selectedIndices.add(index);
3482
4703
  this._state.selectedItems.set(index, item);
@@ -3538,16 +4759,34 @@ class EnhancedSelect extends HTMLElement {
3538
4759
  });
3539
4760
  }
3540
4761
  _handleOptionRemove(index) {
4762
+ const item = this._state.selectedItems.get(index);
3541
4763
  const option = this._getOptionElementByIndex(index);
3542
- if (!option)
3543
- return;
3544
4764
  this._state.selectedIndices.delete(index);
3545
4765
  this._state.selectedItems.delete(index);
3546
- option.setSelected(false);
4766
+ if (option && 'setSelected' in option && typeof option.setSelected === 'function') {
4767
+ option.setSelected(false);
4768
+ }
4769
+ else if (option) {
4770
+ option.classList.remove('selected', 'sm-selected', 'smilodon-option--selected');
4771
+ option.setAttribute('aria-selected', 'false');
4772
+ const stateTokens = (option.dataset.smState || '')
4773
+ .split(' ')
4774
+ .map(token => token.trim())
4775
+ .filter(token => token && token !== 'selected');
4776
+ if (stateTokens.length > 0) {
4777
+ option.dataset.smState = stateTokens.join(' ');
4778
+ }
4779
+ else {
4780
+ delete option.dataset.smState;
4781
+ }
4782
+ }
3547
4783
  this._updateInputDisplay();
4784
+ this._renderOptions();
3548
4785
  this._emitChange();
3549
- const config = option.getConfig();
3550
- this._emit('remove', { item: config.item, index });
4786
+ const config = option && 'getConfig' in option && typeof option.getConfig === 'function'
4787
+ ? option.getConfig()
4788
+ : undefined;
4789
+ this._emit('remove', { item: config?.item ?? item, index });
3551
4790
  }
3552
4791
  _updateInputDisplay() {
3553
4792
  const selectedItems = Array.from(this._state.selectedItems.values());
@@ -3556,7 +4795,7 @@ class EnhancedSelect extends HTMLElement {
3556
4795
  this._input.value = '';
3557
4796
  this._input.placeholder = this._config.placeholder || 'Select an option...';
3558
4797
  // Clear any badges
3559
- const existingBadges = this._inputContainer.querySelectorAll('.selection-badge');
4798
+ const existingBadges = this._inputContent.querySelectorAll('.selection-badge');
3560
4799
  existingBadges.forEach(badge => badge.remove());
3561
4800
  }
3562
4801
  else if (this._config.selection.mode === 'single') {
@@ -3567,31 +4806,55 @@ class EnhancedSelect extends HTMLElement {
3567
4806
  this._input.value = '';
3568
4807
  this._input.placeholder = '';
3569
4808
  // Clear existing badges
3570
- const existingBadges = this._inputContainer.querySelectorAll('.selection-badge');
4809
+ const existingBadges = this._inputContent.querySelectorAll('.selection-badge');
3571
4810
  existingBadges.forEach(badge => badge.remove());
3572
4811
  // Create badges for each selected item
3573
4812
  const selectedEntries = Array.from(this._state.selectedItems.entries());
3574
4813
  selectedEntries.forEach(([index, item]) => {
3575
4814
  const badge = document.createElement('span');
3576
4815
  badge.className = 'selection-badge';
4816
+ if (this._config.styles.classNames?.badge) {
4817
+ badge.classList.add(...this._config.styles.classNames.badge.split(' ').filter(Boolean));
4818
+ }
3577
4819
  badge.setAttribute('part', 'chip');
3578
- badge.textContent = getLabel(item);
4820
+ const badgeLabel = document.createElement('span');
4821
+ badgeLabel.className = 'selection-badge-label';
4822
+ if (this._config.styles.classNames?.badgeLabel) {
4823
+ badgeLabel.classList.add(...this._config.styles.classNames.badgeLabel.split(' ').filter(Boolean));
4824
+ }
4825
+ badgeLabel.setAttribute('part', 'chip-label');
4826
+ badgeLabel.textContent = getLabel(item);
4827
+ badge.appendChild(badgeLabel);
3579
4828
  // Add remove button to badge
3580
- const removeBtn = document.createElement('button');
3581
- removeBtn.className = 'badge-remove';
3582
- removeBtn.setAttribute('part', 'chip-remove');
3583
- removeBtn.innerHTML = '×';
3584
- removeBtn.setAttribute('aria-label', `Remove ${getLabel(item)}`);
3585
- removeBtn.addEventListener('click', (e) => {
3586
- e.stopPropagation();
3587
- this._state.selectedIndices.delete(index);
3588
- this._state.selectedItems.delete(index);
3589
- this._updateInputDisplay();
3590
- this._renderOptions();
3591
- this._emitChange();
3592
- });
3593
- badge.appendChild(removeBtn);
3594
- this._inputContainer.insertBefore(badge, this._input);
4829
+ if (this._config.selection.showRemoveButton !== false) {
4830
+ const removeBtn = document.createElement('button');
4831
+ removeBtn.type = 'button';
4832
+ removeBtn.className = 'badge-remove';
4833
+ if (this._config.styles.classNames?.removeButton) {
4834
+ removeBtn.classList.add(...this._config.styles.classNames.removeButton.split(' ').filter(Boolean));
4835
+ }
4836
+ if (this._config.styles.classNames?.badgeRemove) {
4837
+ removeBtn.classList.add(...this._config.styles.classNames.badgeRemove.split(' ').filter(Boolean));
4838
+ }
4839
+ removeBtn.setAttribute('part', 'chip-remove');
4840
+ removeBtn.setAttribute('aria-label', `Remove ${getLabel(item)}`);
4841
+ const removeIcon = document.createElement('span');
4842
+ removeIcon.className = 'badge-remove-icon';
4843
+ removeIcon.setAttribute('part', 'chip-remove-icon');
4844
+ this._setIconContent(removeIcon, this._config.selection.removeButtonIcon, '×');
4845
+ removeBtn.appendChild(removeIcon);
4846
+ removeBtn.addEventListener('pointerdown', (e) => {
4847
+ e.stopPropagation();
4848
+ e.preventDefault();
4849
+ });
4850
+ removeBtn.addEventListener('click', (e) => {
4851
+ e.stopPropagation();
4852
+ e.preventDefault();
4853
+ this._handleOptionRemove(index);
4854
+ });
4855
+ badge.appendChild(removeBtn);
4856
+ }
4857
+ this._inputContent.insertBefore(badge, this._input);
3595
4858
  });
3596
4859
  }
3597
4860
  this._syncClearControlState();
@@ -3642,10 +4905,12 @@ class EnhancedSelect extends HTMLElement {
3642
4905
  const option = this._getOptionElementByIndex(targetIndex);
3643
4906
  if (option) {
3644
4907
  // Use smooth scrolling with center alignment for better UX
3645
- option.scrollIntoView({
3646
- block: this._config.scrollToSelected.block || 'center',
3647
- behavior: 'smooth',
3648
- });
4908
+ if (typeof option.scrollIntoView === 'function') {
4909
+ option.scrollIntoView({
4910
+ block: this._config.scrollToSelected.block || 'center',
4911
+ behavior: 'smooth',
4912
+ });
4913
+ }
3649
4914
  // Also set it as active for keyboard navigation
3650
4915
  this._setActive(targetIndex);
3651
4916
  }
@@ -3857,7 +5122,6 @@ class EnhancedSelect extends HTMLElement {
3857
5122
  const getValue = this._config.serverSide.getValueFromItem || ((item) => item?.value ?? item);
3858
5123
  const selectedValues = selectedItems.map(getValue);
3859
5124
  const selectedIndices = Array.from(this._state.selectedIndices);
3860
- // Debug: log change payload
3861
5125
  this._emit('change', { selectedItems, selectedValues, selectedIndices });
3862
5126
  this._config.callbacks.onChange?.(selectedItems, selectedValues);
3863
5127
  }
@@ -4061,8 +5325,9 @@ class EnhancedSelect extends HTMLElement {
4061
5325
  this._clearControl.setAttribute('aria-label', this._config.clearControl.ariaLabel || 'Clear selection and search');
4062
5326
  }
4063
5327
  if (this._clearControlIcon) {
4064
- this._clearControlIcon.textContent = this._config.clearControl.icon || '×';
5328
+ this._clearControlIcon.innerHTML = `<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 4L4 12M4 4L12 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
4065
5329
  }
5330
+ this._syncStyleConfigVariables();
4066
5331
  if (this._dropdown) {
4067
5332
  if (this._config.selection.mode === 'multi') {
4068
5333
  this._dropdown.setAttribute('aria-multiselectable', 'true');
@@ -4071,9 +5336,14 @@ class EnhancedSelect extends HTMLElement {
4071
5336
  this._dropdown.removeAttribute('aria-multiselectable');
4072
5337
  }
4073
5338
  }
5339
+ this._syncDirectionConfig();
5340
+ this._syncInputContainerMode();
5341
+ this._syncMultiSelectDisplayConfig();
5342
+ this._syncDropdownPlacement();
4074
5343
  // Re-initialize observers in case infinite scroll was enabled/disabled
4075
5344
  this._initializeObservers();
4076
5345
  this._syncClearControlState();
5346
+ this._updateInputDisplay();
4077
5347
  this._renderOptions();
4078
5348
  }
4079
5349
  _mergeConfig(target, source) {
@@ -4176,6 +5446,8 @@ class EnhancedSelect extends HTMLElement {
4176
5446
  * Render options based on current state
4177
5447
  */
4178
5448
  _renderOptions() {
5449
+ this._renderCycleId += 1;
5450
+ const renderCycleId = this._renderCycleId;
4179
5451
  // Cleanup observer
4180
5452
  if (this._loadMoreTrigger && this._intersectionObserver) {
4181
5453
  this._intersectionObserver.unobserve(this._loadMoreTrigger);
@@ -4227,6 +5499,9 @@ class EnhancedSelect extends HTMLElement {
4227
5499
  header.textContent = group.label;
4228
5500
  }
4229
5501
  header.classList.add('group-header');
5502
+ if (this._config.styles.classNames?.groupHeader) {
5503
+ header.classList.add(...this._config.styles.classNames.groupHeader.split(' ').filter(Boolean));
5504
+ }
4230
5505
  header.setAttribute('part', 'group-header');
4231
5506
  this._optionsContainer.appendChild(header);
4232
5507
  group.options.forEach(item => {
@@ -4240,23 +5515,21 @@ class EnhancedSelect extends HTMLElement {
4240
5515
  }
4241
5516
  else {
4242
5517
  // Normal rendering (flat list or filtered)
4243
- let hasRenderedItems = false;
5518
+ const filteredIndices = [];
4244
5519
  this._state.loadedItems.forEach((item, index) => {
4245
- // Apply filter if query exists
4246
5520
  if (query) {
4247
5521
  try {
4248
5522
  const label = String(getLabel(item)).toLowerCase();
4249
5523
  if (!label.includes(query))
4250
5524
  return;
4251
5525
  }
4252
- catch (e) {
5526
+ catch (_e) {
4253
5527
  return;
4254
5528
  }
4255
5529
  }
4256
- hasRenderedItems = true;
4257
- this._renderSingleOption(item, index, getValue, getLabel);
5530
+ filteredIndices.push(index);
4258
5531
  });
4259
- if (!hasRenderedItems && !this._state.isBusy) {
5532
+ if (filteredIndices.length === 0 && !this._state.isBusy) {
4260
5533
  const empty = document.createElement('div');
4261
5534
  empty.setAttribute('part', 'no-results');
4262
5535
  empty.className = 'empty-state';
@@ -4268,6 +5541,54 @@ class EnhancedSelect extends HTMLElement {
4268
5541
  }
4269
5542
  this._optionsContainer.appendChild(empty);
4270
5543
  }
5544
+ else {
5545
+ const shouldIncrementalRender = this._config.virtualize !== false
5546
+ && this._state.groupedItems.length === 0
5547
+ && filteredIndices.length > 300;
5548
+ if (shouldIncrementalRender) {
5549
+ const chunkSize = 80;
5550
+ let cursor = 0;
5551
+ let maxRenderTarget = 0;
5552
+ if (this._state.selectedIndices.size > 0 && this._config.scrollToSelected.enabled) {
5553
+ const indices = Array.from(this._state.selectedIndices).sort((a, b) => a - b);
5554
+ const targetIndex = this._config.scrollToSelected.multiSelectTarget === 'first' ? indices[0] : indices[indices.length - 1];
5555
+ const filteredPos = filteredIndices.indexOf(targetIndex);
5556
+ if (filteredPos !== -1) {
5557
+ maxRenderTarget = filteredPos + 20; // Ensure we render up to the selection
5558
+ }
5559
+ }
5560
+ const renderChunk = () => {
5561
+ if (renderCycleId !== this._renderCycleId)
5562
+ return;
5563
+ const fragment = document.createDocumentFragment();
5564
+ const chunkEnd = Math.min(Math.max(cursor + chunkSize, maxRenderTarget), filteredIndices.length);
5565
+ maxRenderTarget = 0; // Reset after fast-forwarding
5566
+ for (; cursor < chunkEnd; cursor += 1) {
5567
+ const itemIndex = filteredIndices[cursor];
5568
+ const item = this._state.loadedItems[itemIndex];
5569
+ this._renderSingleOption(item, itemIndex, getValue, getLabel, fragment);
5570
+ }
5571
+ this._optionsContainer.appendChild(fragment);
5572
+ if (cursor < filteredIndices.length) {
5573
+ requestAnimationFrame(renderChunk);
5574
+ }
5575
+ else {
5576
+ if (renderCycleId !== this._renderCycleId)
5577
+ return;
5578
+ if (!this._state.isBusy && (this._config.loadMore.enabled || this._config.infiniteScroll.enabled) && this._state.loadedItems.length > 0) {
5579
+ this._addLoadMoreTrigger();
5580
+ }
5581
+ this._finalizePerfMarks();
5582
+ }
5583
+ };
5584
+ renderChunk();
5585
+ return;
5586
+ }
5587
+ filteredIndices.forEach((itemIndex) => {
5588
+ const item = this._state.loadedItems[itemIndex];
5589
+ this._renderSingleOption(item, itemIndex, getValue, getLabel);
5590
+ });
5591
+ }
4271
5592
  }
4272
5593
  // Append Busy Indicator if busy
4273
5594
  if (this._state.isBusy && this._config.busyBucket.enabled) {
@@ -4292,7 +5613,7 @@ class EnhancedSelect extends HTMLElement {
4292
5613
  }
4293
5614
  this._finalizePerfMarks();
4294
5615
  }
4295
- _renderSingleOption(item, index, getValue, getLabel) {
5616
+ _renderSingleOption(item, index, getValue, getLabel, targetContainer = this._optionsContainer) {
4296
5617
  const isSelected = this._state.selectedIndices.has(index);
4297
5618
  const isDisabled = Boolean(item?.disabled);
4298
5619
  const optionId = `${this._uniqueId}-option-${index}`;
@@ -4308,7 +5629,8 @@ class EnhancedSelect extends HTMLElement {
4308
5629
  disabled: isDisabled,
4309
5630
  id: optionId,
4310
5631
  });
4311
- this._optionsContainer.appendChild(optionElement);
5632
+ optionElement.setAttribute('dir', this._config.direction ?? 'ltr');
5633
+ targetContainer.appendChild(optionElement);
4312
5634
  return;
4313
5635
  }
4314
5636
  const option = new SelectOption({
@@ -4321,8 +5643,23 @@ class EnhancedSelect extends HTMLElement {
4321
5643
  getValue,
4322
5644
  getLabel,
4323
5645
  showRemoveButton: this._config.selection.mode === 'multi' && this._config.selection.showRemoveButton,
5646
+ removeButtonIcon: this._config.selection.removeButtonIcon,
4324
5647
  classMap: this.classMap,
5648
+ className: this._config.styles.classNames?.option,
4325
5649
  });
5650
+ if (this._config.styles.classNames?.option) {
5651
+ option.classList.add(...this._config.styles.classNames.option.split(' ').filter(Boolean));
5652
+ }
5653
+ option.setAttribute('dir', this._config.direction ?? 'ltr');
5654
+ if (isSelected && this._config.styles.classNames?.selectedOption) {
5655
+ option.classList.add(...this._config.styles.classNames.selectedOption.split(' ').filter(Boolean));
5656
+ }
5657
+ if (this._state.activeIndex === index && this._config.styles.classNames?.activeOption) {
5658
+ option.classList.add(...this._config.styles.classNames.activeOption.split(' ').filter(Boolean));
5659
+ }
5660
+ if (isDisabled && this._config.styles.classNames?.disabledOption) {
5661
+ option.classList.add(...this._config.styles.classNames.disabledOption.split(' ').filter(Boolean));
5662
+ }
4326
5663
  // Valid part attribute on the web component host itself
4327
5664
  option.setAttribute('part', 'option');
4328
5665
  option.dataset.index = String(index);
@@ -4337,20 +5674,16 @@ class EnhancedSelect extends HTMLElement {
4337
5674
  option.dataset.smValue = String(val);
4338
5675
  }
4339
5676
  option.id = option.id || optionId;
4340
- option.addEventListener('click', (e) => {
4341
- e.stopPropagation(); // Prevent duplicate handling by delegation
4342
- const mouseEvent = e;
4343
- this._selectOption(index, {
4344
- shiftKey: mouseEvent.shiftKey,
4345
- toggleKey: mouseEvent.ctrlKey || mouseEvent.metaKey,
4346
- });
4347
- });
5677
+ // Do NOT bind a native click listener here for SelectOption elements.
5678
+ // Like custom rendered options, they are fully handled by the `handleOptionEvent` delegator
5679
+ // on `this._optionsContainer` (line 1221).
5680
+ // Adding this listener causes the double-click toggle bug since both fire on selection!
4348
5681
  option.addEventListener('optionRemove', (event) => {
4349
5682
  const detail = event.detail;
4350
5683
  const targetIndex = detail?.index ?? index;
4351
5684
  this._handleOptionRemove(targetIndex);
4352
5685
  });
4353
- this._optionsContainer.appendChild(option);
5686
+ targetContainer.appendChild(option);
4354
5687
  }
4355
5688
  _normalizeCustomOptionElement(element, meta) {
4356
5689
  const optionEl = element instanceof HTMLElement ? element : document.createElement('div');
@@ -4360,6 +5693,9 @@ class EnhancedSelect extends HTMLElement {
4360
5693
  }
4361
5694
  // Add both semantic namespaced classes and the legacy internal classes that CSS uses
4362
5695
  optionEl.classList.add('smilodon-option', 'option');
5696
+ if (this._config.styles.classNames?.option) {
5697
+ optionEl.classList.add(...this._config.styles.classNames.option.split(' ').filter(Boolean));
5698
+ }
4363
5699
  // Toggle state classes using classMap if available
4364
5700
  const isSelected = meta.selected;
4365
5701
  const isActive = meta.active;
@@ -4371,26 +5707,44 @@ class EnhancedSelect extends HTMLElement {
4371
5707
  if (isSelected) {
4372
5708
  optionEl.classList.add(...selectedClasses);
4373
5709
  optionEl.classList.add('smilodon-option--selected');
5710
+ if (this._config.styles.classNames?.selectedOption) {
5711
+ optionEl.classList.add(...this._config.styles.classNames.selectedOption.split(' ').filter(Boolean));
5712
+ }
4374
5713
  }
4375
5714
  else {
4376
5715
  optionEl.classList.remove(...selectedClasses);
4377
5716
  optionEl.classList.remove('smilodon-option--selected');
5717
+ if (this._config.styles.classNames?.selectedOption) {
5718
+ optionEl.classList.remove(...this._config.styles.classNames.selectedOption.split(' ').filter(Boolean));
5719
+ }
4378
5720
  }
4379
5721
  if (isActive) {
4380
5722
  optionEl.classList.add(...activeClasses);
4381
5723
  optionEl.classList.add('smilodon-option--active');
5724
+ if (this._config.styles.classNames?.activeOption) {
5725
+ optionEl.classList.add(...this._config.styles.classNames.activeOption.split(' ').filter(Boolean));
5726
+ }
4382
5727
  }
4383
5728
  else {
4384
5729
  optionEl.classList.remove(...activeClasses);
4385
5730
  optionEl.classList.remove('smilodon-option--active');
5731
+ if (this._config.styles.classNames?.activeOption) {
5732
+ optionEl.classList.remove(...this._config.styles.classNames.activeOption.split(' ').filter(Boolean));
5733
+ }
4386
5734
  }
4387
5735
  if (isDisabled) {
4388
5736
  optionEl.classList.add(...disabledClasses);
4389
5737
  optionEl.classList.add('smilodon-option--disabled');
5738
+ if (this._config.styles.classNames?.disabledOption) {
5739
+ optionEl.classList.add(...this._config.styles.classNames.disabledOption.split(' ').filter(Boolean));
5740
+ }
4390
5741
  }
4391
5742
  else {
4392
5743
  optionEl.classList.remove(...disabledClasses);
4393
5744
  optionEl.classList.remove('smilodon-option--disabled');
5745
+ if (this._config.styles.classNames?.disabledOption) {
5746
+ optionEl.classList.remove(...this._config.styles.classNames.disabledOption.split(' ').filter(Boolean));
5747
+ }
4394
5748
  }
4395
5749
  // Data Attributes Contract
4396
5750
  const state = [];
@@ -4437,35 +5791,9 @@ class EnhancedSelect extends HTMLElement {
4437
5791
  optionEl.tabIndex = -1;
4438
5792
  }
4439
5793
  if (!this._customOptionBoundElements.has(optionEl)) {
4440
- optionEl.addEventListener('click', (e) => {
4441
- e.stopPropagation();
4442
- const current = e.currentTarget;
4443
- if (current.getAttribute('aria-disabled') === 'true')
4444
- return;
4445
- const parsedIndex = Number(current.dataset.index);
4446
- if (!Number.isFinite(parsedIndex))
4447
- return;
4448
- const mouseEvent = e;
4449
- this._selectOption(parsedIndex, {
4450
- shiftKey: mouseEvent.shiftKey,
4451
- toggleKey: mouseEvent.ctrlKey || mouseEvent.metaKey,
4452
- });
4453
- });
4454
- optionEl.addEventListener('keydown', (e) => {
4455
- if (e.key !== 'Enter' && e.key !== ' ')
4456
- return;
4457
- const current = e.currentTarget;
4458
- if (current.getAttribute('aria-disabled') === 'true')
4459
- return;
4460
- const parsedIndex = Number(current.dataset.index);
4461
- if (!Number.isFinite(parsedIndex))
4462
- return;
4463
- e.preventDefault();
4464
- this._selectOption(parsedIndex, {
4465
- shiftKey: e.shiftKey,
4466
- toggleKey: e.ctrlKey || e.metaKey,
4467
- });
4468
- });
5794
+ // Intentionally NOT binding native option click listeners for custom options!
5795
+ // All option interactions are globally handled by _optionsContainer.addEventListener('click', handleOptionEvent);
5796
+ // Re-attaching here causes the double-click toggle bug if a child component fails to stopPropagation.
4469
5797
  this._customOptionBoundElements.add(optionEl);
4470
5798
  }
4471
5799
  return optionEl;