@shortfuse/materialdesignweb 0.7.6 → 0.8.0

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.
Files changed (114) hide show
  1. package/README.md +57 -68
  2. package/components/Badge.js +2 -2
  3. package/components/BottomAppBar.js +3 -5
  4. package/components/Box.js +33 -3
  5. package/components/Button.js +48 -21
  6. package/components/Button.md +9 -9
  7. package/components/Card.js +9 -16
  8. package/components/Checkbox.js +45 -36
  9. package/components/CheckboxIcon.js +2 -2
  10. package/components/Chip.js +1 -1
  11. package/components/Dialog.js +228 -359
  12. package/components/DialogActions.js +2 -2
  13. package/components/Divider.js +3 -3
  14. package/components/ExtendedFab.js +4 -8
  15. package/components/Fab.js +1 -2
  16. package/components/FilterChip.js +4 -4
  17. package/components/Headline.js +1 -1
  18. package/components/Icon.js +8 -8
  19. package/components/IconButton.js +9 -14
  20. package/components/Input.js +273 -1
  21. package/components/Layout.js +485 -16
  22. package/components/List.js +6 -4
  23. package/components/ListItem.js +12 -12
  24. package/components/ListOption.js +21 -5
  25. package/components/Listbox.js +239 -0
  26. package/components/Menu.js +77 -526
  27. package/components/MenuItem.js +12 -14
  28. package/components/Nav.js +0 -2
  29. package/components/NavBar.js +8 -79
  30. package/components/NavDrawer.js +12 -11
  31. package/components/NavDrawerItem.js +2 -1
  32. package/components/NavItem.js +18 -8
  33. package/components/NavRail.js +15 -7
  34. package/components/NavRailItem.js +3 -1
  35. package/components/Popup.js +20 -0
  36. package/components/Progress.js +24 -23
  37. package/components/Radio.js +42 -35
  38. package/components/RadioIcon.js +3 -3
  39. package/components/Ripple.js +2 -3
  40. package/components/Search.js +85 -0
  41. package/components/SegmentedButton.js +1 -10
  42. package/components/SegmentedButtonGroup.js +16 -10
  43. package/components/Select.js +4 -4
  44. package/components/Shape.js +1 -1
  45. package/components/Slider.js +43 -50
  46. package/components/Snackbar.js +4 -5
  47. package/components/Surface.js +3 -3
  48. package/components/Switch.js +55 -21
  49. package/components/SwitchIcon.js +10 -8
  50. package/components/Tab.js +11 -9
  51. package/components/TabContent.js +4 -3
  52. package/components/TabList.js +2 -2
  53. package/components/TabPanel.js +11 -8
  54. package/components/TextArea.js +38 -35
  55. package/components/Tooltip.js +2 -2
  56. package/components/TopAppBar.js +65 -147
  57. package/core/Composition.js +985 -628
  58. package/core/CompositionAdapter.js +315 -0
  59. package/core/CustomElement.js +153 -90
  60. package/core/DomAdapter.js +586 -0
  61. package/core/ICustomElement.d.ts +2 -2
  62. package/core/css.js +8 -7
  63. package/core/customTypes.js +53 -31
  64. package/{utils → core}/jsonMergePatch.js +36 -14
  65. package/core/observe.js +111 -57
  66. package/core/optimizations.js +23 -0
  67. package/core/template.js +17 -11
  68. package/core/test.js +126 -0
  69. package/core/typings.d.ts +11 -5
  70. package/core/uid.js +13 -0
  71. package/dist/index.min.js +83 -152
  72. package/dist/index.min.js.map +4 -4
  73. package/dist/meta.json +1 -1
  74. package/mixins/AriaReflectorMixin.js +1 -2
  75. package/mixins/AriaToolbarMixin.js +2 -3
  76. package/mixins/ControlMixin.js +25 -17
  77. package/mixins/DensityMixin.js +0 -1
  78. package/mixins/FlexableMixin.js +1 -2
  79. package/mixins/FormAssociatedMixin.js +13 -10
  80. package/mixins/InputMixin.js +2 -9
  81. package/mixins/KeyboardNavMixin.js +14 -1
  82. package/mixins/PopupMixin.js +757 -0
  83. package/mixins/RTLObserverMixin.js +0 -1
  84. package/mixins/ResizeObserverMixin.js +0 -1
  85. package/mixins/RippleMixin.js +3 -4
  86. package/mixins/ScrollListenerMixin.js +41 -32
  87. package/mixins/SemiStickyMixin.js +151 -0
  88. package/mixins/ShapeMixin.js +29 -24
  89. package/mixins/StateMixin.js +11 -6
  90. package/mixins/SurfaceMixin.js +3 -57
  91. package/mixins/TextFieldMixin.js +57 -65
  92. package/mixins/ThemableMixin.js +78 -156
  93. package/mixins/TooltipTriggerMixin.js +7 -13
  94. package/mixins/TouchTargetMixin.js +4 -3
  95. package/package.json +9 -5
  96. package/theming/index.js +1 -1
  97. package/theming/themableMixinLoader.js +12 -0
  98. package/utils/{hct → material-color}/blend.js +8 -10
  99. package/utils/{hct → material-color/hct}/Cam16.js +196 -69
  100. package/utils/{hct → material-color/hct}/Hct.js +61 -19
  101. package/utils/{hct → material-color/hct}/ViewingConditions.js +3 -3
  102. package/utils/{hct → material-color/hct}/hctSolver.js +9 -16
  103. package/utils/{hct → material-color}/helper.js +11 -18
  104. package/utils/{hct → material-color/palettes}/CorePalette.js +79 -19
  105. package/utils/{hct → material-color/palettes}/TonalPalette.js +12 -4
  106. package/utils/material-color/scheme/Scheme.js +376 -0
  107. package/utils/{hct/colorUtils.js → material-color/utils/color.js} +61 -1
  108. package/utils/popup.js +46 -25
  109. package/components/ListSelect.js +0 -220
  110. package/components/Option.js +0 -91
  111. package/components/Pane.js +0 -281
  112. package/core/identify.js +0 -40
  113. package/utils/hct/Scheme.js +0 -587
  114. /package/utils/{hct/mathUtils.js → material-color/utils/math.js} +0 -0
@@ -1,15 +1,15 @@
1
+ /* https://m3.material.io/components/dividers/specs */
2
+
1
3
  import CustomElement from '../core/CustomElement.js';
2
4
  import ThemableMixin from '../mixins/ThemableMixin.js';
3
5
 
4
6
  export default CustomElement
5
- .mixin(ThemableMixin)
6
7
  .extend()
8
+ .mixin(ThemableMixin)
7
9
  .observe({
8
10
  vertical: 'boolean',
9
11
  })
10
12
  .css`
11
- /* https://m3.material.io/components/dividers/specs */
12
-
13
13
  :host {
14
14
  --mdw-ink: var(--mdw-color__outline-variant);
15
15
  position: relative;
@@ -6,11 +6,9 @@ export default Button
6
6
  /* https://m3.material.io/components/extended-fab/specs */
7
7
 
8
8
  :host {
9
- --mdw-bg: var(--mdw-color__primary-container);
10
- --mdw-ink: var(--mdw-color__on-primary-container);
9
+ --mdw-bg: var(--mdw-color__surface-container-high);
10
+ --mdw-ink: var(--mdw-color__primary);
11
11
  --mdw-shape__size: 16px;
12
- --mdw-surface__tint: var(--mdw-surface__tint__3);
13
- --mdw-surface__tint__raised: var(--mdw-surface__tint__4);
14
12
  --mdw-surface__shadow__resting: var(--mdw-surface__shadow__3);
15
13
  --mdw-surface__shadow__raised: var(--mdw-surface__shadow__4);
16
14
  min-inline-size: 48px;
@@ -21,13 +19,11 @@ export default Button
21
19
  font-size: 24px;
22
20
  }
23
21
 
24
- :host([lowered]) {
25
- --mdw-surface__tint: var(--mdw-surface__tint__1);
26
- --mdw-surface__tint__raised: var(--mdw-surface__tint__2);
22
+ :host(:where([lowered])) {
23
+ --mdw-bg: var(--mdw-color__surface-container-low);
27
24
  --mdw-surface__shadow__resting: var(--mdw-surface__shadow__1);
28
25
  --mdw-surface__shadow__raised: var(--mdw-surface__shadow__2);
29
26
  }
30
-
31
27
  `
32
28
  .observe({
33
29
  filled: { empty: 'tonal' },
package/components/Fab.js CHANGED
@@ -3,8 +3,8 @@ import TooltipTriggerMixin from '../mixins/TooltipTriggerMixin.js';
3
3
  import ExtendedFab from './ExtendedFab.js';
4
4
 
5
5
  export default ExtendedFab
6
- .mixin(TooltipTriggerMixin)
7
6
  .extend()
7
+ .mixin(TooltipTriggerMixin)
8
8
  .observe({
9
9
  fabSize: {
10
10
  /** @type {null|'small'|'large'} */
@@ -35,7 +35,6 @@ export default ExtendedFab
35
35
  --mdw-shape__size: 28px;
36
36
  padding: calc(30px + (var(--mdw-density) * 2px));
37
37
  }
38
-
39
38
  `
40
39
  .on({
41
40
  composed() {
@@ -18,9 +18,9 @@ export default Chip
18
18
  return null;
19
19
  },
20
20
  })
21
- .html/* html */`
22
- <mdw-icon _if={!icon} id=check-icon disabled={disabledState} selected={checked} aria-hidden=true>check</mdw-icon>
23
- <mdw-icon _if={computedTrailingIcon} id=trailing-icon aria-hidden=true src={trailingSrc}>{computedTrailingIcon}</mdw-icon>
21
+ .html`
22
+ <mdw-icon mdw-if={!icon} id=check-icon disabled={disabledState} selected={checked} aria-hidden=true>check</mdw-icon>
23
+ <mdw-icon mdw-if={computedTrailingIcon} id=trailing-icon aria-hidden=true src={trailingSrc}>{computedTrailingIcon}</mdw-icon>
24
24
  `
25
25
  .on({
26
26
  composed() {
@@ -29,7 +29,7 @@ export default Chip
29
29
  shape.setAttribute('selected', '{checked}');
30
30
  shape.setAttribute('icon', '');
31
31
  shape.setAttribute('trailing-icon', '{computedTrailingIcon}');
32
- icon.setAttribute('_if', '{icon}');
32
+ icon.setAttribute('mdw-if', '{icon}');
33
33
  icon.setAttribute('ink', '{iconInk}');
34
34
 
35
35
  control.removeAttribute('role');
@@ -3,8 +3,8 @@ import ThemableMixin from '../mixins/ThemableMixin.js';
3
3
  import Box from './Box.js';
4
4
 
5
5
  export default Box
6
- .mixin(ThemableMixin)
7
6
  .extend()
7
+ .mixin(ThemableMixin)
8
8
  .observe({
9
9
  ariaLevel: 'string',
10
10
  size: {
@@ -15,8 +15,8 @@ const documentLoadedStyleSheets = new Set();
15
15
 
16
16
  /** @implements {Omit<HTMLImageElement,DeprecatedHTMLImageElementProperties>} */
17
17
  export default class Icon extends CustomElement
18
- .mixin(ThemableMixin)
19
18
  .extend()
19
+ .mixin(ThemableMixin)
20
20
  .define({
21
21
  _img() { return /** @type {HTMLImageElement} */ (this.refs.img); },
22
22
  })
@@ -48,7 +48,7 @@ export default class Icon extends CustomElement
48
48
  height: 'integer',
49
49
  forceFont: 'boolean',
50
50
  _linkLoaded: 'boolean',
51
- viewBox: { attr: 'viewBox' },
51
+ viewBox: 'string',
52
52
  fontClass: { empty: 'material-symbols-outlined' },
53
53
  fontLibrary: { empty: 'https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:FILL@0..1&display=block' },
54
54
  })
@@ -88,13 +88,13 @@ export default class Icon extends CustomElement
88
88
  },
89
89
 
90
90
  })
91
- .html/* html */`
92
- <link _if={_showSlot} id=link rel=stylesheet href={fontLibrary} />
93
- <svg _if="{showSVG}" id="svg" viewBox="{_computedViewBox}">
94
- <use id="use" _if="{svg}" href="{svg}" fill="currentColor"/>
95
- <path id="path" _if="{_computedSVGPath}" d="{_computedSVGPath}"/>
91
+ .html`
92
+ <link mdw-if={_showSlot} id=link rel=stylesheet href={fontLibrary} />
93
+ <svg mdw-if="{showSVG}" id="svg" viewBox="{_computedViewBox}">
94
+ <use id="use" mdw-if="{svg}" href="{svg}" fill="currentColor"/>
95
+ <path id="path" mdw-if="{_computedSVGPath}" d="{_computedSVGPath}"/>
96
96
  </svg>
97
- <img _if={src} id=img
97
+ <img mdw-if={src} id=img
98
98
  disabled={disabled}
99
99
  alt={alt} src={src} srcset={srcset} sizes={sizes}
100
100
  crossorigin={crossOrigin} usemap={useMap} ismap={isMap}
@@ -3,8 +3,8 @@ import TooltipTriggerMixin from '../mixins/TooltipTriggerMixin.js';
3
3
  import Button from './Button.js';
4
4
 
5
5
  export default Button
6
- .mixin(TooltipTriggerMixin)
7
6
  .extend()
7
+ .mixin(TooltipTriggerMixin)
8
8
  .observe({
9
9
  _ariaPressed: {
10
10
  get({ type, checked }) {
@@ -41,21 +41,20 @@ export default Button
41
41
  })
42
42
  .on({
43
43
  composed() {
44
- const { slot, shape, tooltipSlot, icon, label, surfaceTint, control, outline } = this.refs;
44
+ const { slot, shape, tooltipSlot, icon, control, outline, anchor } = this.refs;
45
45
  shape.classList.add('colored');
46
- label.classList.add('colored');
47
- for (const el of [shape, label, icon]) {
46
+ icon.classList.add('colored');
47
+ for (const el of [shape, icon]) {
48
48
  el.setAttribute('toggle', '{isToggle}');
49
49
  el.setAttribute('selected', '{checked}');
50
50
  }
51
51
  slot.remove();
52
- icon.removeAttribute('_if');
52
+ icon.removeAttribute('mdw-if');
53
53
  tooltipSlot.removeAttribute('name');
54
54
 
55
- surfaceTint.remove();
56
-
57
55
  control.setAttribute('aria-pressed', '{_ariaPressed}');
58
56
  control.setAttribute('aria-labelledby', 'tooltip');
57
+ anchor.setAttribute('aria-labelledby', 'tooltip');
59
58
 
60
59
  outline.setAttribute('selected', '{checked}');
61
60
  },
@@ -106,11 +105,6 @@ export default Button
106
105
  --mdw-ink: var(--mdw-color__inverse-on-surface);
107
106
  }
108
107
 
109
- #label {
110
- font-size: inherit;
111
- letter-spacing: inherit;
112
- }
113
-
114
108
  #shape[toggle] {
115
109
  background-color: transparent;
116
110
  }
@@ -125,7 +119,7 @@ export default Button
125
119
  }
126
120
 
127
121
  #shape[filled][toggle] {
128
- background-color: rgb(var(--mdw-color__surface-variant));
122
+ background-color: rgb(var(--mdw-color__surface-container-highest));
129
123
  }
130
124
 
131
125
  .colored[filled][toggle] {
@@ -133,7 +127,8 @@ export default Button
133
127
  }
134
128
 
135
129
  #shape[filled="tonal"][toggle] {
136
- background-color: rgb(var(--mdw-color__surface-variant));
130
+ /* Redundant */
131
+ /* background-color: rgb(var(--mdw-color__surface-container-highest)); */
137
132
  }
138
133
 
139
134
  .colored[filled="tonal"][toggle] {
@@ -1,16 +1,288 @@
1
+ /* https://www.w3.org/WAI/ARIA/apg/patterns/combobox/ */
2
+
1
3
  import CustomElement from '../core/CustomElement.js';
2
4
  import InputMixin from '../mixins/InputMixin.js';
5
+ import ResizeObserverMixin from '../mixins/ResizeObserverMixin.js';
3
6
  import StateMixin from '../mixins/StateMixin.js';
4
7
  import TextFieldMixin from '../mixins/TextFieldMixin.js';
5
8
  import ThemableMixin from '../mixins/ThemableMixin.js';
6
9
 
10
+ import Popup from './Popup.js';
11
+
12
+ /** @typedef {import('./Listbox.js').default} Listbox */
13
+
14
+ /** @type {InstanceType<import('./Popup.js').default>} */
15
+ let sharedPopup;
16
+
17
+ /** @return {InstanceType<import('./Popup.js').default>} */
18
+ function getSharedPopup() {
19
+ if (!sharedPopup) {
20
+ sharedPopup = new Popup();
21
+ sharedPopup.scrollable = true;
22
+ sharedPopup.shapeStyle = 'extra-small';
23
+ sharedPopup.color = 'surface';
24
+ sharedPopup.matchSourceWidth = true;
25
+ }
26
+ return sharedPopup;
27
+ }
28
+
7
29
  export default CustomElement
30
+ .extend()
8
31
  .mixin(ThemableMixin)
9
32
  .mixin(StateMixin)
10
33
  .mixin(InputMixin)
11
34
  .mixin(TextFieldMixin)
12
- .extend()
35
+ .mixin(ResizeObserverMixin)
36
+ .observe({
37
+ suggestInline: 'boolean',
38
+ _expanded: 'boolean',
39
+ _listbox: {
40
+ type: 'object',
41
+ /** @type {Listbox} */
42
+ value: null,
43
+ },
44
+ _focusedValue: 'string',
45
+ _focusedIndex: { value: -1 },
46
+ _focusedPosInSet: { value: -1 },
47
+ _listboxSize: { value: -1 },
48
+ _draftInput: { type: 'string', nullable: false },
49
+ })
50
+ .observe({
51
+ _hasListbox({ _listbox }) {
52
+ return _listbox ? 'listbox' : null;
53
+ },
54
+ })
55
+ .set({
56
+ _onListboxChangeListener: null,
57
+ })
13
58
  .define({
14
59
  stateTargetElement() { return this.refs.control; },
15
60
  })
61
+ .methods({
62
+ onResizeObserved() {
63
+ if (!this._expanded) return;
64
+ const popup = getSharedPopup();
65
+ popup.updatePopupPosition(this.refs.shape);
66
+ },
67
+ /**
68
+ * @param {Event} event
69
+ */
70
+ onListboxChange(event) {
71
+ const selectedItem = this._listbox.selectedOptions.item(0);
72
+ this.selectOption(selectedItem);
73
+ this.closeListbox();
74
+ // Revert focus back
75
+ this.refs.control.focus();
76
+ },
77
+ showListbox() {
78
+ // Move contents of list slot into top-layer
79
+ // Should only have one element
80
+
81
+ const { _listbox, refs } = this;
82
+ if (!_listbox) return;
83
+ this._expanded = true;
84
+ const { control, ariaListbox, shape } = refs;
85
+ control.setAttribute('role', 'combobox');
86
+ ariaListbox.setAttribute('aria-hidden', 'false');
87
+ const popup = getSharedPopup();
88
+ document.body.append(popup);
89
+ popup.replaceChildren(_listbox);
90
+ _listbox.selectedIndex = -1;
91
+ popup.showPopup(shape, false);
92
+ },
93
+ closeListbox() {
94
+ this._expanded = false;
95
+ const { _listbox } = this;
96
+ if (!_listbox) return;
97
+ const popup = getSharedPopup();
98
+ this.refs.ariaListbox.setAttribute('aria-hidden', 'true');
99
+ this.replaceChildren(_listbox);
100
+ popup.close(undefined, false);
101
+ // TODO: Animate
102
+ popup.remove();
103
+ },
104
+
105
+ /**
106
+ * @param {{label:string, value:string}} option
107
+ * @return {void}
108
+ */
109
+ selectOption(option) {
110
+ this.render({
111
+ selectedOption: option,
112
+ });
113
+
114
+ const { _draftInput, _value, _input } = this;
115
+ const { label: suggestion, value } = option;
116
+
117
+ if (_draftInput && suggestion && suggestion.toLowerCase().startsWith(_draftInput.toLowerCase())) {
118
+ _input.value = _draftInput + suggestion.slice(_draftInput.length);
119
+ _input.setSelectionRange(_value.length, suggestion.length);
120
+ } else {
121
+ _input.value = suggestion;
122
+ _input.setSelectionRange(suggestion.length, suggestion.length);
123
+ }
124
+ this._value = value;
125
+ },
126
+ })
127
+ .on({
128
+ _focusedIndexChanged(previous, current) {
129
+ const _listbox = this._listbox;
130
+ const previousItem = _listbox.item(previous);
131
+ if (previousItem) {
132
+ previousItem.focused = false;
133
+ }
134
+ const currentItem = _listbox.item(current);
135
+ if (currentItem) {
136
+ this._focusedPosInSet = current + 1;
137
+ currentItem.focused = true;
138
+ this.selectOption(currentItem);
139
+ } else {
140
+ this._focusedPosInSet = -1;
141
+ }
142
+ },
143
+ })
144
+ .childEvents({
145
+ control: {
146
+ input(event) {
147
+ if (!this._listbox) return;
148
+ // Intercept event and dispatch a new one.
149
+ // This allow authors to modify listbox (filter) and value (custom pattern)
150
+ event.stopPropagation();
151
+ this.dispatchEvent(new InputEvent('input', {
152
+ composed: true,
153
+ data: event.data,
154
+ bubbles: true,
155
+ dataTransfer: event.dataTransfer,
156
+ detail: event.detail,
157
+ inputType: event.inputType,
158
+ view: event.view,
159
+ targetRanges: event.getTargetRanges(),
160
+ isComposing: event.isComposing,
161
+ }));
162
+ this._draftInput = event.currentTarget.value;
163
+ this._focusedIndex = -1;
164
+ if (!this._expanded && this._listbox.length) {
165
+ this.showListbox();
166
+ }
167
+ },
168
+ keydown(event) {
169
+ switch (event.key) {
170
+ case 'ArrowUp':
171
+ case 'Up':
172
+ if (!this._expanded) return;
173
+ if (this._focusedIndex <= 0) {
174
+ this._focusedIndex = (this._listbox.length - 1);
175
+ } else {
176
+ this._focusedIndex--;
177
+ }
178
+ break;
179
+ case 'ArrowDown':
180
+ case 'Down':
181
+ if (this._expanded) {
182
+ if (this._focusedIndex >= this._listbox.length - 1) {
183
+ this._focusedIndex = 0;
184
+ } else {
185
+ this._focusedIndex++;
186
+ }
187
+ } else {
188
+ if (!this._listbox) return;
189
+ this.showListbox();
190
+ this._focusedIndex = 0;
191
+ }
192
+ break;
193
+ case 'Escape':
194
+ if (!this._expanded) return;
195
+ this.closeListbox();
196
+ break;
197
+ case 'Tab':
198
+ this.closeListbox();
199
+ event.stopPropagation();
200
+ return; // Don't prevent default
201
+ default:
202
+ return;
203
+ }
204
+ event.stopPropagation(); // Avoid kbd within kbd (sub menus)
205
+ event.preventDefault();
206
+ },
207
+ },
208
+ slot: {
209
+ /**
210
+ * @param {{currentTarget: HTMLSlotElement}} event
211
+ * @type {any}
212
+ */
213
+ slotchange({ currentTarget }) {
214
+ if (this._expanded) return;
215
+ /** @type {Listbox[]} */
216
+ const [listbox] = currentTarget.assignedElements();
217
+ const { _listbox } = this;
218
+ if (_listbox === listbox) {
219
+ // Internal already matches
220
+ return;
221
+ }
222
+ if (_listbox) {
223
+ // Unbind and release
224
+ _listbox.removeEventListener('change', this._onListboxChangeListener);
225
+ this._onListboxChangeListener = null;
226
+ }
227
+ if (listbox) {
228
+ // Bind and store
229
+ this._onListboxChangeListener = this.onListboxChange.bind(this);
230
+ listbox.addEventListener('change', this._onListboxChangeListener);
231
+ }
232
+ this._listbox = listbox;
233
+ },
234
+ },
235
+ })
236
+ .events({
237
+ blur({ relatedTarget }) {
238
+ if (!this._expanded) return;
239
+ // Ignore if focus lost to pop-up (likely pointerdown).
240
+ if (relatedTarget && getSharedPopup().contains(relatedTarget)) return;
241
+ this.closeListbox();
242
+ },
243
+ })
244
+ .expressions({
245
+ ariaExpandedAttrValue({ _hasListbox, _expanded }) {
246
+ return _hasListbox ? `${_expanded}` : null;
247
+ },
248
+ ariaControlsAttrValue({ _hasListbox }) {
249
+ return _hasListbox ? 'aria-listbox' : null;
250
+ },
251
+ ariaAutocompleteAttrValue({ _hasListbox, suggestInline }) {
252
+ return _hasListbox
253
+ ? (suggestInline ? 'both' : 'list')
254
+ : null;
255
+ },
256
+ ariaActiveDescendantAttrValue({ _hasListbox, _expanded, _focusedValue }) {
257
+ return _hasListbox
258
+ // eslint-disable-next-line unicorn/no-nested-ternary
259
+ ? ((_expanded && _focusedValue) ? 'aria-active' : '')
260
+ : null;
261
+ },
262
+ })
263
+ .on({
264
+ composed() {
265
+ const { control } = this.refs;
266
+ // Can't cross DOM boundaries
267
+ control.setAttribute('aria-activedescendant', '{ariaActiveDescendantAttrValue}');
268
+ control.setAttribute('aria-autocomplete', '{ariaAutocompleteAttrValue}');
269
+ control.setAttribute('aria-controls', '{ariaControlsAttrValue}');
270
+ control.setAttribute('aria-expanded', '{ariaExpandedAttrValue}');
271
+ },
272
+ })
273
+ .html`
274
+ <slot id=slot></slot>
275
+ <div id=aria-listbox role=listbox>
276
+ <div id=aria-active role=option aria-setsize="{_listbox.length}" aria-posinset={_focusedPosInSet} aria-label={selectedOption.label}></div>
277
+ </div>
278
+ `
279
+ .css`
280
+ #slot {
281
+ display: none;
282
+ }
283
+
284
+ #aria-listbox {
285
+ display: none;
286
+ }
287
+ `
16
288
  .autoRegister('mdw-input');