@spectrum-web-components/picker 1.2.0-beta.9 → 1.3.0-beta.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.
- package/custom-elements.json +176 -30
- package/package.json +16 -16
- package/src/InteractionController.dev.js +7 -1
- package/src/InteractionController.dev.js.map +2 -2
- package/src/InteractionController.js +1 -1
- package/src/InteractionController.js.map +2 -2
- package/src/MobileController.dev.js +4 -1
- package/src/MobileController.dev.js.map +2 -2
- package/src/MobileController.js +1 -1
- package/src/MobileController.js.map +2 -2
- package/src/Picker.d.ts +35 -5
- package/src/Picker.dev.js +143 -67
- package/src/Picker.dev.js.map +2 -2
- package/src/Picker.js +17 -21
- package/src/Picker.js.map +3 -3
- package/test/index.js +318 -298
- package/test/index.js.map +3 -3
- package/test/picker-reparenting.test.js +12 -3
- package/test/picker-reparenting.test.js.map +2 -2
package/src/Picker.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { CSSResultArray, PropertyValues, TemplateResult } from '@spectrum-web-components/base';
|
|
1
|
+
import { CSSResultArray, PropertyValues, SpectrumElement, TemplateResult } from '@spectrum-web-components/base';
|
|
2
2
|
import { StyleInfo } from '@spectrum-web-components/base/src/directives.js';
|
|
3
|
-
import { Focusable } from '@spectrum-web-components/shared/src/focusable.js';
|
|
4
3
|
import type { Tooltip } from '@spectrum-web-components/tooltip';
|
|
5
4
|
import '@spectrum-web-components/icons-ui/icons/sp-icon-chevron100.js';
|
|
6
5
|
import '@spectrum-web-components/icons-workflow/icons/sp-icon-alert.js';
|
|
7
6
|
import '@spectrum-web-components/menu/sp-menu.js';
|
|
8
7
|
import type { Menu, MenuItem, MenuItemChildren } from '@spectrum-web-components/menu';
|
|
8
|
+
import type { MenuItemKeydownEvent } from '@spectrum-web-components/menu';
|
|
9
9
|
import { Placement } from '@spectrum-web-components/overlay';
|
|
10
10
|
import { MatchMediaController } from '@spectrum-web-components/reactive-controllers/src/MatchMedia.js';
|
|
11
11
|
import { DependencyManagerController } from '@spectrum-web-components/reactive-controllers/src/DependencyManger.js';
|
|
@@ -16,11 +16,16 @@ import type { FieldLabel } from '@spectrum-web-components/field-label';
|
|
|
16
16
|
import { DesktopController } from './DesktopController.js';
|
|
17
17
|
import { MobileController } from './MobileController.js';
|
|
18
18
|
export declare const DESCRIPTION_ID = "option-picker";
|
|
19
|
-
declare const PickerBase_base: typeof
|
|
19
|
+
declare const PickerBase_base: typeof SpectrumElement & {
|
|
20
20
|
new (...args: any[]): import("@spectrum-web-components/base").SizedElementInterface;
|
|
21
21
|
prototype: import("@spectrum-web-components/base").SizedElementInterface;
|
|
22
22
|
};
|
|
23
23
|
export declare class PickerBase extends PickerBase_base {
|
|
24
|
+
static shadowRootOptions: {
|
|
25
|
+
delegatesFocus: boolean;
|
|
26
|
+
mode: ShadowRootMode;
|
|
27
|
+
slotAssignment?: SlotAssignmentMode | undefined;
|
|
28
|
+
};
|
|
24
29
|
isMobile: MatchMediaController;
|
|
25
30
|
strategy: DesktopController | MobileController;
|
|
26
31
|
appliedLabel?: string;
|
|
@@ -48,7 +53,9 @@ export declare class PickerBase extends PickerBase_base {
|
|
|
48
53
|
labelAlignment?: 'inline';
|
|
49
54
|
protected get menuItems(): MenuItem[];
|
|
50
55
|
optionsMenu: Menu;
|
|
51
|
-
|
|
56
|
+
/**
|
|
57
|
+
* @deprecated
|
|
58
|
+
* */
|
|
52
59
|
get selfManageFocusElement(): boolean;
|
|
53
60
|
overlayElement: Overlay;
|
|
54
61
|
protected tooltipEl?: Tooltip;
|
|
@@ -73,12 +80,19 @@ export declare class PickerBase extends PickerBase_base {
|
|
|
73
80
|
get focusElement(): HTMLElement;
|
|
74
81
|
forceFocusVisible(): void;
|
|
75
82
|
click(): void;
|
|
83
|
+
handleButtonClick(): void;
|
|
76
84
|
handleButtonBlur(): void;
|
|
77
85
|
focus(options?: FocusOptions): void;
|
|
86
|
+
/**
|
|
87
|
+
* @deprecated - Use `focus` instead.
|
|
88
|
+
*/
|
|
78
89
|
handleHelperFocus(): void;
|
|
90
|
+
handleFocus(): void;
|
|
79
91
|
handleChange(event: Event): void;
|
|
80
92
|
handleButtonFocus(event: FocusEvent): void;
|
|
93
|
+
protected handleEscape: (event: MenuItemKeydownEvent | KeyboardEvent) => void;
|
|
81
94
|
protected handleKeydown: (event: KeyboardEvent) => void;
|
|
95
|
+
protected keyboardOpen(): Promise<void>;
|
|
82
96
|
protected setValueFromItem(item: MenuItem, menuChangeEvent?: Event): Promise<void>;
|
|
83
97
|
protected setMenuItemSelected(item: MenuItem, value: boolean): void;
|
|
84
98
|
toggle(target?: boolean): void;
|
|
@@ -94,9 +108,12 @@ export declare class PickerBase extends PickerBase_base {
|
|
|
94
108
|
protected renderLabelContent(content: Node[]): TemplateResult | Node[];
|
|
95
109
|
protected get buttonContent(): TemplateResult[];
|
|
96
110
|
applyFocusElementLabel: (value: string, labelElement: FieldLabel) => void;
|
|
111
|
+
protected hasAccessibleLabel(): boolean;
|
|
112
|
+
protected warnNoLabel(): void;
|
|
97
113
|
protected renderOverlay(menu: TemplateResult): TemplateResult;
|
|
98
114
|
protected get renderDescriptionSlot(): TemplateResult;
|
|
99
115
|
protected render(): TemplateResult;
|
|
116
|
+
protected willUpdate(changes: PropertyValues<this>): void;
|
|
100
117
|
protected update(changes: PropertyValues<this>): void;
|
|
101
118
|
protected bindButtonKeydownListener(): void;
|
|
102
119
|
protected updated(changes: PropertyValues<this>): void;
|
|
@@ -106,9 +123,22 @@ export declare class PickerBase extends PickerBase_base {
|
|
|
106
123
|
protected hasRenderedOverlay: boolean;
|
|
107
124
|
private onScroll;
|
|
108
125
|
protected get renderMenu(): TemplateResult;
|
|
109
|
-
|
|
126
|
+
/**
|
|
127
|
+
* whether a selection change is already scheduled
|
|
128
|
+
*/
|
|
129
|
+
willManageSelection: boolean;
|
|
130
|
+
/**
|
|
131
|
+
* when the value changes or the menu slot changes, manage the selection on the next frame, if not already scheduled
|
|
132
|
+
* @param event
|
|
133
|
+
*/
|
|
110
134
|
protected shouldScheduleManageSelection(event?: Event): void;
|
|
135
|
+
/**
|
|
136
|
+
* when an item is added or updated, manage the selection, if it's not already scheduled
|
|
137
|
+
*/
|
|
111
138
|
protected shouldManageSelection(): void;
|
|
139
|
+
/**
|
|
140
|
+
* updates menu selection based on value
|
|
141
|
+
*/
|
|
112
142
|
protected manageSelection(): Promise<void>;
|
|
113
143
|
private selectionPromise;
|
|
114
144
|
private selectionResolver;
|
package/src/Picker.dev.js
CHANGED
|
@@ -13,7 +13,8 @@ import {
|
|
|
13
13
|
html,
|
|
14
14
|
nothing,
|
|
15
15
|
render,
|
|
16
|
-
SizedMixin
|
|
16
|
+
SizedMixin,
|
|
17
|
+
SpectrumElement
|
|
17
18
|
} from "@spectrum-web-components/base";
|
|
18
19
|
import {
|
|
19
20
|
classMap,
|
|
@@ -27,8 +28,6 @@ import {
|
|
|
27
28
|
} from "@spectrum-web-components/base/src/decorators.js";
|
|
28
29
|
import pickerStyles from "./picker.css.js";
|
|
29
30
|
import chevronStyles from "@spectrum-web-components/icon/src/spectrum-icon-chevron.css.js";
|
|
30
|
-
import chevronIconOverrides from "@spectrum-web-components/icon/src/icon-chevron-overrides.css.js";
|
|
31
|
-
import { Focusable } from "@spectrum-web-components/shared/src/focusable.js";
|
|
32
31
|
import "@spectrum-web-components/icons-ui/icons/sp-icon-chevron100.js";
|
|
33
32
|
import "@spectrum-web-components/icons-workflow/icons/sp-icon-alert.js";
|
|
34
33
|
import "@spectrum-web-components/menu/sp-menu.js";
|
|
@@ -46,7 +45,9 @@ const chevronClass = {
|
|
|
46
45
|
xl: "spectrum-UIIcon-ChevronDown300"
|
|
47
46
|
};
|
|
48
47
|
export const DESCRIPTION_ID = "option-picker";
|
|
49
|
-
export class PickerBase extends SizedMixin(
|
|
48
|
+
export class PickerBase extends SizedMixin(SpectrumElement, {
|
|
49
|
+
noDefaultSize: true
|
|
50
|
+
}) {
|
|
50
51
|
/**
|
|
51
52
|
* Initializes the `PendingStateController` for the Picker component.
|
|
52
53
|
* The `PendingStateController` manages the pending state of the Picker.
|
|
@@ -65,20 +66,32 @@ export class PickerBase extends SizedMixin(Focusable, { noDefaultSize: true }) {
|
|
|
65
66
|
this.open = false;
|
|
66
67
|
this.readonly = false;
|
|
67
68
|
this.selects = "single";
|
|
68
|
-
this._selfManageFocusElement = false;
|
|
69
69
|
this.placement = "bottom-start";
|
|
70
70
|
this.quiet = false;
|
|
71
71
|
this.value = "";
|
|
72
72
|
this.listRole = "listbox";
|
|
73
73
|
this.itemRole = "option";
|
|
74
|
+
this.handleEscape = (event) => {
|
|
75
|
+
if (event.key === "Escape") {
|
|
76
|
+
event.stopPropagation();
|
|
77
|
+
event.preventDefault();
|
|
78
|
+
this.toggle(false);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
74
81
|
this.handleKeydown = (event) => {
|
|
75
82
|
this.focused = true;
|
|
76
|
-
if (
|
|
83
|
+
if (!["ArrowUp", "ArrowDown", "Enter", " ", "Escape"].includes(
|
|
84
|
+
event.key
|
|
85
|
+
)) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (event.key === "Escape") {
|
|
89
|
+
this.handleEscape(event);
|
|
77
90
|
return;
|
|
78
91
|
}
|
|
79
92
|
event.stopPropagation();
|
|
80
93
|
event.preventDefault();
|
|
81
|
-
this.
|
|
94
|
+
this.keyboardOpen();
|
|
82
95
|
};
|
|
83
96
|
this.handleSlottableRequest = (_event) => {
|
|
84
97
|
};
|
|
@@ -87,12 +100,20 @@ export class PickerBase extends SizedMixin(Focusable, { noDefaultSize: true }) {
|
|
|
87
100
|
this.labelAlignment = labelElement.sideAligned ? "inline" : void 0;
|
|
88
101
|
};
|
|
89
102
|
this.hasRenderedOverlay = false;
|
|
103
|
+
/**
|
|
104
|
+
* whether a selection change is already scheduled
|
|
105
|
+
*/
|
|
90
106
|
this.willManageSelection = false;
|
|
91
107
|
this.selectionPromise = Promise.resolve();
|
|
92
108
|
this.recentlyConnected = false;
|
|
93
109
|
this.enterKeydownOn = null;
|
|
94
110
|
this.handleEnterKeydown = (event) => {
|
|
95
|
-
if (event.
|
|
111
|
+
if (event.key !== "Enter") {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const target = event == null ? void 0 : event.target;
|
|
115
|
+
if (!target.open && target.hasSubmenu) {
|
|
116
|
+
event.preventDefault();
|
|
96
117
|
return;
|
|
97
118
|
}
|
|
98
119
|
if (this.enterKeydownOn) {
|
|
@@ -103,7 +124,7 @@ export class PickerBase extends SizedMixin(Focusable, { noDefaultSize: true }) {
|
|
|
103
124
|
this.addEventListener(
|
|
104
125
|
"keyup",
|
|
105
126
|
async (keyupEvent) => {
|
|
106
|
-
if (keyupEvent.
|
|
127
|
+
if (keyupEvent.key !== "Enter") {
|
|
107
128
|
return;
|
|
108
129
|
}
|
|
109
130
|
this.enterKeydownOn = null;
|
|
@@ -116,8 +137,11 @@ export class PickerBase extends SizedMixin(Focusable, { noDefaultSize: true }) {
|
|
|
116
137
|
get menuItems() {
|
|
117
138
|
return this.optionsMenu.childItems;
|
|
118
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* @deprecated
|
|
142
|
+
* */
|
|
119
143
|
get selfManageFocusElement() {
|
|
120
|
-
return
|
|
144
|
+
return true;
|
|
121
145
|
}
|
|
122
146
|
get selectedItem() {
|
|
123
147
|
return this._selectedItem;
|
|
@@ -141,7 +165,12 @@ export class PickerBase extends SizedMixin(Focusable, { noDefaultSize: true }) {
|
|
|
141
165
|
}
|
|
142
166
|
this.focused = true;
|
|
143
167
|
}
|
|
168
|
+
// handled by interaction controller, desktop or mobile; this is only called with a programmatic this.click()
|
|
144
169
|
click() {
|
|
170
|
+
this.toggle();
|
|
171
|
+
}
|
|
172
|
+
// pointer events handled by interaction controller, desktop or mobile; this is only called with a programmatic this.button.click()
|
|
173
|
+
handleButtonClick() {
|
|
145
174
|
if (this.disabled) {
|
|
146
175
|
return;
|
|
147
176
|
}
|
|
@@ -151,15 +180,21 @@ export class PickerBase extends SizedMixin(Focusable, { noDefaultSize: true }) {
|
|
|
151
180
|
this.focused = false;
|
|
152
181
|
}
|
|
153
182
|
focus(options) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
this.focused = this.hasVisibleFocusInTree();
|
|
157
|
-
}
|
|
183
|
+
var _a;
|
|
184
|
+
(_a = this.focusElement) == null ? void 0 : _a.focus(options);
|
|
158
185
|
}
|
|
186
|
+
/**
|
|
187
|
+
* @deprecated - Use `focus` instead.
|
|
188
|
+
*/
|
|
159
189
|
handleHelperFocus() {
|
|
160
190
|
this.focused = true;
|
|
161
191
|
this.button.focus();
|
|
162
192
|
}
|
|
193
|
+
handleFocus() {
|
|
194
|
+
if (!this.disabled && this.focusElement) {
|
|
195
|
+
this.focused = this.hasVisibleFocusInTree();
|
|
196
|
+
}
|
|
197
|
+
}
|
|
163
198
|
handleChange(event) {
|
|
164
199
|
if (this.strategy) {
|
|
165
200
|
this.strategy.preventNextToggle = "no";
|
|
@@ -180,6 +215,9 @@ export class PickerBase extends SizedMixin(Focusable, { noDefaultSize: true }) {
|
|
|
180
215
|
var _a;
|
|
181
216
|
(_a = this.strategy) == null ? void 0 : _a.handleButtonFocus(event);
|
|
182
217
|
}
|
|
218
|
+
async keyboardOpen() {
|
|
219
|
+
this.toggle(true);
|
|
220
|
+
}
|
|
183
221
|
async setValueFromItem(item, menuChangeEvent) {
|
|
184
222
|
var _a;
|
|
185
223
|
this.open = false;
|
|
@@ -229,18 +267,25 @@ export class PickerBase extends SizedMixin(Focusable, { noDefaultSize: true }) {
|
|
|
229
267
|
item.selected = value;
|
|
230
268
|
}
|
|
231
269
|
toggle(target) {
|
|
232
|
-
if (this.readonly || this.pending) {
|
|
270
|
+
if (this.readonly || this.pending || this.disabled) {
|
|
233
271
|
return;
|
|
234
272
|
}
|
|
235
|
-
|
|
273
|
+
const open = typeof target !== "undefined" ? target : !this.open;
|
|
274
|
+
if (open && !this.open)
|
|
275
|
+
this.addEventListener(
|
|
276
|
+
"sp-opened",
|
|
277
|
+
() => {
|
|
278
|
+
var _a;
|
|
279
|
+
return (_a = this.optionsMenu) == null ? void 0 : _a.focusOnFirstSelectedItem();
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
once: true
|
|
283
|
+
}
|
|
284
|
+
);
|
|
285
|
+
this.open = open;
|
|
236
286
|
if (this.strategy) {
|
|
237
287
|
this.strategy.open = this.open;
|
|
238
288
|
}
|
|
239
|
-
if (this.open) {
|
|
240
|
-
this._selfManageFocusElement = true;
|
|
241
|
-
} else {
|
|
242
|
-
this._selfManageFocusElement = false;
|
|
243
|
-
}
|
|
244
289
|
}
|
|
245
290
|
close() {
|
|
246
291
|
if (this.readonly) {
|
|
@@ -332,11 +377,33 @@ export class PickerBase extends SizedMixin(Focusable, { noDefaultSize: true }) {
|
|
|
332
377
|
aria-hidden="true"
|
|
333
378
|
name="tooltip"
|
|
334
379
|
id="tooltip"
|
|
380
|
+
@keydown=${this.handleKeydown}
|
|
335
381
|
@slotchange=${this.handleTooltipSlotchange}
|
|
336
382
|
></slot>
|
|
337
383
|
`
|
|
338
384
|
];
|
|
339
385
|
}
|
|
386
|
+
hasAccessibleLabel() {
|
|
387
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
388
|
+
const slotContent = ((_a = this.querySelector('[slot="label"]')) == null ? void 0 : _a.textContent) && ((_c = (_b = this.querySelector('[slot="label"]')) == null ? void 0 : _b.textContent) == null ? void 0 : _c.trim()) !== "";
|
|
389
|
+
const slotAlt = ((_e = (_d = this.querySelector('[slot="label"]')) == null ? void 0 : _d.getAttribute("alt")) == null ? void 0 : _e.trim()) && ((_g = (_f = this.querySelector('[slot="label"]')) == null ? void 0 : _f.getAttribute("alt")) == null ? void 0 : _g.trim()) !== "";
|
|
390
|
+
return !!this.label || !!this.getAttribute("aria-label") || !!this.getAttribute("aria-labelledby") || !!this.appliedLabel || !!slotContent || !!slotAlt;
|
|
391
|
+
}
|
|
392
|
+
warnNoLabel() {
|
|
393
|
+
window.__swc.warn(
|
|
394
|
+
this,
|
|
395
|
+
`<${this.localName}> needs one of the following to be accessible:`,
|
|
396
|
+
"https://opensource.adobe.com/spectrum-web-components/components/picker/#accessibility",
|
|
397
|
+
{
|
|
398
|
+
type: "accessibility",
|
|
399
|
+
issues: [
|
|
400
|
+
`an <sp-field-label> element with a \`for\` attribute referencing the \`id\` of the \`<${this.localName}>\`, or`,
|
|
401
|
+
'value supplied to the "label" attribute, which will be displayed visually as placeholder text, or',
|
|
402
|
+
'text content supplied in a <span> with slot="label", which will also be displayed visually as placeholder text.'
|
|
403
|
+
]
|
|
404
|
+
}
|
|
405
|
+
);
|
|
406
|
+
}
|
|
340
407
|
renderOverlay(menu) {
|
|
341
408
|
var _a, _b, _c;
|
|
342
409
|
if (((_a = this.strategy) == null ? void 0 : _a.overlay) === void 0) {
|
|
@@ -362,15 +429,9 @@ export class PickerBase extends SizedMixin(Focusable, { noDefaultSize: true }) {
|
|
|
362
429
|
this.tooltipEl.disabled = this.open;
|
|
363
430
|
}
|
|
364
431
|
return html`
|
|
365
|
-
<span
|
|
366
|
-
id="focus-helper"
|
|
367
|
-
tabindex="${this.focused || this.open ? "-1" : "0"}"
|
|
368
|
-
@focus=${this.handleHelperFocus}
|
|
369
|
-
aria-describedby=${DESCRIPTION_ID}
|
|
370
|
-
></span>
|
|
371
432
|
<button
|
|
372
433
|
aria-controls=${ifDefined(this.open ? "menu" : void 0)}
|
|
373
|
-
aria-describedby="tooltip"
|
|
434
|
+
aria-describedby="tooltip ${DESCRIPTION_ID}"
|
|
374
435
|
aria-expanded=${this.open ? "true" : "false"}
|
|
375
436
|
aria-haspopup="true"
|
|
376
437
|
aria-labelledby="loader icon label applied-label"
|
|
@@ -378,35 +439,36 @@ export class PickerBase extends SizedMixin(Focusable, { noDefaultSize: true }) {
|
|
|
378
439
|
class=${ifDefined(
|
|
379
440
|
this.labelAlignment ? `label-${this.labelAlignment}` : void 0
|
|
380
441
|
)}
|
|
442
|
+
@focus=${this.handleButtonFocus}
|
|
381
443
|
@blur=${this.handleButtonBlur}
|
|
382
444
|
@keydown=${{
|
|
383
445
|
handleEvent: this.handleEnterKeydown,
|
|
384
446
|
capture: true
|
|
385
447
|
}}
|
|
386
448
|
?disabled=${this.disabled}
|
|
387
|
-
tabindex="-1"
|
|
388
449
|
>
|
|
389
450
|
${this.buttonContent}
|
|
390
451
|
</button>
|
|
391
452
|
${this.renderMenu} ${this.renderDescriptionSlot}
|
|
392
453
|
`;
|
|
393
454
|
}
|
|
455
|
+
willUpdate(changes) {
|
|
456
|
+
super.willUpdate(changes);
|
|
457
|
+
if (changes.has("tabIndex") && !!this.tabIndex) {
|
|
458
|
+
this.button.tabIndex = this.tabIndex;
|
|
459
|
+
this.removeAttribute("tabindex");
|
|
460
|
+
}
|
|
461
|
+
}
|
|
394
462
|
update(changes) {
|
|
395
463
|
var _a, _b;
|
|
396
464
|
if (this.selects) {
|
|
397
465
|
this.selects = "single";
|
|
398
466
|
}
|
|
399
467
|
if (changes.has("disabled") && this.disabled) {
|
|
400
|
-
|
|
401
|
-
this.open = false;
|
|
402
|
-
this.strategy.open = false;
|
|
403
|
-
}
|
|
468
|
+
this.close();
|
|
404
469
|
}
|
|
405
470
|
if (changes.has("pending") && this.pending) {
|
|
406
|
-
|
|
407
|
-
this.open = false;
|
|
408
|
-
this.strategy.open = false;
|
|
409
|
-
}
|
|
471
|
+
this.close();
|
|
410
472
|
}
|
|
411
473
|
if (changes.has("value")) {
|
|
412
474
|
this.shouldScheduleManageSelection();
|
|
@@ -429,20 +491,8 @@ export class PickerBase extends SizedMixin(Focusable, { noDefaultSize: true }) {
|
|
|
429
491
|
this.updateComplete.then(async () => {
|
|
430
492
|
await new Promise((res) => requestAnimationFrame(res));
|
|
431
493
|
await new Promise((res) => requestAnimationFrame(res));
|
|
432
|
-
if (!this.
|
|
433
|
-
|
|
434
|
-
this,
|
|
435
|
-
`<${this.localName}> needs one of the following to be accessible:`,
|
|
436
|
-
"https://opensource.adobe.com/spectrum-web-components/components/picker/#accessibility",
|
|
437
|
-
{
|
|
438
|
-
type: "accessibility",
|
|
439
|
-
issues: [
|
|
440
|
-
`an <sp-field-label> element with a \`for\` attribute referencing the \`id\` of the \`<${this.localName}>\`, or`,
|
|
441
|
-
'value supplied to the "label" attribute, which will be displayed visually as placeholder text, or',
|
|
442
|
-
'text content supplied in a <span> with slot="label", which will also be displayed visually as placeholder text.'
|
|
443
|
-
]
|
|
444
|
-
}
|
|
445
|
-
);
|
|
494
|
+
if (!this.hasAccessibleLabel()) {
|
|
495
|
+
this.warnNoLabel();
|
|
446
496
|
}
|
|
447
497
|
});
|
|
448
498
|
}
|
|
@@ -526,6 +576,7 @@ export class PickerBase extends SizedMixin(Focusable, { noDefaultSize: true }) {
|
|
|
526
576
|
.selects=${this.selects}
|
|
527
577
|
.selected=${this.value ? [this.value] : []}
|
|
528
578
|
size=${this.size}
|
|
579
|
+
@sp-menu-item-keydown=${this.handleEscape}
|
|
529
580
|
@sp-menu-item-added-or-updated=${this.shouldManageSelection}
|
|
530
581
|
>
|
|
531
582
|
<slot @slotchange=${this.shouldScheduleManageSelection}></slot>
|
|
@@ -540,6 +591,10 @@ export class PickerBase extends SizedMixin(Focusable, { noDefaultSize: true }) {
|
|
|
540
591
|
}
|
|
541
592
|
return menu;
|
|
542
593
|
}
|
|
594
|
+
/**
|
|
595
|
+
* when the value changes or the menu slot changes, manage the selection on the next frame, if not already scheduled
|
|
596
|
+
* @param event
|
|
597
|
+
*/
|
|
543
598
|
shouldScheduleManageSelection(event) {
|
|
544
599
|
if (!this.willManageSelection && (!event || event.target.getRootNode().host === this)) {
|
|
545
600
|
this.willManageSelection = true;
|
|
@@ -550,6 +605,9 @@ export class PickerBase extends SizedMixin(Focusable, { noDefaultSize: true }) {
|
|
|
550
605
|
});
|
|
551
606
|
}
|
|
552
607
|
}
|
|
608
|
+
/**
|
|
609
|
+
* when an item is added or updated, manage the selection, if it's not already scheduled
|
|
610
|
+
*/
|
|
553
611
|
shouldManageSelection() {
|
|
554
612
|
if (this.willManageSelection) {
|
|
555
613
|
return;
|
|
@@ -557,6 +615,9 @@ export class PickerBase extends SizedMixin(Focusable, { noDefaultSize: true }) {
|
|
|
557
615
|
this.willManageSelection = true;
|
|
558
616
|
this.manageSelection();
|
|
559
617
|
}
|
|
618
|
+
/**
|
|
619
|
+
* updates menu selection based on value
|
|
620
|
+
*/
|
|
560
621
|
async manageSelection() {
|
|
561
622
|
if (this.selects == null) return;
|
|
562
623
|
this.selectionPromise = new Promise(
|
|
@@ -606,6 +667,7 @@ export class PickerBase extends SizedMixin(Focusable, { noDefaultSize: true }) {
|
|
|
606
667
|
connectedCallback() {
|
|
607
668
|
super.connectedCallback();
|
|
608
669
|
this.recentlyConnected = this.hasUpdated;
|
|
670
|
+
this.addEventListener("focus", this.handleFocus);
|
|
609
671
|
}
|
|
610
672
|
disconnectedCallback() {
|
|
611
673
|
var _a;
|
|
@@ -614,6 +676,10 @@ export class PickerBase extends SizedMixin(Focusable, { noDefaultSize: true }) {
|
|
|
614
676
|
super.disconnectedCallback();
|
|
615
677
|
}
|
|
616
678
|
}
|
|
679
|
+
PickerBase.shadowRootOptions = {
|
|
680
|
+
...SpectrumElement.shadowRootOptions,
|
|
681
|
+
delegatesFocus: true
|
|
682
|
+
};
|
|
617
683
|
__decorateClass([
|
|
618
684
|
state()
|
|
619
685
|
], PickerBase.prototype, "appliedLabel", 2);
|
|
@@ -678,33 +744,43 @@ export class Picker extends PickerBase {
|
|
|
678
744
|
constructor() {
|
|
679
745
|
super(...arguments);
|
|
680
746
|
this.handleKeydown = (event) => {
|
|
681
|
-
|
|
747
|
+
var _a;
|
|
748
|
+
const { key } = event;
|
|
749
|
+
const handledKeys = [
|
|
750
|
+
"ArrowUp",
|
|
751
|
+
"ArrowDown",
|
|
752
|
+
"ArrowLeft",
|
|
753
|
+
"ArrowRight",
|
|
754
|
+
"Enter",
|
|
755
|
+
" ",
|
|
756
|
+
"Escape"
|
|
757
|
+
].includes(key);
|
|
758
|
+
const openKeys = ["ArrowUp", "ArrowDown", "Enter", " "].includes(key);
|
|
682
759
|
this.focused = true;
|
|
683
|
-
if (
|
|
760
|
+
if ("Escape" === key) {
|
|
761
|
+
this.handleEscape(event);
|
|
684
762
|
return;
|
|
685
763
|
}
|
|
686
|
-
if (
|
|
687
|
-
this.toggle(true);
|
|
688
|
-
event.preventDefault();
|
|
764
|
+
if (!handledKeys || this.readonly || this.pending) {
|
|
689
765
|
return;
|
|
690
766
|
}
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
let nextIndex = selectedIndex + nextOffset;
|
|
695
|
-
while (this.menuItems[nextIndex] && this.menuItems[nextIndex].disabled) {
|
|
696
|
-
nextIndex += nextOffset;
|
|
697
|
-
}
|
|
698
|
-
if (!this.menuItems[nextIndex] || this.menuItems[nextIndex].disabled) {
|
|
767
|
+
if (openKeys) {
|
|
768
|
+
this.keyboardOpen();
|
|
769
|
+
event.preventDefault();
|
|
699
770
|
return;
|
|
700
771
|
}
|
|
701
|
-
|
|
702
|
-
|
|
772
|
+
event.preventDefault();
|
|
773
|
+
const nextItem = (_a = this.optionsMenu) == null ? void 0 : _a.getNeighboringFocusableElement(
|
|
774
|
+
this.selectedItem,
|
|
775
|
+
key === "ArrowLeft"
|
|
776
|
+
);
|
|
777
|
+
if (!this.value || nextItem !== this.selectedItem) {
|
|
778
|
+
if (!!nextItem) this.setValueFromItem(nextItem);
|
|
703
779
|
}
|
|
704
780
|
};
|
|
705
781
|
}
|
|
706
782
|
static get styles() {
|
|
707
|
-
return [pickerStyles, chevronStyles
|
|
783
|
+
return [pickerStyles, chevronStyles];
|
|
708
784
|
}
|
|
709
785
|
get containerStyles() {
|
|
710
786
|
const styles = super.containerStyles;
|