@smilodon/core 1.4.9 → 1.4.11
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 +56 -0
- package/dist/index.cjs +126 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +126 -41
- 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 +126 -41
- 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 +10 -0
- package/dist/types/src/types.d.ts +5 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -610,6 +610,11 @@ Dark mode is **opt-in only** and can be enabled by adding a class or data attrib
|
|
|
610
610
|
|
|
611
611
|
<!-- Using data attribute -->
|
|
612
612
|
<enhanced-select data-theme="dark"></enhanced-select>
|
|
613
|
+
|
|
614
|
+
<!-- Also supported (attribute aliases) -->
|
|
615
|
+
<enhanced-select dark-mode></enhanced-select>
|
|
616
|
+
<enhanced-select darkmode></enhanced-select>
|
|
617
|
+
<enhanced-select theme="dark"></enhanced-select>
|
|
613
618
|
```
|
|
614
619
|
|
|
615
620
|
```css
|
|
@@ -631,6 +636,8 @@ enhanced-select.dark-mode {
|
|
|
631
636
|
**Light Mode (Default)**
|
|
632
637
|
```css
|
|
633
638
|
--select-options-bg /* Options container background (white) */
|
|
639
|
+
--select-width /* Host width (100% default) */
|
|
640
|
+
--select-height /* Host height (auto default) */
|
|
634
641
|
--select-option-color /* Option text color (#1f2937) */
|
|
635
642
|
--select-option-bg /* Option background (white) */
|
|
636
643
|
--select-option-padding /* Option padding (8px 12px) */
|
|
@@ -643,9 +650,41 @@ enhanced-select.dark-mode {
|
|
|
643
650
|
--select-option-selected-hover-border /* Selected+hover border (inherits selected border by default) */
|
|
644
651
|
--select-option-active-bg /* Active background (#f3f4f6) */
|
|
645
652
|
--select-option-active-color /* Active text color (#1f2937) */
|
|
653
|
+
--select-input-width /* Input field width */
|
|
654
|
+
--select-input-height /* Input container height */
|
|
646
655
|
--select-dropdown-bg /* Dropdown background (white) */
|
|
647
656
|
--select-dropdown-border /* Dropdown border color (#ccc) */
|
|
648
657
|
--select-dropdown-shadow /* Dropdown shadow */
|
|
658
|
+
--select-empty-padding /* Empty/no-results container padding */
|
|
659
|
+
--select-empty-color /* Text color for empty/no-results models */
|
|
660
|
+
--select-empty-font-size /* Font size */
|
|
661
|
+
--select-empty-bg /* Background for empty/no-results state */
|
|
662
|
+
--select-empty-min-height /* Minimum height of empty state box */
|
|
663
|
+
|
|
664
|
+
/* Arrow/button */
|
|
665
|
+
--select-arrow-size /* Width & height of SVG icon (16px default) */
|
|
666
|
+
--select-arrow-color /* Icon color (#667eea) */
|
|
667
|
+
--select-arrow-hover-color /* Icon color when hovered (#667eea) */
|
|
668
|
+
--select-arrow-hover-bg /* Background when hover (rgba(102,126,234,0.08)) */
|
|
669
|
+
--select-arrow-width /* Container width (40px) */
|
|
670
|
+
--select-arrow-border-radius /* Container border radius */
|
|
671
|
+
|
|
672
|
+
/* Group headers (when using groupedItems or flat items with `group` property) */
|
|
673
|
+
--select-group-header-padding /* Padding inside header (8px 12px) */
|
|
674
|
+
--select-group-header-color /* Text color (#6b7280) */
|
|
675
|
+
--select-group-header-bg /* Background (#f3f4f6) */
|
|
676
|
+
--select-group-header-font-size
|
|
677
|
+
--select-group-header-text-align /* Header text alignment (left default) */
|
|
678
|
+
--select-group-header-text-transform
|
|
679
|
+
--select-group-header-letter-spacing
|
|
680
|
+
--select-group-header-border-bottom
|
|
681
|
+
|
|
682
|
+
/* Option content and checkmark hooks */
|
|
683
|
+
--select-option-content-overflow
|
|
684
|
+
--select-option-content-text-overflow
|
|
685
|
+
--select-option-content-white-space
|
|
686
|
+
--select-checkmark-margin-left
|
|
687
|
+
--select-checkmark-color
|
|
649
688
|
```
|
|
650
689
|
|
|
651
690
|
**Dark Mode (Opt-in)**
|
|
@@ -659,6 +698,8 @@ enhanced-select.dark-mode {
|
|
|
659
698
|
--select-dark-option-bg /* Dark option background (#1f2937) */
|
|
660
699
|
--select-dark-option-hover-bg /* Dark hover background (#374151) */
|
|
661
700
|
--select-dark-option-selected-bg /* Dark selected bg (#3730a3) */
|
|
701
|
+
--select-dark-group-header-color /* Dark header text */
|
|
702
|
+
--select-dark-group-header-bg /* Dark header background */
|
|
662
703
|
```
|
|
663
704
|
|
|
664
705
|
**Complete CSS Variables List (60+ variables)**
|
|
@@ -693,11 +734,26 @@ enhanced-select {
|
|
|
693
734
|
transparent 100%
|
|
694
735
|
);
|
|
695
736
|
}
|
|
737
|
+
|
|
738
|
+
/* Typo-compatible aliases are also accepted */
|
|
739
|
+
enhanced-select {
|
|
740
|
+
--select-seperator-width: 2px;
|
|
741
|
+
--select-seperator-height: 80%;
|
|
742
|
+
--select-seperator-position: 40px;
|
|
743
|
+
--select-seperator-gradient: linear-gradient(to bottom, transparent 0%, #3b82f6 20%, #3b82f6 80%, transparent 100%);
|
|
744
|
+
}
|
|
696
745
|
```
|
|
697
746
|
|
|
747
|
+
**Gradient Dropdown + Hover/Selected States**
|
|
748
|
+
If your dropdown uses a gradient background (for example via `--select-dropdown-bg`), option hover/selected colors still work as expected. The component intentionally uses the `background` shorthand for hover/selected option states so any inherited `background-image` layers are cleared correctly.
|
|
749
|
+
|
|
698
750
|
**Badge Remove/Delete Button (Multi-Select)**
|
|
699
751
|
The × button that removes selected items in multi-select mode is fully customizable:
|
|
700
752
|
|
|
753
|
+
**Group Header & No‑Results Parts**
|
|
754
|
+
Both the group header and the no-results message are exposed as shadow parts (`group-header` and `no-results`) so you can target them with `::part()` selectors or CSS variables. This makes it straightforward to match the look of your host framework or UI kit.
|
|
755
|
+
|
|
756
|
+
|
|
701
757
|
```css
|
|
702
758
|
enhanced-select {
|
|
703
759
|
/* Customize badge appearance */
|
package/dist/index.cjs
CHANGED
|
@@ -1534,6 +1534,8 @@ class SelectOption extends HTMLElement {
|
|
|
1534
1534
|
padding: var(--select-option-padding, 8px 12px);
|
|
1535
1535
|
cursor: pointer;
|
|
1536
1536
|
user-select: none;
|
|
1537
|
+
color: var(--select-option-color, var(--select-text-color, #1f2937));
|
|
1538
|
+
background: var(--select-option-bg, var(--select-dropdown-bg, var(--select-bg, white)));
|
|
1537
1539
|
transition: var(--select-option-transition, background-color 0.2s ease);
|
|
1538
1540
|
border: var(--select-option-border, none);
|
|
1539
1541
|
border-bottom: var(--select-option-border-bottom, none);
|
|
@@ -1578,9 +1580,20 @@ class SelectOption extends HTMLElement {
|
|
|
1578
1580
|
|
|
1579
1581
|
.option-content {
|
|
1580
1582
|
flex: 1;
|
|
1581
|
-
overflow: hidden;
|
|
1582
|
-
text-overflow: ellipsis;
|
|
1583
|
-
white-space: nowrap;
|
|
1583
|
+
overflow: var(--select-option-content-overflow, hidden);
|
|
1584
|
+
text-overflow: var(--select-option-content-text-overflow, ellipsis);
|
|
1585
|
+
white-space: var(--select-option-content-white-space, nowrap);
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
.checkmark-icon {
|
|
1589
|
+
display: none;
|
|
1590
|
+
margin-left: var(--select-checkmark-margin-left, 8px);
|
|
1591
|
+
color: var(--select-checkmark-color, currentColor);
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
:host([aria-selected="true"]) .checkmark-icon,
|
|
1595
|
+
.option-container.selected .checkmark-icon {
|
|
1596
|
+
display: inline-flex;
|
|
1584
1597
|
}
|
|
1585
1598
|
|
|
1586
1599
|
.remove-button {
|
|
@@ -1708,16 +1721,6 @@ class SelectOption extends HTMLElement {
|
|
|
1708
1721
|
<path d="M4 8.5L6.5 11L12 5.5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
1709
1722
|
</svg>
|
|
1710
1723
|
`;
|
|
1711
|
-
// Visibility control via CSS or inline style
|
|
1712
|
-
// We set it to display: none unless selected.
|
|
1713
|
-
// User can override this behavior via part styling if they want transitions
|
|
1714
|
-
if (!selected) {
|
|
1715
|
-
checkmark.style.display = 'none';
|
|
1716
|
-
}
|
|
1717
|
-
else {
|
|
1718
|
-
checkmark.style.marginLeft = '8px';
|
|
1719
|
-
checkmark.style.color = 'currentColor';
|
|
1720
|
-
}
|
|
1721
1724
|
this._container.appendChild(checkmark);
|
|
1722
1725
|
}
|
|
1723
1726
|
// Data Attributes Contract on Host
|
|
@@ -1875,7 +1878,22 @@ class EnhancedSelect extends HTMLElement {
|
|
|
1875
1878
|
}
|
|
1876
1879
|
set classMap(map) {
|
|
1877
1880
|
this._classMap = map;
|
|
1878
|
-
this._setGlobalStylesMirroring(Boolean(this._optionRenderer || map));
|
|
1881
|
+
this._setGlobalStylesMirroring(Boolean(this._optionRenderer || map || this._groupHeaderRenderer));
|
|
1882
|
+
if (!this.isConnected)
|
|
1883
|
+
return;
|
|
1884
|
+
this._renderOptions();
|
|
1885
|
+
}
|
|
1886
|
+
/**
|
|
1887
|
+
* DOM-based renderer for group headers. When provided, the component will
|
|
1888
|
+
* call this function for each group during rendering. The returned element
|
|
1889
|
+
* will receive `.group-header` and `part="group-header"` automatically.
|
|
1890
|
+
*/
|
|
1891
|
+
get groupHeaderRenderer() {
|
|
1892
|
+
return this._groupHeaderRenderer;
|
|
1893
|
+
}
|
|
1894
|
+
set groupHeaderRenderer(renderer) {
|
|
1895
|
+
this._groupHeaderRenderer = renderer;
|
|
1896
|
+
this._setGlobalStylesMirroring(Boolean(this._optionRenderer || this._classMap || renderer));
|
|
1879
1897
|
if (!this.isConnected)
|
|
1880
1898
|
return;
|
|
1881
1899
|
this._renderOptions();
|
|
@@ -1934,11 +1952,12 @@ class EnhancedSelect extends HTMLElement {
|
|
|
1934
1952
|
this._initializeObservers();
|
|
1935
1953
|
}
|
|
1936
1954
|
connectedCallback() {
|
|
1955
|
+
// register instance
|
|
1956
|
+
EnhancedSelect._instances.add(this);
|
|
1937
1957
|
// WORKAROUND: Force display style on host element for Angular compatibility
|
|
1938
1958
|
// Angular's rendering seems to not apply :host styles correctly in some cases
|
|
1939
1959
|
// Must be done in connectedCallback when element is attached to DOM
|
|
1940
1960
|
this.style.display = 'block';
|
|
1941
|
-
this.style.width = '100%';
|
|
1942
1961
|
if (this._optionRenderer) {
|
|
1943
1962
|
this._setGlobalStylesMirroring(true);
|
|
1944
1963
|
}
|
|
@@ -1952,6 +1971,8 @@ class EnhancedSelect extends HTMLElement {
|
|
|
1952
1971
|
}
|
|
1953
1972
|
}
|
|
1954
1973
|
disconnectedCallback() {
|
|
1974
|
+
// unregister instance
|
|
1975
|
+
EnhancedSelect._instances.delete(this);
|
|
1955
1976
|
// Cleanup observers
|
|
1956
1977
|
this._resizeObserver?.disconnect();
|
|
1957
1978
|
this._intersectionObserver?.disconnect();
|
|
@@ -2179,7 +2200,8 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2179
2200
|
:host {
|
|
2180
2201
|
display: block;
|
|
2181
2202
|
position: relative;
|
|
2182
|
-
width: 100
|
|
2203
|
+
width: var(--select-width, 100%);
|
|
2204
|
+
height: var(--select-height, auto);
|
|
2183
2205
|
}
|
|
2184
2206
|
|
|
2185
2207
|
.select-container {
|
|
@@ -2195,6 +2217,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2195
2217
|
flex-wrap: wrap;
|
|
2196
2218
|
gap: var(--select-input-gap, 6px);
|
|
2197
2219
|
padding: var(--select-input-padding, 6px 52px 6px 8px);
|
|
2220
|
+
height: var(--select-input-height, auto);
|
|
2198
2221
|
min-height: var(--select-input-min-height, 44px);
|
|
2199
2222
|
max-height: var(--select-input-max-height, 160px);
|
|
2200
2223
|
overflow-y: var(--select-input-overflow-y, auto);
|
|
@@ -2216,17 +2239,17 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2216
2239
|
content: '';
|
|
2217
2240
|
position: absolute;
|
|
2218
2241
|
top: 50%;
|
|
2219
|
-
right: var(--select-separator-position, 40px);
|
|
2242
|
+
right: var(--select-separator-position, var(--select-seperator-position, 40px));
|
|
2220
2243
|
transform: translateY(-50%);
|
|
2221
|
-
width: var(--select-separator-width, 1px);
|
|
2222
|
-
height: var(--select-separator-height, 60%);
|
|
2223
|
-
background: var(--select-separator-bg, var(--select-separator-gradient, linear-gradient(
|
|
2244
|
+
width: var(--select-separator-width, var(--select-seperator-width, 1px));
|
|
2245
|
+
height: var(--select-separator-height, var(--select-seperator-height, 60%));
|
|
2246
|
+
background: var(--select-separator-bg, var(--select-seperator-bg, var(--select-separator-gradient, var(--select-seperator-gradient, linear-gradient(
|
|
2224
2247
|
to bottom,
|
|
2225
2248
|
transparent 0%,
|
|
2226
2249
|
rgba(0, 0, 0, 0.1) 20%,
|
|
2227
2250
|
rgba(0, 0, 0, 0.1) 80%,
|
|
2228
2251
|
transparent 100%
|
|
2229
|
-
)));
|
|
2252
|
+
))));
|
|
2230
2253
|
pointer-events: none;
|
|
2231
2254
|
z-index: 1;
|
|
2232
2255
|
}
|
|
@@ -2237,6 +2260,8 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2237
2260
|
right: 0;
|
|
2238
2261
|
bottom: 0;
|
|
2239
2262
|
width: var(--select-arrow-width, 40px);
|
|
2263
|
+
/* allow explicit height override even though container normally stretches */
|
|
2264
|
+
height: var(--select-arrow-height, auto);
|
|
2240
2265
|
display: flex;
|
|
2241
2266
|
align-items: center;
|
|
2242
2267
|
justify-content: center;
|
|
@@ -2251,7 +2276,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2251
2276
|
}
|
|
2252
2277
|
|
|
2253
2278
|
.input-container.has-clear-control::after {
|
|
2254
|
-
right: var(--select-separator-position-with-clear, 72px);
|
|
2279
|
+
right: var(--select-separator-position-with-clear, var(--select-seperator-position-with-clear, 72px));
|
|
2255
2280
|
}
|
|
2256
2281
|
|
|
2257
2282
|
.dropdown-arrow-container.with-clear-control {
|
|
@@ -2302,9 +2327,14 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2302
2327
|
background-color: var(--select-arrow-hover-bg, rgba(102, 126, 234, 0.08));
|
|
2303
2328
|
}
|
|
2304
2329
|
|
|
2330
|
+
.dropdown-arrow:hover {
|
|
2331
|
+
/* legacy alias --select-arrow-hover for icon color */
|
|
2332
|
+
color: var(--select-arrow-hover, var(--select-arrow-hover-color, #667eea));
|
|
2333
|
+
}
|
|
2334
|
+
|
|
2305
2335
|
.dropdown-arrow {
|
|
2306
|
-
width: var(--select-arrow-size, 16px);
|
|
2307
|
-
height: var(--select-arrow-size, 16px);
|
|
2336
|
+
width: var(--select-arrow-width, var(--select-arrow-size, 16px));
|
|
2337
|
+
height: var(--select-arrow-height, var(--select-arrow-size, 16px));
|
|
2308
2338
|
color: var(--select-arrow-color, #667eea);
|
|
2309
2339
|
transition: transform 0.2s ease, color 0.2s ease;
|
|
2310
2340
|
transform: translateY(0);
|
|
@@ -2324,6 +2354,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2324
2354
|
|
|
2325
2355
|
.select-input {
|
|
2326
2356
|
flex: 1;
|
|
2357
|
+
width: var(--select-input-width, auto);
|
|
2327
2358
|
min-width: var(--select-input-min-width, 120px);
|
|
2328
2359
|
padding: var(--select-input-field-padding, 4px);
|
|
2329
2360
|
border: none;
|
|
@@ -2418,6 +2449,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2418
2449
|
font-weight: var(--select-group-header-weight, 600);
|
|
2419
2450
|
color: var(--select-group-header-color, #6b7280);
|
|
2420
2451
|
background-color: var(--select-group-header-bg, #f3f4f6);
|
|
2452
|
+
text-align: var(--select-group-header-text-align, left);
|
|
2421
2453
|
font-size: var(--select-group-header-font-size, 12px);
|
|
2422
2454
|
text-transform: var(--select-group-header-text-transform, uppercase);
|
|
2423
2455
|
letter-spacing: var(--select-group-header-letter-spacing, 0.05em);
|
|
@@ -2602,10 +2634,25 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2602
2634
|
|
|
2603
2635
|
/* Dark mode - Opt-in via class, data attribute, or ancestor context */
|
|
2604
2636
|
:host(.dark-mode),
|
|
2637
|
+
:host([dark-mode]),
|
|
2638
|
+
:host([darkmode]),
|
|
2605
2639
|
:host([data-theme="dark"]),
|
|
2640
|
+
:host([theme="dark"]),
|
|
2606
2641
|
:host-context(.dark-mode),
|
|
2607
2642
|
:host-context(.dark),
|
|
2608
|
-
:host-context([
|
|
2643
|
+
:host-context([dark-mode]),
|
|
2644
|
+
:host-context([darkmode]),
|
|
2645
|
+
:host-context([data-theme="dark"]),
|
|
2646
|
+
:host-context([theme="dark"]) {
|
|
2647
|
+
/* map dark tokens to base option tokens so nested <select-option>
|
|
2648
|
+
components also pick up dark mode via inherited CSS variables */
|
|
2649
|
+
--select-option-bg: var(--select-dark-option-bg, #1f2937);
|
|
2650
|
+
--select-option-color: var(--select-dark-option-color, #f9fafb);
|
|
2651
|
+
--select-option-hover-bg: var(--select-dark-option-hover-bg, #374151);
|
|
2652
|
+
--select-option-hover-color: var(--select-dark-option-hover-color, #f9fafb);
|
|
2653
|
+
--select-option-selected-bg: var(--select-dark-option-selected-bg, #3730a3);
|
|
2654
|
+
--select-option-selected-color: var(--select-dark-option-selected-text, #e0e7ff);
|
|
2655
|
+
|
|
2609
2656
|
.input-container {
|
|
2610
2657
|
background: var(--select-dark-bg, #1f2937);
|
|
2611
2658
|
border-color: var(--select-dark-border, #4b5563);
|
|
@@ -2662,6 +2709,12 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2662
2709
|
outline: var(--select-dark-option-active-outline, 2px solid rgba(129, 140, 248, 0.55));
|
|
2663
2710
|
}
|
|
2664
2711
|
|
|
2712
|
+
/* Group header in dark mode */
|
|
2713
|
+
.group-header {
|
|
2714
|
+
color: var(--select-dark-group-header-color, var(--select-group-header-color, #6b7280));
|
|
2715
|
+
background-color: var(--select-dark-group-header-bg, var(--select-group-header-bg, #374151));
|
|
2716
|
+
}
|
|
2717
|
+
|
|
2665
2718
|
.option.selected.active {
|
|
2666
2719
|
background-color: var(--select-dark-option-selected-active-bg, var(--select-dark-option-selected-bg, #3730a3));
|
|
2667
2720
|
color: var(--select-dark-option-selected-active-color, var(--select-dark-option-selected-text, #e0e7ff));
|
|
@@ -2740,19 +2793,14 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2740
2793
|
this._boundArrowClick = (e) => {
|
|
2741
2794
|
e.stopPropagation();
|
|
2742
2795
|
e.preventDefault();
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
this.
|
|
2747
|
-
|
|
2748
|
-
this._config.callbacks.onOpen();
|
|
2749
|
-
}
|
|
2750
|
-
else if (!this._state.isOpen && this._config.callbacks.onClose) {
|
|
2751
|
-
this._config.callbacks.onClose();
|
|
2796
|
+
// delegate to the existing open/close helpers so we don't accidentally
|
|
2797
|
+
// drift out of sync with the logic in those methods (focus, events,
|
|
2798
|
+
// scroll-to-selected, etc.)
|
|
2799
|
+
if (this._state.isOpen) {
|
|
2800
|
+
this._handleClose();
|
|
2752
2801
|
}
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
setTimeout(() => this._scrollToSelected(), 50);
|
|
2802
|
+
else {
|
|
2803
|
+
this._handleOpen();
|
|
2756
2804
|
}
|
|
2757
2805
|
};
|
|
2758
2806
|
this._arrowContainer.addEventListener('click', this._boundArrowClick);
|
|
@@ -2786,6 +2834,16 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2786
2834
|
if (wasClosed) {
|
|
2787
2835
|
this._handleOpen();
|
|
2788
2836
|
}
|
|
2837
|
+
else {
|
|
2838
|
+
// Keep open while interacting directly with the input so users can
|
|
2839
|
+
// place cursor/type without accidental collapse.
|
|
2840
|
+
if (target === this._input) {
|
|
2841
|
+
this._input.focus();
|
|
2842
|
+
return;
|
|
2843
|
+
}
|
|
2844
|
+
// clicking other parts of the input container while open toggles close
|
|
2845
|
+
this._handleClose();
|
|
2846
|
+
}
|
|
2789
2847
|
// Focus the input (do not prevent default behavior)
|
|
2790
2848
|
this._input.focus();
|
|
2791
2849
|
// If we just opened the dropdown, transfer pointer capture to the
|
|
@@ -2961,6 +3019,16 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2961
3019
|
_handleOpen() {
|
|
2962
3020
|
if (!this._config.enabled || this._state.isOpen)
|
|
2963
3021
|
return;
|
|
3022
|
+
// close any other open selects before proceeding
|
|
3023
|
+
EnhancedSelect._instances.forEach(inst => {
|
|
3024
|
+
if (inst !== this)
|
|
3025
|
+
inst._handleClose();
|
|
3026
|
+
});
|
|
3027
|
+
// Always focus the input when opening so callers (arrow click,
|
|
3028
|
+
// programmatic `open()`, etc.) get the keyboard cursor. This was a
|
|
3029
|
+
// frequent source of confusion in #14 where people opened the dropdown
|
|
3030
|
+
// but the text field never received focus.
|
|
3031
|
+
this._input.focus();
|
|
2964
3032
|
this._markOpenStart();
|
|
2965
3033
|
this._state.isOpen = true;
|
|
2966
3034
|
this._dropdown.style.display = 'block';
|
|
@@ -3934,10 +4002,25 @@ class EnhancedSelect extends HTMLElement {
|
|
|
3934
4002
|
const query = this._state.searchQuery.toLowerCase();
|
|
3935
4003
|
// Handle Grouped Items Rendering (when no search query)
|
|
3936
4004
|
if (this._state.groupedItems.length > 0 && !query) {
|
|
3937
|
-
this._state.groupedItems.forEach(group => {
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
4005
|
+
this._state.groupedItems.forEach((group, groupIndex) => {
|
|
4006
|
+
let header;
|
|
4007
|
+
if (this.groupHeaderRenderer) {
|
|
4008
|
+
header = this.groupHeaderRenderer(group, groupIndex);
|
|
4009
|
+
// make sure the returned element has the correct semantics so
|
|
4010
|
+
// people can style it. we add the class/part even if the renderer
|
|
4011
|
+
// returned something else to ensure backward compatibility.
|
|
4012
|
+
if (!(header instanceof HTMLElement)) {
|
|
4013
|
+
// fall back to default if API is misused
|
|
4014
|
+
header = document.createElement('div');
|
|
4015
|
+
header.textContent = String(group.label);
|
|
4016
|
+
}
|
|
4017
|
+
}
|
|
4018
|
+
else {
|
|
4019
|
+
header = document.createElement('div');
|
|
4020
|
+
header.textContent = group.label;
|
|
4021
|
+
}
|
|
4022
|
+
header.classList.add('group-header');
|
|
4023
|
+
header.setAttribute('part', 'group-header');
|
|
3941
4024
|
this._optionsContainer.appendChild(header);
|
|
3942
4025
|
group.options.forEach(item => {
|
|
3943
4026
|
// Find original index for correct ID generation and selection
|
|
@@ -4210,6 +4293,8 @@ class EnhancedSelect extends HTMLElement {
|
|
|
4210
4293
|
}
|
|
4211
4294
|
}
|
|
4212
4295
|
}
|
|
4296
|
+
/** live set of all connected instances; used to auto-close siblings */
|
|
4297
|
+
EnhancedSelect._instances = new Set();
|
|
4213
4298
|
// Register custom element
|
|
4214
4299
|
if (!customElements.get('enhanced-select')) {
|
|
4215
4300
|
customElements.define('enhanced-select', EnhancedSelect);
|