@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/dist/index.umd.js
CHANGED
|
@@ -1538,6 +1538,8 @@
|
|
|
1538
1538
|
padding: var(--select-option-padding, 8px 12px);
|
|
1539
1539
|
cursor: pointer;
|
|
1540
1540
|
user-select: none;
|
|
1541
|
+
color: var(--select-option-color, var(--select-text-color, #1f2937));
|
|
1542
|
+
background: var(--select-option-bg, var(--select-dropdown-bg, var(--select-bg, white)));
|
|
1541
1543
|
transition: var(--select-option-transition, background-color 0.2s ease);
|
|
1542
1544
|
border: var(--select-option-border, none);
|
|
1543
1545
|
border-bottom: var(--select-option-border-bottom, none);
|
|
@@ -1582,9 +1584,20 @@
|
|
|
1582
1584
|
|
|
1583
1585
|
.option-content {
|
|
1584
1586
|
flex: 1;
|
|
1585
|
-
overflow: hidden;
|
|
1586
|
-
text-overflow: ellipsis;
|
|
1587
|
-
white-space: nowrap;
|
|
1587
|
+
overflow: var(--select-option-content-overflow, hidden);
|
|
1588
|
+
text-overflow: var(--select-option-content-text-overflow, ellipsis);
|
|
1589
|
+
white-space: var(--select-option-content-white-space, nowrap);
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
.checkmark-icon {
|
|
1593
|
+
display: none;
|
|
1594
|
+
margin-left: var(--select-checkmark-margin-left, 8px);
|
|
1595
|
+
color: var(--select-checkmark-color, currentColor);
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
:host([aria-selected="true"]) .checkmark-icon,
|
|
1599
|
+
.option-container.selected .checkmark-icon {
|
|
1600
|
+
display: inline-flex;
|
|
1588
1601
|
}
|
|
1589
1602
|
|
|
1590
1603
|
.remove-button {
|
|
@@ -1712,16 +1725,6 @@
|
|
|
1712
1725
|
<path d="M4 8.5L6.5 11L12 5.5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
1713
1726
|
</svg>
|
|
1714
1727
|
`;
|
|
1715
|
-
// Visibility control via CSS or inline style
|
|
1716
|
-
// We set it to display: none unless selected.
|
|
1717
|
-
// User can override this behavior via part styling if they want transitions
|
|
1718
|
-
if (!selected) {
|
|
1719
|
-
checkmark.style.display = 'none';
|
|
1720
|
-
}
|
|
1721
|
-
else {
|
|
1722
|
-
checkmark.style.marginLeft = '8px';
|
|
1723
|
-
checkmark.style.color = 'currentColor';
|
|
1724
|
-
}
|
|
1725
1728
|
this._container.appendChild(checkmark);
|
|
1726
1729
|
}
|
|
1727
1730
|
// Data Attributes Contract on Host
|
|
@@ -1879,7 +1882,22 @@
|
|
|
1879
1882
|
}
|
|
1880
1883
|
set classMap(map) {
|
|
1881
1884
|
this._classMap = map;
|
|
1882
|
-
this._setGlobalStylesMirroring(Boolean(this._optionRenderer || map));
|
|
1885
|
+
this._setGlobalStylesMirroring(Boolean(this._optionRenderer || map || this._groupHeaderRenderer));
|
|
1886
|
+
if (!this.isConnected)
|
|
1887
|
+
return;
|
|
1888
|
+
this._renderOptions();
|
|
1889
|
+
}
|
|
1890
|
+
/**
|
|
1891
|
+
* DOM-based renderer for group headers. When provided, the component will
|
|
1892
|
+
* call this function for each group during rendering. The returned element
|
|
1893
|
+
* will receive `.group-header` and `part="group-header"` automatically.
|
|
1894
|
+
*/
|
|
1895
|
+
get groupHeaderRenderer() {
|
|
1896
|
+
return this._groupHeaderRenderer;
|
|
1897
|
+
}
|
|
1898
|
+
set groupHeaderRenderer(renderer) {
|
|
1899
|
+
this._groupHeaderRenderer = renderer;
|
|
1900
|
+
this._setGlobalStylesMirroring(Boolean(this._optionRenderer || this._classMap || renderer));
|
|
1883
1901
|
if (!this.isConnected)
|
|
1884
1902
|
return;
|
|
1885
1903
|
this._renderOptions();
|
|
@@ -1938,11 +1956,12 @@
|
|
|
1938
1956
|
this._initializeObservers();
|
|
1939
1957
|
}
|
|
1940
1958
|
connectedCallback() {
|
|
1959
|
+
// register instance
|
|
1960
|
+
EnhancedSelect._instances.add(this);
|
|
1941
1961
|
// WORKAROUND: Force display style on host element for Angular compatibility
|
|
1942
1962
|
// Angular's rendering seems to not apply :host styles correctly in some cases
|
|
1943
1963
|
// Must be done in connectedCallback when element is attached to DOM
|
|
1944
1964
|
this.style.display = 'block';
|
|
1945
|
-
this.style.width = '100%';
|
|
1946
1965
|
if (this._optionRenderer) {
|
|
1947
1966
|
this._setGlobalStylesMirroring(true);
|
|
1948
1967
|
}
|
|
@@ -1956,6 +1975,8 @@
|
|
|
1956
1975
|
}
|
|
1957
1976
|
}
|
|
1958
1977
|
disconnectedCallback() {
|
|
1978
|
+
// unregister instance
|
|
1979
|
+
EnhancedSelect._instances.delete(this);
|
|
1959
1980
|
// Cleanup observers
|
|
1960
1981
|
this._resizeObserver?.disconnect();
|
|
1961
1982
|
this._intersectionObserver?.disconnect();
|
|
@@ -2183,7 +2204,8 @@
|
|
|
2183
2204
|
:host {
|
|
2184
2205
|
display: block;
|
|
2185
2206
|
position: relative;
|
|
2186
|
-
width: 100
|
|
2207
|
+
width: var(--select-width, 100%);
|
|
2208
|
+
height: var(--select-height, auto);
|
|
2187
2209
|
}
|
|
2188
2210
|
|
|
2189
2211
|
.select-container {
|
|
@@ -2199,6 +2221,7 @@
|
|
|
2199
2221
|
flex-wrap: wrap;
|
|
2200
2222
|
gap: var(--select-input-gap, 6px);
|
|
2201
2223
|
padding: var(--select-input-padding, 6px 52px 6px 8px);
|
|
2224
|
+
height: var(--select-input-height, auto);
|
|
2202
2225
|
min-height: var(--select-input-min-height, 44px);
|
|
2203
2226
|
max-height: var(--select-input-max-height, 160px);
|
|
2204
2227
|
overflow-y: var(--select-input-overflow-y, auto);
|
|
@@ -2220,17 +2243,17 @@
|
|
|
2220
2243
|
content: '';
|
|
2221
2244
|
position: absolute;
|
|
2222
2245
|
top: 50%;
|
|
2223
|
-
right: var(--select-separator-position, 40px);
|
|
2246
|
+
right: var(--select-separator-position, var(--select-seperator-position, 40px));
|
|
2224
2247
|
transform: translateY(-50%);
|
|
2225
|
-
width: var(--select-separator-width, 1px);
|
|
2226
|
-
height: var(--select-separator-height, 60%);
|
|
2227
|
-
background: var(--select-separator-bg, var(--select-separator-gradient, linear-gradient(
|
|
2248
|
+
width: var(--select-separator-width, var(--select-seperator-width, 1px));
|
|
2249
|
+
height: var(--select-separator-height, var(--select-seperator-height, 60%));
|
|
2250
|
+
background: var(--select-separator-bg, var(--select-seperator-bg, var(--select-separator-gradient, var(--select-seperator-gradient, linear-gradient(
|
|
2228
2251
|
to bottom,
|
|
2229
2252
|
transparent 0%,
|
|
2230
2253
|
rgba(0, 0, 0, 0.1) 20%,
|
|
2231
2254
|
rgba(0, 0, 0, 0.1) 80%,
|
|
2232
2255
|
transparent 100%
|
|
2233
|
-
)));
|
|
2256
|
+
))));
|
|
2234
2257
|
pointer-events: none;
|
|
2235
2258
|
z-index: 1;
|
|
2236
2259
|
}
|
|
@@ -2241,6 +2264,8 @@
|
|
|
2241
2264
|
right: 0;
|
|
2242
2265
|
bottom: 0;
|
|
2243
2266
|
width: var(--select-arrow-width, 40px);
|
|
2267
|
+
/* allow explicit height override even though container normally stretches */
|
|
2268
|
+
height: var(--select-arrow-height, auto);
|
|
2244
2269
|
display: flex;
|
|
2245
2270
|
align-items: center;
|
|
2246
2271
|
justify-content: center;
|
|
@@ -2255,7 +2280,7 @@
|
|
|
2255
2280
|
}
|
|
2256
2281
|
|
|
2257
2282
|
.input-container.has-clear-control::after {
|
|
2258
|
-
right: var(--select-separator-position-with-clear, 72px);
|
|
2283
|
+
right: var(--select-separator-position-with-clear, var(--select-seperator-position-with-clear, 72px));
|
|
2259
2284
|
}
|
|
2260
2285
|
|
|
2261
2286
|
.dropdown-arrow-container.with-clear-control {
|
|
@@ -2306,9 +2331,14 @@
|
|
|
2306
2331
|
background-color: var(--select-arrow-hover-bg, rgba(102, 126, 234, 0.08));
|
|
2307
2332
|
}
|
|
2308
2333
|
|
|
2334
|
+
.dropdown-arrow:hover {
|
|
2335
|
+
/* legacy alias --select-arrow-hover for icon color */
|
|
2336
|
+
color: var(--select-arrow-hover, var(--select-arrow-hover-color, #667eea));
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2309
2339
|
.dropdown-arrow {
|
|
2310
|
-
width: var(--select-arrow-size, 16px);
|
|
2311
|
-
height: var(--select-arrow-size, 16px);
|
|
2340
|
+
width: var(--select-arrow-width, var(--select-arrow-size, 16px));
|
|
2341
|
+
height: var(--select-arrow-height, var(--select-arrow-size, 16px));
|
|
2312
2342
|
color: var(--select-arrow-color, #667eea);
|
|
2313
2343
|
transition: transform 0.2s ease, color 0.2s ease;
|
|
2314
2344
|
transform: translateY(0);
|
|
@@ -2328,6 +2358,7 @@
|
|
|
2328
2358
|
|
|
2329
2359
|
.select-input {
|
|
2330
2360
|
flex: 1;
|
|
2361
|
+
width: var(--select-input-width, auto);
|
|
2331
2362
|
min-width: var(--select-input-min-width, 120px);
|
|
2332
2363
|
padding: var(--select-input-field-padding, 4px);
|
|
2333
2364
|
border: none;
|
|
@@ -2422,6 +2453,7 @@
|
|
|
2422
2453
|
font-weight: var(--select-group-header-weight, 600);
|
|
2423
2454
|
color: var(--select-group-header-color, #6b7280);
|
|
2424
2455
|
background-color: var(--select-group-header-bg, #f3f4f6);
|
|
2456
|
+
text-align: var(--select-group-header-text-align, left);
|
|
2425
2457
|
font-size: var(--select-group-header-font-size, 12px);
|
|
2426
2458
|
text-transform: var(--select-group-header-text-transform, uppercase);
|
|
2427
2459
|
letter-spacing: var(--select-group-header-letter-spacing, 0.05em);
|
|
@@ -2606,10 +2638,25 @@
|
|
|
2606
2638
|
|
|
2607
2639
|
/* Dark mode - Opt-in via class, data attribute, or ancestor context */
|
|
2608
2640
|
:host(.dark-mode),
|
|
2641
|
+
:host([dark-mode]),
|
|
2642
|
+
:host([darkmode]),
|
|
2609
2643
|
:host([data-theme="dark"]),
|
|
2644
|
+
:host([theme="dark"]),
|
|
2610
2645
|
:host-context(.dark-mode),
|
|
2611
2646
|
:host-context(.dark),
|
|
2612
|
-
:host-context([
|
|
2647
|
+
:host-context([dark-mode]),
|
|
2648
|
+
:host-context([darkmode]),
|
|
2649
|
+
:host-context([data-theme="dark"]),
|
|
2650
|
+
:host-context([theme="dark"]) {
|
|
2651
|
+
/* map dark tokens to base option tokens so nested <select-option>
|
|
2652
|
+
components also pick up dark mode via inherited CSS variables */
|
|
2653
|
+
--select-option-bg: var(--select-dark-option-bg, #1f2937);
|
|
2654
|
+
--select-option-color: var(--select-dark-option-color, #f9fafb);
|
|
2655
|
+
--select-option-hover-bg: var(--select-dark-option-hover-bg, #374151);
|
|
2656
|
+
--select-option-hover-color: var(--select-dark-option-hover-color, #f9fafb);
|
|
2657
|
+
--select-option-selected-bg: var(--select-dark-option-selected-bg, #3730a3);
|
|
2658
|
+
--select-option-selected-color: var(--select-dark-option-selected-text, #e0e7ff);
|
|
2659
|
+
|
|
2613
2660
|
.input-container {
|
|
2614
2661
|
background: var(--select-dark-bg, #1f2937);
|
|
2615
2662
|
border-color: var(--select-dark-border, #4b5563);
|
|
@@ -2666,6 +2713,12 @@
|
|
|
2666
2713
|
outline: var(--select-dark-option-active-outline, 2px solid rgba(129, 140, 248, 0.55));
|
|
2667
2714
|
}
|
|
2668
2715
|
|
|
2716
|
+
/* Group header in dark mode */
|
|
2717
|
+
.group-header {
|
|
2718
|
+
color: var(--select-dark-group-header-color, var(--select-group-header-color, #6b7280));
|
|
2719
|
+
background-color: var(--select-dark-group-header-bg, var(--select-group-header-bg, #374151));
|
|
2720
|
+
}
|
|
2721
|
+
|
|
2669
2722
|
.option.selected.active {
|
|
2670
2723
|
background-color: var(--select-dark-option-selected-active-bg, var(--select-dark-option-selected-bg, #3730a3));
|
|
2671
2724
|
color: var(--select-dark-option-selected-active-color, var(--select-dark-option-selected-text, #e0e7ff));
|
|
@@ -2744,19 +2797,14 @@
|
|
|
2744
2797
|
this._boundArrowClick = (e) => {
|
|
2745
2798
|
e.stopPropagation();
|
|
2746
2799
|
e.preventDefault();
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
this.
|
|
2751
|
-
|
|
2752
|
-
this._config.callbacks.onOpen();
|
|
2753
|
-
}
|
|
2754
|
-
else if (!this._state.isOpen && this._config.callbacks.onClose) {
|
|
2755
|
-
this._config.callbacks.onClose();
|
|
2800
|
+
// delegate to the existing open/close helpers so we don't accidentally
|
|
2801
|
+
// drift out of sync with the logic in those methods (focus, events,
|
|
2802
|
+
// scroll-to-selected, etc.)
|
|
2803
|
+
if (this._state.isOpen) {
|
|
2804
|
+
this._handleClose();
|
|
2756
2805
|
}
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
setTimeout(() => this._scrollToSelected(), 50);
|
|
2806
|
+
else {
|
|
2807
|
+
this._handleOpen();
|
|
2760
2808
|
}
|
|
2761
2809
|
};
|
|
2762
2810
|
this._arrowContainer.addEventListener('click', this._boundArrowClick);
|
|
@@ -2790,6 +2838,16 @@
|
|
|
2790
2838
|
if (wasClosed) {
|
|
2791
2839
|
this._handleOpen();
|
|
2792
2840
|
}
|
|
2841
|
+
else {
|
|
2842
|
+
// Keep open while interacting directly with the input so users can
|
|
2843
|
+
// place cursor/type without accidental collapse.
|
|
2844
|
+
if (target === this._input) {
|
|
2845
|
+
this._input.focus();
|
|
2846
|
+
return;
|
|
2847
|
+
}
|
|
2848
|
+
// clicking other parts of the input container while open toggles close
|
|
2849
|
+
this._handleClose();
|
|
2850
|
+
}
|
|
2793
2851
|
// Focus the input (do not prevent default behavior)
|
|
2794
2852
|
this._input.focus();
|
|
2795
2853
|
// If we just opened the dropdown, transfer pointer capture to the
|
|
@@ -2965,6 +3023,16 @@
|
|
|
2965
3023
|
_handleOpen() {
|
|
2966
3024
|
if (!this._config.enabled || this._state.isOpen)
|
|
2967
3025
|
return;
|
|
3026
|
+
// close any other open selects before proceeding
|
|
3027
|
+
EnhancedSelect._instances.forEach(inst => {
|
|
3028
|
+
if (inst !== this)
|
|
3029
|
+
inst._handleClose();
|
|
3030
|
+
});
|
|
3031
|
+
// Always focus the input when opening so callers (arrow click,
|
|
3032
|
+
// programmatic `open()`, etc.) get the keyboard cursor. This was a
|
|
3033
|
+
// frequent source of confusion in #14 where people opened the dropdown
|
|
3034
|
+
// but the text field never received focus.
|
|
3035
|
+
this._input.focus();
|
|
2968
3036
|
this._markOpenStart();
|
|
2969
3037
|
this._state.isOpen = true;
|
|
2970
3038
|
this._dropdown.style.display = 'block';
|
|
@@ -3938,10 +4006,25 @@
|
|
|
3938
4006
|
const query = this._state.searchQuery.toLowerCase();
|
|
3939
4007
|
// Handle Grouped Items Rendering (when no search query)
|
|
3940
4008
|
if (this._state.groupedItems.length > 0 && !query) {
|
|
3941
|
-
this._state.groupedItems.forEach(group => {
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
4009
|
+
this._state.groupedItems.forEach((group, groupIndex) => {
|
|
4010
|
+
let header;
|
|
4011
|
+
if (this.groupHeaderRenderer) {
|
|
4012
|
+
header = this.groupHeaderRenderer(group, groupIndex);
|
|
4013
|
+
// make sure the returned element has the correct semantics so
|
|
4014
|
+
// people can style it. we add the class/part even if the renderer
|
|
4015
|
+
// returned something else to ensure backward compatibility.
|
|
4016
|
+
if (!(header instanceof HTMLElement)) {
|
|
4017
|
+
// fall back to default if API is misused
|
|
4018
|
+
header = document.createElement('div');
|
|
4019
|
+
header.textContent = String(group.label);
|
|
4020
|
+
}
|
|
4021
|
+
}
|
|
4022
|
+
else {
|
|
4023
|
+
header = document.createElement('div');
|
|
4024
|
+
header.textContent = group.label;
|
|
4025
|
+
}
|
|
4026
|
+
header.classList.add('group-header');
|
|
4027
|
+
header.setAttribute('part', 'group-header');
|
|
3945
4028
|
this._optionsContainer.appendChild(header);
|
|
3946
4029
|
group.options.forEach(item => {
|
|
3947
4030
|
// Find original index for correct ID generation and selection
|
|
@@ -4214,6 +4297,8 @@
|
|
|
4214
4297
|
}
|
|
4215
4298
|
}
|
|
4216
4299
|
}
|
|
4300
|
+
/** live set of all connected instances; used to auto-close siblings */
|
|
4301
|
+
EnhancedSelect._instances = new Set();
|
|
4217
4302
|
// Register custom element
|
|
4218
4303
|
if (!customElements.get('enhanced-select')) {
|
|
4219
4304
|
customElements.define('enhanced-select', EnhancedSelect);
|