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