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