@smilodon/core 1.4.13 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1348,9 +1348,21 @@ const defaultConfig = {
1348
1348
  allowDeselect: false,
1349
1349
  maxSelections: 0,
1350
1350
  showRemoveButton: true,
1351
+ removeButtonIcon: '×',
1351
1352
  closeOnSelect: true,
1352
1353
  toggleOnTriggerClick: true,
1353
1354
  },
1355
+ direction: 'ltr',
1356
+ dropdownPlacement: {
1357
+ mode: 'bottom',
1358
+ },
1359
+ multiSelectDisplay: {
1360
+ mode: 'wrap',
1361
+ maxHeight: '160px',
1362
+ overflowX: 'hidden',
1363
+ overflowY: 'auto',
1364
+ dragScroll: true,
1365
+ },
1354
1366
  scrollToSelected: {
1355
1367
  enabled: true,
1356
1368
  multiSelectTarget: 'first',
@@ -1511,6 +1523,8 @@ class SelectOption extends HTMLElement {
1511
1523
  :host {
1512
1524
  display: block;
1513
1525
  position: relative;
1526
+ font: inherit;
1527
+ color: inherit;
1514
1528
  }
1515
1529
 
1516
1530
  /* Allow authors to style selected state from outside the shadow root
@@ -1542,7 +1556,7 @@ class SelectOption extends HTMLElement {
1542
1556
  display: flex;
1543
1557
  align-items: center;
1544
1558
  justify-content: space-between;
1545
- padding: var(--select-option-padding, 8px 12px);
1559
+ padding: var(--select-option-padding, 10px 14px);
1546
1560
  cursor: pointer;
1547
1561
  user-select: none;
1548
1562
  color: var(--select-option-color, var(--select-text-color, #1f2937));
@@ -1550,13 +1564,19 @@ class SelectOption extends HTMLElement {
1550
1564
  transition: var(--select-option-transition, background-color 0.2s ease);
1551
1565
  border: var(--select-option-border, none);
1552
1566
  border-bottom: var(--select-option-border-bottom, none);
1553
- border-radius: var(--select-option-border-radius, 0);
1567
+ border-radius: var(--select-option-border-radius, var(--select-radius-sm, 6px));
1554
1568
  box-shadow: var(--select-option-shadow, none);
1555
1569
  transform: var(--select-option-transform, none);
1570
+ font: inherit;
1556
1571
  }
1557
1572
 
1558
1573
  .option-container:hover {
1559
1574
  background: var(--select-option-hover-bg, #f0f0f0);
1575
+ color: var(--select-option-hover-color, var(--select-option-color, var(--select-text-color, #1f2937)));
1576
+ border: var(--select-option-hover-border, var(--select-option-border, none));
1577
+ border-bottom: var(--select-option-hover-border-bottom, var(--select-option-border-bottom, none));
1578
+ box-shadow: var(--select-option-hover-shadow, var(--select-option-shadow, none));
1579
+ transform: var(--select-option-hover-transform, var(--select-option-transform, none));
1560
1580
  }
1561
1581
 
1562
1582
  .option-container.selected {
@@ -1579,13 +1599,22 @@ class SelectOption extends HTMLElement {
1579
1599
  }
1580
1600
 
1581
1601
  .option-container.active {
1582
- outline: 2px solid var(--select-option-active-outline, #1976d2);
1583
- outline-offset: -2px;
1602
+ background: var(--select-option-active-bg, var(--select-option-hover-bg, #f0f0f0));
1603
+ color: var(--select-option-active-color, var(--select-option-hover-color, var(--select-option-color, var(--select-text-color, #1f2937))));
1604
+ border: var(--select-option-active-border, var(--select-option-hover-border, var(--select-option-border, none)));
1605
+ box-shadow: var(--select-option-active-shadow, var(--select-option-shadow, none));
1606
+ transform: var(--select-option-active-transform, var(--select-option-transform, none));
1607
+ outline: var(--select-option-active-outline, 2px solid #1976d2);
1608
+ outline-offset: var(--select-option-active-outline-offset, -2px);
1584
1609
  }
1585
1610
 
1586
1611
  .option-container.disabled {
1587
- opacity: 0.5;
1588
- cursor: not-allowed;
1612
+ background: var(--select-option-disabled-bg, var(--select-option-bg, var(--select-dropdown-bg, var(--select-bg, white))));
1613
+ color: var(--select-option-disabled-color, var(--select-option-color, var(--select-text-color, #1f2937)));
1614
+ border: var(--select-option-disabled-border, var(--select-option-border, none));
1615
+ border-bottom: var(--select-option-disabled-border-bottom, var(--select-option-border-bottom, none));
1616
+ opacity: var(--select-option-disabled-opacity, 0.5);
1617
+ cursor: var(--select-option-disabled-cursor, not-allowed);
1589
1618
  pointer-events: none;
1590
1619
  }
1591
1620
 
@@ -1602,6 +1631,12 @@ class SelectOption extends HTMLElement {
1602
1631
  color: var(--select-checkmark-color, currentColor);
1603
1632
  }
1604
1633
 
1634
+ :host([dir="rtl"]) .checkmark-icon,
1635
+ :host-context([dir="rtl"]) .checkmark-icon {
1636
+ margin-left: 0;
1637
+ margin-right: var(--select-checkmark-margin-left, 8px);
1638
+ }
1639
+
1605
1640
  :host([aria-selected="true"]) .checkmark-icon,
1606
1641
  .option-container.selected .checkmark-icon {
1607
1642
  display: inline-flex;
@@ -1609,20 +1644,49 @@ class SelectOption extends HTMLElement {
1609
1644
 
1610
1645
  .remove-button {
1611
1646
  margin-left: 8px;
1612
- padding: 2px 6px;
1647
+ width: var(--select-badge-remove-size, 18px);
1648
+ height: var(--select-badge-remove-size, 18px);
1649
+ min-width: var(--select-badge-remove-min-width, var(--select-badge-remove-size, 18px));
1650
+ min-height: var(--select-badge-remove-min-height, var(--select-badge-remove-size, 18px));
1651
+ padding: 0;
1613
1652
  border: none;
1614
- background-color: var(--select-remove-btn-bg, transparent);
1615
- color: var(--select-remove-btn-color, #666);
1653
+ background-color: var(--select-badge-remove-bg, rgba(255, 255, 255, 0.2));
1654
+ color: var(--select-badge-remove-color, currentColor);
1616
1655
  cursor: pointer;
1617
- border-radius: 3px;
1618
- font-size: 16px;
1656
+ border-radius: var(--select-badge-remove-radius, 50%);
1657
+ font-size: var(--select-badge-remove-font-size, 0.7333em);
1658
+ font-weight: var(--select-badge-remove-font-weight, 600);
1619
1659
  line-height: 1;
1660
+ display: inline-flex;
1661
+ align-items: center;
1662
+ justify-content: center;
1620
1663
  transition: all 0.2s ease;
1621
1664
  }
1665
+
1666
+ :host([dir="rtl"]) .remove-button,
1667
+ :host-context([dir="rtl"]) .remove-button {
1668
+ margin-left: 0;
1669
+ margin-right: 8px;
1670
+ }
1671
+
1672
+ .remove-button-icon {
1673
+ display: inline-flex;
1674
+ align-items: center;
1675
+ justify-content: center;
1676
+ width: var(--select-badge-remove-icon-size, 10px);
1677
+ height: var(--select-badge-remove-icon-size, 10px);
1678
+ pointer-events: none;
1679
+ }
1680
+
1681
+ .remove-button-icon svg {
1682
+ width: 100%;
1683
+ height: 100%;
1684
+ display: block;
1685
+ }
1622
1686
 
1623
1687
  .remove-button:hover {
1624
- background-color: var(--select-remove-btn-hover-bg, #ffebee);
1625
- color: var(--select-remove-btn-hover-color, #c62828);
1688
+ background-color: var(--select-badge-remove-hover-bg, #ffebee);
1689
+ color: var(--select-badge-remove-hover-color, #c62828);
1626
1690
  }
1627
1691
 
1628
1692
  .remove-button:focus {
@@ -1709,10 +1773,22 @@ class SelectOption extends HTMLElement {
1709
1773
  if (showRemoveButton && selected) {
1710
1774
  this._removeButton = document.createElement('button');
1711
1775
  this._removeButton.className = 'remove-button';
1712
- this._removeButton.innerHTML = '×';
1713
1776
  this._removeButton.setAttribute('part', 'chip-remove');
1714
1777
  this._removeButton.setAttribute('aria-label', 'Remove option');
1715
1778
  this._removeButton.setAttribute('type', 'button');
1779
+ const removeIcon = document.createElement('span');
1780
+ removeIcon.className = 'remove-button-icon';
1781
+ removeIcon.setAttribute('part', 'chip-remove-icon');
1782
+ const iconMarkup = this._config.removeButtonIcon && this._config.removeButtonIcon.trim()
1783
+ ? this._config.removeButtonIcon
1784
+ : '×';
1785
+ if (iconMarkup.trim().startsWith('<')) {
1786
+ removeIcon.innerHTML = iconMarkup;
1787
+ }
1788
+ else {
1789
+ removeIcon.textContent = iconMarkup;
1790
+ }
1791
+ this._removeButton.appendChild(removeIcon);
1716
1792
  this._container.appendChild(this._removeButton);
1717
1793
  }
1718
1794
  // Set ARIA attributes and State attributes on Host
@@ -1933,6 +2009,16 @@ class EnhancedSelect extends HTMLElement {
1933
2009
  this._tracking = { events: [], styles: [], limitations: [] };
1934
2010
  this._suppressBlurClose = false;
1935
2011
  this._renderCycleId = 0;
2012
+ this._suppressNextOpenClick = false;
2013
+ this._resolvedDropdownPlacement = 'bottom';
2014
+ this._multiScrollDrag = {
2015
+ active: false,
2016
+ moved: false,
2017
+ pointerId: -1,
2018
+ startX: 0,
2019
+ startScrollLeft: 0,
2020
+ };
2021
+ this._liftedAncestors = [];
1936
2022
  this._shadow = this.attachShadow({ mode: 'open' });
1937
2023
  this._uniqueId = `enhanced-select-${Math.random().toString(36).substr(2, 9)}`;
1938
2024
  this._rendererHelpers = this._buildRendererHelpers();
@@ -1960,6 +2046,7 @@ class EnhancedSelect extends HTMLElement {
1960
2046
  // Create DOM structure
1961
2047
  this._container = this._createContainer();
1962
2048
  this._inputContainer = this._createInputContainer();
2049
+ this._inputContent = this._createInputContent();
1963
2050
  this._input = this._createInput();
1964
2051
  this._arrowContainer = this._createArrowContainer();
1965
2052
  this._clearControl = this._createClearControl();
@@ -1968,6 +2055,8 @@ class EnhancedSelect extends HTMLElement {
1968
2055
  this._liveRegion = this._createLiveRegion();
1969
2056
  // Initialize styles BEFORE assembling DOM (order matters in shadow DOM)
1970
2057
  this._initializeStyles();
2058
+ this._syncStyleConfigVariables();
2059
+ this._syncDirectionConfig();
1971
2060
  this._assembleDOM();
1972
2061
  this._attachEventListeners();
1973
2062
  this._initializeObservers();
@@ -2003,6 +2092,7 @@ class EnhancedSelect extends HTMLElement {
2003
2092
  clearTimeout(this._typeTimeout);
2004
2093
  if (this._searchTimeout)
2005
2094
  clearTimeout(this._searchTimeout);
2095
+ this._endMultiScrollDrag();
2006
2096
  // Cleanup arrow click listener
2007
2097
  if (this._boundArrowClick && this._arrowContainer) {
2008
2098
  this._arrowContainer.removeEventListener('click', this._boundArrowClick);
@@ -2136,6 +2226,203 @@ class EnhancedSelect extends HTMLElement {
2136
2226
  }
2137
2227
  return container;
2138
2228
  }
2229
+ _createInputContent() {
2230
+ const content = document.createElement('div');
2231
+ content.className = 'input-content';
2232
+ return content;
2233
+ }
2234
+ _setCssVariable(name, value) {
2235
+ if (value == null || value === '') {
2236
+ this.style.removeProperty(name);
2237
+ return;
2238
+ }
2239
+ this.style.setProperty(name, String(value));
2240
+ }
2241
+ _applyStyleVariableMap(styleConfig, variableMap) {
2242
+ Object.entries(variableMap).forEach(([styleKey, variableName]) => {
2243
+ this._setCssVariable(variableName, styleConfig?.[styleKey]);
2244
+ });
2245
+ }
2246
+ _syncStyleConfigVariables() {
2247
+ const styles = this._config.styles;
2248
+ this._applyStyleVariableMap(styles.option, {
2249
+ background: '--select-option-bg',
2250
+ backgroundColor: '--select-option-bg',
2251
+ color: '--select-option-color',
2252
+ border: '--select-option-border',
2253
+ borderBottom: '--select-option-border-bottom',
2254
+ borderRadius: '--select-option-border-radius',
2255
+ boxShadow: '--select-option-shadow',
2256
+ transform: '--select-option-transform',
2257
+ padding: '--select-option-padding',
2258
+ margin: '--select-option-margin',
2259
+ fontSize: '--select-option-font-size',
2260
+ fontWeight: '--select-option-font-weight',
2261
+ lineHeight: '--select-option-line-height',
2262
+ textAlign: '--select-option-text-align',
2263
+ });
2264
+ this._applyStyleVariableMap(styles.selectedOption, {
2265
+ background: '--select-option-selected-bg',
2266
+ backgroundColor: '--select-option-selected-bg',
2267
+ color: '--select-option-selected-color',
2268
+ border: '--select-option-selected-border',
2269
+ borderBottom: '--select-option-selected-border-bottom',
2270
+ borderRadius: '--select-option-selected-border-radius',
2271
+ boxShadow: '--select-option-selected-shadow',
2272
+ transform: '--select-option-selected-transform',
2273
+ fontWeight: '--select-option-selected-weight',
2274
+ });
2275
+ this._applyStyleVariableMap(styles.hoverOption, {
2276
+ background: '--select-option-hover-bg',
2277
+ backgroundColor: '--select-option-hover-bg',
2278
+ color: '--select-option-hover-color',
2279
+ border: '--select-option-hover-border',
2280
+ borderBottom: '--select-option-hover-border-bottom',
2281
+ boxShadow: '--select-option-hover-shadow',
2282
+ transform: '--select-option-hover-transform',
2283
+ });
2284
+ this._applyStyleVariableMap(styles.activeOption, {
2285
+ background: '--select-option-active-bg',
2286
+ backgroundColor: '--select-option-active-bg',
2287
+ color: '--select-option-active-color',
2288
+ border: '--select-option-active-border',
2289
+ outline: '--select-option-active-outline',
2290
+ outlineOffset: '--select-option-active-outline-offset',
2291
+ boxShadow: '--select-option-active-shadow',
2292
+ transform: '--select-option-active-transform',
2293
+ });
2294
+ this._applyStyleVariableMap(styles.disabledOption, {
2295
+ background: '--select-option-disabled-bg',
2296
+ backgroundColor: '--select-option-disabled-bg',
2297
+ color: '--select-option-disabled-color',
2298
+ border: '--select-option-disabled-border',
2299
+ borderBottom: '--select-option-disabled-border-bottom',
2300
+ opacity: '--select-option-disabled-opacity',
2301
+ cursor: '--select-option-disabled-cursor',
2302
+ });
2303
+ this._applyStyleVariableMap(styles.badge, {
2304
+ width: '--select-badge-width',
2305
+ minWidth: '--select-badge-min-width',
2306
+ maxWidth: '--select-badge-max-width',
2307
+ height: '--select-badge-height',
2308
+ minHeight: '--select-badge-min-height',
2309
+ padding: '--select-badge-padding',
2310
+ margin: '--select-badge-margin',
2311
+ gap: '--select-badge-gap',
2312
+ background: '--select-badge-bg',
2313
+ backgroundColor: '--select-badge-bg',
2314
+ color: '--select-badge-color',
2315
+ border: '--select-badge-border',
2316
+ borderRadius: '--select-badge-border-radius',
2317
+ boxShadow: '--select-badge-shadow',
2318
+ fontSize: '--select-badge-font-size',
2319
+ fontWeight: '--select-badge-font-weight',
2320
+ lineHeight: '--select-badge-line-height',
2321
+ letterSpacing: '--select-badge-letter-spacing',
2322
+ });
2323
+ this._applyStyleVariableMap(styles.badgeHover, {
2324
+ background: '--select-badge-hover-bg',
2325
+ backgroundColor: '--select-badge-hover-bg',
2326
+ color: '--select-badge-hover-color',
2327
+ border: '--select-badge-hover-border',
2328
+ boxShadow: '--select-badge-hover-shadow',
2329
+ transform: '--select-badge-hover-transform',
2330
+ });
2331
+ this._applyStyleVariableMap(styles.badgeActive, {
2332
+ background: '--select-badge-active-bg',
2333
+ backgroundColor: '--select-badge-active-bg',
2334
+ color: '--select-badge-active-color',
2335
+ border: '--select-badge-active-border',
2336
+ boxShadow: '--select-badge-active-shadow',
2337
+ transform: '--select-badge-active-transform',
2338
+ });
2339
+ this._applyStyleVariableMap(styles.badgeLabel, {
2340
+ color: '--select-badge-label-color',
2341
+ fontSize: '--select-badge-label-font-size',
2342
+ fontWeight: '--select-badge-label-font-weight',
2343
+ lineHeight: '--select-badge-label-line-height',
2344
+ letterSpacing: '--select-badge-label-letter-spacing',
2345
+ textAlign: '--select-badge-label-text-align',
2346
+ });
2347
+ this._applyStyleVariableMap(styles.badgeRemove, {
2348
+ width: '--select-badge-remove-size',
2349
+ height: '--select-badge-remove-size',
2350
+ minWidth: '--select-badge-remove-min-width',
2351
+ minHeight: '--select-badge-remove-min-height',
2352
+ marginLeft: '--select-badge-remove-margin-left',
2353
+ background: '--select-badge-remove-bg',
2354
+ backgroundColor: '--select-badge-remove-bg',
2355
+ border: '--select-badge-remove-border',
2356
+ borderRadius: '--select-badge-remove-radius',
2357
+ color: '--select-badge-remove-color',
2358
+ fontSize: '--select-badge-remove-font-size',
2359
+ fontWeight: '--select-badge-remove-font-weight',
2360
+ });
2361
+ this._applyStyleVariableMap(styles.badgeRemoveHover, {
2362
+ background: '--select-badge-remove-hover-bg',
2363
+ backgroundColor: '--select-badge-remove-hover-bg',
2364
+ color: '--select-badge-remove-hover-color',
2365
+ border: '--select-badge-remove-hover-border',
2366
+ boxShadow: '--select-badge-remove-hover-shadow',
2367
+ transform: '--select-badge-remove-hover-transform',
2368
+ });
2369
+ this._applyStyleVariableMap(styles.badgeRemoveActive, {
2370
+ background: '--select-badge-remove-active-bg',
2371
+ backgroundColor: '--select-badge-remove-active-bg',
2372
+ color: '--select-badge-remove-active-color',
2373
+ border: '--select-badge-remove-active-border',
2374
+ boxShadow: '--select-badge-remove-active-shadow',
2375
+ transform: '--select-badge-remove-active-transform',
2376
+ });
2377
+ this._applyStyleVariableMap(styles.groupHeader, {
2378
+ padding: '--select-group-header-padding',
2379
+ margin: '--select-group-header-margin',
2380
+ color: '--select-group-header-color',
2381
+ background: '--select-group-header-bg',
2382
+ backgroundColor: '--select-group-header-bg',
2383
+ border: '--select-group-header-border',
2384
+ borderBottom: '--select-group-header-border-bottom',
2385
+ borderRadius: '--select-group-header-border-radius',
2386
+ boxShadow: '--select-group-header-shadow',
2387
+ textAlign: '--select-group-header-text-align',
2388
+ fontSize: '--select-group-header-font-size',
2389
+ fontWeight: '--select-group-header-weight',
2390
+ textTransform: '--select-group-header-text-transform',
2391
+ letterSpacing: '--select-group-header-letter-spacing',
2392
+ });
2393
+ }
2394
+ _resolveDropdownPlacement() {
2395
+ const placementMode = this._config.dropdownPlacement?.mode ?? 'bottom';
2396
+ if (placementMode === 'bottom' || placementMode === 'top') {
2397
+ return placementMode;
2398
+ }
2399
+ const hostRect = this._container.getBoundingClientRect();
2400
+ const availableBelow = window.innerHeight - hostRect.bottom;
2401
+ const computedDropdown = window.getComputedStyle(this._dropdown);
2402
+ const maxHeight = Number.parseFloat(computedDropdown.maxHeight || '0');
2403
+ const desiredHeight = Number.isFinite(maxHeight) && maxHeight > 0
2404
+ ? Math.min(this._dropdown.scrollHeight, maxHeight)
2405
+ : this._dropdown.scrollHeight;
2406
+ return availableBelow >= desiredHeight ? 'bottom' : 'top';
2407
+ }
2408
+ _syncDropdownPlacement() {
2409
+ if (!this._dropdown)
2410
+ return;
2411
+ this._resolvedDropdownPlacement = this._resolveDropdownPlacement();
2412
+ this._dropdown.setAttribute('data-placement', this._resolvedDropdownPlacement);
2413
+ }
2414
+ _syncDirectionConfig() {
2415
+ this.setAttribute('dir', this._config.direction ?? 'ltr');
2416
+ }
2417
+ _setIconContent(target, markup, fallback) {
2418
+ const content = markup && markup.trim() ? markup : fallback;
2419
+ target.innerHTML = '';
2420
+ if (content.trim().startsWith('<')) {
2421
+ target.innerHTML = content;
2422
+ return;
2423
+ }
2424
+ target.textContent = content;
2425
+ }
2139
2426
  _syncInputContainerMode() {
2140
2427
  if (!this._inputContainer || !this._input)
2141
2428
  return;
@@ -2152,6 +2439,121 @@ class EnhancedSelect extends HTMLElement {
2152
2439
  this._input.style.width = '100%';
2153
2440
  this._input.style.minWidth = '0';
2154
2441
  }
2442
+ this._syncMultiSelectDisplayConfig();
2443
+ }
2444
+ _syncMultiSelectDisplayConfig() {
2445
+ if (!this._inputContainer || !this._input)
2446
+ return;
2447
+ const isMulti = this._config.selection.mode === 'multi';
2448
+ const mode = this._config.multiSelectDisplay?.mode ?? 'wrap';
2449
+ const dragEnabled = this._config.multiSelectDisplay?.dragScroll !== false;
2450
+ if (!isMulti) {
2451
+ this._inputContainer.removeAttribute('data-multi-scroll-mode');
2452
+ this._inputContainer.removeAttribute('data-drag-scroll');
2453
+ this._inputContainer.classList.remove('is-dragging-scroll');
2454
+ this._inputContainer.style.removeProperty('--select-multi-input-max-height');
2455
+ this._inputContainer.style.removeProperty('--select-multi-input-overflow-x');
2456
+ this._inputContainer.style.removeProperty('--select-multi-input-overflow-y');
2457
+ this._inputContainer.style.removeProperty('--select-multi-input-flex-wrap');
2458
+ this._inputContainer.style.removeProperty('--select-multi-input-align-content');
2459
+ return;
2460
+ }
2461
+ const maxHeight = this._config.multiSelectDisplay?.maxHeight ?? '160px';
2462
+ const overflowX = this._config.multiSelectDisplay?.overflowX ?? (mode === 'horizontal' ? 'auto' : 'hidden');
2463
+ const overflowY = this._config.multiSelectDisplay?.overflowY ?? (mode === 'horizontal' ? 'hidden' : 'auto');
2464
+ const flexWrap = mode === 'horizontal' ? 'nowrap' : 'wrap';
2465
+ const alignContent = mode === 'horizontal' ? 'center' : 'flex-start';
2466
+ this._inputContainer.setAttribute('data-multi-scroll-mode', mode);
2467
+ this._inputContainer.setAttribute('data-drag-scroll', String(dragEnabled && mode === 'horizontal'));
2468
+ this._inputContainer.style.setProperty('--select-multi-input-max-height', maxHeight);
2469
+ this._inputContainer.style.setProperty('--select-multi-input-overflow-x', overflowX);
2470
+ this._inputContainer.style.setProperty('--select-multi-input-overflow-y', overflowY);
2471
+ this._inputContainer.style.setProperty('--select-multi-input-flex-wrap', flexWrap);
2472
+ this._inputContainer.style.setProperty('--select-multi-input-align-content', alignContent);
2473
+ }
2474
+ _canUseHorizontalMultiScroll(target) {
2475
+ if (this._config.selection.mode !== 'multi')
2476
+ return false;
2477
+ if ((this._config.multiSelectDisplay?.mode ?? 'wrap') !== 'horizontal')
2478
+ return false;
2479
+ if (this._config.multiSelectDisplay?.dragScroll === false)
2480
+ return false;
2481
+ if (!target)
2482
+ return true;
2483
+ if (target.closest('.dropdown-arrow-container, .clear-control-button, .badge-remove'))
2484
+ return false;
2485
+ if (target.matches('.select-input'))
2486
+ return false;
2487
+ return true;
2488
+ }
2489
+ _isScrollableMultiSelectMode() {
2490
+ if (this._config.selection.mode !== 'multi')
2491
+ return false;
2492
+ const mode = this._config.multiSelectDisplay?.mode ?? 'wrap';
2493
+ return mode === 'vertical' || mode === 'horizontal';
2494
+ }
2495
+ _isPointerOnInputScrollbar(event) {
2496
+ if (!this._isScrollableMultiSelectMode())
2497
+ return false;
2498
+ const rect = this._inputContent.getBoundingClientRect();
2499
+ const verticalScrollbarWidth = this._inputContent.offsetWidth - this._inputContent.clientWidth;
2500
+ const horizontalScrollbarHeight = this._inputContent.offsetHeight - this._inputContent.clientHeight;
2501
+ if (verticalScrollbarWidth > 0
2502
+ && event.clientX >= rect.right - verticalScrollbarWidth
2503
+ && event.clientX <= rect.right
2504
+ && event.clientY >= rect.top
2505
+ && event.clientY <= rect.bottom) {
2506
+ return true;
2507
+ }
2508
+ if (horizontalScrollbarHeight > 0
2509
+ && event.clientY >= rect.bottom - horizontalScrollbarHeight
2510
+ && event.clientY <= rect.bottom
2511
+ && event.clientX >= rect.left
2512
+ && event.clientX <= rect.right) {
2513
+ return true;
2514
+ }
2515
+ return false;
2516
+ }
2517
+ _beginMultiScrollDrag(e) {
2518
+ const scrollHost = this._inputContent;
2519
+ this._multiScrollDrag.active = true;
2520
+ this._multiScrollDrag.moved = false;
2521
+ this._multiScrollDrag.pointerId = e.pointerId;
2522
+ this._multiScrollDrag.startX = e.clientX;
2523
+ this._multiScrollDrag.startScrollLeft = scrollHost.scrollLeft;
2524
+ this._inputContainer.classList.add('is-dragging-scroll');
2525
+ try {
2526
+ scrollHost.setPointerCapture(e.pointerId);
2527
+ }
2528
+ catch (_error) {
2529
+ // ignore pointer capture issues
2530
+ }
2531
+ }
2532
+ _updateMultiScrollDrag(e) {
2533
+ if (!this._multiScrollDrag.active || this._multiScrollDrag.pointerId !== e.pointerId)
2534
+ return;
2535
+ const deltaX = e.clientX - this._multiScrollDrag.startX;
2536
+ if (Math.abs(deltaX) > 3) {
2537
+ this._multiScrollDrag.moved = true;
2538
+ }
2539
+ this._inputContent.scrollLeft = this._multiScrollDrag.startScrollLeft - deltaX;
2540
+ }
2541
+ _endMultiScrollDrag(pointerId) {
2542
+ if (!this._multiScrollDrag.active)
2543
+ return;
2544
+ if (pointerId != null && this._multiScrollDrag.pointerId !== pointerId)
2545
+ return;
2546
+ try {
2547
+ if (this._multiScrollDrag.pointerId >= 0) {
2548
+ this._inputContent.releasePointerCapture(this._multiScrollDrag.pointerId);
2549
+ }
2550
+ }
2551
+ catch (_error) {
2552
+ // ignore pointer capture issues
2553
+ }
2554
+ this._multiScrollDrag.active = false;
2555
+ this._multiScrollDrag.pointerId = -1;
2556
+ this._inputContainer.classList.remove('is-dragging-scroll');
2155
2557
  }
2156
2558
  _createInput() {
2157
2559
  const input = document.createElement('input');
@@ -2280,7 +2682,8 @@ class EnhancedSelect extends HTMLElement {
2280
2682
  return button;
2281
2683
  }
2282
2684
  _assembleDOM() {
2283
- this._inputContainer.appendChild(this._input);
2685
+ this._inputContent.appendChild(this._input);
2686
+ this._inputContainer.appendChild(this._inputContent);
2284
2687
  if (this._clearControl) {
2285
2688
  this._inputContainer.appendChild(this._clearControl);
2286
2689
  }
@@ -2337,9 +2740,11 @@ class EnhancedSelect extends HTMLElement {
2337
2740
  --select-badge-enter-from-transform: scale(0.8) translateY(-4px);
2338
2741
  --select-badge-enter-to-opacity: 1;
2339
2742
  --select-badge-enter-to-transform: scale(1) translateY(0);
2743
+ --select-badge-active-transform: scale(0.98);
2340
2744
  --select-badge-hover-transform: scale(1.02);
2341
2745
  --select-badge-letter-spacing: 0.01em;
2342
2746
  --select-badge-hover-shadow: var(--select-shadow-md), inset 0 1px 0 rgba(255, 255, 255, 0.15);
2747
+ --select-badge-remove-icon-size: 10px;
2343
2748
  --select-badge-remove-focus-outline: 2px solid rgba(255, 255, 255, 0.5);
2344
2749
  --select-badge-remove-focus-offset: 1px;
2345
2750
  --select-badge-remove-hover-transform: scale(1.15) rotate(90deg);
@@ -2349,8 +2754,25 @@ class EnhancedSelect extends HTMLElement {
2349
2754
  --select-input-font-weight: 450;
2350
2755
  --select-input-letter-spacing: 0.01em;
2351
2756
  --select-input-disabled-opacity: 0.6;
2757
+ --select-input-overflow-x: hidden;
2758
+ --select-input-align-items: center;
2759
+ --select-input-align-content: center;
2760
+ --select-input-align-self: center;
2761
+ --select-multi-input-max-height: 160px;
2762
+ --select-multi-input-overflow-x: hidden;
2763
+ --select-multi-input-overflow-y: auto;
2764
+ --select-multi-input-flex-wrap: wrap;
2765
+ --select-multi-input-align-content: flex-start;
2766
+ --select-multi-input-horizontal-input-flex: 0 0 var(--select-multi-input-min-width, 96px);
2767
+ --select-multi-input-horizontal-cursor: grab;
2768
+ --select-multi-input-horizontal-active-cursor: grabbing;
2769
+ --select-multi-separator-inset-block: 10px;
2770
+ --select-multi-action-surface-bg: var(--select-input-bg, var(--select-surface));
2771
+ --select-multi-action-divider: 1px solid var(--select-border);
2352
2772
  --select-separator-opacity: 0.6;
2353
2773
  --select-separator-active-opacity: 1;
2774
+ --select-separator-position: var(--select-arrow-width, 42px);
2775
+ --select-separator-position-with-clear: calc(var(--select-arrow-right-with-clear, 34px) + var(--select-arrow-width, 42px));
2354
2776
  --select-separator-dark-bg: linear-gradient(
2355
2777
  to bottom,
2356
2778
  transparent 0%,
@@ -2364,11 +2786,15 @@ class EnhancedSelect extends HTMLElement {
2364
2786
  --select-clear-button-focus-offset: 2px;
2365
2787
  --select-clear-icon-size: 14px;
2366
2788
  --select-dropdown-top: calc(100% + 6px);
2789
+ --select-dropdown-bottom: calc(100% + 6px);
2367
2790
  --select-dropdown-animation: dropdownEnter 200ms cubic-bezier(0.4, 0, 0.2, 1) forwards;
2368
2791
  --select-dropdown-enter-from-opacity: 0;
2369
2792
  --select-dropdown-enter-from-transform: translateY(-8px) scale(0.98);
2370
2793
  --select-dropdown-enter-to-opacity: 1;
2371
2794
  --select-dropdown-enter-to-transform: translateY(0) scale(1);
2795
+ --select-dropdown-top-transform-origin: bottom center;
2796
+ --select-dropdown-top-enter-from-transform: translateY(8px) scale(0.98);
2797
+ --select-dropdown-padding: 6px;
2372
2798
  --select-dropdown-scroll-behavior: smooth;
2373
2799
  --select-dropdown-transform-origin: top center;
2374
2800
  --select-scrollbar-width: 6px;
@@ -2376,6 +2802,8 @@ class EnhancedSelect extends HTMLElement {
2376
2802
  --select-option-hover-transform: translateX(2px);
2377
2803
  --select-option-font-weight: 450;
2378
2804
  --select-option-margin: 2px 0;
2805
+ --select-option-disabled-opacity: 0.5;
2806
+ --select-option-disabled-cursor: not-allowed;
2379
2807
  --select-option-active-outline-offset: -2px;
2380
2808
  --select-option-selected-active-outline-offset: -2px;
2381
2809
  --select-option-selected-indicator-width: 3px;
@@ -2417,12 +2845,31 @@ class EnhancedSelect extends HTMLElement {
2417
2845
  --select-high-contrast-focus-outline-color: Highlight;
2418
2846
  --select-touch-target-min-height: 44px;
2419
2847
  --select-badge-dark-bg: linear-gradient(135deg, var(--select-accent) 0%, #4f46e5 100%);
2848
+ --select-host-z-index: auto;
2849
+ --select-host-open-z-index: var(--select-dropdown-z-index, 1000);
2850
+ --select-ancestor-open-z-index: var(--select-host-open-z-index, var(--select-dropdown-z-index, 1000));
2420
2851
 
2421
2852
  display: block;
2422
2853
  position: relative;
2854
+ z-index: var(--select-host-z-index, auto);
2423
2855
  width: var(--select-width, 100%);
2424
2856
  height: var(--select-height, auto);
2425
- font-family: var(--select-font-family, 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif);
2857
+ font-family: var(--select-font-family, inherit);
2858
+ font-size: var(--select-font-size, inherit);
2859
+ line-height: var(--select-line-height, inherit);
2860
+ color: var(--select-color, inherit);
2861
+ }
2862
+
2863
+ :host([data-open="true"]) {
2864
+ z-index: var(--select-host-open-z-index, var(--select-dropdown-z-index, 1000));
2865
+ }
2866
+
2867
+ :host([dir="ltr"]) {
2868
+ direction: ltr;
2869
+ }
2870
+
2871
+ :host([dir="rtl"]) {
2872
+ direction: rtl;
2426
2873
  }
2427
2874
 
2428
2875
  /* ─────────────────────────────────────────────────────────────────────────
@@ -2434,6 +2881,9 @@ class EnhancedSelect extends HTMLElement {
2434
2881
  align-items: center;
2435
2882
  gap: var(--select-badge-gap, 6px);
2436
2883
  flex: 0 1 auto;
2884
+ width: var(--select-badge-width, auto);
2885
+ min-width: var(--select-badge-min-width, 0);
2886
+ height: var(--select-badge-height, auto);
2437
2887
  min-height: var(--select-badge-min-height, 26px);
2438
2888
  padding: var(--select-badge-padding, 4px 10px 4px 12px);
2439
2889
  margin: var(--select-badge-margin, 3px);
@@ -2443,7 +2893,7 @@ class EnhancedSelect extends HTMLElement {
2443
2893
  border-radius: var(--select-badge-border-radius, 999px);
2444
2894
  box-shadow: var(--select-badge-shadow, var(--select-shadow-sm), inset 0 1px 0 rgba(255, 255, 255, 0.1));
2445
2895
  box-sizing: border-box;
2446
- font-size: var(--select-badge-font-size, 13px);
2896
+ font-size: var(--select-badge-font-size, 0.8667em);
2447
2897
  font-weight: var(--select-badge-font-weight, 500);
2448
2898
  line-height: var(--select-badge-line-height, 1.2);
2449
2899
  letter-spacing: var(--select-badge-letter-spacing);
@@ -2469,17 +2919,33 @@ class EnhancedSelect extends HTMLElement {
2469
2919
  }
2470
2920
 
2471
2921
  .selection-badge:hover {
2922
+ background: var(--select-badge-hover-bg, var(--select-badge-bg, linear-gradient(135deg, var(--select-accent) 0%, var(--select-primary-light) 100%)));
2923
+ color: var(--select-badge-hover-color, var(--select-badge-color, #ffffff));
2924
+ border: var(--select-badge-hover-border, var(--select-badge-border, none));
2472
2925
  box-shadow: var(--select-badge-hover-shadow);
2473
2926
  transform: var(--select-badge-hover-transform);
2474
2927
  }
2475
2928
 
2929
+ .selection-badge:active {
2930
+ background: var(--select-badge-active-bg, var(--select-badge-hover-bg, var(--select-badge-bg, linear-gradient(135deg, var(--select-accent) 0%, var(--select-primary-light) 100%))));
2931
+ color: var(--select-badge-active-color, var(--select-badge-hover-color, var(--select-badge-color, #ffffff)));
2932
+ border: var(--select-badge-active-border, var(--select-badge-hover-border, var(--select-badge-border, none)));
2933
+ box-shadow: var(--select-badge-active-shadow, var(--select-badge-hover-shadow));
2934
+ transform: var(--select-badge-active-transform);
2935
+ }
2936
+
2476
2937
  .selection-badge-label {
2477
2938
  display: block;
2478
2939
  min-width: 0;
2479
2940
  overflow: hidden;
2480
2941
  text-overflow: ellipsis;
2481
2942
  white-space: nowrap;
2482
- line-height: var(--select-badge-line-height, 1.2);
2943
+ color: var(--select-badge-label-color, inherit);
2944
+ font-size: var(--select-badge-label-font-size, inherit);
2945
+ font-weight: var(--select-badge-label-font-weight, inherit);
2946
+ line-height: var(--select-badge-label-line-height, var(--select-badge-line-height, 1.2));
2947
+ letter-spacing: var(--select-badge-label-letter-spacing, inherit);
2948
+ text-align: var(--select-badge-label-text-align, start);
2483
2949
  }
2484
2950
 
2485
2951
  .badge-remove {
@@ -2488,24 +2954,47 @@ class EnhancedSelect extends HTMLElement {
2488
2954
  justify-content: center;
2489
2955
  width: var(--select-badge-remove-size, 18px);
2490
2956
  height: var(--select-badge-remove-size, 18px);
2957
+ min-width: var(--select-badge-remove-min-width, var(--select-badge-remove-size, 18px));
2958
+ min-height: var(--select-badge-remove-min-height, var(--select-badge-remove-size, 18px));
2491
2959
  padding: 0;
2492
2960
  margin-left: var(--select-badge-remove-margin-left);
2493
2961
  background: var(--select-badge-remove-bg, rgba(255, 255, 255, 0.2));
2494
2962
  border: var(--select-badge-remove-border, none);
2495
2963
  border-radius: var(--select-badge-remove-radius);
2496
2964
  color: var(--select-badge-remove-color, #ffffff);
2497
- font-size: var(--select-badge-remove-font-size, 11px);
2965
+ font-size: var(--select-badge-remove-font-size, 0.7333em);
2498
2966
  font-weight: var(--select-badge-remove-font-weight);
2499
2967
  line-height: 1;
2500
2968
  cursor: pointer;
2501
2969
  flex: 0 0 auto;
2502
2970
  transition:
2503
2971
  background var(--select-transition-fast),
2972
+ color var(--select-transition-fast),
2973
+ border-color var(--select-transition-fast),
2974
+ box-shadow var(--select-transition-fast),
2504
2975
  transform var(--select-transition-bounce);
2505
2976
  }
2977
+
2978
+ .badge-remove-icon {
2979
+ display: inline-flex;
2980
+ align-items: center;
2981
+ justify-content: center;
2982
+ width: var(--select-badge-remove-icon-size, 10px);
2983
+ height: var(--select-badge-remove-icon-size, 10px);
2984
+ pointer-events: none;
2985
+ }
2986
+
2987
+ .badge-remove-icon svg {
2988
+ width: 100%;
2989
+ height: 100%;
2990
+ display: block;
2991
+ }
2506
2992
 
2507
2993
  .badge-remove:hover {
2508
2994
  background: var(--select-badge-remove-hover-bg, rgba(233, 69, 96, 0.9));
2995
+ color: var(--select-badge-remove-hover-color, var(--select-badge-remove-color, #ffffff));
2996
+ border: var(--select-badge-remove-hover-border, var(--select-badge-remove-border, none));
2997
+ box-shadow: var(--select-badge-remove-hover-shadow, none);
2509
2998
  transform: var(--select-badge-remove-hover-transform);
2510
2999
  }
2511
3000
 
@@ -2515,8 +3004,17 @@ class EnhancedSelect extends HTMLElement {
2515
3004
  }
2516
3005
 
2517
3006
  .badge-remove:active {
3007
+ background: var(--select-badge-remove-active-bg, var(--select-badge-remove-hover-bg, rgba(233, 69, 96, 0.9)));
3008
+ color: var(--select-badge-remove-active-color, var(--select-badge-remove-hover-color, var(--select-badge-remove-color, #ffffff)));
3009
+ border: var(--select-badge-remove-active-border, var(--select-badge-remove-hover-border, var(--select-badge-remove-border, none)));
3010
+ box-shadow: var(--select-badge-remove-active-shadow, var(--select-badge-remove-hover-shadow, none));
2518
3011
  transform: var(--select-badge-remove-active-transform);
2519
3012
  }
3013
+
3014
+ :host([dir="rtl"]) .badge-remove {
3015
+ margin-left: 0;
3016
+ margin-right: var(--select-badge-remove-margin-left);
3017
+ }
2520
3018
 
2521
3019
  /* ─────────────────────────────────────────────────────────────────────────
2522
3020
  Input Container — Sophisticated control shell
@@ -2531,15 +3029,17 @@ class EnhancedSelect extends HTMLElement {
2531
3029
  position: relative;
2532
3030
  width: 100%;
2533
3031
  display: flex;
2534
- align-items: center;
3032
+ align-items: var(--select-input-align-items, center);
2535
3033
  flex-wrap: nowrap;
3034
+ justify-content: var(--select-input-justify-content, flex-start);
2536
3035
  gap: var(--select-input-gap, 6px);
2537
3036
  padding: var(--select-input-padding, 10px 52px 10px 14px);
2538
3037
  height: var(--select-input-height, auto);
2539
3038
  min-height: var(--select-input-min-height, 48px);
2540
3039
  max-height: var(--select-input-max-height, 160px);
2541
- overflow-y: var(--select-input-overflow-y, auto);
2542
- align-content: center;
3040
+ overflow: hidden;
3041
+ align-content: var(--select-input-align-content, center);
3042
+ text-align: var(--select-input-text-align, start);
2543
3043
  background: var(--select-input-bg, var(--select-surface));
2544
3044
  border: var(--select-input-border, 1.5px solid var(--select-border));
2545
3045
  border-radius: var(--select-input-border-radius, var(--select-radius-md));
@@ -2550,6 +3050,25 @@ class EnhancedSelect extends HTMLElement {
2550
3050
  box-shadow var(--select-transition-smooth),
2551
3051
  transform var(--select-transition-fast);
2552
3052
  }
3053
+
3054
+ .input-content {
3055
+ position: relative;
3056
+ display: flex;
3057
+ flex: 1 1 auto;
3058
+ width: 100%;
3059
+ min-width: 0;
3060
+ min-height: 0;
3061
+ align-self: stretch;
3062
+ align-items: var(--select-input-align-items, center);
3063
+ align-content: var(--select-input-align-content, center);
3064
+ justify-content: var(--select-input-justify-content, flex-start);
3065
+ flex-wrap: nowrap;
3066
+ gap: var(--select-input-gap, 6px);
3067
+ overflow-x: var(--select-input-overflow-x, hidden);
3068
+ overflow-y: var(--select-input-overflow-y, hidden);
3069
+ scrollbar-width: thin;
3070
+ box-sizing: border-box;
3071
+ }
2553
3072
 
2554
3073
  .input-container:hover {
2555
3074
  border-color: var(--select-input-hover-border);
@@ -2562,8 +3081,75 @@ class EnhancedSelect extends HTMLElement {
2562
3081
  }
2563
3082
 
2564
3083
  .input-container.input-container--multi {
2565
- flex-wrap: wrap;
2566
- align-content: flex-start;
3084
+ max-height: var(--select-multi-input-max-height, var(--select-input-max-height, 160px));
3085
+ }
3086
+
3087
+ .input-container.input-container--multi .input-content {
3088
+ flex-wrap: var(--select-multi-input-flex-wrap, wrap);
3089
+ align-content: var(--select-multi-input-align-content, flex-start);
3090
+ overflow-x: var(--select-multi-input-overflow-x, hidden);
3091
+ overflow-y: var(--select-multi-input-overflow-y, auto);
3092
+ }
3093
+
3094
+ .input-container.input-container--multi::after {
3095
+ top: var(--select-multi-separator-inset-block, 10px);
3096
+ bottom: var(--select-multi-separator-inset-block, 10px);
3097
+ height: auto;
3098
+ transform: none;
3099
+ }
3100
+
3101
+ .input-container.input-container--multi[data-multi-scroll-mode="horizontal"] {
3102
+ align-items: stretch;
3103
+ }
3104
+
3105
+ .input-container.input-container--multi[data-multi-scroll-mode="horizontal"] .input-content {
3106
+ cursor: var(--select-multi-input-horizontal-cursor, grab);
3107
+ overflow-y: hidden;
3108
+ overscroll-behavior-x: contain;
3109
+ }
3110
+
3111
+ .input-container.input-container--multi[data-multi-scroll-mode="horizontal"]::after {
3112
+ display: none;
3113
+ }
3114
+
3115
+ .input-container.input-container--multi[data-multi-scroll-mode="horizontal"].is-dragging-scroll .input-content {
3116
+ cursor: var(--select-multi-input-horizontal-active-cursor, grabbing);
3117
+ user-select: none;
3118
+ }
3119
+
3120
+ .input-container.input-container--multi[data-multi-scroll-mode="horizontal"] .input-content .selection-badge {
3121
+ flex: 0 0 auto;
3122
+ }
3123
+
3124
+ .input-container.input-container--multi[data-multi-scroll-mode="horizontal"] .input-content > .select-input,
3125
+ .input-container.input-container--multi[data-multi-scroll-mode="horizontal"] .input-content > input.select-input,
3126
+ .input-container.input-container--multi[data-multi-scroll-mode="horizontal"] .input-content > .select-input[type="text"] {
3127
+ flex: var(--select-multi-input-horizontal-input-flex, 0 0 var(--select-multi-input-min-width, 96px));
3128
+ }
3129
+
3130
+ .input-container.input-container--multi[data-multi-scroll-mode="horizontal"] .dropdown-arrow-container {
3131
+ background: var(--select-multi-action-surface-bg, var(--select-input-bg, var(--select-surface)));
3132
+ border-left: var(--select-multi-action-divider, 1px solid var(--select-border));
3133
+ z-index: 4;
3134
+ }
3135
+
3136
+ .input-container.input-container--multi[data-multi-scroll-mode="horizontal"] .dropdown-arrow-container.with-clear-control {
3137
+ right: var(--select-arrow-right-with-clear, 34px);
3138
+ }
3139
+
3140
+ :host([dir="rtl"]) .input-container.input-container--multi[data-multi-scroll-mode="horizontal"] .dropdown-arrow-container {
3141
+ border-left: none;
3142
+ border-right: var(--select-multi-action-divider, 1px solid var(--select-border));
3143
+ }
3144
+
3145
+ :host([dir="rtl"]) .input-container.input-container--multi[data-multi-scroll-mode="horizontal"] .dropdown-arrow-container.with-clear-control {
3146
+ right: auto;
3147
+ left: var(--select-arrow-right-with-clear, 34px);
3148
+ }
3149
+
3150
+ .input-container.input-container--multi[data-multi-scroll-mode="horizontal"] .clear-control-button {
3151
+ background: var(--select-multi-action-surface-bg, var(--select-input-bg, var(--select-surface)));
3152
+ z-index: 4;
2567
3153
  }
2568
3154
 
2569
3155
  .input-container:focus-within {
@@ -2576,7 +3162,7 @@ class EnhancedSelect extends HTMLElement {
2576
3162
  content: '';
2577
3163
  position: absolute;
2578
3164
  top: 50%;
2579
- right: var(--select-separator-position, 42px);
3165
+ right: var(--select-separator-position, var(--select-arrow-width, 42px));
2580
3166
  transform: translateY(-50%);
2581
3167
  width: var(--select-separator-width, 1px);
2582
3168
  height: var(--select-separator-height, 50%);
@@ -2597,6 +3183,11 @@ class EnhancedSelect extends HTMLElement {
2597
3183
  .input-container:focus-within::after {
2598
3184
  opacity: var(--select-separator-active-opacity);
2599
3185
  }
3186
+
3187
+ :host([dir="rtl"]) .input-container::after {
3188
+ right: auto;
3189
+ left: var(--select-separator-position, var(--select-arrow-width, 42px));
3190
+ }
2600
3191
 
2601
3192
  /* ─────────────────────────────────────────────────────────────────────────
2602
3193
  Dropdown Arrow — Smooth rotation with refined styling
@@ -2623,13 +3214,29 @@ class EnhancedSelect extends HTMLElement {
2623
3214
  }
2624
3215
 
2625
3216
  .input-container.has-clear-control::after {
2626
- right: var(--select-separator-position-with-clear, 74px);
3217
+ right: var(--select-separator-position-with-clear, calc(var(--select-arrow-right-with-clear, 34px) + var(--select-arrow-width, 42px)));
2627
3218
  }
2628
3219
 
2629
3220
  .dropdown-arrow-container.with-clear-control {
2630
3221
  right: var(--select-arrow-right-with-clear, 34px);
2631
3222
  }
2632
3223
 
3224
+ :host([dir="rtl"]) .dropdown-arrow-container {
3225
+ right: auto;
3226
+ left: 0;
3227
+ border-radius: var(--select-arrow-border-radius-rtl, var(--select-radius-md) 0 0 var(--select-radius-md));
3228
+ }
3229
+
3230
+ :host([dir="rtl"]) .input-container.has-clear-control::after {
3231
+ right: auto;
3232
+ left: var(--select-separator-position-with-clear, calc(var(--select-arrow-right-with-clear, 34px) + var(--select-arrow-width, 42px)));
3233
+ }
3234
+
3235
+ :host([dir="rtl"]) .dropdown-arrow-container.with-clear-control {
3236
+ right: auto;
3237
+ left: var(--select-arrow-right-with-clear, 34px);
3238
+ }
3239
+
2633
3240
  /* ─────────────────────────────────────────────────────────────────────────
2634
3241
  Clear Control — Minimal and elegant dismiss button
2635
3242
  ───────────────────────────────────────────────────────────────────────── */
@@ -2675,6 +3282,11 @@ class EnhancedSelect extends HTMLElement {
2675
3282
  display: none;
2676
3283
  }
2677
3284
 
3285
+ :host([dir="rtl"]) .clear-control-button {
3286
+ right: auto;
3287
+ left: var(--select-clear-button-right, 8px);
3288
+ }
3289
+
2678
3290
  .clear-control-icon {
2679
3291
  width: var(--select-clear-icon-size);
2680
3292
  height: var(--select-clear-icon-size);
@@ -2729,7 +3341,7 @@ class EnhancedSelect extends HTMLElement {
2729
3341
  padding: var(--select-input-field-padding, 0) !important;
2730
3342
  margin: 0 !important;
2731
3343
  border: 0 !important;
2732
- font-size: var(--select-input-font-size, 15px);
3344
+ font-size: var(--select-input-font-size, inherit);
2733
3345
  line-height: var(--select-input-line-height, 1.5);
2734
3346
  color: var(--select-input-color, var(--select-text));
2735
3347
  background: transparent !important;
@@ -2737,13 +3349,16 @@ class EnhancedSelect extends HTMLElement {
2737
3349
  outline: none !important;
2738
3350
  font-weight: var(--select-input-font-weight);
2739
3351
  letter-spacing: var(--select-input-letter-spacing);
2740
- align-self: center;
3352
+ font-family: var(--select-input-font-family, inherit);
3353
+ font-style: var(--select-input-font-style, normal);
3354
+ align-self: var(--select-input-align-self, center);
2741
3355
  appearance: none !important;
2742
3356
  -webkit-appearance: none !important;
2743
3357
  box-shadow: none !important;
2744
3358
  border-radius: 0 !important;
2745
3359
  overflow: hidden;
2746
3360
  text-overflow: ellipsis;
3361
+ text-align: var(--select-input-text-align, inherit);
2747
3362
  white-space: nowrap;
2748
3363
  }
2749
3364
 
@@ -2752,6 +3367,14 @@ class EnhancedSelect extends HTMLElement {
2752
3367
  font-weight: 400;
2753
3368
  }
2754
3369
 
3370
+ :host([dir="rtl"]) .input-container {
3371
+ padding: var(--select-input-padding-rtl, 10px 14px 10px 52px);
3372
+ }
3373
+
3374
+ :host([dir="rtl"]) .input-container.has-clear-control {
3375
+ padding: var(--select-input-padding-with-clear-rtl, 10px 14px 10px 84px);
3376
+ }
3377
+
2755
3378
  .input-container.input-container--multi > .select-input,
2756
3379
  .input-container.input-container--multi > input.select-input,
2757
3380
  .input-container.input-container--multi > .select-input[type="text"] {
@@ -2782,10 +3405,12 @@ class EnhancedSelect extends HTMLElement {
2782
3405
  position: absolute;
2783
3406
  scroll-behavior: var(--select-dropdown-scroll-behavior);
2784
3407
  top: var(--select-dropdown-top);
3408
+ bottom: auto;
2785
3409
  left: 0;
2786
3410
  right: 0;
2787
3411
  max-height: var(--select-dropdown-max-height, 320px);
2788
3412
  overflow: hidden;
3413
+ box-sizing: border-box;
2789
3414
  background: var(--select-dropdown-bg, var(--select-surface));
2790
3415
  border: 1px solid var(--select-dropdown-border, var(--select-border));
2791
3416
  border-radius: var(--select-dropdown-border-radius, var(--select-radius-lg));
@@ -2794,6 +3419,16 @@ class EnhancedSelect extends HTMLElement {
2794
3419
  transform-origin: var(--select-dropdown-transform-origin);
2795
3420
  animation: var(--select-dropdown-animation);
2796
3421
  }
3422
+
3423
+ .select-dropdown[data-placement="top"] {
3424
+ top: auto;
3425
+ bottom: var(--select-dropdown-bottom, calc(100% + 6px));
3426
+ transform-origin: var(--select-dropdown-top-transform-origin, bottom center);
3427
+ }
3428
+
3429
+ .select-dropdown[data-placement="top"] {
3430
+ --select-dropdown-enter-from-transform: var(--select-dropdown-top-enter-from-transform, translateY(8px) scale(0.98));
3431
+ }
2797
3432
 
2798
3433
  @keyframes dropdownEnter {
2799
3434
  0% {
@@ -2810,8 +3445,9 @@ class EnhancedSelect extends HTMLElement {
2810
3445
  position: relative;
2811
3446
  max-height: var(--select-options-max-height, 320px);
2812
3447
  overflow: auto;
2813
- padding: var(--select-options-padding, 6px);
2814
- background: var(--select-options-bg, var(--select-surface));
3448
+ box-sizing: border-box;
3449
+ padding: var(--select-options-padding, var(--select-dropdown-padding, 6px));
3450
+ background: var(--select-options-bg, var(--select-dropdown-bg, var(--select-surface)));
2815
3451
 
2816
3452
  /* Custom scrollbar styling */
2817
3453
  scrollbar-width: thin;
@@ -2841,17 +3477,21 @@ class EnhancedSelect extends HTMLElement {
2841
3477
 
2842
3478
  .group-header {
2843
3479
  padding: var(--select-group-header-padding, 10px 12px 6px);
3480
+ margin: var(--select-group-header-margin, 0);
2844
3481
  font-weight: var(--select-group-header-weight, 600);
2845
3482
  color: var(--select-group-header-color, var(--select-text-muted));
2846
3483
  background-color: var(--select-group-header-bg, var(--select-surface));
2847
3484
  text-align: var(--select-group-header-text-align, left);
2848
- font-size: var(--select-group-header-font-size, 11px);
3485
+ font-size: var(--select-group-header-font-size, 0.7333em);
2849
3486
  text-transform: var(--select-group-header-text-transform, uppercase);
2850
3487
  letter-spacing: var(--select-group-header-letter-spacing, 0.08em);
2851
3488
  position: sticky;
2852
3489
  top: 0;
2853
3490
  z-index: 1;
3491
+ border: var(--select-group-header-border, none);
2854
3492
  border-bottom: var(--select-group-header-border-bottom, none);
3493
+ border-radius: var(--select-group-header-border-radius, 0);
3494
+ box-shadow: var(--select-group-header-shadow, none);
2855
3495
  }
2856
3496
 
2857
3497
  .group-header:not(:first-child) {
@@ -2870,13 +3510,16 @@ class EnhancedSelect extends HTMLElement {
2870
3510
  cursor: pointer;
2871
3511
  color: var(--select-option-color, var(--select-text));
2872
3512
  background: var(--select-option-bg, transparent);
3513
+ border: var(--select-option-border, none);
3514
+ text-align: var(--select-option-text-align, start);
2873
3515
  transition:
2874
3516
  background var(--select-transition-fast),
2875
3517
  color var(--select-transition-fast),
3518
+ border-color var(--select-transition-fast),
2876
3519
  transform var(--select-transition-fast),
2877
3520
  box-shadow var(--select-transition-fast);
2878
3521
  user-select: none;
2879
- font-size: var(--select-option-font-size, 14px);
3522
+ font-size: var(--select-option-font-size, inherit);
2880
3523
  font-weight: var(--select-option-font-weight);
2881
3524
  line-height: var(--select-option-line-height, 1.5);
2882
3525
  border-radius: var(--select-option-border-radius, var(--select-radius-sm));
@@ -2885,12 +3528,14 @@ class EnhancedSelect extends HTMLElement {
2885
3528
 
2886
3529
  .option:hover {
2887
3530
  background: var(--select-option-hover-bg, var(--select-surface-elevated));
3531
+ border: var(--select-option-hover-border, var(--select-option-border, none));
2888
3532
  color: var(--select-option-hover-color, var(--select-text));
2889
3533
  transform: var(--select-option-hover-transform);
2890
3534
  }
2891
3535
 
2892
3536
  .option.selected {
2893
3537
  background: var(--select-option-selected-bg, linear-gradient(135deg, rgba(15, 52, 96, 0.08) 0%, rgba(15, 52, 96, 0.04) 100%));
3538
+ border: var(--select-option-selected-border, var(--select-option-border, none));
2894
3539
  color: var(--select-option-selected-color, var(--select-accent));
2895
3540
  font-weight: var(--select-option-selected-weight, 550);
2896
3541
  }
@@ -2907,6 +3552,12 @@ class EnhancedSelect extends HTMLElement {
2907
3552
  border-radius: var(--select-option-selected-indicator-radius);
2908
3553
  animation: var(--select-option-selected-indicator-animation);
2909
3554
  }
3555
+
3556
+ :host([dir="rtl"]) .option.selected::before {
3557
+ left: auto;
3558
+ right: var(--select-option-selected-indicator-right, var(--select-option-selected-indicator-left));
3559
+ border-radius: var(--select-option-selected-indicator-radius-rtl, 2px 0 0 2px);
3560
+ }
2910
3561
 
2911
3562
  @keyframes selectedIndicator {
2912
3563
  0% {
@@ -2920,13 +3571,19 @@ class EnhancedSelect extends HTMLElement {
2920
3571
  }
2921
3572
 
2922
3573
  .option.selected:hover {
2923
- background: var(--select-option-selected-hover-bg, linear-gradient(135deg, rgba(15, 52, 96, 0.12) 0%, rgba(15, 52, 96, 0.06) 100%));
3574
+ background: var(--select-option-selected-hover-bg, var(--select-option-selected-bg, linear-gradient(135deg, rgba(15, 52, 96, 0.12) 0%, rgba(15, 52, 96, 0.06) 100%)));
3575
+ border: var(--select-option-selected-hover-border, var(--select-option-selected-border, var(--select-option-hover-border, var(--select-option-border, none))));
3576
+ color: var(--select-option-selected-hover-color, var(--select-option-selected-color, var(--select-accent)));
2924
3577
  }
2925
3578
 
2926
3579
  .option.active:not(.selected) {
2927
3580
  background: var(--select-option-active-bg, var(--select-surface-elevated));
3581
+ color: var(--select-option-active-color, var(--select-option-hover-color, var(--select-option-color, var(--select-text))));
3582
+ border: var(--select-option-active-border, var(--select-option-hover-border, var(--select-option-border, none)));
2928
3583
  outline: var(--select-option-active-outline, 2px solid rgba(15, 52, 96, 0.3));
2929
3584
  outline-offset: var(--select-option-active-outline-offset);
3585
+ box-shadow: var(--select-option-active-shadow, none);
3586
+ transform: var(--select-option-active-transform, none);
2930
3587
  }
2931
3588
 
2932
3589
  .option.selected.active {
@@ -2934,6 +3591,14 @@ class EnhancedSelect extends HTMLElement {
2934
3591
  outline-offset: var(--select-option-selected-active-outline-offset);
2935
3592
  }
2936
3593
 
3594
+ .option.disabled {
3595
+ background: var(--select-option-disabled-bg, var(--select-option-bg, transparent));
3596
+ color: var(--select-option-disabled-color, var(--select-text-muted));
3597
+ border: var(--select-option-disabled-border, var(--select-option-border, none));
3598
+ opacity: var(--select-option-disabled-opacity, 0.5);
3599
+ cursor: var(--select-option-disabled-cursor, not-allowed);
3600
+ }
3601
+
2937
3602
  .option:active:not(.selected) {
2938
3603
  background: var(--select-option-pressed-bg, rgba(15, 52, 96, 0.08));
2939
3604
  transform: var(--select-option-pressed-transform);
@@ -2959,7 +3624,7 @@ class EnhancedSelect extends HTMLElement {
2959
3624
  color: var(--select-button-color, var(--select-accent));
2960
3625
  border-radius: var(--select-button-border-radius, var(--select-radius-md));
2961
3626
  cursor: pointer;
2962
- font-size: var(--select-button-font-size, 13px);
3627
+ font-size: var(--select-button-font-size, 0.8667em);
2963
3628
  font-weight: var(--select-button-font-weight);
2964
3629
  letter-spacing: var(--select-button-letter-spacing);
2965
3630
  transition:
@@ -2991,7 +3656,7 @@ class EnhancedSelect extends HTMLElement {
2991
3656
  text-align: center;
2992
3657
  color: var(--select-busy-color, var(--select-text-muted));
2993
3658
  background: var(--select-busy-bg, transparent);
2994
- font-size: var(--select-busy-font-size, 13px);
3659
+ font-size: var(--select-busy-font-size, 0.8667em);
2995
3660
  }
2996
3661
 
2997
3662
  .spinner {
@@ -3012,7 +3677,7 @@ class EnhancedSelect extends HTMLElement {
3012
3677
  padding: var(--select-empty-padding, 32px 24px);
3013
3678
  text-align: center;
3014
3679
  color: var(--select-empty-color, var(--select-text-muted));
3015
- font-size: var(--select-empty-font-size, 14px);
3680
+ font-size: var(--select-empty-font-size, 0.9333em);
3016
3681
  background: var(--select-empty-bg, transparent);
3017
3682
  display: flex;
3018
3683
  flex-direction: column;
@@ -3025,7 +3690,7 @@ class EnhancedSelect extends HTMLElement {
3025
3690
  padding: var(--select-searching-padding, 32px 24px);
3026
3691
  text-align: center;
3027
3692
  color: var(--select-searching-color, var(--select-accent));
3028
- font-size: var(--select-searching-font-size, 14px);
3693
+ font-size: var(--select-searching-font-size, 0.9333em);
3029
3694
  background: var(--select-searching-bg, transparent);
3030
3695
  display: flex;
3031
3696
  flex-direction: column;
@@ -3115,24 +3780,28 @@ class EnhancedSelect extends HTMLElement {
3115
3780
  --select-primary-light: #2a2a3e;
3116
3781
  --select-accent: #6366f1;
3117
3782
  --select-accent-hover: #f43f5e;
3118
- --select-surface: #1a1a2e;
3783
+ --select-surface: var(--select-dark-bg, #1a1a2e);
3119
3784
  --select-surface-elevated: #252540;
3120
- --select-border: #3f3f5a;
3785
+ --select-border: var(--select-dark-border, #3f3f5a);
3121
3786
  --select-border-focus: #6366f1;
3122
- --select-text: #f5f5f5;
3787
+ --select-text: var(--select-dark-text, #f5f5f5);
3123
3788
  --select-text-muted: #9ca3af;
3124
3789
  --select-text-placeholder: #6b7280;
3125
3790
  --select-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.2);
3126
3791
  --select-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.3);
3127
3792
  --select-shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.4);
3128
3793
  --select-shadow-focus: 0 0 0 3px rgba(99, 102, 241, 0.25);
3794
+
3795
+ --select-dropdown-bg: var(--select-dark-dropdown-bg, var(--select-surface));
3129
3796
 
3130
- --select-option-bg: transparent;
3131
- --select-option-color: var(--select-text);
3132
- --select-option-hover-bg: var(--select-surface-elevated);
3133
- --select-option-hover-color: var(--select-text);
3134
- --select-option-selected-bg: linear-gradient(135deg, rgba(99, 102, 241, 0.15) 0%, rgba(99, 102, 241, 0.08) 100%);
3135
- --select-option-selected-color: #a5b4fc;
3797
+ --select-option-bg: var(--select-dark-option-bg, transparent);
3798
+ --select-option-color: var(--select-dark-option-color, var(--select-text));
3799
+ --select-option-hover-bg: var(--select-dark-option-hover-bg, var(--select-surface-elevated));
3800
+ --select-option-hover-color: var(--select-dark-option-hover-color, var(--select-text));
3801
+ --select-option-selected-bg: var(--select-dark-option-selected-bg, linear-gradient(135deg, rgba(99, 102, 241, 0.15) 0%, rgba(99, 102, 241, 0.08) 100%));
3802
+ --select-option-selected-color: var(--select-dark-option-selected-color, #a5b4fc);
3803
+ --select-option-selected-hover-bg: var(--select-dark-option-selected-hover-bg, var(--select-option-selected-bg));
3804
+ --select-option-selected-hover-color: var(--select-dark-option-selected-hover-color, var(--select-option-selected-color));
3136
3805
  }
3137
3806
 
3138
3807
  :host(.dark-mode) .input-container,
@@ -3175,7 +3844,7 @@ class EnhancedSelect extends HTMLElement {
3175
3844
  :host-context([darkmode]) .select-dropdown,
3176
3845
  :host-context([data-theme="dark"]) .select-dropdown,
3177
3846
  :host-context([theme="dark"]) .select-dropdown {
3178
- background: var(--select-surface);
3847
+ background: var(--select-dropdown-bg, var(--select-surface));
3179
3848
  border-color: var(--select-border);
3180
3849
  }
3181
3850
 
@@ -3190,7 +3859,7 @@ class EnhancedSelect extends HTMLElement {
3190
3859
  :host-context([darkmode]) .options-container,
3191
3860
  :host-context([data-theme="dark"]) .options-container,
3192
3861
  :host-context([theme="dark"]) .options-container {
3193
- background: var(--select-surface);
3862
+ background: var(--select-dropdown-bg, var(--select-surface));
3194
3863
  scrollbar-color: var(--select-border) transparent;
3195
3864
  }
3196
3865
 
@@ -3287,6 +3956,19 @@ class EnhancedSelect extends HTMLElement {
3287
3956
  return;
3288
3957
  if (target && target.closest('.clear-control-button'))
3289
3958
  return;
3959
+ if (this._isPointerOnInputScrollbar(e)) {
3960
+ this._suppressNextOpenClick = true;
3961
+ return;
3962
+ }
3963
+ const isHorizontalMultiMode = this._config.selection.mode === 'multi'
3964
+ && (this._config.multiSelectDisplay?.mode ?? 'wrap') === 'horizontal';
3965
+ const enableHorizontalDrag = this._canUseHorizontalMultiScroll(target);
3966
+ if (enableHorizontalDrag && e.button === 0) {
3967
+ this._beginMultiScrollDrag(e);
3968
+ this._suppressNextOpenClick = false;
3969
+ e.preventDefault();
3970
+ return;
3971
+ }
3290
3972
  // If we clicked the container, but not the input itself, we must prevent default
3291
3973
  // otherwise the browser moves focus from our input to the body, immediately triggering blur.
3292
3974
  if (target && !target.matches('.select-input')) {
@@ -3295,6 +3977,8 @@ class EnhancedSelect extends HTMLElement {
3295
3977
  const clickedInput = Boolean(target && target.matches('.select-input'));
3296
3978
  if (this._state.isOpen &&
3297
3979
  this._config.selection.toggleOnTriggerClick !== false &&
3980
+ !isHorizontalMultiMode &&
3981
+ !enableHorizontalDrag &&
3298
3982
  !(clickedInput && this._config.searchable)) {
3299
3983
  this._handleClose();
3300
3984
  return;
@@ -3317,6 +4001,47 @@ class EnhancedSelect extends HTMLElement {
3317
4001
  }
3318
4002
  }
3319
4003
  });
4004
+ this._inputContainer.addEventListener('click', (e) => {
4005
+ const target = e.target;
4006
+ const isHorizontalMultiMode = this._config.selection.mode === 'multi'
4007
+ && (this._config.multiSelectDisplay?.mode ?? 'wrap') === 'horizontal';
4008
+ if (!this._config.enabled || !isHorizontalMultiMode) {
4009
+ this._suppressNextOpenClick = false;
4010
+ this._multiScrollDrag.moved = false;
4011
+ return;
4012
+ }
4013
+ if (target && target.closest('.dropdown-arrow-container, .clear-control-button')) {
4014
+ this._suppressNextOpenClick = false;
4015
+ this._multiScrollDrag.moved = false;
4016
+ return;
4017
+ }
4018
+ if (this._suppressNextOpenClick || this._multiScrollDrag.moved || this._isPointerOnInputScrollbar(e)) {
4019
+ this._suppressNextOpenClick = false;
4020
+ this._multiScrollDrag.moved = false;
4021
+ return;
4022
+ }
4023
+ this._suppressNextOpenClick = false;
4024
+ this._multiScrollDrag.moved = false;
4025
+ if (!this._state.isOpen) {
4026
+ this._handleOpen();
4027
+ }
4028
+ this._input.focus();
4029
+ });
4030
+ this._inputContent.addEventListener('pointermove', (e) => {
4031
+ this._updateMultiScrollDrag(e);
4032
+ });
4033
+ this._inputContent.addEventListener('pointerup', (e) => {
4034
+ this._endMultiScrollDrag(e.pointerId);
4035
+ });
4036
+ this._inputContent.addEventListener('pointercancel', (e) => {
4037
+ this._endMultiScrollDrag(e.pointerId);
4038
+ });
4039
+ this._inputContent.addEventListener('wheel', (e) => {
4040
+ if (this._config.selection.mode === 'multi'
4041
+ && (this._config.multiSelectDisplay?.mode ?? 'wrap') === 'horizontal') {
4042
+ e.preventDefault();
4043
+ }
4044
+ }, { passive: false });
3320
4045
  // Input container click - prevent event from reaching document listener
3321
4046
  this._container.addEventListener('click', (e) => {
3322
4047
  e.stopPropagation();
@@ -3487,11 +4212,14 @@ class EnhancedSelect extends HTMLElement {
3487
4212
  this._input.focus();
3488
4213
  this._markOpenStart();
3489
4214
  this._state.isOpen = true;
4215
+ this.setAttribute('data-open', 'true');
4216
+ this._liftStackingAncestors();
3490
4217
  this._dropdown.style.display = 'block';
3491
4218
  this._input.setAttribute('aria-expanded', 'true');
3492
4219
  this._updateArrowRotation();
3493
4220
  // Render options when opening
3494
4221
  this._renderOptions();
4222
+ this._syncDropdownPlacement();
3495
4223
  this._setInitialActiveOption();
3496
4224
  this._emit('open', {});
3497
4225
  this._config.callbacks.onOpen?.();
@@ -3511,7 +4239,10 @@ class EnhancedSelect extends HTMLElement {
3511
4239
  if (!this._state.isOpen)
3512
4240
  return;
3513
4241
  this._state.isOpen = false;
4242
+ this.removeAttribute('data-open');
4243
+ this._restoreLiftedAncestors();
3514
4244
  this._dropdown.style.display = 'none';
4245
+ this._dropdown.setAttribute('data-placement', this._resolvedDropdownPlacement);
3515
4246
  this._input.setAttribute('aria-expanded', 'false');
3516
4247
  this._input.removeAttribute('aria-activedescendant');
3517
4248
  this._updateArrowRotation();
@@ -3521,14 +4252,76 @@ class EnhancedSelect extends HTMLElement {
3521
4252
  }
3522
4253
  _updateDropdownVisibility() {
3523
4254
  if (this._state.isOpen) {
4255
+ this.setAttribute('data-open', 'true');
4256
+ this._liftStackingAncestors();
3524
4257
  this._dropdown.style.display = 'block';
4258
+ this._syncDropdownPlacement();
3525
4259
  this._input.setAttribute('aria-expanded', 'true');
3526
4260
  }
3527
4261
  else {
4262
+ this.removeAttribute('data-open');
4263
+ this._restoreLiftedAncestors();
3528
4264
  this._dropdown.style.display = 'none';
3529
4265
  this._input.setAttribute('aria-expanded', 'false');
3530
4266
  }
3531
4267
  }
4268
+ _liftStackingAncestors() {
4269
+ this._restoreLiftedAncestors();
4270
+ let ancestor = this.parentElement;
4271
+ while (ancestor && ancestor !== document.body) {
4272
+ if (this._createsStackingContext(ancestor)) {
4273
+ const hadMarker = ancestor.hasAttribute('data-smilodon-open-ancestor');
4274
+ this._liftedAncestors.push({
4275
+ element: ancestor,
4276
+ position: ancestor.style.position,
4277
+ zIndex: ancestor.style.zIndex,
4278
+ hadMarker,
4279
+ });
4280
+ if (getComputedStyle(ancestor).position === 'static') {
4281
+ ancestor.style.position = 'relative';
4282
+ }
4283
+ ancestor.style.zIndex = 'var(--select-ancestor-open-z-index, var(--select-host-open-z-index, var(--select-dropdown-z-index, 1000)))';
4284
+ ancestor.setAttribute('data-smilodon-open-ancestor', 'true');
4285
+ }
4286
+ ancestor = ancestor.parentElement;
4287
+ }
4288
+ }
4289
+ _restoreLiftedAncestors() {
4290
+ for (const lifted of this._liftedAncestors) {
4291
+ lifted.element.style.position = lifted.position;
4292
+ lifted.element.style.zIndex = lifted.zIndex;
4293
+ if (!lifted.hadMarker) {
4294
+ lifted.element.removeAttribute('data-smilodon-open-ancestor');
4295
+ }
4296
+ }
4297
+ this._liftedAncestors = [];
4298
+ }
4299
+ _createsStackingContext(element) {
4300
+ const style = getComputedStyle(element);
4301
+ if (style.position === 'fixed' || style.position === 'sticky')
4302
+ return true;
4303
+ if (style.zIndex !== 'auto' && style.position !== 'static')
4304
+ return true;
4305
+ if (style.opacity !== '1')
4306
+ return true;
4307
+ if (style.transform !== 'none')
4308
+ return true;
4309
+ if (style.filter !== 'none')
4310
+ return true;
4311
+ if (style.backdropFilter && style.backdropFilter !== 'none')
4312
+ return true;
4313
+ if (style.perspective !== 'none')
4314
+ return true;
4315
+ if (style.mixBlendMode !== 'normal')
4316
+ return true;
4317
+ if (style.isolation === 'isolate')
4318
+ return true;
4319
+ if (style.contain.includes('paint') || style.contain.includes('layout'))
4320
+ return true;
4321
+ if (style.willChange.includes('transform') || style.willChange.includes('opacity') || style.willChange.includes('filter'))
4322
+ return true;
4323
+ return false;
4324
+ }
3532
4325
  _updateArrowRotation() {
3533
4326
  if (this._arrowContainer) {
3534
4327
  const arrow = this._arrowContainer.querySelector('.dropdown-arrow');
@@ -4000,7 +4793,7 @@ class EnhancedSelect extends HTMLElement {
4000
4793
  this._input.value = '';
4001
4794
  this._input.placeholder = this._config.placeholder || 'Select an option...';
4002
4795
  // Clear any badges
4003
- const existingBadges = this._inputContainer.querySelectorAll('.selection-badge');
4796
+ const existingBadges = this._inputContent.querySelectorAll('.selection-badge');
4004
4797
  existingBadges.forEach(badge => badge.remove());
4005
4798
  }
4006
4799
  else if (this._config.selection.mode === 'single') {
@@ -4011,16 +4804,23 @@ class EnhancedSelect extends HTMLElement {
4011
4804
  this._input.value = '';
4012
4805
  this._input.placeholder = '';
4013
4806
  // Clear existing badges
4014
- const existingBadges = this._inputContainer.querySelectorAll('.selection-badge');
4807
+ const existingBadges = this._inputContent.querySelectorAll('.selection-badge');
4015
4808
  existingBadges.forEach(badge => badge.remove());
4016
4809
  // Create badges for each selected item
4017
4810
  const selectedEntries = Array.from(this._state.selectedItems.entries());
4018
4811
  selectedEntries.forEach(([index, item]) => {
4019
4812
  const badge = document.createElement('span');
4020
4813
  badge.className = 'selection-badge';
4814
+ if (this._config.styles.classNames?.badge) {
4815
+ badge.classList.add(...this._config.styles.classNames.badge.split(' ').filter(Boolean));
4816
+ }
4021
4817
  badge.setAttribute('part', 'chip');
4022
4818
  const badgeLabel = document.createElement('span');
4023
4819
  badgeLabel.className = 'selection-badge-label';
4820
+ if (this._config.styles.classNames?.badgeLabel) {
4821
+ badgeLabel.classList.add(...this._config.styles.classNames.badgeLabel.split(' ').filter(Boolean));
4822
+ }
4823
+ badgeLabel.setAttribute('part', 'chip-label');
4024
4824
  badgeLabel.textContent = getLabel(item);
4025
4825
  badge.appendChild(badgeLabel);
4026
4826
  // Add remove button to badge
@@ -4028,9 +4828,19 @@ class EnhancedSelect extends HTMLElement {
4028
4828
  const removeBtn = document.createElement('button');
4029
4829
  removeBtn.type = 'button';
4030
4830
  removeBtn.className = 'badge-remove';
4831
+ if (this._config.styles.classNames?.removeButton) {
4832
+ removeBtn.classList.add(...this._config.styles.classNames.removeButton.split(' ').filter(Boolean));
4833
+ }
4834
+ if (this._config.styles.classNames?.badgeRemove) {
4835
+ removeBtn.classList.add(...this._config.styles.classNames.badgeRemove.split(' ').filter(Boolean));
4836
+ }
4031
4837
  removeBtn.setAttribute('part', 'chip-remove');
4032
- removeBtn.innerHTML = '×';
4033
4838
  removeBtn.setAttribute('aria-label', `Remove ${getLabel(item)}`);
4839
+ const removeIcon = document.createElement('span');
4840
+ removeIcon.className = 'badge-remove-icon';
4841
+ removeIcon.setAttribute('part', 'chip-remove-icon');
4842
+ this._setIconContent(removeIcon, this._config.selection.removeButtonIcon, '×');
4843
+ removeBtn.appendChild(removeIcon);
4034
4844
  removeBtn.addEventListener('pointerdown', (e) => {
4035
4845
  e.stopPropagation();
4036
4846
  e.preventDefault();
@@ -4042,7 +4852,7 @@ class EnhancedSelect extends HTMLElement {
4042
4852
  });
4043
4853
  badge.appendChild(removeBtn);
4044
4854
  }
4045
- this._inputContainer.insertBefore(badge, this._input);
4855
+ this._inputContent.insertBefore(badge, this._input);
4046
4856
  });
4047
4857
  }
4048
4858
  this._syncClearControlState();
@@ -4515,6 +5325,7 @@ class EnhancedSelect extends HTMLElement {
4515
5325
  if (this._clearControlIcon) {
4516
5326
  this._clearControlIcon.innerHTML = `<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 4L4 12M4 4L12 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
4517
5327
  }
5328
+ this._syncStyleConfigVariables();
4518
5329
  if (this._dropdown) {
4519
5330
  if (this._config.selection.mode === 'multi') {
4520
5331
  this._dropdown.setAttribute('aria-multiselectable', 'true');
@@ -4523,10 +5334,14 @@ class EnhancedSelect extends HTMLElement {
4523
5334
  this._dropdown.removeAttribute('aria-multiselectable');
4524
5335
  }
4525
5336
  }
5337
+ this._syncDirectionConfig();
4526
5338
  this._syncInputContainerMode();
5339
+ this._syncMultiSelectDisplayConfig();
5340
+ this._syncDropdownPlacement();
4527
5341
  // Re-initialize observers in case infinite scroll was enabled/disabled
4528
5342
  this._initializeObservers();
4529
5343
  this._syncClearControlState();
5344
+ this._updateInputDisplay();
4530
5345
  this._renderOptions();
4531
5346
  }
4532
5347
  _mergeConfig(target, source) {
@@ -4682,6 +5497,9 @@ class EnhancedSelect extends HTMLElement {
4682
5497
  header.textContent = group.label;
4683
5498
  }
4684
5499
  header.classList.add('group-header');
5500
+ if (this._config.styles.classNames?.groupHeader) {
5501
+ header.classList.add(...this._config.styles.classNames.groupHeader.split(' ').filter(Boolean));
5502
+ }
4685
5503
  header.setAttribute('part', 'group-header');
4686
5504
  this._optionsContainer.appendChild(header);
4687
5505
  group.options.forEach(item => {
@@ -4809,6 +5627,7 @@ class EnhancedSelect extends HTMLElement {
4809
5627
  disabled: isDisabled,
4810
5628
  id: optionId,
4811
5629
  });
5630
+ optionElement.setAttribute('dir', this._config.direction ?? 'ltr');
4812
5631
  targetContainer.appendChild(optionElement);
4813
5632
  return;
4814
5633
  }
@@ -4822,8 +5641,23 @@ class EnhancedSelect extends HTMLElement {
4822
5641
  getValue,
4823
5642
  getLabel,
4824
5643
  showRemoveButton: this._config.selection.mode === 'multi' && this._config.selection.showRemoveButton,
5644
+ removeButtonIcon: this._config.selection.removeButtonIcon,
4825
5645
  classMap: this.classMap,
5646
+ className: this._config.styles.classNames?.option,
4826
5647
  });
5648
+ if (this._config.styles.classNames?.option) {
5649
+ option.classList.add(...this._config.styles.classNames.option.split(' ').filter(Boolean));
5650
+ }
5651
+ option.setAttribute('dir', this._config.direction ?? 'ltr');
5652
+ if (isSelected && this._config.styles.classNames?.selectedOption) {
5653
+ option.classList.add(...this._config.styles.classNames.selectedOption.split(' ').filter(Boolean));
5654
+ }
5655
+ if (this._state.activeIndex === index && this._config.styles.classNames?.activeOption) {
5656
+ option.classList.add(...this._config.styles.classNames.activeOption.split(' ').filter(Boolean));
5657
+ }
5658
+ if (isDisabled && this._config.styles.classNames?.disabledOption) {
5659
+ option.classList.add(...this._config.styles.classNames.disabledOption.split(' ').filter(Boolean));
5660
+ }
4827
5661
  // Valid part attribute on the web component host itself
4828
5662
  option.setAttribute('part', 'option');
4829
5663
  option.dataset.index = String(index);
@@ -4857,6 +5691,9 @@ class EnhancedSelect extends HTMLElement {
4857
5691
  }
4858
5692
  // Add both semantic namespaced classes and the legacy internal classes that CSS uses
4859
5693
  optionEl.classList.add('smilodon-option', 'option');
5694
+ if (this._config.styles.classNames?.option) {
5695
+ optionEl.classList.add(...this._config.styles.classNames.option.split(' ').filter(Boolean));
5696
+ }
4860
5697
  // Toggle state classes using classMap if available
4861
5698
  const isSelected = meta.selected;
4862
5699
  const isActive = meta.active;
@@ -4868,26 +5705,44 @@ class EnhancedSelect extends HTMLElement {
4868
5705
  if (isSelected) {
4869
5706
  optionEl.classList.add(...selectedClasses);
4870
5707
  optionEl.classList.add('smilodon-option--selected');
5708
+ if (this._config.styles.classNames?.selectedOption) {
5709
+ optionEl.classList.add(...this._config.styles.classNames.selectedOption.split(' ').filter(Boolean));
5710
+ }
4871
5711
  }
4872
5712
  else {
4873
5713
  optionEl.classList.remove(...selectedClasses);
4874
5714
  optionEl.classList.remove('smilodon-option--selected');
5715
+ if (this._config.styles.classNames?.selectedOption) {
5716
+ optionEl.classList.remove(...this._config.styles.classNames.selectedOption.split(' ').filter(Boolean));
5717
+ }
4875
5718
  }
4876
5719
  if (isActive) {
4877
5720
  optionEl.classList.add(...activeClasses);
4878
5721
  optionEl.classList.add('smilodon-option--active');
5722
+ if (this._config.styles.classNames?.activeOption) {
5723
+ optionEl.classList.add(...this._config.styles.classNames.activeOption.split(' ').filter(Boolean));
5724
+ }
4879
5725
  }
4880
5726
  else {
4881
5727
  optionEl.classList.remove(...activeClasses);
4882
5728
  optionEl.classList.remove('smilodon-option--active');
5729
+ if (this._config.styles.classNames?.activeOption) {
5730
+ optionEl.classList.remove(...this._config.styles.classNames.activeOption.split(' ').filter(Boolean));
5731
+ }
4883
5732
  }
4884
5733
  if (isDisabled) {
4885
5734
  optionEl.classList.add(...disabledClasses);
4886
5735
  optionEl.classList.add('smilodon-option--disabled');
5736
+ if (this._config.styles.classNames?.disabledOption) {
5737
+ optionEl.classList.add(...this._config.styles.classNames.disabledOption.split(' ').filter(Boolean));
5738
+ }
4887
5739
  }
4888
5740
  else {
4889
5741
  optionEl.classList.remove(...disabledClasses);
4890
5742
  optionEl.classList.remove('smilodon-option--disabled');
5743
+ if (this._config.styles.classNames?.disabledOption) {
5744
+ optionEl.classList.remove(...this._config.styles.classNames.disabledOption.split(' ').filter(Boolean));
5745
+ }
4891
5746
  }
4892
5747
  // Data Attributes Contract
4893
5748
  const state = [];