@shortfuse/materialdesignweb 0.11.2 → 0.11.4

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.
@@ -26,6 +26,7 @@ function getSharedPopup() {
26
26
  sharedPopup.matchSourceWidth = true;
27
27
  sharedPopup.flow = 'corner';
28
28
  sharedPopup.elevation = 2;
29
+ sharedPopup.setAttribute('aria-hidden', 'true');
29
30
  }
30
31
  return sharedPopup;
31
32
  }
@@ -107,6 +108,13 @@ export default CustomElement
107
108
  /** Last computed listbox value (non-nullable string). */
108
109
  _lastComputedListboxValue: { type: 'string', nullable: false },
109
110
 
111
+ /** Currently suggested option (used for aria-activedescendant labels). */
112
+ _suggestionOption: {
113
+ type: 'object',
114
+ /** @type {Pick<HTMLOptionElement, 'label'|'value'|'selected'>} */
115
+ value: null,
116
+ },
117
+
110
118
  _values: {
111
119
  type: 'array',
112
120
  /** @type {string[]} */
@@ -138,8 +146,6 @@ export default CustomElement
138
146
  _onPopupFocusoutListener: null,
139
147
  _suggestionText: '',
140
148
  _suggestionValue: '',
141
- /** @type {Pick<HTMLOptionElement, 'label'|'value'|'selected'>} */
142
- _suggestionOption: null,
143
149
  })
144
150
  .define({
145
151
  stateTargetElement() { return this.refs.control; },
@@ -190,8 +196,8 @@ export default CustomElement
190
196
  // Ignore if focus lost to pop-up (likely pointerdown).
191
197
  if (relatedTarget) {
192
198
  if (this === relatedTarget) return;
193
- if (this.contains(/** @type {any} */ (relatedTarget))) return;
194
- if (getSharedPopup().contains(/** @type {any} */ (relatedTarget))) return;
199
+ if (this.contains(/** @type {any} */(relatedTarget))) return;
200
+ if (getSharedPopup().contains(/** @type {any} */(relatedTarget))) return;
195
201
  }
196
202
  this.closeListbox();
197
203
  },
@@ -341,6 +347,7 @@ export default CustomElement
341
347
  let backup;
342
348
  let backupIndex = -1;
343
349
  let optionIndex = -1;
350
+ this._listboxSize = _listbox?.options?.length ?? -1;
344
351
  for (const option of _listbox.options) {
345
352
  optionIndex++;
346
353
  if (!current && option.focused) {
@@ -409,10 +416,12 @@ export default CustomElement
409
416
  }
410
417
  if (suggestionIndex === -1) {
411
418
  this._focusedPosInSet = -1;
419
+ this._focusedValue = '';
412
420
  this._hasSuggestion = false;
413
421
  return;
414
422
  }
415
423
  this._focusedPosInSet = suggestionIndex + 1;
424
+ this._focusedValue = suggestion?.value ?? '';
416
425
  suggestion.focused = true;
417
426
  suggestion.scrollIntoView({
418
427
  behavior: 'instant',
@@ -668,6 +677,7 @@ export default CustomElement
668
677
  }
669
678
  this._listbox = listbox;
670
679
  if (listbox) {
680
+ this._listboxSize = listbox.length;
671
681
  // Bind and store
672
682
  if (!this.multiple) {
673
683
  listbox.required = true; // Don't allow unclick
@@ -695,7 +705,7 @@ export default CustomElement
695
705
  // Ignore if focus lost to pop-up (likely pointerdown).
696
706
  const popup = getSharedPopup();
697
707
  if (popup === relatedTarget) return;
698
- if (relatedTarget && popup.contains(/** @type {Node} */ (relatedTarget))) return;
708
+ if (relatedTarget && popup.contains(/** @type {Node} */(relatedTarget))) return;
699
709
  if (popup.matches(':focus-within,:focus')) return;
700
710
  this.closeListbox();
701
711
  },
@@ -770,6 +780,9 @@ export default CustomElement
770
780
  if (_expanded && _focusedValue) return 'aria-active';
771
781
  return '';
772
782
  },
783
+ ariaActiveLabel({ _suggestionOption }) {
784
+ return _suggestionOption?.label ?? '';
785
+ },
773
786
  controlRoleAttrValue({ _hasListbox }) {
774
787
  if (_hasListbox) return 'combobox';
775
788
  return null;
@@ -801,7 +814,7 @@ export default CustomElement
801
814
  } else if (this._isSelect) {
802
815
  this._value = value;
803
816
  } else {
804
- // Apply user value to input and read back result to apply control to parse
817
+ // Apply user value to input and read back result to apply control to parse
805
818
  this._input.value = value;
806
819
  this._value = this._input.value;
807
820
  }
@@ -872,13 +885,28 @@ export default CustomElement
872
885
  this._chipSelected = false;
873
886
  this.closeListbox();
874
887
  },
888
+ disconnected() {
889
+ // Ensure shared popup/listbox are cleaned up if element is removed while open.
890
+ if (this._expanded) {
891
+ if (sharedPopup?.contains(this._listbox)) {
892
+ this.closeListbox();
893
+ } else {
894
+ this._expanded = false;
895
+ }
896
+ } else if (this._listbox && sharedPopup?.contains(this._listbox)) {
897
+ // Defensive cleanup in case popup stole the listbox.
898
+ this.replaceChildren(this._listbox);
899
+ sharedPopup?.remove();
900
+ }
901
+ sharedPopup?.removeEventListener('focusout', this._onPopupFocusoutListener);
902
+ },
875
903
  })
876
904
  .html`
877
905
  <div id=chips mdw-if={multiple}></div>
878
- <slot id=slot></slot>
906
+ <slot id=slot aria-hidden=true></slot>
879
907
  <div id=aria-listbox role=listbox mdw-if={_hasListbox}>
880
- <div id=aria-active role=option aria-hidden=false aria-label={selectedOption.label}
881
- aria-setsize="{_listbox.length}" aria-posinset={_focusedPosInSet}></div>
908
+ <div id=aria-active role=option aria-hidden=false aria-label={ariaActiveLabel}
909
+ aria-setsize={_listboxSize} aria-posinset={_focusedPosInSet}></div>
882
910
  </div>
883
911
  `
884
912
  .css`
@@ -887,7 +915,7 @@ export default CustomElement
887
915
  }
888
916
 
889
917
  #aria-listbox {
890
- display: none;
918
+ pointer-events: none;
891
919
  }
892
920
 
893
921
  #trailing-icon {
@@ -152,7 +152,7 @@ export default CustomElement
152
152
  const lastClientHeight = textarea.clientHeight;
153
153
  textarea.rows--;
154
154
  if ((lastClientHeight === textarea.clientHeight)
155
- || (textarea.scrollHeight > textarea.clientHeight)) {
155
+ || (textarea.scrollHeight > textarea.clientHeight)) {
156
156
  textarea.rows++;
157
157
  break;
158
158
  }
@@ -171,6 +171,13 @@ export default CustomElement
171
171
  this.resize();
172
172
  },
173
173
  })
174
+ .overrides({
175
+ _onSetValue(value) {
176
+ this._textarea.value = value;
177
+ this._value = this._textarea.value;
178
+ this.resize();
179
+ },
180
+ })
174
181
  .childEvents({
175
182
  slot: {
176
183
  /**
@@ -232,8 +239,7 @@ export default CustomElement
232
239
  },
233
240
  _formResetChanged(oldValue, newValue) {
234
241
  if (!newValue) return;
235
- this._textarea.value = this.defaultValue;
236
- this._value = this._textarea.value;
242
+ this._onSetValue(this.defaultValue);
237
243
  },
238
244
  attrs: {
239
245
  cols: cloneAttributeCallback('cols', 'control'),