@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.js
CHANGED
|
@@ -1532,6 +1532,8 @@ class SelectOption extends HTMLElement {
|
|
|
1532
1532
|
padding: var(--select-option-padding, 8px 12px);
|
|
1533
1533
|
cursor: pointer;
|
|
1534
1534
|
user-select: none;
|
|
1535
|
+
color: var(--select-option-color, var(--select-text-color, #1f2937));
|
|
1536
|
+
background: var(--select-option-bg, var(--select-dropdown-bg, var(--select-bg, white)));
|
|
1535
1537
|
transition: var(--select-option-transition, background-color 0.2s ease);
|
|
1536
1538
|
border: var(--select-option-border, none);
|
|
1537
1539
|
border-bottom: var(--select-option-border-bottom, none);
|
|
@@ -1576,9 +1578,20 @@ class SelectOption extends HTMLElement {
|
|
|
1576
1578
|
|
|
1577
1579
|
.option-content {
|
|
1578
1580
|
flex: 1;
|
|
1579
|
-
overflow: hidden;
|
|
1580
|
-
text-overflow: ellipsis;
|
|
1581
|
-
white-space: nowrap;
|
|
1581
|
+
overflow: var(--select-option-content-overflow, hidden);
|
|
1582
|
+
text-overflow: var(--select-option-content-text-overflow, ellipsis);
|
|
1583
|
+
white-space: var(--select-option-content-white-space, nowrap);
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
.checkmark-icon {
|
|
1587
|
+
display: none;
|
|
1588
|
+
margin-left: var(--select-checkmark-margin-left, 8px);
|
|
1589
|
+
color: var(--select-checkmark-color, currentColor);
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
:host([aria-selected="true"]) .checkmark-icon,
|
|
1593
|
+
.option-container.selected .checkmark-icon {
|
|
1594
|
+
display: inline-flex;
|
|
1582
1595
|
}
|
|
1583
1596
|
|
|
1584
1597
|
.remove-button {
|
|
@@ -1706,16 +1719,6 @@ class SelectOption extends HTMLElement {
|
|
|
1706
1719
|
<path d="M4 8.5L6.5 11L12 5.5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
1707
1720
|
</svg>
|
|
1708
1721
|
`;
|
|
1709
|
-
// Visibility control via CSS or inline style
|
|
1710
|
-
// We set it to display: none unless selected.
|
|
1711
|
-
// User can override this behavior via part styling if they want transitions
|
|
1712
|
-
if (!selected) {
|
|
1713
|
-
checkmark.style.display = 'none';
|
|
1714
|
-
}
|
|
1715
|
-
else {
|
|
1716
|
-
checkmark.style.marginLeft = '8px';
|
|
1717
|
-
checkmark.style.color = 'currentColor';
|
|
1718
|
-
}
|
|
1719
1722
|
this._container.appendChild(checkmark);
|
|
1720
1723
|
}
|
|
1721
1724
|
// Data Attributes Contract on Host
|
|
@@ -1873,7 +1876,22 @@ class EnhancedSelect extends HTMLElement {
|
|
|
1873
1876
|
}
|
|
1874
1877
|
set classMap(map) {
|
|
1875
1878
|
this._classMap = map;
|
|
1876
|
-
this._setGlobalStylesMirroring(Boolean(this._optionRenderer || map));
|
|
1879
|
+
this._setGlobalStylesMirroring(Boolean(this._optionRenderer || map || this._groupHeaderRenderer));
|
|
1880
|
+
if (!this.isConnected)
|
|
1881
|
+
return;
|
|
1882
|
+
this._renderOptions();
|
|
1883
|
+
}
|
|
1884
|
+
/**
|
|
1885
|
+
* DOM-based renderer for group headers. When provided, the component will
|
|
1886
|
+
* call this function for each group during rendering. The returned element
|
|
1887
|
+
* will receive `.group-header` and `part="group-header"` automatically.
|
|
1888
|
+
*/
|
|
1889
|
+
get groupHeaderRenderer() {
|
|
1890
|
+
return this._groupHeaderRenderer;
|
|
1891
|
+
}
|
|
1892
|
+
set groupHeaderRenderer(renderer) {
|
|
1893
|
+
this._groupHeaderRenderer = renderer;
|
|
1894
|
+
this._setGlobalStylesMirroring(Boolean(this._optionRenderer || this._classMap || renderer));
|
|
1877
1895
|
if (!this.isConnected)
|
|
1878
1896
|
return;
|
|
1879
1897
|
this._renderOptions();
|
|
@@ -1932,11 +1950,12 @@ class EnhancedSelect extends HTMLElement {
|
|
|
1932
1950
|
this._initializeObservers();
|
|
1933
1951
|
}
|
|
1934
1952
|
connectedCallback() {
|
|
1953
|
+
// register instance
|
|
1954
|
+
EnhancedSelect._instances.add(this);
|
|
1935
1955
|
// WORKAROUND: Force display style on host element for Angular compatibility
|
|
1936
1956
|
// Angular's rendering seems to not apply :host styles correctly in some cases
|
|
1937
1957
|
// Must be done in connectedCallback when element is attached to DOM
|
|
1938
1958
|
this.style.display = 'block';
|
|
1939
|
-
this.style.width = '100%';
|
|
1940
1959
|
if (this._optionRenderer) {
|
|
1941
1960
|
this._setGlobalStylesMirroring(true);
|
|
1942
1961
|
}
|
|
@@ -1950,6 +1969,8 @@ class EnhancedSelect extends HTMLElement {
|
|
|
1950
1969
|
}
|
|
1951
1970
|
}
|
|
1952
1971
|
disconnectedCallback() {
|
|
1972
|
+
// unregister instance
|
|
1973
|
+
EnhancedSelect._instances.delete(this);
|
|
1953
1974
|
// Cleanup observers
|
|
1954
1975
|
this._resizeObserver?.disconnect();
|
|
1955
1976
|
this._intersectionObserver?.disconnect();
|
|
@@ -2177,7 +2198,8 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2177
2198
|
:host {
|
|
2178
2199
|
display: block;
|
|
2179
2200
|
position: relative;
|
|
2180
|
-
width: 100
|
|
2201
|
+
width: var(--select-width, 100%);
|
|
2202
|
+
height: var(--select-height, auto);
|
|
2181
2203
|
}
|
|
2182
2204
|
|
|
2183
2205
|
.select-container {
|
|
@@ -2193,6 +2215,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2193
2215
|
flex-wrap: wrap;
|
|
2194
2216
|
gap: var(--select-input-gap, 6px);
|
|
2195
2217
|
padding: var(--select-input-padding, 6px 52px 6px 8px);
|
|
2218
|
+
height: var(--select-input-height, auto);
|
|
2196
2219
|
min-height: var(--select-input-min-height, 44px);
|
|
2197
2220
|
max-height: var(--select-input-max-height, 160px);
|
|
2198
2221
|
overflow-y: var(--select-input-overflow-y, auto);
|
|
@@ -2214,17 +2237,17 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2214
2237
|
content: '';
|
|
2215
2238
|
position: absolute;
|
|
2216
2239
|
top: 50%;
|
|
2217
|
-
right: var(--select-separator-position, 40px);
|
|
2240
|
+
right: var(--select-separator-position, var(--select-seperator-position, 40px));
|
|
2218
2241
|
transform: translateY(-50%);
|
|
2219
|
-
width: var(--select-separator-width, 1px);
|
|
2220
|
-
height: var(--select-separator-height, 60%);
|
|
2221
|
-
background: var(--select-separator-bg, var(--select-separator-gradient, linear-gradient(
|
|
2242
|
+
width: var(--select-separator-width, var(--select-seperator-width, 1px));
|
|
2243
|
+
height: var(--select-separator-height, var(--select-seperator-height, 60%));
|
|
2244
|
+
background: var(--select-separator-bg, var(--select-seperator-bg, var(--select-separator-gradient, var(--select-seperator-gradient, linear-gradient(
|
|
2222
2245
|
to bottom,
|
|
2223
2246
|
transparent 0%,
|
|
2224
2247
|
rgba(0, 0, 0, 0.1) 20%,
|
|
2225
2248
|
rgba(0, 0, 0, 0.1) 80%,
|
|
2226
2249
|
transparent 100%
|
|
2227
|
-
)));
|
|
2250
|
+
))));
|
|
2228
2251
|
pointer-events: none;
|
|
2229
2252
|
z-index: 1;
|
|
2230
2253
|
}
|
|
@@ -2235,6 +2258,8 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2235
2258
|
right: 0;
|
|
2236
2259
|
bottom: 0;
|
|
2237
2260
|
width: var(--select-arrow-width, 40px);
|
|
2261
|
+
/* allow explicit height override even though container normally stretches */
|
|
2262
|
+
height: var(--select-arrow-height, auto);
|
|
2238
2263
|
display: flex;
|
|
2239
2264
|
align-items: center;
|
|
2240
2265
|
justify-content: center;
|
|
@@ -2249,7 +2274,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2249
2274
|
}
|
|
2250
2275
|
|
|
2251
2276
|
.input-container.has-clear-control::after {
|
|
2252
|
-
right: var(--select-separator-position-with-clear, 72px);
|
|
2277
|
+
right: var(--select-separator-position-with-clear, var(--select-seperator-position-with-clear, 72px));
|
|
2253
2278
|
}
|
|
2254
2279
|
|
|
2255
2280
|
.dropdown-arrow-container.with-clear-control {
|
|
@@ -2300,9 +2325,14 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2300
2325
|
background-color: var(--select-arrow-hover-bg, rgba(102, 126, 234, 0.08));
|
|
2301
2326
|
}
|
|
2302
2327
|
|
|
2328
|
+
.dropdown-arrow:hover {
|
|
2329
|
+
/* legacy alias --select-arrow-hover for icon color */
|
|
2330
|
+
color: var(--select-arrow-hover, var(--select-arrow-hover-color, #667eea));
|
|
2331
|
+
}
|
|
2332
|
+
|
|
2303
2333
|
.dropdown-arrow {
|
|
2304
|
-
width: var(--select-arrow-size, 16px);
|
|
2305
|
-
height: var(--select-arrow-size, 16px);
|
|
2334
|
+
width: var(--select-arrow-width, var(--select-arrow-size, 16px));
|
|
2335
|
+
height: var(--select-arrow-height, var(--select-arrow-size, 16px));
|
|
2306
2336
|
color: var(--select-arrow-color, #667eea);
|
|
2307
2337
|
transition: transform 0.2s ease, color 0.2s ease;
|
|
2308
2338
|
transform: translateY(0);
|
|
@@ -2322,6 +2352,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2322
2352
|
|
|
2323
2353
|
.select-input {
|
|
2324
2354
|
flex: 1;
|
|
2355
|
+
width: var(--select-input-width, auto);
|
|
2325
2356
|
min-width: var(--select-input-min-width, 120px);
|
|
2326
2357
|
padding: var(--select-input-field-padding, 4px);
|
|
2327
2358
|
border: none;
|
|
@@ -2416,6 +2447,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2416
2447
|
font-weight: var(--select-group-header-weight, 600);
|
|
2417
2448
|
color: var(--select-group-header-color, #6b7280);
|
|
2418
2449
|
background-color: var(--select-group-header-bg, #f3f4f6);
|
|
2450
|
+
text-align: var(--select-group-header-text-align, left);
|
|
2419
2451
|
font-size: var(--select-group-header-font-size, 12px);
|
|
2420
2452
|
text-transform: var(--select-group-header-text-transform, uppercase);
|
|
2421
2453
|
letter-spacing: var(--select-group-header-letter-spacing, 0.05em);
|
|
@@ -2600,10 +2632,25 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2600
2632
|
|
|
2601
2633
|
/* Dark mode - Opt-in via class, data attribute, or ancestor context */
|
|
2602
2634
|
:host(.dark-mode),
|
|
2635
|
+
:host([dark-mode]),
|
|
2636
|
+
:host([darkmode]),
|
|
2603
2637
|
:host([data-theme="dark"]),
|
|
2638
|
+
:host([theme="dark"]),
|
|
2604
2639
|
:host-context(.dark-mode),
|
|
2605
2640
|
:host-context(.dark),
|
|
2606
|
-
:host-context([
|
|
2641
|
+
:host-context([dark-mode]),
|
|
2642
|
+
:host-context([darkmode]),
|
|
2643
|
+
:host-context([data-theme="dark"]),
|
|
2644
|
+
:host-context([theme="dark"]) {
|
|
2645
|
+
/* map dark tokens to base option tokens so nested <select-option>
|
|
2646
|
+
components also pick up dark mode via inherited CSS variables */
|
|
2647
|
+
--select-option-bg: var(--select-dark-option-bg, #1f2937);
|
|
2648
|
+
--select-option-color: var(--select-dark-option-color, #f9fafb);
|
|
2649
|
+
--select-option-hover-bg: var(--select-dark-option-hover-bg, #374151);
|
|
2650
|
+
--select-option-hover-color: var(--select-dark-option-hover-color, #f9fafb);
|
|
2651
|
+
--select-option-selected-bg: var(--select-dark-option-selected-bg, #3730a3);
|
|
2652
|
+
--select-option-selected-color: var(--select-dark-option-selected-text, #e0e7ff);
|
|
2653
|
+
|
|
2607
2654
|
.input-container {
|
|
2608
2655
|
background: var(--select-dark-bg, #1f2937);
|
|
2609
2656
|
border-color: var(--select-dark-border, #4b5563);
|
|
@@ -2660,6 +2707,12 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2660
2707
|
outline: var(--select-dark-option-active-outline, 2px solid rgba(129, 140, 248, 0.55));
|
|
2661
2708
|
}
|
|
2662
2709
|
|
|
2710
|
+
/* Group header in dark mode */
|
|
2711
|
+
.group-header {
|
|
2712
|
+
color: var(--select-dark-group-header-color, var(--select-group-header-color, #6b7280));
|
|
2713
|
+
background-color: var(--select-dark-group-header-bg, var(--select-group-header-bg, #374151));
|
|
2714
|
+
}
|
|
2715
|
+
|
|
2663
2716
|
.option.selected.active {
|
|
2664
2717
|
background-color: var(--select-dark-option-selected-active-bg, var(--select-dark-option-selected-bg, #3730a3));
|
|
2665
2718
|
color: var(--select-dark-option-selected-active-color, var(--select-dark-option-selected-text, #e0e7ff));
|
|
@@ -2738,19 +2791,14 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2738
2791
|
this._boundArrowClick = (e) => {
|
|
2739
2792
|
e.stopPropagation();
|
|
2740
2793
|
e.preventDefault();
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
this.
|
|
2745
|
-
|
|
2746
|
-
this._config.callbacks.onOpen();
|
|
2747
|
-
}
|
|
2748
|
-
else if (!this._state.isOpen && this._config.callbacks.onClose) {
|
|
2749
|
-
this._config.callbacks.onClose();
|
|
2794
|
+
// delegate to the existing open/close helpers so we don't accidentally
|
|
2795
|
+
// drift out of sync with the logic in those methods (focus, events,
|
|
2796
|
+
// scroll-to-selected, etc.)
|
|
2797
|
+
if (this._state.isOpen) {
|
|
2798
|
+
this._handleClose();
|
|
2750
2799
|
}
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
setTimeout(() => this._scrollToSelected(), 50);
|
|
2800
|
+
else {
|
|
2801
|
+
this._handleOpen();
|
|
2754
2802
|
}
|
|
2755
2803
|
};
|
|
2756
2804
|
this._arrowContainer.addEventListener('click', this._boundArrowClick);
|
|
@@ -2784,6 +2832,16 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2784
2832
|
if (wasClosed) {
|
|
2785
2833
|
this._handleOpen();
|
|
2786
2834
|
}
|
|
2835
|
+
else {
|
|
2836
|
+
// Keep open while interacting directly with the input so users can
|
|
2837
|
+
// place cursor/type without accidental collapse.
|
|
2838
|
+
if (target === this._input) {
|
|
2839
|
+
this._input.focus();
|
|
2840
|
+
return;
|
|
2841
|
+
}
|
|
2842
|
+
// clicking other parts of the input container while open toggles close
|
|
2843
|
+
this._handleClose();
|
|
2844
|
+
}
|
|
2787
2845
|
// Focus the input (do not prevent default behavior)
|
|
2788
2846
|
this._input.focus();
|
|
2789
2847
|
// If we just opened the dropdown, transfer pointer capture to the
|
|
@@ -2959,6 +3017,16 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2959
3017
|
_handleOpen() {
|
|
2960
3018
|
if (!this._config.enabled || this._state.isOpen)
|
|
2961
3019
|
return;
|
|
3020
|
+
// close any other open selects before proceeding
|
|
3021
|
+
EnhancedSelect._instances.forEach(inst => {
|
|
3022
|
+
if (inst !== this)
|
|
3023
|
+
inst._handleClose();
|
|
3024
|
+
});
|
|
3025
|
+
// Always focus the input when opening so callers (arrow click,
|
|
3026
|
+
// programmatic `open()`, etc.) get the keyboard cursor. This was a
|
|
3027
|
+
// frequent source of confusion in #14 where people opened the dropdown
|
|
3028
|
+
// but the text field never received focus.
|
|
3029
|
+
this._input.focus();
|
|
2962
3030
|
this._markOpenStart();
|
|
2963
3031
|
this._state.isOpen = true;
|
|
2964
3032
|
this._dropdown.style.display = 'block';
|
|
@@ -3932,10 +4000,25 @@ class EnhancedSelect extends HTMLElement {
|
|
|
3932
4000
|
const query = this._state.searchQuery.toLowerCase();
|
|
3933
4001
|
// Handle Grouped Items Rendering (when no search query)
|
|
3934
4002
|
if (this._state.groupedItems.length > 0 && !query) {
|
|
3935
|
-
this._state.groupedItems.forEach(group => {
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
4003
|
+
this._state.groupedItems.forEach((group, groupIndex) => {
|
|
4004
|
+
let header;
|
|
4005
|
+
if (this.groupHeaderRenderer) {
|
|
4006
|
+
header = this.groupHeaderRenderer(group, groupIndex);
|
|
4007
|
+
// make sure the returned element has the correct semantics so
|
|
4008
|
+
// people can style it. we add the class/part even if the renderer
|
|
4009
|
+
// returned something else to ensure backward compatibility.
|
|
4010
|
+
if (!(header instanceof HTMLElement)) {
|
|
4011
|
+
// fall back to default if API is misused
|
|
4012
|
+
header = document.createElement('div');
|
|
4013
|
+
header.textContent = String(group.label);
|
|
4014
|
+
}
|
|
4015
|
+
}
|
|
4016
|
+
else {
|
|
4017
|
+
header = document.createElement('div');
|
|
4018
|
+
header.textContent = group.label;
|
|
4019
|
+
}
|
|
4020
|
+
header.classList.add('group-header');
|
|
4021
|
+
header.setAttribute('part', 'group-header');
|
|
3939
4022
|
this._optionsContainer.appendChild(header);
|
|
3940
4023
|
group.options.forEach(item => {
|
|
3941
4024
|
// Find original index for correct ID generation and selection
|
|
@@ -4208,6 +4291,8 @@ class EnhancedSelect extends HTMLElement {
|
|
|
4208
4291
|
}
|
|
4209
4292
|
}
|
|
4210
4293
|
}
|
|
4294
|
+
/** live set of all connected instances; used to auto-close siblings */
|
|
4295
|
+
EnhancedSelect._instances = new Set();
|
|
4211
4296
|
// Register custom element
|
|
4212
4297
|
if (!customElements.get('enhanced-select')) {
|
|
4213
4298
|
customElements.define('enhanced-select', EnhancedSelect);
|