@patternfly/pfe-core 3.0.0 → 4.0.1
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/controllers/activedescendant-controller.d.ts +99 -0
- package/controllers/activedescendant-controller.js +230 -0
- package/controllers/activedescendant-controller.js.map +1 -0
- package/controllers/at-focus-controller.d.ts +56 -0
- package/controllers/at-focus-controller.js +168 -0
- package/controllers/at-focus-controller.js.map +1 -0
- package/controllers/cascade-controller.d.ts +7 -2
- package/controllers/cascade-controller.js +6 -1
- package/controllers/cascade-controller.js.map +1 -1
- package/controllers/combobox-controller.d.ts +117 -0
- package/controllers/combobox-controller.js +611 -0
- package/controllers/combobox-controller.js.map +1 -0
- package/controllers/css-variable-controller.js +1 -1
- package/controllers/css-variable-controller.js.map +1 -1
- package/controllers/floating-dom-controller.d.ts +8 -1
- package/controllers/floating-dom-controller.js +12 -5
- package/controllers/floating-dom-controller.js.map +1 -1
- package/controllers/internals-controller.d.ts +26 -9
- package/controllers/internals-controller.js +45 -13
- package/controllers/internals-controller.js.map +1 -1
- package/controllers/light-dom-controller.js +2 -2
- package/controllers/light-dom-controller.js.map +1 -1
- package/controllers/listbox-controller.d.ts +118 -33
- package/controllers/listbox-controller.js +347 -154
- package/controllers/listbox-controller.js.map +1 -1
- package/controllers/logger.d.ts +13 -10
- package/controllers/logger.js +15 -11
- package/controllers/logger.js.map +1 -1
- package/controllers/overflow-controller.js +10 -6
- package/controllers/overflow-controller.js.map +1 -1
- package/controllers/perf-controller.js.map +1 -1
- package/controllers/property-observer-controller.d.ts +13 -16
- package/controllers/property-observer-controller.js +54 -25
- package/controllers/property-observer-controller.js.map +1 -1
- package/controllers/roving-tabindex-controller.d.ts +12 -61
- package/controllers/roving-tabindex-controller.js +57 -203
- package/controllers/roving-tabindex-controller.js.map +1 -1
- package/controllers/scroll-spy-controller.d.ts +4 -1
- package/controllers/scroll-spy-controller.js +9 -6
- package/controllers/scroll-spy-controller.js.map +1 -1
- package/controllers/slot-controller.d.ts +12 -16
- package/controllers/slot-controller.js +17 -20
- package/controllers/slot-controller.js.map +1 -1
- package/controllers/style-controller.js +3 -1
- package/controllers/style-controller.js.map +1 -1
- package/controllers/tabs-aria-controller.d.ts +2 -0
- package/controllers/tabs-aria-controller.js +2 -0
- package/controllers/tabs-aria-controller.js.map +1 -1
- package/controllers/test/combobox-controller.spec.d.ts +1 -0
- package/controllers/test/combobox-controller.spec.js +282 -0
- package/controllers/test/combobox-controller.spec.js.map +1 -0
- package/controllers/timestamp-controller.js +7 -2
- package/controllers/timestamp-controller.js.map +1 -1
- package/core.d.ts +0 -23
- package/core.js +1 -38
- package/core.js.map +1 -1
- package/custom-elements.json +3862 -1369
- package/decorators/bound.d.ts +3 -1
- package/decorators/bound.js +3 -1
- package/decorators/bound.js.map +1 -1
- package/decorators/cascades.d.ts +2 -1
- package/decorators/cascades.js +2 -1
- package/decorators/cascades.js.map +1 -1
- package/decorators/deprecation.d.ts +6 -5
- package/decorators/deprecation.js +6 -5
- package/decorators/deprecation.js.map +1 -1
- package/decorators/initializer.js.map +1 -1
- package/decorators/listen.d.ts +8 -0
- package/decorators/listen.js +22 -0
- package/decorators/listen.js.map +1 -0
- package/decorators/observed.d.ts +12 -16
- package/decorators/observed.js +39 -44
- package/decorators/observed.js.map +1 -1
- package/decorators/observes.d.ts +15 -0
- package/decorators/observes.js +30 -0
- package/decorators/observes.js.map +1 -0
- package/decorators/time.d.ts +1 -0
- package/decorators/time.js +6 -9
- package/decorators/time.js.map +1 -1
- package/decorators/trace.d.ts +4 -1
- package/decorators/trace.js +4 -1
- package/decorators/trace.js.map +1 -1
- package/decorators.d.ts +2 -0
- package/decorators.js +2 -0
- package/decorators.js.map +1 -1
- package/functions/arraysAreEquivalent.d.ts +9 -0
- package/functions/arraysAreEquivalent.js +28 -0
- package/functions/arraysAreEquivalent.js.map +1 -0
- package/functions/containsDeep.d.ts +2 -0
- package/functions/containsDeep.js +2 -0
- package/functions/containsDeep.js.map +1 -1
- package/functions/context.d.ts +3 -4
- package/functions/context.js +6 -2
- package/functions/context.js.map +1 -1
- package/functions/debounce.js.map +1 -1
- package/functions/isElementInView.d.ts +4 -6
- package/functions/isElementInView.js +9 -11
- package/functions/isElementInView.js.map +1 -1
- package/package.json +12 -4
- package/ssr-shims.d.ts +17 -0
- package/ssr-shims.js +55 -0
- package/ssr-shims.js.map +1 -0
|
@@ -1,249 +1,442 @@
|
|
|
1
|
-
var _ListboxController_instances, _ListboxController_shiftStartingItem, _ListboxController_items, _ListboxController_listening,
|
|
1
|
+
var _ListboxController_instances, _ListboxController_shiftStartingItem, _ListboxController_options, _ListboxController_items, _ListboxController_selectedItems, _ListboxController_listening, _ListboxController_controlsElements, _ListboxController_removeControlsListeners, _ListboxController_isExpanded_get, _ListboxController_getItemFromEvent, _ListboxController_onClick, _ListboxController_onKeyup, _ListboxController_onKeydown, _ListboxController_selectItem;
|
|
2
2
|
import { __classPrivateFieldGet, __classPrivateFieldSet } from "tslib";
|
|
3
|
+
import { isServer } from 'lit';
|
|
4
|
+
import { arraysAreEquivalent } from '../functions/arraysAreEquivalent.js';
|
|
5
|
+
/**
|
|
6
|
+
* This is the default method for setting the selected state on an item element
|
|
7
|
+
* @param item the item
|
|
8
|
+
* @param selected is this item selected
|
|
9
|
+
*/
|
|
10
|
+
function setItemSelected(item, selected) {
|
|
11
|
+
if (selected) {
|
|
12
|
+
item.setAttribute('aria-selected', 'true');
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
item.removeAttribute('aria-selected');
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* @param item possible disabled item
|
|
20
|
+
* @package do not import this outside of `@patternfly/pfe-core`, it is subject to change at any time
|
|
21
|
+
*/
|
|
22
|
+
export function isItem(item) {
|
|
23
|
+
return item instanceof Element
|
|
24
|
+
&& item?.parentElement?.role === 'listbox'
|
|
25
|
+
&& item?.role !== 'presentation'
|
|
26
|
+
&& item?.localName !== 'hr';
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* This is a fib. aria-disabled might not be present on an element that uses internals,
|
|
30
|
+
* and the `disabled` attribute may not accurately represent the disabled state.
|
|
31
|
+
* short of patching the `attachInternals` constructor, it may not be possible at
|
|
32
|
+
* runtime to know with certainty that an arbitrary custom element is disabled or not.
|
|
33
|
+
* @param item possibly disabled item
|
|
34
|
+
* @package do not import this outside of `@patternfly/pfe-core`, it is subject to change at any time
|
|
35
|
+
*/
|
|
36
|
+
export function isItemDisabled(item) {
|
|
37
|
+
return ('disabled' in item && typeof item.disabled === 'boolean' && item.disabled)
|
|
38
|
+
|| item.getAttribute('aria-disabled') === 'true'
|
|
39
|
+
|| item.hasAttribute('disabled')
|
|
40
|
+
|| item.hasAttribute('inert')
|
|
41
|
+
|| item.matches(':disabled');
|
|
42
|
+
}
|
|
3
43
|
let constructingAllowed = false;
|
|
4
44
|
/**
|
|
5
45
|
* Implements listbox semantics and accesibility. As there are two recognized
|
|
6
46
|
* patterns for implementing keyboard interactions with listbox patterns,
|
|
7
47
|
* provide a secondary controller (either RovingTabindexController or
|
|
8
48
|
* ActiveDescendantController) to complete the implementation.
|
|
49
|
+
*
|
|
50
|
+
* @see https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#kbd_focus_vs_selection
|
|
51
|
+
*
|
|
52
|
+
* > Occasionally, it may appear as if two elements on the page have focus at the same time.
|
|
53
|
+
* > For example, in a multi-select list box, when an option is selected it may be greyed.
|
|
54
|
+
* > Yet, the focus indicator can still be moved to other options, which may also be selected.
|
|
55
|
+
* > Similarly, when a user activates a tab in a tablist, the selected state is set on the tab
|
|
56
|
+
* > and its visual appearance changes. However, the user can still navigate, moving the focus
|
|
57
|
+
* > indicator elsewhere on the page while the tab retains its selected appearance and state.
|
|
58
|
+
* >
|
|
59
|
+
* > Focus and selection are quite different. From the keyboard user's perspective,
|
|
60
|
+
* > focus is a pointer, like a mouse pointer; it tracks the path of navigation.
|
|
61
|
+
* > There is only one point of focus at any time and all operations take place at the
|
|
62
|
+
* > point of focus. On the other hand, selection is an operation that can be performed in
|
|
63
|
+
* > some widgets, such as list boxes, trees, and tablists. If a widget supports only single
|
|
64
|
+
* > selection, then only one item can be selected and very often the selected state will simply
|
|
65
|
+
* > follow the focus when focus is moved inside of the widget.
|
|
66
|
+
* > That is, in some widgets, moving focus may also perform the select operation.
|
|
67
|
+
* > However, if the widget supports multiple selection, then more than one item can be in a
|
|
68
|
+
* > selected state, and keys for moving focus do not perform selection. Some multi-select widgets
|
|
69
|
+
* > do support key commands that both move focus and change selection, but those keys are
|
|
70
|
+
* > different from the normal navigation keys. Finally, when focus leaves a widget that includes
|
|
71
|
+
* > a selected element, the selected state persists.
|
|
72
|
+
* >
|
|
73
|
+
* > From the developer's perspective, the difference is simple -- the focused element is the
|
|
74
|
+
* > active element (document.activeElement). Selected elements are elements that have
|
|
75
|
+
* > aria-selected="true".
|
|
76
|
+
* >
|
|
77
|
+
* > With respect to focus and the selected state, the most important considerations for designers
|
|
78
|
+
* > and developers are:
|
|
79
|
+
* >
|
|
80
|
+
* > - The visual focus indicator must always be visible.
|
|
81
|
+
* > - The selected state must be visually distinct from the focus indicator.
|
|
9
82
|
*/
|
|
10
83
|
export class ListboxController {
|
|
11
84
|
static of(host, options) {
|
|
12
85
|
constructingAllowed = true;
|
|
13
|
-
const instance =
|
|
86
|
+
const instance = new ListboxController(host, options);
|
|
14
87
|
constructingAllowed = false;
|
|
15
88
|
return instance;
|
|
16
89
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
90
|
+
get container() {
|
|
91
|
+
return __classPrivateFieldGet(this, _ListboxController_options, "f").getItemsContainer?.() ?? this.host;
|
|
92
|
+
}
|
|
93
|
+
get multi() {
|
|
94
|
+
return !!__classPrivateFieldGet(this, _ListboxController_options, "f").multi;
|
|
95
|
+
}
|
|
96
|
+
set multi(v) {
|
|
97
|
+
__classPrivateFieldGet(this, _ListboxController_options, "f").multi = v;
|
|
98
|
+
this.host.requestUpdate();
|
|
99
|
+
}
|
|
100
|
+
get items() {
|
|
101
|
+
return __classPrivateFieldGet(this, _ListboxController_items, "f");
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* register's the host's Item elements as listbox controller items
|
|
105
|
+
* sets aria-setsize and aria-posinset on items
|
|
106
|
+
* @param items items
|
|
107
|
+
*/
|
|
108
|
+
set items(items) {
|
|
109
|
+
__classPrivateFieldSet(this, _ListboxController_items, items, "f");
|
|
110
|
+
__classPrivateFieldGet(this, _ListboxController_items, "f").forEach((item, index, _items) => {
|
|
111
|
+
item.ariaSetSize = _items.length.toString();
|
|
112
|
+
item.ariaPosInSet = (index + 1).toString();
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* sets the listbox value based on selected options
|
|
117
|
+
* @param selected item or items
|
|
118
|
+
*/
|
|
119
|
+
set selected(selected) {
|
|
120
|
+
if (!arraysAreEquivalent(selected, Array.from(__classPrivateFieldGet(this, _ListboxController_selectedItems, "f")))) {
|
|
121
|
+
__classPrivateFieldSet(this, _ListboxController_selectedItems, new Set(selected), "f");
|
|
122
|
+
for (const item of this.items) {
|
|
123
|
+
__classPrivateFieldGet(this, _ListboxController_options, "f").setItemSelected(item, __classPrivateFieldGet(this, _ListboxController_selectedItems, "f").has(item));
|
|
124
|
+
}
|
|
125
|
+
this.host.requestUpdate();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* array of options which are selected
|
|
130
|
+
*/
|
|
131
|
+
get selected() {
|
|
132
|
+
return [...__classPrivateFieldGet(this, _ListboxController_selectedItems, "f")];
|
|
133
|
+
}
|
|
134
|
+
constructor(host, options) {
|
|
22
135
|
_ListboxController_instances.add(this);
|
|
23
136
|
this.host = host;
|
|
24
|
-
this._options = _options;
|
|
25
137
|
/** Current active descendant when shift key is pressed */
|
|
26
138
|
_ListboxController_shiftStartingItem.set(this, null);
|
|
27
|
-
|
|
139
|
+
_ListboxController_options.set(this, void 0);
|
|
140
|
+
/** All items */
|
|
28
141
|
_ListboxController_items.set(this, []);
|
|
142
|
+
_ListboxController_selectedItems.set(this, new Set);
|
|
29
143
|
_ListboxController_listening.set(this, false);
|
|
30
144
|
/** Whether listbox is disabled */
|
|
31
145
|
this.disabled = false;
|
|
32
|
-
|
|
33
|
-
* handles focusing on an option:
|
|
34
|
-
* updates roving tabindex and active descendant
|
|
35
|
-
*/
|
|
36
|
-
_ListboxController_onFocus.set(this, (event) => {
|
|
37
|
-
const target = __classPrivateFieldGet(this, _ListboxController_instances, "m", _ListboxController_getEventOption).call(this, event);
|
|
38
|
-
if (target && target !== this._options.a11yController.activeItem) {
|
|
39
|
-
this._options.a11yController.setActiveItem(target);
|
|
40
|
-
}
|
|
41
|
-
});
|
|
146
|
+
_ListboxController_controlsElements.set(this, []);
|
|
42
147
|
/**
|
|
43
148
|
* handles clicking on a listbox option:
|
|
44
149
|
* which selects an item by default
|
|
45
150
|
* or toggles selection if multiselectable
|
|
151
|
+
* @param event click event
|
|
46
152
|
*/
|
|
47
153
|
_ListboxController_onClick.set(this, (event) => {
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
else if (__classPrivateFieldGet(this, _ListboxController_shiftStartingItem, "f") && target) {
|
|
56
|
-
__classPrivateFieldGet(this, _ListboxController_instances, "m", _ListboxController_updateMultiselect).call(this, target, __classPrivateFieldGet(this, _ListboxController_shiftStartingItem, "f"));
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
154
|
+
const item = __classPrivateFieldGet(this, _ListboxController_instances, "m", _ListboxController_getItemFromEvent).call(this, event);
|
|
155
|
+
__classPrivateFieldSet(this, _ListboxController_shiftStartingItem, __classPrivateFieldGet(this, _ListboxController_shiftStartingItem, "f") ?? __classPrivateFieldGet(this, _ListboxController_instances, "m", _ListboxController_getItemFromEvent).call(this, event), "f");
|
|
156
|
+
if (item && !__classPrivateFieldGet(this, _ListboxController_options, "f").isItemDisabled(item)) {
|
|
157
|
+
// Case: single select?
|
|
158
|
+
// just reset the selected list.
|
|
159
|
+
if (!this.multi) {
|
|
60
160
|
// select target and deselect all other options
|
|
61
|
-
this.
|
|
161
|
+
this.selected = [item];
|
|
162
|
+
// Case: multi select, but no shift key
|
|
163
|
+
// toggle target, keep all other previously selected
|
|
62
164
|
}
|
|
63
|
-
if (
|
|
64
|
-
this.
|
|
165
|
+
else if (!event.shiftKey) {
|
|
166
|
+
this.selected = this.items.filter(possiblySelectedItem => __classPrivateFieldGet(this, _ListboxController_selectedItems, "f").has(possiblySelectedItem) ? possiblySelectedItem !== item
|
|
167
|
+
: possiblySelectedItem === item);
|
|
168
|
+
// Case: multi select, with shift key
|
|
169
|
+
// find all items between previously selected and target,
|
|
170
|
+
// and select them (if reference item is selected) or deselect them (if reference item is deselected)
|
|
171
|
+
// Do not wrap around from end to start, rather, only select withing the range of 0-end
|
|
65
172
|
}
|
|
66
|
-
|
|
67
|
-
this
|
|
173
|
+
else {
|
|
174
|
+
const startingItem = __classPrivateFieldGet(this, _ListboxController_shiftStartingItem, "f");
|
|
175
|
+
// whether options will be selected (true) or deselected (false)
|
|
176
|
+
const selecting = __classPrivateFieldGet(this, _ListboxController_selectedItems, "f").has(startingItem);
|
|
177
|
+
const [start, end] = [this.items.indexOf(startingItem), this.items.indexOf(item)].sort();
|
|
178
|
+
// de/select all options between active descendant and target
|
|
179
|
+
this.selected = this.items.filter((item, i) => {
|
|
180
|
+
if (i >= start && i <= end) {
|
|
181
|
+
return selecting;
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
return __classPrivateFieldGet(this, _ListboxController_selectedItems, "f").has(item);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
68
187
|
}
|
|
69
188
|
}
|
|
189
|
+
__classPrivateFieldSet(this, _ListboxController_shiftStartingItem, item, "f");
|
|
190
|
+
this.host.requestUpdate();
|
|
70
191
|
});
|
|
71
192
|
/**
|
|
72
|
-
* handles keyup:
|
|
73
193
|
* track whether shift key is being used for multiselectable listbox
|
|
194
|
+
* @param event keyup event
|
|
74
195
|
*/
|
|
75
196
|
_ListboxController_onKeyup.set(this, (event) => {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (__classPrivateFieldGet(this, _ListboxController_shiftStartingItem, "f") && target) {
|
|
79
|
-
__classPrivateFieldGet(this, _ListboxController_instances, "m", _ListboxController_updateMultiselect).call(this, target, __classPrivateFieldGet(this, _ListboxController_shiftStartingItem, "f"));
|
|
80
|
-
}
|
|
81
|
-
if (event.key === 'Shift') {
|
|
82
|
-
__classPrivateFieldSet(this, _ListboxController_shiftStartingItem, null, "f");
|
|
83
|
-
}
|
|
197
|
+
if (event.key === 'Shift') {
|
|
198
|
+
__classPrivateFieldSet(this, _ListboxController_shiftStartingItem, null, "f");
|
|
84
199
|
}
|
|
85
200
|
});
|
|
86
201
|
/**
|
|
87
|
-
* handles keydown:
|
|
88
202
|
* filters listbox by keyboard event when slotted option has focus,
|
|
89
203
|
* or by external element such as a text field
|
|
204
|
+
* @param event keydown event
|
|
90
205
|
*/
|
|
91
206
|
_ListboxController_onKeydown.set(this, (event) => {
|
|
92
|
-
const
|
|
93
|
-
if (
|
|
207
|
+
const item = __classPrivateFieldGet(this, _ListboxController_instances, "m", _ListboxController_getItemFromEvent).call(this, event);
|
|
208
|
+
if (this.disabled
|
|
209
|
+
|| event.altKey
|
|
210
|
+
|| event.metaKey
|
|
211
|
+
|| !__classPrivateFieldGet(this, _ListboxController_instances, "a", _ListboxController_isExpanded_get)) {
|
|
94
212
|
return;
|
|
95
213
|
}
|
|
96
|
-
const first = this._options.a11yController.firstItem;
|
|
97
|
-
const last = this._options.a11yController.lastItem;
|
|
98
214
|
// need to set for keyboard support of multiselect
|
|
99
|
-
if (event.key === 'Shift' && this.
|
|
100
|
-
__classPrivateFieldSet(this, _ListboxController_shiftStartingItem, this.
|
|
215
|
+
if (event.key === 'Shift' && this.multi) {
|
|
216
|
+
__classPrivateFieldSet(this, _ListboxController_shiftStartingItem, __classPrivateFieldGet(this, _ListboxController_shiftStartingItem, "f") ?? (__classPrivateFieldGet(this, _ListboxController_options, "f").getATFocusedItem() ?? null), "f");
|
|
101
217
|
}
|
|
102
218
|
switch (event.key) {
|
|
219
|
+
// ctrl+A de/selects all options
|
|
103
220
|
case 'a':
|
|
104
221
|
case 'A':
|
|
105
|
-
if (event.ctrlKey
|
|
106
|
-
|
|
107
|
-
|
|
222
|
+
if (event.ctrlKey
|
|
223
|
+
&& (event.target === this.container
|
|
224
|
+
|| __classPrivateFieldGet(this, _ListboxController_options, "f").isItem(event.target))) {
|
|
225
|
+
const selectableItems = this.items.filter(item => !__classPrivateFieldGet(this, _ListboxController_options, "f").isItemDisabled(item));
|
|
226
|
+
if (arraysAreEquivalent(this.selected, selectableItems)) {
|
|
227
|
+
this.selected = [];
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
this.selected = selectableItems;
|
|
231
|
+
}
|
|
108
232
|
event.preventDefault();
|
|
109
233
|
}
|
|
110
234
|
break;
|
|
111
235
|
case 'Enter':
|
|
236
|
+
// enter and space are only applicable if a listbox option is clicked
|
|
237
|
+
// an external text input should not trigger multiselect
|
|
238
|
+
if (item && !event.shiftKey) {
|
|
239
|
+
const focused = item;
|
|
240
|
+
__classPrivateFieldGet(this, _ListboxController_instances, "m", _ListboxController_selectItem).call(this, focused, event.shiftKey);
|
|
241
|
+
event.preventDefault();
|
|
242
|
+
}
|
|
243
|
+
break;
|
|
244
|
+
case 'ArrowUp':
|
|
245
|
+
if (this.multi && event.shiftKey && __classPrivateFieldGet(this, _ListboxController_options, "f").isItem(event.target)) {
|
|
246
|
+
const item = event.target;
|
|
247
|
+
this.selected = this.items.filter((x, i) => __classPrivateFieldGet(this, _ListboxController_selectedItems, "f").has(x)
|
|
248
|
+
|| i === this.items.indexOf(item) - 1)
|
|
249
|
+
.filter(x => !__classPrivateFieldGet(this, _ListboxController_options, "f").isItemDisabled(x));
|
|
250
|
+
}
|
|
251
|
+
break;
|
|
252
|
+
case 'ArrowDown':
|
|
253
|
+
if (this.multi && event.shiftKey && __classPrivateFieldGet(this, _ListboxController_options, "f").isItem(event.target)) {
|
|
254
|
+
const item = event.target;
|
|
255
|
+
this.selected = this.items.filter((x, i) => __classPrivateFieldGet(this, _ListboxController_selectedItems, "f").has(x)
|
|
256
|
+
|| i === this.items.indexOf(item) + 1)
|
|
257
|
+
.filter(x => !__classPrivateFieldGet(this, _ListboxController_options, "f").isItemDisabled(x));
|
|
258
|
+
}
|
|
259
|
+
break;
|
|
112
260
|
case ' ':
|
|
113
261
|
// enter and space are only applicable if a listbox option is clicked
|
|
114
262
|
// an external text input should not trigger multiselect
|
|
115
|
-
if (this.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
else if (!this.disabled) {
|
|
120
|
-
this._options.requestSelect(target, !this._options.isSelected(target));
|
|
121
|
-
}
|
|
263
|
+
if (item && event.target === this.container) {
|
|
264
|
+
__classPrivateFieldGet(this, _ListboxController_instances, "m", _ListboxController_selectItem).call(this, item, event.shiftKey);
|
|
265
|
+
event.preventDefault();
|
|
122
266
|
}
|
|
123
|
-
else {
|
|
124
|
-
__classPrivateFieldGet(this, _ListboxController_instances, "m",
|
|
267
|
+
else if (__classPrivateFieldGet(this, _ListboxController_options, "f").isItem(event.target)) {
|
|
268
|
+
__classPrivateFieldGet(this, _ListboxController_instances, "m", _ListboxController_selectItem).call(this, event.target, event.shiftKey);
|
|
269
|
+
event.preventDefault();
|
|
125
270
|
}
|
|
126
|
-
event.preventDefault();
|
|
127
271
|
break;
|
|
128
272
|
default:
|
|
129
273
|
break;
|
|
130
274
|
}
|
|
275
|
+
this.host.requestUpdate();
|
|
131
276
|
});
|
|
277
|
+
__classPrivateFieldSet(this, _ListboxController_options, { setItemSelected, isItemDisabled, isItem, ...options }, "f");
|
|
132
278
|
if (!constructingAllowed) {
|
|
133
279
|
throw new Error('ListboxController must be constructed with `ListboxController.of()`');
|
|
134
280
|
}
|
|
135
|
-
if (!
|
|
136
|
-
|
|
281
|
+
if (!isServer
|
|
282
|
+
&& !(host instanceof HTMLElement)
|
|
283
|
+
&& typeof options.getItemsContainer !== 'function') {
|
|
284
|
+
throw new Error([
|
|
285
|
+
'ListboxController requires the host to be an HTMLElement',
|
|
286
|
+
'or for the initializer to include a getItemsContainer() function',
|
|
287
|
+
].join(' '));
|
|
137
288
|
}
|
|
138
|
-
|
|
139
|
-
|
|
289
|
+
const instance = ListboxController.instances.get(host);
|
|
290
|
+
if (instance) {
|
|
291
|
+
return instance;
|
|
140
292
|
}
|
|
141
293
|
ListboxController.instances.set(host, this);
|
|
142
294
|
this.host.addController(this);
|
|
143
|
-
|
|
295
|
+
this.multi = __classPrivateFieldGet(this, _ListboxController_options, "f").multi ?? false;
|
|
296
|
+
if (this.container?.isConnected) {
|
|
144
297
|
this.hostConnected();
|
|
145
298
|
}
|
|
146
299
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
300
|
+
async hostConnected() {
|
|
301
|
+
await this.host.updateComplete;
|
|
302
|
+
this.hostUpdate();
|
|
303
|
+
this.hostUpdated();
|
|
150
304
|
}
|
|
151
|
-
|
|
152
|
-
|
|
305
|
+
hostUpdate() {
|
|
306
|
+
const last = __classPrivateFieldGet(this, _ListboxController_controlsElements, "f");
|
|
307
|
+
__classPrivateFieldSet(this, _ListboxController_controlsElements, __classPrivateFieldGet(this, _ListboxController_options, "f").getControlsElements?.() ?? [], "f");
|
|
308
|
+
if (!arraysAreEquivalent(last, __classPrivateFieldGet(this, _ListboxController_controlsElements, "f"))) {
|
|
309
|
+
__classPrivateFieldGet(this, _ListboxController_instances, "m", _ListboxController_removeControlsListeners).call(this, last);
|
|
310
|
+
for (const el of __classPrivateFieldGet(this, _ListboxController_controlsElements, "f")) {
|
|
311
|
+
el.addEventListener('keydown', __classPrivateFieldGet(this, _ListboxController_onKeydown, "f"));
|
|
312
|
+
el.addEventListener('keyup', __classPrivateFieldGet(this, _ListboxController_onKeyup, "f"));
|
|
313
|
+
}
|
|
314
|
+
}
|
|
153
315
|
}
|
|
154
|
-
|
|
155
|
-
|
|
316
|
+
hostUpdated() {
|
|
317
|
+
if (!__classPrivateFieldGet(this, _ListboxController_listening, "f")) {
|
|
318
|
+
this.container?.addEventListener('click', __classPrivateFieldGet(this, _ListboxController_onClick, "f"));
|
|
319
|
+
this.container?.addEventListener('keydown', __classPrivateFieldGet(this, _ListboxController_onKeydown, "f"));
|
|
320
|
+
this.container?.addEventListener('keyup', __classPrivateFieldGet(this, _ListboxController_onKeyup, "f"));
|
|
321
|
+
__classPrivateFieldSet(this, _ListboxController_listening, true, "f");
|
|
322
|
+
}
|
|
323
|
+
this.container?.setAttribute('role', 'listbox');
|
|
324
|
+
this.container?.setAttribute('aria-disabled', String(!!this.disabled));
|
|
325
|
+
this.container?.setAttribute('aria-multiselectable', String(!!__classPrivateFieldGet(this, _ListboxController_options, "f").multi));
|
|
156
326
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
327
|
+
hostDisconnected() {
|
|
328
|
+
this.container?.removeEventListener('click', __classPrivateFieldGet(this, _ListboxController_onClick, "f"));
|
|
329
|
+
this.container?.removeEventListener('keydown', __classPrivateFieldGet(this, _ListboxController_onKeydown, "f"));
|
|
330
|
+
this.container?.removeEventListener('keyup', __classPrivateFieldGet(this, _ListboxController_onKeyup, "f"));
|
|
331
|
+
__classPrivateFieldGet(this, _ListboxController_instances, "m", _ListboxController_removeControlsListeners).call(this);
|
|
332
|
+
__classPrivateFieldSet(this, _ListboxController_listening, false, "f");
|
|
162
333
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
return this._options.multi ? this.selectedOptions : firstItem;
|
|
334
|
+
isSelected(item) {
|
|
335
|
+
return __classPrivateFieldGet(this, _ListboxController_selectedItems, "f").has(item);
|
|
166
336
|
}
|
|
167
|
-
|
|
168
|
-
|
|
337
|
+
}
|
|
338
|
+
_ListboxController_shiftStartingItem = new WeakMap(), _ListboxController_options = new WeakMap(), _ListboxController_items = new WeakMap(), _ListboxController_selectedItems = new WeakMap(), _ListboxController_listening = new WeakMap(), _ListboxController_controlsElements = new WeakMap(), _ListboxController_onClick = new WeakMap(), _ListboxController_onKeyup = new WeakMap(), _ListboxController_onKeydown = new WeakMap(), _ListboxController_instances = new WeakSet(), _ListboxController_removeControlsListeners = function _ListboxController_removeControlsListeners(els = __classPrivateFieldGet(this, _ListboxController_controlsElements, "f")) {
|
|
339
|
+
for (const el of els) {
|
|
340
|
+
el.removeEventListener('keydown', __classPrivateFieldGet(this, _ListboxController_onKeydown, "f"));
|
|
341
|
+
el.removeEventListener('keyup', __classPrivateFieldGet(this, _ListboxController_onKeyup, "f"));
|
|
169
342
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
343
|
+
}, _ListboxController_isExpanded_get = function _ListboxController_isExpanded_get() {
|
|
344
|
+
return !__classPrivateFieldGet(this, _ListboxController_controlsElements, "f").length ? true
|
|
345
|
+
: __classPrivateFieldGet(this, _ListboxController_controlsElements, "f").every(x => x.ariaExpanded === 'true');
|
|
346
|
+
}, _ListboxController_getItemFromEvent = function _ListboxController_getItemFromEvent(event) {
|
|
347
|
+
// NOTE(bennypowers): I am aware that this function *sucks*
|
|
348
|
+
// you're more than welcome to improve it.
|
|
349
|
+
// make sure there are unit tests first
|
|
350
|
+
const path = event.composedPath();
|
|
351
|
+
const tabindexed = this.items.some(x => x.hasAttribute('tabindex'));
|
|
352
|
+
if (tabindexed) {
|
|
353
|
+
const item = path.find(__classPrivateFieldGet(this, _ListboxController_options, "f").isItem);
|
|
354
|
+
if (item) {
|
|
355
|
+
return item;
|
|
178
356
|
}
|
|
179
357
|
}
|
|
180
|
-
|
|
181
|
-
this.
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
358
|
+
else if (__classPrivateFieldGet(this, _ListboxController_options, "f").isItem(event.target)
|
|
359
|
+
&& event.target.getRootNode() !== this.container.getRootNode()
|
|
360
|
+
&& 'ariaActiveDescendantElement' in HTMLElement.prototype) {
|
|
361
|
+
return event.target;
|
|
362
|
+
}
|
|
363
|
+
else if (event.target instanceof HTMLElement && event.target.ariaActiveDescendantElement) {
|
|
364
|
+
return event.target.ariaActiveDescendantElement;
|
|
365
|
+
}
|
|
366
|
+
else if (event.type === 'click'
|
|
367
|
+
&& __classPrivateFieldGet(this, _ListboxController_options, "f").isItem(event.target)
|
|
368
|
+
&& event.target.id) {
|
|
369
|
+
const element = event.target;
|
|
370
|
+
const root = element.getRootNode();
|
|
371
|
+
if (root instanceof ShadowRoot && this.container.getRootNode() === root) {
|
|
372
|
+
const shadowRootListboxElement = this.container;
|
|
373
|
+
const shadowRootItem = element;
|
|
374
|
+
if (shadowRootItem && shadowRootListboxElement) {
|
|
375
|
+
if (this.items.includes(shadowRootItem)) {
|
|
376
|
+
return shadowRootItem;
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
const index = Array.from(shadowRootListboxElement?.children ?? [])
|
|
380
|
+
.filter(__classPrivateFieldGet(this, _ListboxController_options, "f").isItem)
|
|
381
|
+
.filter(x => !x.hidden)
|
|
382
|
+
.indexOf(shadowRootItem);
|
|
383
|
+
return __classPrivateFieldGet(this, _ListboxController_items, "f").filter(x => !x.hidden)[index];
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
// otherwise, query the root (e.g. shadow root) for the associated element
|
|
390
|
+
const element = event.target;
|
|
391
|
+
const root = element.getRootNode();
|
|
392
|
+
const controlsId = element?.getAttribute('aria-controls');
|
|
393
|
+
const shadowRootListboxElement = __classPrivateFieldGet(this, _ListboxController_options, "f").isItem(element) ? this.container
|
|
394
|
+
: controlsId ? root.getElementById(controlsId)
|
|
395
|
+
: null;
|
|
396
|
+
const shadowRootHasActiveDescendantElement = root.querySelector(`[aria-controls="${shadowRootListboxElement?.id}"][aria-activedescendant]`);
|
|
397
|
+
const shadowRootItemId = shadowRootHasActiveDescendantElement?.getAttribute('aria-activedescendant');
|
|
398
|
+
const shadowRootItem = shadowRootItemId && root.getElementById(shadowRootItemId);
|
|
399
|
+
if (shadowRootItem && shadowRootListboxElement) {
|
|
400
|
+
if (this.items.includes(shadowRootItem)) {
|
|
401
|
+
return shadowRootItem;
|
|
187
402
|
}
|
|
188
403
|
else {
|
|
189
|
-
|
|
404
|
+
const index = Array.from(shadowRootListboxElement?.children ?? [])
|
|
405
|
+
.filter(__classPrivateFieldGet(this, _ListboxController_options, "f").isItem)
|
|
406
|
+
.filter(x => !x.hidden)
|
|
407
|
+
.indexOf(shadowRootItem);
|
|
408
|
+
return __classPrivateFieldGet(this, _ListboxController_items, "f").filter(x => !x.hidden)[index];
|
|
190
409
|
}
|
|
191
410
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
*/
|
|
203
|
-
setValue(value) {
|
|
204
|
-
const selected = Array.isArray(value) ? value : [value];
|
|
205
|
-
const [firstItem = null] = selected;
|
|
206
|
-
for (const option of this.options) {
|
|
207
|
-
this._options.requestSelect(option, (!!this._options.multi && Array.isArray(value) ? value?.includes(option)
|
|
208
|
-
: firstItem === option));
|
|
411
|
+
const itemFromEventContainer = shadowRootListboxElement ? shadowRootListboxElement
|
|
412
|
+
: path.find(x => x instanceof HTMLElement && x.role === 'listbox');
|
|
413
|
+
if (itemFromEventContainer) {
|
|
414
|
+
const possiblyShadowRootContainerItems = Array.from(itemFromEventContainer.children)
|
|
415
|
+
.filter(__classPrivateFieldGet(this, _ListboxController_options, "f").isItem);
|
|
416
|
+
const index = possiblyShadowRootContainerItems
|
|
417
|
+
.findIndex(node => path.includes(node));
|
|
418
|
+
if (index >= 0) {
|
|
419
|
+
return this.items[index] ?? null;
|
|
420
|
+
}
|
|
209
421
|
}
|
|
210
422
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
const oldOptions = [...__classPrivateFieldGet(this, _ListboxController_items, "f")];
|
|
216
|
-
__classPrivateFieldSet(this, _ListboxController_items, options, "f");
|
|
217
|
-
__classPrivateFieldGet(this, _ListboxController_instances, "m", _ListboxController_optionsChanged).call(this, oldOptions);
|
|
423
|
+
return null;
|
|
424
|
+
}, _ListboxController_selectItem = function _ListboxController_selectItem(item, shiftDown = false) {
|
|
425
|
+
if (__classPrivateFieldGet(this, _ListboxController_options, "f").isItemDisabled(item)) {
|
|
426
|
+
return;
|
|
218
427
|
}
|
|
219
|
-
|
|
220
|
-
_ListboxController_shiftStartingItem = new WeakMap(), _ListboxController_items = new WeakMap(), _ListboxController_listening = new WeakMap(), _ListboxController_onFocus = new WeakMap(), _ListboxController_onClick = new WeakMap(), _ListboxController_onKeyup = new WeakMap(), _ListboxController_onKeydown = new WeakMap(), _ListboxController_instances = new WeakSet(), _ListboxController_getEnabledOptions = function _ListboxController_getEnabledOptions(options = this.options) {
|
|
221
|
-
return options.filter(option => !option.ariaDisabled && !option.closest('[disabled]'));
|
|
222
|
-
}, _ListboxController_getEventOption = function _ListboxController_getEventOption(event) {
|
|
223
|
-
return event.composedPath().find(node => __classPrivateFieldGet(this, _ListboxController_items, "f").includes(node));
|
|
224
|
-
}, _ListboxController_optionsChanged = function _ListboxController_optionsChanged(oldOptions) {
|
|
225
|
-
const setSize = __classPrivateFieldGet(this, _ListboxController_items, "f").length;
|
|
226
|
-
if (setSize !== oldOptions.length || !oldOptions.every((element, index) => element === __classPrivateFieldGet(this, _ListboxController_items, "f")[index])) {
|
|
227
|
-
this._options.a11yController.updateItems(this.options);
|
|
228
|
-
}
|
|
229
|
-
}, _ListboxController_updateSingleselect = function _ListboxController_updateSingleselect() {
|
|
230
|
-
if (!this._options.multi && !this.disabled) {
|
|
231
|
-
__classPrivateFieldGet(this, _ListboxController_instances, "m", _ListboxController_getEnabledOptions).call(this)
|
|
232
|
-
.forEach(option => this._options.requestSelect(option, option === this._options.a11yController.activeItem));
|
|
233
|
-
}
|
|
234
|
-
}, _ListboxController_updateMultiselect = function _ListboxController_updateMultiselect(currentItem, referenceItem = this.activeItem, ctrlA = false) {
|
|
235
|
-
if (referenceItem && this._options.multi && !this.disabled && currentItem) {
|
|
236
|
-
// select all options between active descendant and target
|
|
237
|
-
const [start, end] = [this.options.indexOf(referenceItem), this.options.indexOf(currentItem)].sort();
|
|
238
|
-
const options = [...this.options].slice(start, end + 1);
|
|
239
|
-
// by default CTRL+A will select all options
|
|
240
|
-
// if all options are selected, CTRL+A will deselect all options
|
|
241
|
-
const allSelected = __classPrivateFieldGet(this, _ListboxController_instances, "m", _ListboxController_getEnabledOptions).call(this, options).filter(option => !this._options.isSelected(option))?.length === 0;
|
|
242
|
-
// whether options will be selected (true) or deselected (false)
|
|
243
|
-
const selected = ctrlA ? !allSelected : this._options.isSelected(referenceItem);
|
|
244
|
-
__classPrivateFieldGet(this, _ListboxController_instances, "m", _ListboxController_getEnabledOptions).call(this, options).forEach(option => this._options.requestSelect(option, selected));
|
|
428
|
+
else if (this.multi && shiftDown) {
|
|
245
429
|
// update starting item for other multiselect
|
|
246
|
-
|
|
430
|
+
this.selected = [...this.selected, item];
|
|
431
|
+
}
|
|
432
|
+
else if (this.multi && __classPrivateFieldGet(this, _ListboxController_selectedItems, "f").has(item)) {
|
|
433
|
+
this.selected = this.selected.filter(x => x !== item);
|
|
434
|
+
}
|
|
435
|
+
else if (this.multi) {
|
|
436
|
+
this.selected = this.selected.concat(item);
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
this.selected = [item];
|
|
247
440
|
}
|
|
248
441
|
};
|
|
249
442
|
ListboxController.instances = new WeakMap();
|