@smilodon/core 1.4.9 → 1.4.10
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 +28 -0
- package/dist/index.cjs +78 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +78 -19
- 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 +78 -19
- 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
|
@@ -646,6 +646,28 @@ enhanced-select.dark-mode {
|
|
|
646
646
|
--select-dropdown-bg /* Dropdown background (white) */
|
|
647
647
|
--select-dropdown-border /* Dropdown border color (#ccc) */
|
|
648
648
|
--select-dropdown-shadow /* Dropdown shadow */
|
|
649
|
+
--select-empty-padding /* Empty/no-results container padding */
|
|
650
|
+
--select-empty-color /* Text color for empty/no-results models */
|
|
651
|
+
--select-empty-font-size /* Font size */
|
|
652
|
+
--select-empty-bg /* Background for empty/no-results state */
|
|
653
|
+
--select-empty-min-height /* Minimum height of empty state box */
|
|
654
|
+
|
|
655
|
+
/* Arrow/button */
|
|
656
|
+
--select-arrow-size /* Width & height of SVG icon (16px default) */
|
|
657
|
+
--select-arrow-color /* Icon color (#667eea) */
|
|
658
|
+
--select-arrow-hover-color /* Icon color when hovered (#667eea) */
|
|
659
|
+
--select-arrow-hover-bg /* Background when hover (rgba(102,126,234,0.08)) */
|
|
660
|
+
--select-arrow-width /* Container width (40px) */
|
|
661
|
+
--select-arrow-border-radius /* Container border radius */
|
|
662
|
+
|
|
663
|
+
/* Group headers (when using groupedItems or flat items with `group` property) */
|
|
664
|
+
--select-group-header-padding /* Padding inside header (8px 12px) */
|
|
665
|
+
--select-group-header-color /* Text color (#6b7280) */
|
|
666
|
+
--select-group-header-bg /* Background (#f3f4f6) */
|
|
667
|
+
--select-group-header-font-size
|
|
668
|
+
--select-group-header-text-transform
|
|
669
|
+
--select-group-header-letter-spacing
|
|
670
|
+
--select-group-header-border-bottom
|
|
649
671
|
```
|
|
650
672
|
|
|
651
673
|
**Dark Mode (Opt-in)**
|
|
@@ -659,6 +681,8 @@ enhanced-select.dark-mode {
|
|
|
659
681
|
--select-dark-option-bg /* Dark option background (#1f2937) */
|
|
660
682
|
--select-dark-option-hover-bg /* Dark hover background (#374151) */
|
|
661
683
|
--select-dark-option-selected-bg /* Dark selected bg (#3730a3) */
|
|
684
|
+
--select-dark-group-header-color /* Dark header text */
|
|
685
|
+
--select-dark-group-header-bg /* Dark header background */
|
|
662
686
|
```
|
|
663
687
|
|
|
664
688
|
**Complete CSS Variables List (60+ variables)**
|
|
@@ -698,6 +722,10 @@ enhanced-select {
|
|
|
698
722
|
**Badge Remove/Delete Button (Multi-Select)**
|
|
699
723
|
The × button that removes selected items in multi-select mode is fully customizable:
|
|
700
724
|
|
|
725
|
+
**Group Header & No‑Results Parts**
|
|
726
|
+
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.
|
|
727
|
+
|
|
728
|
+
|
|
701
729
|
```css
|
|
702
730
|
enhanced-select {
|
|
703
731
|
/* Customize badge appearance */
|
package/dist/index.cjs
CHANGED
|
@@ -1875,7 +1875,22 @@ class EnhancedSelect extends HTMLElement {
|
|
|
1875
1875
|
}
|
|
1876
1876
|
set classMap(map) {
|
|
1877
1877
|
this._classMap = map;
|
|
1878
|
-
this._setGlobalStylesMirroring(Boolean(this._optionRenderer || map));
|
|
1878
|
+
this._setGlobalStylesMirroring(Boolean(this._optionRenderer || map || this._groupHeaderRenderer));
|
|
1879
|
+
if (!this.isConnected)
|
|
1880
|
+
return;
|
|
1881
|
+
this._renderOptions();
|
|
1882
|
+
}
|
|
1883
|
+
/**
|
|
1884
|
+
* DOM-based renderer for group headers. When provided, the component will
|
|
1885
|
+
* call this function for each group during rendering. The returned element
|
|
1886
|
+
* will receive `.group-header` and `part="group-header"` automatically.
|
|
1887
|
+
*/
|
|
1888
|
+
get groupHeaderRenderer() {
|
|
1889
|
+
return this._groupHeaderRenderer;
|
|
1890
|
+
}
|
|
1891
|
+
set groupHeaderRenderer(renderer) {
|
|
1892
|
+
this._groupHeaderRenderer = renderer;
|
|
1893
|
+
this._setGlobalStylesMirroring(Boolean(this._optionRenderer || this._classMap || renderer));
|
|
1879
1894
|
if (!this.isConnected)
|
|
1880
1895
|
return;
|
|
1881
1896
|
this._renderOptions();
|
|
@@ -1934,6 +1949,8 @@ class EnhancedSelect extends HTMLElement {
|
|
|
1934
1949
|
this._initializeObservers();
|
|
1935
1950
|
}
|
|
1936
1951
|
connectedCallback() {
|
|
1952
|
+
// register instance
|
|
1953
|
+
EnhancedSelect._instances.add(this);
|
|
1937
1954
|
// WORKAROUND: Force display style on host element for Angular compatibility
|
|
1938
1955
|
// Angular's rendering seems to not apply :host styles correctly in some cases
|
|
1939
1956
|
// Must be done in connectedCallback when element is attached to DOM
|
|
@@ -1952,6 +1969,8 @@ class EnhancedSelect extends HTMLElement {
|
|
|
1952
1969
|
}
|
|
1953
1970
|
}
|
|
1954
1971
|
disconnectedCallback() {
|
|
1972
|
+
// unregister instance
|
|
1973
|
+
EnhancedSelect._instances.delete(this);
|
|
1955
1974
|
// Cleanup observers
|
|
1956
1975
|
this._resizeObserver?.disconnect();
|
|
1957
1976
|
this._intersectionObserver?.disconnect();
|
|
@@ -2237,6 +2256,8 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2237
2256
|
right: 0;
|
|
2238
2257
|
bottom: 0;
|
|
2239
2258
|
width: var(--select-arrow-width, 40px);
|
|
2259
|
+
/* allow explicit height override even though container normally stretches */
|
|
2260
|
+
height: var(--select-arrow-height, auto);
|
|
2240
2261
|
display: flex;
|
|
2241
2262
|
align-items: center;
|
|
2242
2263
|
justify-content: center;
|
|
@@ -2302,9 +2323,14 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2302
2323
|
background-color: var(--select-arrow-hover-bg, rgba(102, 126, 234, 0.08));
|
|
2303
2324
|
}
|
|
2304
2325
|
|
|
2326
|
+
.dropdown-arrow:hover {
|
|
2327
|
+
/* legacy alias --select-arrow-hover for icon color */
|
|
2328
|
+
color: var(--select-arrow-hover, var(--select-arrow-hover-color, #667eea));
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2305
2331
|
.dropdown-arrow {
|
|
2306
|
-
width: var(--select-arrow-size, 16px);
|
|
2307
|
-
height: var(--select-arrow-size, 16px);
|
|
2332
|
+
width: var(--select-arrow-width, var(--select-arrow-size, 16px));
|
|
2333
|
+
height: var(--select-arrow-height, var(--select-arrow-size, 16px));
|
|
2308
2334
|
color: var(--select-arrow-color, #667eea);
|
|
2309
2335
|
transition: transform 0.2s ease, color 0.2s ease;
|
|
2310
2336
|
transform: translateY(0);
|
|
@@ -2658,6 +2684,13 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2658
2684
|
|
|
2659
2685
|
.option.active:not(.selected) {
|
|
2660
2686
|
background-color: var(--select-dark-option-active-bg, #374151);
|
|
2687
|
+
}
|
|
2688
|
+
|
|
2689
|
+
/* Group header in dark mode */
|
|
2690
|
+
.group-header {
|
|
2691
|
+
color: var(--select-dark-group-header-color, var(--select-group-header-color, #6b7280));
|
|
2692
|
+
background-color: var(--select-dark-group-header-bg, var(--select-group-header-bg, #374151));
|
|
2693
|
+
}
|
|
2661
2694
|
color: var(--select-dark-option-active-color, #f9fafb);
|
|
2662
2695
|
outline: var(--select-dark-option-active-outline, 2px solid rgba(129, 140, 248, 0.55));
|
|
2663
2696
|
}
|
|
@@ -2740,19 +2773,14 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2740
2773
|
this._boundArrowClick = (e) => {
|
|
2741
2774
|
e.stopPropagation();
|
|
2742
2775
|
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();
|
|
2776
|
+
// delegate to the existing open/close helpers so we don't accidentally
|
|
2777
|
+
// drift out of sync with the logic in those methods (focus, events,
|
|
2778
|
+
// scroll-to-selected, etc.)
|
|
2779
|
+
if (this._state.isOpen) {
|
|
2780
|
+
this._handleClose();
|
|
2752
2781
|
}
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
setTimeout(() => this._scrollToSelected(), 50);
|
|
2782
|
+
else {
|
|
2783
|
+
this._handleOpen();
|
|
2756
2784
|
}
|
|
2757
2785
|
};
|
|
2758
2786
|
this._arrowContainer.addEventListener('click', this._boundArrowClick);
|
|
@@ -2786,6 +2814,10 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2786
2814
|
if (wasClosed) {
|
|
2787
2815
|
this._handleOpen();
|
|
2788
2816
|
}
|
|
2817
|
+
else {
|
|
2818
|
+
// clicking the input while open should close the dropdown too
|
|
2819
|
+
this._handleClose();
|
|
2820
|
+
}
|
|
2789
2821
|
// Focus the input (do not prevent default behavior)
|
|
2790
2822
|
this._input.focus();
|
|
2791
2823
|
// If we just opened the dropdown, transfer pointer capture to the
|
|
@@ -2961,6 +2993,16 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2961
2993
|
_handleOpen() {
|
|
2962
2994
|
if (!this._config.enabled || this._state.isOpen)
|
|
2963
2995
|
return;
|
|
2996
|
+
// close any other open selects before proceeding
|
|
2997
|
+
EnhancedSelect._instances.forEach(inst => {
|
|
2998
|
+
if (inst !== this)
|
|
2999
|
+
inst._handleClose();
|
|
3000
|
+
});
|
|
3001
|
+
// Always focus the input when opening so callers (arrow click,
|
|
3002
|
+
// programmatic `open()`, etc.) get the keyboard cursor. This was a
|
|
3003
|
+
// frequent source of confusion in #14 where people opened the dropdown
|
|
3004
|
+
// but the text field never received focus.
|
|
3005
|
+
this._input.focus();
|
|
2964
3006
|
this._markOpenStart();
|
|
2965
3007
|
this._state.isOpen = true;
|
|
2966
3008
|
this._dropdown.style.display = 'block';
|
|
@@ -3934,10 +3976,25 @@ class EnhancedSelect extends HTMLElement {
|
|
|
3934
3976
|
const query = this._state.searchQuery.toLowerCase();
|
|
3935
3977
|
// Handle Grouped Items Rendering (when no search query)
|
|
3936
3978
|
if (this._state.groupedItems.length > 0 && !query) {
|
|
3937
|
-
this._state.groupedItems.forEach(group => {
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3979
|
+
this._state.groupedItems.forEach((group, groupIndex) => {
|
|
3980
|
+
let header;
|
|
3981
|
+
if (this.groupHeaderRenderer) {
|
|
3982
|
+
header = this.groupHeaderRenderer(group, groupIndex);
|
|
3983
|
+
// make sure the returned element has the correct semantics so
|
|
3984
|
+
// people can style it. we add the class/part even if the renderer
|
|
3985
|
+
// returned something else to ensure backward compatibility.
|
|
3986
|
+
if (!(header instanceof HTMLElement)) {
|
|
3987
|
+
// fall back to default if API is misused
|
|
3988
|
+
header = document.createElement('div');
|
|
3989
|
+
header.textContent = String(group.label);
|
|
3990
|
+
}
|
|
3991
|
+
}
|
|
3992
|
+
else {
|
|
3993
|
+
header = document.createElement('div');
|
|
3994
|
+
header.textContent = group.label;
|
|
3995
|
+
}
|
|
3996
|
+
header.classList.add('group-header');
|
|
3997
|
+
header.setAttribute('part', 'group-header');
|
|
3941
3998
|
this._optionsContainer.appendChild(header);
|
|
3942
3999
|
group.options.forEach(item => {
|
|
3943
4000
|
// Find original index for correct ID generation and selection
|
|
@@ -4210,6 +4267,8 @@ class EnhancedSelect extends HTMLElement {
|
|
|
4210
4267
|
}
|
|
4211
4268
|
}
|
|
4212
4269
|
}
|
|
4270
|
+
/** live set of all connected instances; used to auto-close siblings */
|
|
4271
|
+
EnhancedSelect._instances = new Set();
|
|
4213
4272
|
// Register custom element
|
|
4214
4273
|
if (!customElements.get('enhanced-select')) {
|
|
4215
4274
|
customElements.define('enhanced-select', EnhancedSelect);
|