@smilodon/core 1.4.9 → 1.4.10

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.js CHANGED
@@ -1873,7 +1873,22 @@ class EnhancedSelect extends HTMLElement {
1873
1873
  }
1874
1874
  set classMap(map) {
1875
1875
  this._classMap = map;
1876
- this._setGlobalStylesMirroring(Boolean(this._optionRenderer || map));
1876
+ this._setGlobalStylesMirroring(Boolean(this._optionRenderer || map || this._groupHeaderRenderer));
1877
+ if (!this.isConnected)
1878
+ return;
1879
+ this._renderOptions();
1880
+ }
1881
+ /**
1882
+ * DOM-based renderer for group headers. When provided, the component will
1883
+ * call this function for each group during rendering. The returned element
1884
+ * will receive `.group-header` and `part="group-header"` automatically.
1885
+ */
1886
+ get groupHeaderRenderer() {
1887
+ return this._groupHeaderRenderer;
1888
+ }
1889
+ set groupHeaderRenderer(renderer) {
1890
+ this._groupHeaderRenderer = renderer;
1891
+ this._setGlobalStylesMirroring(Boolean(this._optionRenderer || this._classMap || renderer));
1877
1892
  if (!this.isConnected)
1878
1893
  return;
1879
1894
  this._renderOptions();
@@ -1932,6 +1947,8 @@ class EnhancedSelect extends HTMLElement {
1932
1947
  this._initializeObservers();
1933
1948
  }
1934
1949
  connectedCallback() {
1950
+ // register instance
1951
+ EnhancedSelect._instances.add(this);
1935
1952
  // WORKAROUND: Force display style on host element for Angular compatibility
1936
1953
  // Angular's rendering seems to not apply :host styles correctly in some cases
1937
1954
  // Must be done in connectedCallback when element is attached to DOM
@@ -1950,6 +1967,8 @@ class EnhancedSelect extends HTMLElement {
1950
1967
  }
1951
1968
  }
1952
1969
  disconnectedCallback() {
1970
+ // unregister instance
1971
+ EnhancedSelect._instances.delete(this);
1953
1972
  // Cleanup observers
1954
1973
  this._resizeObserver?.disconnect();
1955
1974
  this._intersectionObserver?.disconnect();
@@ -2235,6 +2254,8 @@ class EnhancedSelect extends HTMLElement {
2235
2254
  right: 0;
2236
2255
  bottom: 0;
2237
2256
  width: var(--select-arrow-width, 40px);
2257
+ /* allow explicit height override even though container normally stretches */
2258
+ height: var(--select-arrow-height, auto);
2238
2259
  display: flex;
2239
2260
  align-items: center;
2240
2261
  justify-content: center;
@@ -2300,9 +2321,14 @@ class EnhancedSelect extends HTMLElement {
2300
2321
  background-color: var(--select-arrow-hover-bg, rgba(102, 126, 234, 0.08));
2301
2322
  }
2302
2323
 
2324
+ .dropdown-arrow:hover {
2325
+ /* legacy alias --select-arrow-hover for icon color */
2326
+ color: var(--select-arrow-hover, var(--select-arrow-hover-color, #667eea));
2327
+ }
2328
+
2303
2329
  .dropdown-arrow {
2304
- width: var(--select-arrow-size, 16px);
2305
- height: var(--select-arrow-size, 16px);
2330
+ width: var(--select-arrow-width, var(--select-arrow-size, 16px));
2331
+ height: var(--select-arrow-height, var(--select-arrow-size, 16px));
2306
2332
  color: var(--select-arrow-color, #667eea);
2307
2333
  transition: transform 0.2s ease, color 0.2s ease;
2308
2334
  transform: translateY(0);
@@ -2656,6 +2682,13 @@ class EnhancedSelect extends HTMLElement {
2656
2682
 
2657
2683
  .option.active:not(.selected) {
2658
2684
  background-color: var(--select-dark-option-active-bg, #374151);
2685
+ }
2686
+
2687
+ /* Group header in dark mode */
2688
+ .group-header {
2689
+ color: var(--select-dark-group-header-color, var(--select-group-header-color, #6b7280));
2690
+ background-color: var(--select-dark-group-header-bg, var(--select-group-header-bg, #374151));
2691
+ }
2659
2692
  color: var(--select-dark-option-active-color, #f9fafb);
2660
2693
  outline: var(--select-dark-option-active-outline, 2px solid rgba(129, 140, 248, 0.55));
2661
2694
  }
@@ -2738,19 +2771,14 @@ class EnhancedSelect extends HTMLElement {
2738
2771
  this._boundArrowClick = (e) => {
2739
2772
  e.stopPropagation();
2740
2773
  e.preventDefault();
2741
- const wasOpen = this._state.isOpen;
2742
- this._state.isOpen = !this._state.isOpen;
2743
- this._updateDropdownVisibility();
2744
- this._updateArrowRotation();
2745
- if (this._state.isOpen && this._config.callbacks.onOpen) {
2746
- this._config.callbacks.onOpen();
2747
- }
2748
- else if (!this._state.isOpen && this._config.callbacks.onClose) {
2749
- this._config.callbacks.onClose();
2774
+ // delegate to the existing open/close helpers so we don't accidentally
2775
+ // drift out of sync with the logic in those methods (focus, events,
2776
+ // scroll-to-selected, etc.)
2777
+ if (this._state.isOpen) {
2778
+ this._handleClose();
2750
2779
  }
2751
- // Scroll to selected when opening
2752
- if (!wasOpen && this._state.isOpen && this._state.selectedIndices.size > 0) {
2753
- setTimeout(() => this._scrollToSelected(), 50);
2780
+ else {
2781
+ this._handleOpen();
2754
2782
  }
2755
2783
  };
2756
2784
  this._arrowContainer.addEventListener('click', this._boundArrowClick);
@@ -2784,6 +2812,10 @@ class EnhancedSelect extends HTMLElement {
2784
2812
  if (wasClosed) {
2785
2813
  this._handleOpen();
2786
2814
  }
2815
+ else {
2816
+ // clicking the input while open should close the dropdown too
2817
+ this._handleClose();
2818
+ }
2787
2819
  // Focus the input (do not prevent default behavior)
2788
2820
  this._input.focus();
2789
2821
  // If we just opened the dropdown, transfer pointer capture to the
@@ -2959,6 +2991,16 @@ class EnhancedSelect extends HTMLElement {
2959
2991
  _handleOpen() {
2960
2992
  if (!this._config.enabled || this._state.isOpen)
2961
2993
  return;
2994
+ // close any other open selects before proceeding
2995
+ EnhancedSelect._instances.forEach(inst => {
2996
+ if (inst !== this)
2997
+ inst._handleClose();
2998
+ });
2999
+ // Always focus the input when opening so callers (arrow click,
3000
+ // programmatic `open()`, etc.) get the keyboard cursor. This was a
3001
+ // frequent source of confusion in #14 where people opened the dropdown
3002
+ // but the text field never received focus.
3003
+ this._input.focus();
2962
3004
  this._markOpenStart();
2963
3005
  this._state.isOpen = true;
2964
3006
  this._dropdown.style.display = 'block';
@@ -3932,10 +3974,25 @@ class EnhancedSelect extends HTMLElement {
3932
3974
  const query = this._state.searchQuery.toLowerCase();
3933
3975
  // Handle Grouped Items Rendering (when no search query)
3934
3976
  if (this._state.groupedItems.length > 0 && !query) {
3935
- this._state.groupedItems.forEach(group => {
3936
- const header = document.createElement('div');
3937
- header.className = 'group-header';
3938
- header.textContent = group.label;
3977
+ this._state.groupedItems.forEach((group, groupIndex) => {
3978
+ let header;
3979
+ if (this.groupHeaderRenderer) {
3980
+ header = this.groupHeaderRenderer(group, groupIndex);
3981
+ // make sure the returned element has the correct semantics so
3982
+ // people can style it. we add the class/part even if the renderer
3983
+ // returned something else to ensure backward compatibility.
3984
+ if (!(header instanceof HTMLElement)) {
3985
+ // fall back to default if API is misused
3986
+ header = document.createElement('div');
3987
+ header.textContent = String(group.label);
3988
+ }
3989
+ }
3990
+ else {
3991
+ header = document.createElement('div');
3992
+ header.textContent = group.label;
3993
+ }
3994
+ header.classList.add('group-header');
3995
+ header.setAttribute('part', 'group-header');
3939
3996
  this._optionsContainer.appendChild(header);
3940
3997
  group.options.forEach(item => {
3941
3998
  // Find original index for correct ID generation and selection
@@ -4208,6 +4265,8 @@ class EnhancedSelect extends HTMLElement {
4208
4265
  }
4209
4266
  }
4210
4267
  }
4268
+ /** live set of all connected instances; used to auto-close siblings */
4269
+ EnhancedSelect._instances = new Set();
4211
4270
  // Register custom element
4212
4271
  if (!customElements.get('enhanced-select')) {
4213
4272
  customElements.define('enhanced-select', EnhancedSelect);