@smilodon/core 1.4.11 → 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/dist/index.umd.js CHANGED
@@ -1407,6 +1407,18 @@
1407
1407
  icon: '×',
1408
1408
  },
1409
1409
  callbacks: {},
1410
+ tracking: {
1411
+ enabled: false,
1412
+ events: true,
1413
+ styling: true,
1414
+ limitations: true,
1415
+ emitDiagnostics: false,
1416
+ maxEntries: 200,
1417
+ },
1418
+ limitations: {
1419
+ policies: {},
1420
+ autoMitigateRuntimeModeSwitch: true,
1421
+ },
1410
1422
  enabled: true,
1411
1423
  searchable: false,
1412
1424
  placeholder: 'Select an option...',
@@ -1883,6 +1895,10 @@
1883
1895
  set classMap(map) {
1884
1896
  this._classMap = map;
1885
1897
  this._setGlobalStylesMirroring(Boolean(this._optionRenderer || map || this._groupHeaderRenderer));
1898
+ this._track('style', 'classMapChanged', {
1899
+ hasClassMap: Boolean(map),
1900
+ keys: map ? Object.keys(map) : [],
1901
+ });
1886
1902
  if (!this.isConnected)
1887
1903
  return;
1888
1904
  this._renderOptions();
@@ -1898,6 +1914,7 @@
1898
1914
  set groupHeaderRenderer(renderer) {
1899
1915
  this._groupHeaderRenderer = renderer;
1900
1916
  this._setGlobalStylesMirroring(Boolean(this._optionRenderer || this._classMap || renderer));
1917
+ this._track('style', 'groupHeaderRendererChanged', { enabled: Boolean(renderer) });
1901
1918
  if (!this.isConnected)
1902
1919
  return;
1903
1920
  this._renderOptions();
@@ -1916,6 +1933,7 @@
1916
1933
  this._mirrorGlobalStylesForCustomOptions = false;
1917
1934
  this._globalStylesObserver = null;
1918
1935
  this._globalStylesContainer = null;
1936
+ this._tracking = { events: [], styles: [], limitations: [] };
1919
1937
  this._shadow = this.attachShadow({ mode: 'open' });
1920
1938
  this._uniqueId = `enhanced-select-${Math.random().toString(36).substr(2, 9)}`;
1921
1939
  this._rendererHelpers = this._buildRendererHelpers();
@@ -2000,6 +2018,7 @@
2000
2018
  return;
2001
2019
  }
2002
2020
  this._mirrorGlobalStylesForCustomOptions = enabled;
2021
+ this._track('style', 'globalStylesMirroringChanged', { enabled });
2003
2022
  if (enabled) {
2004
2023
  this._setupGlobalStylesMirroring();
2005
2024
  }
@@ -3680,6 +3699,162 @@
3680
3699
  }
3681
3700
  _emit(name, detail) {
3682
3701
  this.dispatchEvent(new CustomEvent(name, { detail, bubbles: true, composed: true }));
3702
+ if (name !== 'diagnostic') {
3703
+ this._track('event', String(name), detail);
3704
+ }
3705
+ }
3706
+ _track(source, name, detail) {
3707
+ const cfg = this._config.tracking;
3708
+ if (!cfg?.enabled)
3709
+ return;
3710
+ if (source === 'event' && !cfg.events)
3711
+ return;
3712
+ if (source === 'style' && !cfg.styling)
3713
+ return;
3714
+ if (source === 'limitation' && !cfg.limitations)
3715
+ return;
3716
+ const entry = {
3717
+ timestamp: Date.now(),
3718
+ source,
3719
+ name,
3720
+ detail,
3721
+ };
3722
+ const bucket = source === 'event'
3723
+ ? this._tracking.events
3724
+ : source === 'style'
3725
+ ? this._tracking.styles
3726
+ : this._tracking.limitations;
3727
+ bucket.push(entry);
3728
+ const maxEntries = Math.max(10, cfg.maxEntries || 200);
3729
+ if (bucket.length > maxEntries) {
3730
+ bucket.splice(0, bucket.length - maxEntries);
3731
+ }
3732
+ if (cfg.emitDiagnostics) {
3733
+ this.dispatchEvent(new CustomEvent('diagnostic', {
3734
+ detail: entry,
3735
+ bubbles: true,
3736
+ composed: true,
3737
+ }));
3738
+ }
3739
+ }
3740
+ _getKnownLimitationDefinitions() {
3741
+ return [
3742
+ {
3743
+ id: 'variableItemHeight',
3744
+ title: 'Variable item height',
3745
+ description: 'Virtualization assumes fixed or estimated item heights; fully dynamic heights are not yet supported.',
3746
+ workaround: 'Use consistent item heights or set estimatedItemHeight to your dominant row size.',
3747
+ },
3748
+ {
3749
+ id: 'builtInFetchPaginationApi',
3750
+ title: 'Built-in fetch/pagination API',
3751
+ description: 'Core does not include a built-in fetchUrl/searchUrl pagination transport.',
3752
+ workaround: 'Use onSearch/onLoadMore callbacks and update data via setItems().',
3753
+ },
3754
+ {
3755
+ id: 'virtualizationOverheadSmallLists',
3756
+ title: 'Virtualization overhead for small lists',
3757
+ description: 'Virtualization can add slight overhead on very small lists.',
3758
+ workaround: 'Disable virtualization for tiny datasets when micro-latency is critical.',
3759
+ },
3760
+ {
3761
+ id: 'runtimeModeSwitching',
3762
+ title: 'Runtime single/multi mode switching',
3763
+ description: 'Switching between single and multi mode can require state reset for consistency.',
3764
+ workaround: 'Enable autoMitigateRuntimeModeSwitch or recreate/reset component state when toggling modes.',
3765
+ },
3766
+ {
3767
+ id: 'legacyBrowserSupport',
3768
+ title: 'Legacy browser support',
3769
+ description: 'Official support targets modern evergreen browsers.',
3770
+ },
3771
+ {
3772
+ id: 'webkitArchLinux',
3773
+ title: 'Playwright WebKit on Arch-based Linux',
3774
+ description: 'Native WebKit Playwright bundle depends on unavailable legacy system libraries on Arch-based distros.',
3775
+ workaround: 'Run WebKit E2E tests via Playwright Docker image.',
3776
+ },
3777
+ ];
3778
+ }
3779
+ _evaluateLimitationStatus(id) {
3780
+ const policyMode = this._config.limitations?.policies?.[id]?.mode ?? 'default';
3781
+ if (policyMode === 'suppress')
3782
+ return 'suppressed';
3783
+ if (id === 'runtimeModeSwitching' && this._config.limitations?.autoMitigateRuntimeModeSwitch) {
3784
+ return 'mitigated';
3785
+ }
3786
+ return 'active';
3787
+ }
3788
+ getKnownLimitations() {
3789
+ return this._getKnownLimitationDefinitions().map((limitation) => {
3790
+ const mode = this._config.limitations?.policies?.[limitation.id]?.mode ?? 'default';
3791
+ return {
3792
+ ...limitation,
3793
+ mode,
3794
+ status: this._evaluateLimitationStatus(limitation.id),
3795
+ };
3796
+ });
3797
+ }
3798
+ setLimitationPolicies(policies) {
3799
+ const next = {
3800
+ ...(this._config.limitations?.policies || {}),
3801
+ ...policies,
3802
+ };
3803
+ this.updateConfig({
3804
+ limitations: {
3805
+ ...(this._config.limitations || { autoMitigateRuntimeModeSwitch: true, policies: {} }),
3806
+ policies: next,
3807
+ },
3808
+ });
3809
+ this._track('limitation', 'policiesUpdated', { policies: next });
3810
+ }
3811
+ getTrackingSnapshot() {
3812
+ return {
3813
+ events: [...this._tracking.events],
3814
+ styles: [...this._tracking.styles],
3815
+ limitations: [...this._tracking.limitations],
3816
+ };
3817
+ }
3818
+ clearTracking(source) {
3819
+ if (!source || source === 'all') {
3820
+ this._tracking.events = [];
3821
+ this._tracking.styles = [];
3822
+ this._tracking.limitations = [];
3823
+ return;
3824
+ }
3825
+ if (source === 'event')
3826
+ this._tracking.events = [];
3827
+ if (source === 'style')
3828
+ this._tracking.styles = [];
3829
+ if (source === 'limitation')
3830
+ this._tracking.limitations = [];
3831
+ }
3832
+ getCapabilities() {
3833
+ return {
3834
+ styling: {
3835
+ classMap: true,
3836
+ optionRenderer: true,
3837
+ groupHeaderRenderer: true,
3838
+ cssCustomProperties: true,
3839
+ shadowParts: true,
3840
+ globalStyleMirroring: true,
3841
+ },
3842
+ events: {
3843
+ emitted: ['select', 'open', 'close', 'search', 'change', 'loadMore', 'remove', 'clear', 'error', 'diagnostic'],
3844
+ diagnosticEvent: true,
3845
+ },
3846
+ functionality: {
3847
+ multiSelect: true,
3848
+ searchable: true,
3849
+ infiniteScroll: true,
3850
+ loadMore: true,
3851
+ clearControl: true,
3852
+ groupedItems: true,
3853
+ serverSideSelection: true,
3854
+ runtimeModeSwitchMitigation: Boolean(this._config.limitations?.autoMitigateRuntimeModeSwitch),
3855
+ },
3856
+ limitations: this.getKnownLimitations(),
3857
+ };
3683
3858
  }
3684
3859
  _emitChange() {
3685
3860
  const selectedItems = Array.from(this._state.selectedItems.values());
@@ -3697,6 +3872,7 @@
3697
3872
  set optionRenderer(renderer) {
3698
3873
  this._optionRenderer = renderer;
3699
3874
  this._setGlobalStylesMirroring(Boolean(renderer || this._classMap));
3875
+ this._track('style', 'optionRendererChanged', { enabled: Boolean(renderer) });
3700
3876
  this._renderOptions();
3701
3877
  }
3702
3878
  /**
@@ -3861,7 +4037,22 @@
3861
4037
  * Update component configuration
3862
4038
  */
3863
4039
  updateConfig(config) {
3864
- this._config = selectConfig.mergeWithComponentConfig(config);
4040
+ const previousMode = this._config.selection.mode;
4041
+ this._config = this._mergeConfig(this._config, config);
4042
+ if (previousMode !== this._config.selection.mode &&
4043
+ this._config.limitations?.autoMitigateRuntimeModeSwitch) {
4044
+ this.clear();
4045
+ this._track('limitation', 'runtimeModeSwitchMitigated', {
4046
+ from: previousMode,
4047
+ to: this._config.selection.mode,
4048
+ });
4049
+ }
4050
+ else if (previousMode !== this._config.selection.mode) {
4051
+ this._track('limitation', 'runtimeModeSwitchDetected', {
4052
+ from: previousMode,
4053
+ to: this._config.selection.mode,
4054
+ });
4055
+ }
3865
4056
  // Update input state based on new config
3866
4057
  if (this._input) {
3867
4058
  this._input.readOnly = !this._config.searchable;
@@ -3889,6 +4080,22 @@
3889
4080
  this._syncClearControlState();
3890
4081
  this._renderOptions();
3891
4082
  }
4083
+ _mergeConfig(target, source) {
4084
+ const result = { ...target };
4085
+ for (const key in source) {
4086
+ if (!Object.prototype.hasOwnProperty.call(source, key))
4087
+ continue;
4088
+ const sourceValue = source[key];
4089
+ const targetValue = result[key];
4090
+ if (sourceValue && typeof sourceValue === 'object' && !Array.isArray(sourceValue)) {
4091
+ result[key] = this._mergeConfig(targetValue && typeof targetValue === 'object' ? targetValue : {}, sourceValue);
4092
+ }
4093
+ else {
4094
+ result[key] = sourceValue;
4095
+ }
4096
+ }
4097
+ return result;
4098
+ }
3892
4099
  _handleClearControlClick() {
3893
4100
  const shouldClearSelection = this._config.clearControl.clearSelection !== false;
3894
4101
  const shouldClearSearch = this._config.clearControl.clearSearch !== false;