@smilodon/core 1.4.10 → 1.4.12
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 +260 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +260 -27
- 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 +260 -27
- 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 +11 -1
- package/dist/types/src/config/global-config.d.ts +25 -0
- package/dist/types/src/types.d.ts +58 -0
- package/dist/types/tests/capabilities-tracking.spec.d.ts +1 -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,6 +650,8 @@ 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 */
|
|
@@ -665,9 +674,17 @@ enhanced-select.dark-mode {
|
|
|
665
674
|
--select-group-header-color /* Text color (#6b7280) */
|
|
666
675
|
--select-group-header-bg /* Background (#f3f4f6) */
|
|
667
676
|
--select-group-header-font-size
|
|
677
|
+
--select-group-header-text-align /* Header text alignment (left default) */
|
|
668
678
|
--select-group-header-text-transform
|
|
669
679
|
--select-group-header-letter-spacing
|
|
670
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
|
|
671
688
|
```
|
|
672
689
|
|
|
673
690
|
**Dark Mode (Opt-in)**
|
|
@@ -717,8 +734,19 @@ enhanced-select {
|
|
|
717
734
|
transparent 100%
|
|
718
735
|
);
|
|
719
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
|
+
}
|
|
720
745
|
```
|
|
721
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
|
+
|
|
722
750
|
**Badge Remove/Delete Button (Multi-Select)**
|
|
723
751
|
The × button that removes selected items in multi-select mode is fully customizable:
|
|
724
752
|
|
package/dist/index.cjs
CHANGED
|
@@ -1403,6 +1403,18 @@ const defaultConfig = {
|
|
|
1403
1403
|
icon: '×',
|
|
1404
1404
|
},
|
|
1405
1405
|
callbacks: {},
|
|
1406
|
+
tracking: {
|
|
1407
|
+
enabled: false,
|
|
1408
|
+
events: true,
|
|
1409
|
+
styling: true,
|
|
1410
|
+
limitations: true,
|
|
1411
|
+
emitDiagnostics: false,
|
|
1412
|
+
maxEntries: 200,
|
|
1413
|
+
},
|
|
1414
|
+
limitations: {
|
|
1415
|
+
policies: {},
|
|
1416
|
+
autoMitigateRuntimeModeSwitch: true,
|
|
1417
|
+
},
|
|
1406
1418
|
enabled: true,
|
|
1407
1419
|
searchable: false,
|
|
1408
1420
|
placeholder: 'Select an option...',
|
|
@@ -1534,6 +1546,8 @@ class SelectOption extends HTMLElement {
|
|
|
1534
1546
|
padding: var(--select-option-padding, 8px 12px);
|
|
1535
1547
|
cursor: pointer;
|
|
1536
1548
|
user-select: none;
|
|
1549
|
+
color: var(--select-option-color, var(--select-text-color, #1f2937));
|
|
1550
|
+
background: var(--select-option-bg, var(--select-dropdown-bg, var(--select-bg, white)));
|
|
1537
1551
|
transition: var(--select-option-transition, background-color 0.2s ease);
|
|
1538
1552
|
border: var(--select-option-border, none);
|
|
1539
1553
|
border-bottom: var(--select-option-border-bottom, none);
|
|
@@ -1578,9 +1592,20 @@ class SelectOption extends HTMLElement {
|
|
|
1578
1592
|
|
|
1579
1593
|
.option-content {
|
|
1580
1594
|
flex: 1;
|
|
1581
|
-
overflow: hidden;
|
|
1582
|
-
text-overflow: ellipsis;
|
|
1583
|
-
white-space: nowrap;
|
|
1595
|
+
overflow: var(--select-option-content-overflow, hidden);
|
|
1596
|
+
text-overflow: var(--select-option-content-text-overflow, ellipsis);
|
|
1597
|
+
white-space: var(--select-option-content-white-space, nowrap);
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
.checkmark-icon {
|
|
1601
|
+
display: none;
|
|
1602
|
+
margin-left: var(--select-checkmark-margin-left, 8px);
|
|
1603
|
+
color: var(--select-checkmark-color, currentColor);
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
:host([aria-selected="true"]) .checkmark-icon,
|
|
1607
|
+
.option-container.selected .checkmark-icon {
|
|
1608
|
+
display: inline-flex;
|
|
1584
1609
|
}
|
|
1585
1610
|
|
|
1586
1611
|
.remove-button {
|
|
@@ -1708,16 +1733,6 @@ class SelectOption extends HTMLElement {
|
|
|
1708
1733
|
<path d="M4 8.5L6.5 11L12 5.5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
1709
1734
|
</svg>
|
|
1710
1735
|
`;
|
|
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
1736
|
this._container.appendChild(checkmark);
|
|
1722
1737
|
}
|
|
1723
1738
|
// Data Attributes Contract on Host
|
|
@@ -1876,6 +1891,10 @@ class EnhancedSelect extends HTMLElement {
|
|
|
1876
1891
|
set classMap(map) {
|
|
1877
1892
|
this._classMap = map;
|
|
1878
1893
|
this._setGlobalStylesMirroring(Boolean(this._optionRenderer || map || this._groupHeaderRenderer));
|
|
1894
|
+
this._track('style', 'classMapChanged', {
|
|
1895
|
+
hasClassMap: Boolean(map),
|
|
1896
|
+
keys: map ? Object.keys(map) : [],
|
|
1897
|
+
});
|
|
1879
1898
|
if (!this.isConnected)
|
|
1880
1899
|
return;
|
|
1881
1900
|
this._renderOptions();
|
|
@@ -1891,6 +1910,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
1891
1910
|
set groupHeaderRenderer(renderer) {
|
|
1892
1911
|
this._groupHeaderRenderer = renderer;
|
|
1893
1912
|
this._setGlobalStylesMirroring(Boolean(this._optionRenderer || this._classMap || renderer));
|
|
1913
|
+
this._track('style', 'groupHeaderRendererChanged', { enabled: Boolean(renderer) });
|
|
1894
1914
|
if (!this.isConnected)
|
|
1895
1915
|
return;
|
|
1896
1916
|
this._renderOptions();
|
|
@@ -1909,6 +1929,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
1909
1929
|
this._mirrorGlobalStylesForCustomOptions = false;
|
|
1910
1930
|
this._globalStylesObserver = null;
|
|
1911
1931
|
this._globalStylesContainer = null;
|
|
1932
|
+
this._tracking = { events: [], styles: [], limitations: [] };
|
|
1912
1933
|
this._shadow = this.attachShadow({ mode: 'open' });
|
|
1913
1934
|
this._uniqueId = `enhanced-select-${Math.random().toString(36).substr(2, 9)}`;
|
|
1914
1935
|
this._rendererHelpers = this._buildRendererHelpers();
|
|
@@ -1955,7 +1976,6 @@ class EnhancedSelect extends HTMLElement {
|
|
|
1955
1976
|
// Angular's rendering seems to not apply :host styles correctly in some cases
|
|
1956
1977
|
// Must be done in connectedCallback when element is attached to DOM
|
|
1957
1978
|
this.style.display = 'block';
|
|
1958
|
-
this.style.width = '100%';
|
|
1959
1979
|
if (this._optionRenderer) {
|
|
1960
1980
|
this._setGlobalStylesMirroring(true);
|
|
1961
1981
|
}
|
|
@@ -1994,6 +2014,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
1994
2014
|
return;
|
|
1995
2015
|
}
|
|
1996
2016
|
this._mirrorGlobalStylesForCustomOptions = enabled;
|
|
2017
|
+
this._track('style', 'globalStylesMirroringChanged', { enabled });
|
|
1997
2018
|
if (enabled) {
|
|
1998
2019
|
this._setupGlobalStylesMirroring();
|
|
1999
2020
|
}
|
|
@@ -2198,7 +2219,8 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2198
2219
|
:host {
|
|
2199
2220
|
display: block;
|
|
2200
2221
|
position: relative;
|
|
2201
|
-
width: 100
|
|
2222
|
+
width: var(--select-width, 100%);
|
|
2223
|
+
height: var(--select-height, auto);
|
|
2202
2224
|
}
|
|
2203
2225
|
|
|
2204
2226
|
.select-container {
|
|
@@ -2214,6 +2236,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2214
2236
|
flex-wrap: wrap;
|
|
2215
2237
|
gap: var(--select-input-gap, 6px);
|
|
2216
2238
|
padding: var(--select-input-padding, 6px 52px 6px 8px);
|
|
2239
|
+
height: var(--select-input-height, auto);
|
|
2217
2240
|
min-height: var(--select-input-min-height, 44px);
|
|
2218
2241
|
max-height: var(--select-input-max-height, 160px);
|
|
2219
2242
|
overflow-y: var(--select-input-overflow-y, auto);
|
|
@@ -2235,17 +2258,17 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2235
2258
|
content: '';
|
|
2236
2259
|
position: absolute;
|
|
2237
2260
|
top: 50%;
|
|
2238
|
-
right: var(--select-separator-position, 40px);
|
|
2261
|
+
right: var(--select-separator-position, var(--select-seperator-position, 40px));
|
|
2239
2262
|
transform: translateY(-50%);
|
|
2240
|
-
width: var(--select-separator-width, 1px);
|
|
2241
|
-
height: var(--select-separator-height, 60%);
|
|
2242
|
-
background: var(--select-separator-bg, var(--select-separator-gradient, linear-gradient(
|
|
2263
|
+
width: var(--select-separator-width, var(--select-seperator-width, 1px));
|
|
2264
|
+
height: var(--select-separator-height, var(--select-seperator-height, 60%));
|
|
2265
|
+
background: var(--select-separator-bg, var(--select-seperator-bg, var(--select-separator-gradient, var(--select-seperator-gradient, linear-gradient(
|
|
2243
2266
|
to bottom,
|
|
2244
2267
|
transparent 0%,
|
|
2245
2268
|
rgba(0, 0, 0, 0.1) 20%,
|
|
2246
2269
|
rgba(0, 0, 0, 0.1) 80%,
|
|
2247
2270
|
transparent 100%
|
|
2248
|
-
)));
|
|
2271
|
+
))));
|
|
2249
2272
|
pointer-events: none;
|
|
2250
2273
|
z-index: 1;
|
|
2251
2274
|
}
|
|
@@ -2272,7 +2295,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2272
2295
|
}
|
|
2273
2296
|
|
|
2274
2297
|
.input-container.has-clear-control::after {
|
|
2275
|
-
right: var(--select-separator-position-with-clear, 72px);
|
|
2298
|
+
right: var(--select-separator-position-with-clear, var(--select-seperator-position-with-clear, 72px));
|
|
2276
2299
|
}
|
|
2277
2300
|
|
|
2278
2301
|
.dropdown-arrow-container.with-clear-control {
|
|
@@ -2350,6 +2373,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2350
2373
|
|
|
2351
2374
|
.select-input {
|
|
2352
2375
|
flex: 1;
|
|
2376
|
+
width: var(--select-input-width, auto);
|
|
2353
2377
|
min-width: var(--select-input-min-width, 120px);
|
|
2354
2378
|
padding: var(--select-input-field-padding, 4px);
|
|
2355
2379
|
border: none;
|
|
@@ -2444,6 +2468,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2444
2468
|
font-weight: var(--select-group-header-weight, 600);
|
|
2445
2469
|
color: var(--select-group-header-color, #6b7280);
|
|
2446
2470
|
background-color: var(--select-group-header-bg, #f3f4f6);
|
|
2471
|
+
text-align: var(--select-group-header-text-align, left);
|
|
2447
2472
|
font-size: var(--select-group-header-font-size, 12px);
|
|
2448
2473
|
text-transform: var(--select-group-header-text-transform, uppercase);
|
|
2449
2474
|
letter-spacing: var(--select-group-header-letter-spacing, 0.05em);
|
|
@@ -2628,10 +2653,25 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2628
2653
|
|
|
2629
2654
|
/* Dark mode - Opt-in via class, data attribute, or ancestor context */
|
|
2630
2655
|
:host(.dark-mode),
|
|
2656
|
+
:host([dark-mode]),
|
|
2657
|
+
:host([darkmode]),
|
|
2631
2658
|
:host([data-theme="dark"]),
|
|
2659
|
+
:host([theme="dark"]),
|
|
2632
2660
|
:host-context(.dark-mode),
|
|
2633
2661
|
:host-context(.dark),
|
|
2634
|
-
:host-context([
|
|
2662
|
+
:host-context([dark-mode]),
|
|
2663
|
+
:host-context([darkmode]),
|
|
2664
|
+
:host-context([data-theme="dark"]),
|
|
2665
|
+
:host-context([theme="dark"]) {
|
|
2666
|
+
/* map dark tokens to base option tokens so nested <select-option>
|
|
2667
|
+
components also pick up dark mode via inherited CSS variables */
|
|
2668
|
+
--select-option-bg: var(--select-dark-option-bg, #1f2937);
|
|
2669
|
+
--select-option-color: var(--select-dark-option-color, #f9fafb);
|
|
2670
|
+
--select-option-hover-bg: var(--select-dark-option-hover-bg, #374151);
|
|
2671
|
+
--select-option-hover-color: var(--select-dark-option-hover-color, #f9fafb);
|
|
2672
|
+
--select-option-selected-bg: var(--select-dark-option-selected-bg, #3730a3);
|
|
2673
|
+
--select-option-selected-color: var(--select-dark-option-selected-text, #e0e7ff);
|
|
2674
|
+
|
|
2635
2675
|
.input-container {
|
|
2636
2676
|
background: var(--select-dark-bg, #1f2937);
|
|
2637
2677
|
border-color: var(--select-dark-border, #4b5563);
|
|
@@ -2684,6 +2724,8 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2684
2724
|
|
|
2685
2725
|
.option.active:not(.selected) {
|
|
2686
2726
|
background-color: var(--select-dark-option-active-bg, #374151);
|
|
2727
|
+
color: var(--select-dark-option-active-color, #f9fafb);
|
|
2728
|
+
outline: var(--select-dark-option-active-outline, 2px solid rgba(129, 140, 248, 0.55));
|
|
2687
2729
|
}
|
|
2688
2730
|
|
|
2689
2731
|
/* Group header in dark mode */
|
|
@@ -2691,9 +2733,6 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2691
2733
|
color: var(--select-dark-group-header-color, var(--select-group-header-color, #6b7280));
|
|
2692
2734
|
background-color: var(--select-dark-group-header-bg, var(--select-group-header-bg, #374151));
|
|
2693
2735
|
}
|
|
2694
|
-
color: var(--select-dark-option-active-color, #f9fafb);
|
|
2695
|
-
outline: var(--select-dark-option-active-outline, 2px solid rgba(129, 140, 248, 0.55));
|
|
2696
|
-
}
|
|
2697
2736
|
|
|
2698
2737
|
.option.selected.active {
|
|
2699
2738
|
background-color: var(--select-dark-option-selected-active-bg, var(--select-dark-option-selected-bg, #3730a3));
|
|
@@ -2815,7 +2854,13 @@ class EnhancedSelect extends HTMLElement {
|
|
|
2815
2854
|
this._handleOpen();
|
|
2816
2855
|
}
|
|
2817
2856
|
else {
|
|
2818
|
-
//
|
|
2857
|
+
// Keep open while interacting directly with the input so users can
|
|
2858
|
+
// place cursor/type without accidental collapse.
|
|
2859
|
+
if (target === this._input) {
|
|
2860
|
+
this._input.focus();
|
|
2861
|
+
return;
|
|
2862
|
+
}
|
|
2863
|
+
// clicking other parts of the input container while open toggles close
|
|
2819
2864
|
this._handleClose();
|
|
2820
2865
|
}
|
|
2821
2866
|
// Focus the input (do not prevent default behavior)
|
|
@@ -3650,6 +3695,162 @@ class EnhancedSelect extends HTMLElement {
|
|
|
3650
3695
|
}
|
|
3651
3696
|
_emit(name, detail) {
|
|
3652
3697
|
this.dispatchEvent(new CustomEvent(name, { detail, bubbles: true, composed: true }));
|
|
3698
|
+
if (name !== 'diagnostic') {
|
|
3699
|
+
this._track('event', String(name), detail);
|
|
3700
|
+
}
|
|
3701
|
+
}
|
|
3702
|
+
_track(source, name, detail) {
|
|
3703
|
+
const cfg = this._config.tracking;
|
|
3704
|
+
if (!cfg?.enabled)
|
|
3705
|
+
return;
|
|
3706
|
+
if (source === 'event' && !cfg.events)
|
|
3707
|
+
return;
|
|
3708
|
+
if (source === 'style' && !cfg.styling)
|
|
3709
|
+
return;
|
|
3710
|
+
if (source === 'limitation' && !cfg.limitations)
|
|
3711
|
+
return;
|
|
3712
|
+
const entry = {
|
|
3713
|
+
timestamp: Date.now(),
|
|
3714
|
+
source,
|
|
3715
|
+
name,
|
|
3716
|
+
detail,
|
|
3717
|
+
};
|
|
3718
|
+
const bucket = source === 'event'
|
|
3719
|
+
? this._tracking.events
|
|
3720
|
+
: source === 'style'
|
|
3721
|
+
? this._tracking.styles
|
|
3722
|
+
: this._tracking.limitations;
|
|
3723
|
+
bucket.push(entry);
|
|
3724
|
+
const maxEntries = Math.max(10, cfg.maxEntries || 200);
|
|
3725
|
+
if (bucket.length > maxEntries) {
|
|
3726
|
+
bucket.splice(0, bucket.length - maxEntries);
|
|
3727
|
+
}
|
|
3728
|
+
if (cfg.emitDiagnostics) {
|
|
3729
|
+
this.dispatchEvent(new CustomEvent('diagnostic', {
|
|
3730
|
+
detail: entry,
|
|
3731
|
+
bubbles: true,
|
|
3732
|
+
composed: true,
|
|
3733
|
+
}));
|
|
3734
|
+
}
|
|
3735
|
+
}
|
|
3736
|
+
_getKnownLimitationDefinitions() {
|
|
3737
|
+
return [
|
|
3738
|
+
{
|
|
3739
|
+
id: 'variableItemHeight',
|
|
3740
|
+
title: 'Variable item height',
|
|
3741
|
+
description: 'Virtualization assumes fixed or estimated item heights; fully dynamic heights are not yet supported.',
|
|
3742
|
+
workaround: 'Use consistent item heights or set estimatedItemHeight to your dominant row size.',
|
|
3743
|
+
},
|
|
3744
|
+
{
|
|
3745
|
+
id: 'builtInFetchPaginationApi',
|
|
3746
|
+
title: 'Built-in fetch/pagination API',
|
|
3747
|
+
description: 'Core does not include a built-in fetchUrl/searchUrl pagination transport.',
|
|
3748
|
+
workaround: 'Use onSearch/onLoadMore callbacks and update data via setItems().',
|
|
3749
|
+
},
|
|
3750
|
+
{
|
|
3751
|
+
id: 'virtualizationOverheadSmallLists',
|
|
3752
|
+
title: 'Virtualization overhead for small lists',
|
|
3753
|
+
description: 'Virtualization can add slight overhead on very small lists.',
|
|
3754
|
+
workaround: 'Disable virtualization for tiny datasets when micro-latency is critical.',
|
|
3755
|
+
},
|
|
3756
|
+
{
|
|
3757
|
+
id: 'runtimeModeSwitching',
|
|
3758
|
+
title: 'Runtime single/multi mode switching',
|
|
3759
|
+
description: 'Switching between single and multi mode can require state reset for consistency.',
|
|
3760
|
+
workaround: 'Enable autoMitigateRuntimeModeSwitch or recreate/reset component state when toggling modes.',
|
|
3761
|
+
},
|
|
3762
|
+
{
|
|
3763
|
+
id: 'legacyBrowserSupport',
|
|
3764
|
+
title: 'Legacy browser support',
|
|
3765
|
+
description: 'Official support targets modern evergreen browsers.',
|
|
3766
|
+
},
|
|
3767
|
+
{
|
|
3768
|
+
id: 'webkitArchLinux',
|
|
3769
|
+
title: 'Playwright WebKit on Arch-based Linux',
|
|
3770
|
+
description: 'Native WebKit Playwright bundle depends on unavailable legacy system libraries on Arch-based distros.',
|
|
3771
|
+
workaround: 'Run WebKit E2E tests via Playwright Docker image.',
|
|
3772
|
+
},
|
|
3773
|
+
];
|
|
3774
|
+
}
|
|
3775
|
+
_evaluateLimitationStatus(id) {
|
|
3776
|
+
const policyMode = this._config.limitations?.policies?.[id]?.mode ?? 'default';
|
|
3777
|
+
if (policyMode === 'suppress')
|
|
3778
|
+
return 'suppressed';
|
|
3779
|
+
if (id === 'runtimeModeSwitching' && this._config.limitations?.autoMitigateRuntimeModeSwitch) {
|
|
3780
|
+
return 'mitigated';
|
|
3781
|
+
}
|
|
3782
|
+
return 'active';
|
|
3783
|
+
}
|
|
3784
|
+
getKnownLimitations() {
|
|
3785
|
+
return this._getKnownLimitationDefinitions().map((limitation) => {
|
|
3786
|
+
const mode = this._config.limitations?.policies?.[limitation.id]?.mode ?? 'default';
|
|
3787
|
+
return {
|
|
3788
|
+
...limitation,
|
|
3789
|
+
mode,
|
|
3790
|
+
status: this._evaluateLimitationStatus(limitation.id),
|
|
3791
|
+
};
|
|
3792
|
+
});
|
|
3793
|
+
}
|
|
3794
|
+
setLimitationPolicies(policies) {
|
|
3795
|
+
const next = {
|
|
3796
|
+
...(this._config.limitations?.policies || {}),
|
|
3797
|
+
...policies,
|
|
3798
|
+
};
|
|
3799
|
+
this.updateConfig({
|
|
3800
|
+
limitations: {
|
|
3801
|
+
...(this._config.limitations || { autoMitigateRuntimeModeSwitch: true, policies: {} }),
|
|
3802
|
+
policies: next,
|
|
3803
|
+
},
|
|
3804
|
+
});
|
|
3805
|
+
this._track('limitation', 'policiesUpdated', { policies: next });
|
|
3806
|
+
}
|
|
3807
|
+
getTrackingSnapshot() {
|
|
3808
|
+
return {
|
|
3809
|
+
events: [...this._tracking.events],
|
|
3810
|
+
styles: [...this._tracking.styles],
|
|
3811
|
+
limitations: [...this._tracking.limitations],
|
|
3812
|
+
};
|
|
3813
|
+
}
|
|
3814
|
+
clearTracking(source) {
|
|
3815
|
+
if (!source || source === 'all') {
|
|
3816
|
+
this._tracking.events = [];
|
|
3817
|
+
this._tracking.styles = [];
|
|
3818
|
+
this._tracking.limitations = [];
|
|
3819
|
+
return;
|
|
3820
|
+
}
|
|
3821
|
+
if (source === 'event')
|
|
3822
|
+
this._tracking.events = [];
|
|
3823
|
+
if (source === 'style')
|
|
3824
|
+
this._tracking.styles = [];
|
|
3825
|
+
if (source === 'limitation')
|
|
3826
|
+
this._tracking.limitations = [];
|
|
3827
|
+
}
|
|
3828
|
+
getCapabilities() {
|
|
3829
|
+
return {
|
|
3830
|
+
styling: {
|
|
3831
|
+
classMap: true,
|
|
3832
|
+
optionRenderer: true,
|
|
3833
|
+
groupHeaderRenderer: true,
|
|
3834
|
+
cssCustomProperties: true,
|
|
3835
|
+
shadowParts: true,
|
|
3836
|
+
globalStyleMirroring: true,
|
|
3837
|
+
},
|
|
3838
|
+
events: {
|
|
3839
|
+
emitted: ['select', 'open', 'close', 'search', 'change', 'loadMore', 'remove', 'clear', 'error', 'diagnostic'],
|
|
3840
|
+
diagnosticEvent: true,
|
|
3841
|
+
},
|
|
3842
|
+
functionality: {
|
|
3843
|
+
multiSelect: true,
|
|
3844
|
+
searchable: true,
|
|
3845
|
+
infiniteScroll: true,
|
|
3846
|
+
loadMore: true,
|
|
3847
|
+
clearControl: true,
|
|
3848
|
+
groupedItems: true,
|
|
3849
|
+
serverSideSelection: true,
|
|
3850
|
+
runtimeModeSwitchMitigation: Boolean(this._config.limitations?.autoMitigateRuntimeModeSwitch),
|
|
3851
|
+
},
|
|
3852
|
+
limitations: this.getKnownLimitations(),
|
|
3853
|
+
};
|
|
3653
3854
|
}
|
|
3654
3855
|
_emitChange() {
|
|
3655
3856
|
const selectedItems = Array.from(this._state.selectedItems.values());
|
|
@@ -3667,6 +3868,7 @@ class EnhancedSelect extends HTMLElement {
|
|
|
3667
3868
|
set optionRenderer(renderer) {
|
|
3668
3869
|
this._optionRenderer = renderer;
|
|
3669
3870
|
this._setGlobalStylesMirroring(Boolean(renderer || this._classMap));
|
|
3871
|
+
this._track('style', 'optionRendererChanged', { enabled: Boolean(renderer) });
|
|
3670
3872
|
this._renderOptions();
|
|
3671
3873
|
}
|
|
3672
3874
|
/**
|
|
@@ -3831,7 +4033,22 @@ class EnhancedSelect extends HTMLElement {
|
|
|
3831
4033
|
* Update component configuration
|
|
3832
4034
|
*/
|
|
3833
4035
|
updateConfig(config) {
|
|
3834
|
-
|
|
4036
|
+
const previousMode = this._config.selection.mode;
|
|
4037
|
+
this._config = this._mergeConfig(this._config, config);
|
|
4038
|
+
if (previousMode !== this._config.selection.mode &&
|
|
4039
|
+
this._config.limitations?.autoMitigateRuntimeModeSwitch) {
|
|
4040
|
+
this.clear();
|
|
4041
|
+
this._track('limitation', 'runtimeModeSwitchMitigated', {
|
|
4042
|
+
from: previousMode,
|
|
4043
|
+
to: this._config.selection.mode,
|
|
4044
|
+
});
|
|
4045
|
+
}
|
|
4046
|
+
else if (previousMode !== this._config.selection.mode) {
|
|
4047
|
+
this._track('limitation', 'runtimeModeSwitchDetected', {
|
|
4048
|
+
from: previousMode,
|
|
4049
|
+
to: this._config.selection.mode,
|
|
4050
|
+
});
|
|
4051
|
+
}
|
|
3835
4052
|
// Update input state based on new config
|
|
3836
4053
|
if (this._input) {
|
|
3837
4054
|
this._input.readOnly = !this._config.searchable;
|
|
@@ -3859,6 +4076,22 @@ class EnhancedSelect extends HTMLElement {
|
|
|
3859
4076
|
this._syncClearControlState();
|
|
3860
4077
|
this._renderOptions();
|
|
3861
4078
|
}
|
|
4079
|
+
_mergeConfig(target, source) {
|
|
4080
|
+
const result = { ...target };
|
|
4081
|
+
for (const key in source) {
|
|
4082
|
+
if (!Object.prototype.hasOwnProperty.call(source, key))
|
|
4083
|
+
continue;
|
|
4084
|
+
const sourceValue = source[key];
|
|
4085
|
+
const targetValue = result[key];
|
|
4086
|
+
if (sourceValue && typeof sourceValue === 'object' && !Array.isArray(sourceValue)) {
|
|
4087
|
+
result[key] = this._mergeConfig(targetValue && typeof targetValue === 'object' ? targetValue : {}, sourceValue);
|
|
4088
|
+
}
|
|
4089
|
+
else {
|
|
4090
|
+
result[key] = sourceValue;
|
|
4091
|
+
}
|
|
4092
|
+
}
|
|
4093
|
+
return result;
|
|
4094
|
+
}
|
|
3862
4095
|
_handleClearControlClick() {
|
|
3863
4096
|
const shouldClearSelection = this._config.clearControl.clearSelection !== false;
|
|
3864
4097
|
const shouldClearSearch = this._config.clearControl.clearSearch !== false;
|