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