@smilodon/core 1.4.4 → 1.4.5
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 +25 -0
- package/dist/index.cjs +194 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +194 -5
- 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 +194 -5
- 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 +9 -0
- package/dist/types/src/config/global-config.d.ts +21 -0
- package/dist/types/src/index.d.ts +1 -1
- package/dist/types/src/types.d.ts +5 -0
- package/dist/types/src/utils/csp-styles.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -136,6 +136,31 @@ See the [main documentation](https://github.com/navidrezadoost/smilodon#readme)
|
|
|
136
136
|
|
|
137
137
|
## API Reference
|
|
138
138
|
|
|
139
|
+
### Clear Control (Selection/Search Reset)
|
|
140
|
+
|
|
141
|
+
```javascript
|
|
142
|
+
const select = document.querySelector('enhanced-select');
|
|
143
|
+
|
|
144
|
+
select.updateConfig({
|
|
145
|
+
clearControl: {
|
|
146
|
+
enabled: true,
|
|
147
|
+
clearSelection: true,
|
|
148
|
+
clearSearch: true,
|
|
149
|
+
hideWhenEmpty: true,
|
|
150
|
+
ariaLabel: 'Clear selected and searched values',
|
|
151
|
+
icon: '✕',
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
select.addEventListener('clear', (e) => {
|
|
156
|
+
console.log(e.detail); // { clearedSelection: boolean, clearedSearch: boolean }
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Style hooks:
|
|
161
|
+
- Parts: `::part(clear-button)`, `::part(clear-icon)`
|
|
162
|
+
- Tokens: `--select-clear-button-*`, `--select-clear-icon-*`, `--select-input-padding-with-clear`
|
|
163
|
+
|
|
139
164
|
### Properties
|
|
140
165
|
|
|
141
166
|
```typescript
|
package/dist/index.cjs
CHANGED
|
@@ -1394,6 +1394,14 @@ const defaultConfig = {
|
|
|
1394
1394
|
expandLabel: 'Show more',
|
|
1395
1395
|
collapseLabel: 'Show less',
|
|
1396
1396
|
},
|
|
1397
|
+
clearControl: {
|
|
1398
|
+
enabled: false,
|
|
1399
|
+
clearSelection: true,
|
|
1400
|
+
clearSearch: true,
|
|
1401
|
+
hideWhenEmpty: true,
|
|
1402
|
+
ariaLabel: 'Clear selection and search',
|
|
1403
|
+
icon: '×',
|
|
1404
|
+
},
|
|
1397
1405
|
callbacks: {},
|
|
1398
1406
|
enabled: true,
|
|
1399
1407
|
searchable: false,
|
|
@@ -1890,6 +1898,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
1890
1898
|
this._inputContainer = this._createInputContainer();
|
|
1891
1899
|
this._input = this._createInput();
|
|
1892
1900
|
this._arrowContainer = this._createArrowContainer();
|
|
1901
|
+
this._clearControl = this._createClearControl();
|
|
1893
1902
|
this._dropdown = this._createDropdown();
|
|
1894
1903
|
this._optionsContainer = this._createOptionsContainer();
|
|
1895
1904
|
this._liveRegion = this._createLiveRegion();
|
|
@@ -2103,8 +2112,25 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2103
2112
|
`;
|
|
2104
2113
|
return container;
|
|
2105
2114
|
}
|
|
2115
|
+
_createClearControl() {
|
|
2116
|
+
const button = document.createElement('button');
|
|
2117
|
+
button.type = 'button';
|
|
2118
|
+
button.className = 'clear-control-button';
|
|
2119
|
+
button.setAttribute('part', 'clear-button');
|
|
2120
|
+
const icon = document.createElement('span');
|
|
2121
|
+
icon.className = 'clear-control-icon';
|
|
2122
|
+
icon.setAttribute('part', 'clear-icon');
|
|
2123
|
+
icon.textContent = this._config.clearControl.icon || '×';
|
|
2124
|
+
button.setAttribute('aria-label', this._config.clearControl.ariaLabel || 'Clear selection and search');
|
|
2125
|
+
button.appendChild(icon);
|
|
2126
|
+
this._clearControlIcon = icon;
|
|
2127
|
+
return button;
|
|
2128
|
+
}
|
|
2106
2129
|
_assembleDOM() {
|
|
2107
2130
|
this._inputContainer.appendChild(this._input);
|
|
2131
|
+
if (this._clearControl) {
|
|
2132
|
+
this._inputContainer.appendChild(this._clearControl);
|
|
2133
|
+
}
|
|
2108
2134
|
if (this._arrowContainer) {
|
|
2109
2135
|
this._inputContainer.appendChild(this._arrowContainer);
|
|
2110
2136
|
}
|
|
@@ -2120,6 +2146,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2120
2146
|
this._dropdown.id = listboxId;
|
|
2121
2147
|
this._input.setAttribute('aria-controls', listboxId);
|
|
2122
2148
|
this._input.setAttribute('aria-owns', listboxId);
|
|
2149
|
+
this._syncClearControlState();
|
|
2123
2150
|
}
|
|
2124
2151
|
_initializeStyles() {
|
|
2125
2152
|
const style = document.createElement('style');
|
|
@@ -2193,6 +2220,58 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2193
2220
|
border-radius: var(--select-arrow-border-radius, 0 4px 4px 0);
|
|
2194
2221
|
z-index: 2;
|
|
2195
2222
|
}
|
|
2223
|
+
|
|
2224
|
+
.input-container.has-clear-control {
|
|
2225
|
+
padding: var(--select-input-padding-with-clear, 6px 84px 6px 8px);
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2228
|
+
.input-container.has-clear-control::after {
|
|
2229
|
+
right: var(--select-separator-position-with-clear, 72px);
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
.dropdown-arrow-container.with-clear-control {
|
|
2233
|
+
right: var(--select-arrow-right-with-clear, 32px);
|
|
2234
|
+
}
|
|
2235
|
+
|
|
2236
|
+
.clear-control-button {
|
|
2237
|
+
position: absolute;
|
|
2238
|
+
top: 50%;
|
|
2239
|
+
right: var(--select-clear-button-right, 6px);
|
|
2240
|
+
transform: translateY(-50%);
|
|
2241
|
+
width: var(--select-clear-button-size, 24px);
|
|
2242
|
+
height: var(--select-clear-button-size, 24px);
|
|
2243
|
+
border: var(--select-clear-button-border, none);
|
|
2244
|
+
border-radius: var(--select-clear-button-radius, 999px);
|
|
2245
|
+
background: var(--select-clear-button-bg, transparent);
|
|
2246
|
+
color: var(--select-clear-button-color, #6b7280);
|
|
2247
|
+
display: inline-flex;
|
|
2248
|
+
align-items: center;
|
|
2249
|
+
justify-content: center;
|
|
2250
|
+
cursor: pointer;
|
|
2251
|
+
z-index: 3;
|
|
2252
|
+
transition: var(--select-clear-button-transition, all 0.2s ease);
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2255
|
+
.clear-control-button:hover {
|
|
2256
|
+
background: var(--select-clear-button-hover-bg, rgba(0, 0, 0, 0.06));
|
|
2257
|
+
color: var(--select-clear-button-hover-color, #111827);
|
|
2258
|
+
}
|
|
2259
|
+
|
|
2260
|
+
.clear-control-button:focus-visible {
|
|
2261
|
+
outline: var(--select-clear-button-focus-outline, 2px solid rgba(102, 126, 234, 0.55));
|
|
2262
|
+
outline-offset: 1px;
|
|
2263
|
+
}
|
|
2264
|
+
|
|
2265
|
+
.clear-control-button[hidden] {
|
|
2266
|
+
display: none;
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2269
|
+
.clear-control-icon {
|
|
2270
|
+
font-size: var(--select-clear-icon-size, 16px);
|
|
2271
|
+
line-height: 1;
|
|
2272
|
+
font-weight: var(--select-clear-icon-weight, 500);
|
|
2273
|
+
pointer-events: none;
|
|
2274
|
+
}
|
|
2196
2275
|
|
|
2197
2276
|
.dropdown-arrow-container:hover {
|
|
2198
2277
|
background-color: var(--select-arrow-hover-bg, rgba(102, 126, 234, 0.08));
|
|
@@ -2350,16 +2429,31 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2350
2429
|
transform: var(--select-option-selected-hover-transform, var(--select-option-selected-transform, var(--select-option-transform, none)));
|
|
2351
2430
|
}
|
|
2352
2431
|
|
|
2353
|
-
.option.active {
|
|
2432
|
+
.option.active:not(.selected) {
|
|
2354
2433
|
background-color: var(--select-option-active-bg, #f3f4f6);
|
|
2355
2434
|
color: var(--select-option-active-color, #1f2937);
|
|
2356
2435
|
outline: var(--select-option-active-outline, 2px solid rgba(99, 102, 241, 0.45));
|
|
2357
2436
|
outline-offset: -2px;
|
|
2358
2437
|
}
|
|
2359
2438
|
|
|
2360
|
-
.option
|
|
2439
|
+
.option.selected.active {
|
|
2440
|
+
background-color: var(--select-option-selected-active-bg, var(--select-option-selected-bg, #e0e7ff));
|
|
2441
|
+
color: var(--select-option-selected-active-color, var(--select-option-selected-color, #4338ca));
|
|
2442
|
+
border: var(--select-option-selected-active-border, var(--select-option-selected-border, var(--select-option-border, none)));
|
|
2443
|
+
border-bottom: var(--select-option-selected-active-border-bottom, var(--select-option-selected-border-bottom, var(--select-option-border-bottom, none)));
|
|
2444
|
+
box-shadow: var(--select-option-selected-active-shadow, var(--select-option-selected-shadow, var(--select-option-shadow, none)));
|
|
2445
|
+
transform: var(--select-option-selected-active-transform, var(--select-option-selected-transform, var(--select-option-transform, none)));
|
|
2446
|
+
outline: var(--select-option-selected-active-outline, var(--select-option-active-outline, 2px solid rgba(99, 102, 241, 0.45)));
|
|
2447
|
+
outline-offset: -2px;
|
|
2448
|
+
}
|
|
2449
|
+
|
|
2450
|
+
.option:active:not(.selected) {
|
|
2361
2451
|
background-color: var(--select-option-pressed-bg, #e5e7eb);
|
|
2362
2452
|
}
|
|
2453
|
+
|
|
2454
|
+
.option.selected:active {
|
|
2455
|
+
background-color: var(--select-option-selected-pressed-bg, var(--select-option-selected-hover-bg, var(--select-option-selected-bg, #e0e7ff)));
|
|
2456
|
+
}
|
|
2363
2457
|
|
|
2364
2458
|
.load-more-container {
|
|
2365
2459
|
padding: var(--select-load-more-padding, 12px);
|
|
@@ -2520,12 +2614,23 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2520
2614
|
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)))));
|
|
2521
2615
|
}
|
|
2522
2616
|
|
|
2523
|
-
.option.active {
|
|
2617
|
+
.option.active:not(.selected) {
|
|
2524
2618
|
background-color: var(--select-dark-option-active-bg, #374151);
|
|
2525
2619
|
color: var(--select-dark-option-active-color, #f9fafb);
|
|
2526
2620
|
outline: var(--select-dark-option-active-outline, 2px solid rgba(129, 140, 248, 0.55));
|
|
2527
2621
|
}
|
|
2528
2622
|
|
|
2623
|
+
.option.selected.active {
|
|
2624
|
+
background-color: var(--select-dark-option-selected-active-bg, var(--select-dark-option-selected-bg, #3730a3));
|
|
2625
|
+
color: var(--select-dark-option-selected-active-color, var(--select-dark-option-selected-text, #e0e7ff));
|
|
2626
|
+
border: var(--select-dark-option-selected-active-border, var(--select-dark-option-selected-border, var(--select-option-selected-active-border, var(--select-option-selected-border, var(--select-option-border, none)))));
|
|
2627
|
+
border-bottom: var(--select-dark-option-selected-active-border-bottom, var(--select-dark-option-selected-border-bottom, var(--select-option-selected-active-border-bottom, var(--select-option-selected-border-bottom, var(--select-option-border-bottom, none)))));
|
|
2628
|
+
box-shadow: var(--select-dark-option-selected-active-shadow, var(--select-dark-option-selected-shadow, var(--select-option-selected-active-shadow, var(--select-option-selected-shadow, var(--select-option-shadow, none)))));
|
|
2629
|
+
transform: var(--select-dark-option-selected-active-transform, var(--select-dark-option-selected-transform, var(--select-option-selected-active-transform, var(--select-option-selected-transform, var(--select-option-transform, none)))));
|
|
2630
|
+
outline: var(--select-dark-option-selected-active-outline, var(--select-dark-option-active-outline, var(--select-option-selected-active-outline, var(--select-option-active-outline, 2px solid rgba(129, 140, 248, 0.55)))));
|
|
2631
|
+
outline-offset: -2px;
|
|
2632
|
+
}
|
|
2633
|
+
|
|
2529
2634
|
.selection-badge {
|
|
2530
2635
|
background: var(--select-dark-badge-bg, #4f46e5);
|
|
2531
2636
|
color: var(--select-dark-badge-color, #eef2ff);
|
|
@@ -2610,6 +2715,17 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2610
2715
|
};
|
|
2611
2716
|
this._arrowContainer.addEventListener('click', this._boundArrowClick);
|
|
2612
2717
|
}
|
|
2718
|
+
if (this._clearControl) {
|
|
2719
|
+
this._clearControl.addEventListener('pointerdown', (e) => {
|
|
2720
|
+
e.stopPropagation();
|
|
2721
|
+
e.preventDefault();
|
|
2722
|
+
});
|
|
2723
|
+
this._clearControl.addEventListener('click', (e) => {
|
|
2724
|
+
e.stopPropagation();
|
|
2725
|
+
e.preventDefault();
|
|
2726
|
+
this._handleClearControlClick();
|
|
2727
|
+
});
|
|
2728
|
+
}
|
|
2613
2729
|
// Input container click - focus input and open dropdown
|
|
2614
2730
|
this._inputContainer.addEventListener('pointerdown', (e) => {
|
|
2615
2731
|
const target = e.target;
|
|
@@ -2617,6 +2733,8 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2617
2733
|
return;
|
|
2618
2734
|
if (target && target.closest('.dropdown-arrow-container'))
|
|
2619
2735
|
return;
|
|
2736
|
+
if (target && target.closest('.clear-control-button'))
|
|
2737
|
+
return;
|
|
2620
2738
|
if (!this._state.isOpen) {
|
|
2621
2739
|
this._handleOpen();
|
|
2622
2740
|
}
|
|
@@ -2829,6 +2947,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2829
2947
|
}
|
|
2830
2948
|
_handleSearch(query) {
|
|
2831
2949
|
this._state.searchQuery = query;
|
|
2950
|
+
this._syncClearControlState();
|
|
2832
2951
|
if (query.length > 0) {
|
|
2833
2952
|
this._perfMark('smilodon-search-input-last');
|
|
2834
2953
|
this._pendingSearchRenderMark = true;
|
|
@@ -3006,7 +3125,9 @@ class EnhancedSelect extends HTMLElement {
|
|
|
3006
3125
|
option.classList.add('smilodon-option--active');
|
|
3007
3126
|
option.setAttribute('aria-selected', 'true');
|
|
3008
3127
|
}
|
|
3009
|
-
option.scrollIntoView
|
|
3128
|
+
if (typeof option.scrollIntoView === 'function') {
|
|
3129
|
+
option.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
|
3130
|
+
}
|
|
3010
3131
|
// Announce position for screen readers
|
|
3011
3132
|
const total = options.length;
|
|
3012
3133
|
this._announce(`Item ${index + 1} of ${total}`);
|
|
@@ -3261,6 +3382,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
3261
3382
|
this._inputContainer.insertBefore(badge, this._input);
|
|
3262
3383
|
});
|
|
3263
3384
|
}
|
|
3385
|
+
this._syncClearControlState();
|
|
3264
3386
|
}
|
|
3265
3387
|
_renderOptionsWithAnimation() {
|
|
3266
3388
|
// Add fade-out animation
|
|
@@ -3509,6 +3631,21 @@ class EnhancedSelect extends HTMLElement {
|
|
|
3509
3631
|
this._renderOptions();
|
|
3510
3632
|
this._updateInputDisplay();
|
|
3511
3633
|
this._emitChange();
|
|
3634
|
+
this._syncClearControlState();
|
|
3635
|
+
}
|
|
3636
|
+
/**
|
|
3637
|
+
* Clear search query and restore full option list
|
|
3638
|
+
*/
|
|
3639
|
+
clearSearch() {
|
|
3640
|
+
this._state.searchQuery = '';
|
|
3641
|
+
if (this._config.searchable) {
|
|
3642
|
+
this._input.value = '';
|
|
3643
|
+
if (this._state.selectedIndices.size > 0) {
|
|
3644
|
+
this._updateInputDisplay();
|
|
3645
|
+
}
|
|
3646
|
+
}
|
|
3647
|
+
this._renderOptions();
|
|
3648
|
+
this._syncClearControlState();
|
|
3512
3649
|
}
|
|
3513
3650
|
/**
|
|
3514
3651
|
* Open dropdown
|
|
@@ -3535,6 +3672,12 @@ class EnhancedSelect extends HTMLElement {
|
|
|
3535
3672
|
this._input.placeholder = this._config.placeholder || 'Select an option...';
|
|
3536
3673
|
}
|
|
3537
3674
|
}
|
|
3675
|
+
if (this._clearControl) {
|
|
3676
|
+
this._clearControl.setAttribute('aria-label', this._config.clearControl.ariaLabel || 'Clear selection and search');
|
|
3677
|
+
}
|
|
3678
|
+
if (this._clearControlIcon) {
|
|
3679
|
+
this._clearControlIcon.textContent = this._config.clearControl.icon || '×';
|
|
3680
|
+
}
|
|
3538
3681
|
if (this._dropdown) {
|
|
3539
3682
|
if (this._config.selection.mode === 'multi') {
|
|
3540
3683
|
this._dropdown.setAttribute('aria-multiselectable', 'true');
|
|
@@ -3545,8 +3688,47 @@ class EnhancedSelect extends HTMLElement {
|
|
|
3545
3688
|
}
|
|
3546
3689
|
// Re-initialize observers in case infinite scroll was enabled/disabled
|
|
3547
3690
|
this._initializeObservers();
|
|
3691
|
+
this._syncClearControlState();
|
|
3548
3692
|
this._renderOptions();
|
|
3549
3693
|
}
|
|
3694
|
+
_handleClearControlClick() {
|
|
3695
|
+
const shouldClearSelection = this._config.clearControl.clearSelection !== false;
|
|
3696
|
+
const shouldClearSearch = this._config.clearControl.clearSearch !== false;
|
|
3697
|
+
const hadSelection = this._state.selectedIndices.size > 0;
|
|
3698
|
+
const hadSearch = this._state.searchQuery.length > 0;
|
|
3699
|
+
if (shouldClearSelection && hadSelection) {
|
|
3700
|
+
this.clear();
|
|
3701
|
+
}
|
|
3702
|
+
if (shouldClearSearch && hadSearch) {
|
|
3703
|
+
this.clearSearch();
|
|
3704
|
+
}
|
|
3705
|
+
this._emit('clear', {
|
|
3706
|
+
clearedSelection: shouldClearSelection && hadSelection,
|
|
3707
|
+
clearedSearch: shouldClearSearch && hadSearch,
|
|
3708
|
+
});
|
|
3709
|
+
this._config.callbacks.onClear?.({
|
|
3710
|
+
clearedSelection: shouldClearSelection && hadSelection,
|
|
3711
|
+
clearedSearch: shouldClearSearch && hadSearch,
|
|
3712
|
+
});
|
|
3713
|
+
this._input.focus();
|
|
3714
|
+
}
|
|
3715
|
+
_syncClearControlState() {
|
|
3716
|
+
if (!this._clearControl || !this._inputContainer || !this._arrowContainer)
|
|
3717
|
+
return;
|
|
3718
|
+
const enabled = this._config.clearControl.enabled === true;
|
|
3719
|
+
const hasSelection = this._state.selectedIndices.size > 0;
|
|
3720
|
+
const hasSearch = this._state.searchQuery.length > 0;
|
|
3721
|
+
const clearSelection = this._config.clearControl.clearSelection !== false;
|
|
3722
|
+
const clearSearch = this._config.clearControl.clearSearch !== false;
|
|
3723
|
+
const hasSomethingToClear = (clearSelection && hasSelection) || (clearSearch && hasSearch);
|
|
3724
|
+
const hideWhenEmpty = this._config.clearControl.hideWhenEmpty !== false;
|
|
3725
|
+
const visible = enabled && (!hideWhenEmpty || hasSomethingToClear);
|
|
3726
|
+
this._inputContainer.classList.toggle('has-clear-control', enabled);
|
|
3727
|
+
this._arrowContainer.classList.toggle('with-clear-control', enabled);
|
|
3728
|
+
this._clearControl.hidden = !visible;
|
|
3729
|
+
this._clearControl.disabled = !hasSomethingToClear;
|
|
3730
|
+
this._clearControl.setAttribute('aria-hidden', String(!visible));
|
|
3731
|
+
}
|
|
3550
3732
|
/**
|
|
3551
3733
|
* Set error state
|
|
3552
3734
|
*/
|
|
@@ -4834,10 +5016,17 @@ const shadowDOMStyles = `
|
|
|
4834
5016
|
color: var(--ns-item-selected-color, #0066cc);
|
|
4835
5017
|
}
|
|
4836
5018
|
|
|
4837
|
-
.ns-item[data-active="true"] {
|
|
5019
|
+
.ns-item[data-active="true"]:not([aria-selected="true"]) {
|
|
4838
5020
|
background: var(--ns-item-active-bg, rgba(0, 102, 204, 0.2));
|
|
4839
5021
|
}
|
|
4840
5022
|
|
|
5023
|
+
.ns-item[aria-selected="true"][data-active="true"] {
|
|
5024
|
+
background: var(--ns-item-selected-active-bg, var(--ns-item-selected-bg, rgba(0, 102, 204, 0.1)));
|
|
5025
|
+
color: var(--ns-item-selected-active-color, var(--ns-item-selected-color, #0066cc));
|
|
5026
|
+
outline: var(--ns-item-selected-active-outline, 2px solid #0066cc);
|
|
5027
|
+
outline-offset: -2px;
|
|
5028
|
+
}
|
|
5029
|
+
|
|
4841
5030
|
.ns-item[aria-disabled="true"] {
|
|
4842
5031
|
opacity: 0.5;
|
|
4843
5032
|
cursor: not-allowed;
|