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