@vaadin/a11y-base 25.2.0-alpha9 → 25.2.0-beta1
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 +192 -11
- package/package.json +5 -5
- package/src/active-mixin.js +0 -4
- package/src/announce.js +1 -1
- package/src/aria-hidden.js +1 -1
- package/src/aria-modal-controller.d.ts +2 -3
- package/src/delegate-focus-mixin.js +222 -223
- package/src/disabled-mixin.js +47 -48
- package/src/focus-mixin.js +83 -84
- package/src/keyboard-direction-mixin.js +1 -4
- package/src/keyboard-mixin.js +65 -66
- package/src/list-mixin.js +5 -38
- package/src/tabindex-mixin.js +0 -3
package/custom-elements.json
CHANGED
|
@@ -10,7 +10,22 @@
|
|
|
10
10
|
"kind": "mixin",
|
|
11
11
|
"description": "A mixin to toggle the `active` attribute.\n\nThe attribute is set whenever the user activates the element by a pointer\nor presses an activation key on the element from the keyboard.\n\nThe attribute is removed as soon as the element is deactivated\nby the pointer or by releasing the activation key.",
|
|
12
12
|
"name": "ActiveMixin",
|
|
13
|
-
"members": [
|
|
13
|
+
"members": [
|
|
14
|
+
{
|
|
15
|
+
"kind": "field",
|
|
16
|
+
"name": "disabled",
|
|
17
|
+
"privacy": "public",
|
|
18
|
+
"type": {
|
|
19
|
+
"text": "boolean"
|
|
20
|
+
},
|
|
21
|
+
"description": "If true, the user cannot interact with this element.",
|
|
22
|
+
"attribute": "disabled",
|
|
23
|
+
"inheritedFrom": {
|
|
24
|
+
"name": "DisabledMixin",
|
|
25
|
+
"module": "src/disabled-mixin.js"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
],
|
|
14
29
|
"mixins": [
|
|
15
30
|
{
|
|
16
31
|
"name": "DisabledMixin",
|
|
@@ -25,6 +40,20 @@
|
|
|
25
40
|
{
|
|
26
41
|
"name": "superclass"
|
|
27
42
|
}
|
|
43
|
+
],
|
|
44
|
+
"attributes": [
|
|
45
|
+
{
|
|
46
|
+
"name": "disabled",
|
|
47
|
+
"type": {
|
|
48
|
+
"text": "boolean"
|
|
49
|
+
},
|
|
50
|
+
"description": "If true, the user cannot interact with this element.",
|
|
51
|
+
"fieldName": "disabled",
|
|
52
|
+
"inheritedFrom": {
|
|
53
|
+
"name": "DisabledMixin",
|
|
54
|
+
"module": "src/disabled-mixin.js"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
28
57
|
]
|
|
29
58
|
}
|
|
30
59
|
],
|
|
@@ -44,9 +73,76 @@
|
|
|
44
73
|
"path": "src/delegate-focus-mixin.js",
|
|
45
74
|
"declarations": [
|
|
46
75
|
{
|
|
47
|
-
"kind": "
|
|
76
|
+
"kind": "mixin",
|
|
77
|
+
"description": "",
|
|
48
78
|
"name": "DelegateFocusMixin",
|
|
49
|
-
"
|
|
79
|
+
"members": [
|
|
80
|
+
{
|
|
81
|
+
"kind": "field",
|
|
82
|
+
"name": "autofocus",
|
|
83
|
+
"privacy": "public",
|
|
84
|
+
"type": {
|
|
85
|
+
"text": "boolean"
|
|
86
|
+
},
|
|
87
|
+
"description": "Specify that this control should have input focus when the page loads.",
|
|
88
|
+
"attribute": "autofocus"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"kind": "field",
|
|
92
|
+
"name": "disabled",
|
|
93
|
+
"privacy": "public",
|
|
94
|
+
"type": {
|
|
95
|
+
"text": "boolean"
|
|
96
|
+
},
|
|
97
|
+
"description": "If true, the user cannot interact with this element.",
|
|
98
|
+
"attribute": "disabled",
|
|
99
|
+
"inheritedFrom": {
|
|
100
|
+
"name": "DisabledMixin",
|
|
101
|
+
"module": "src/disabled-mixin.js"
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
"attributes": [
|
|
106
|
+
{
|
|
107
|
+
"name": "autofocus",
|
|
108
|
+
"type": {
|
|
109
|
+
"text": "boolean"
|
|
110
|
+
},
|
|
111
|
+
"description": "Specify that this control should have input focus when the page loads.",
|
|
112
|
+
"fieldName": "autofocus"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"name": "disabled",
|
|
116
|
+
"type": {
|
|
117
|
+
"text": "boolean"
|
|
118
|
+
},
|
|
119
|
+
"description": "If true, the user cannot interact with this element.",
|
|
120
|
+
"fieldName": "disabled",
|
|
121
|
+
"inheritedFrom": {
|
|
122
|
+
"name": "DisabledMixin",
|
|
123
|
+
"module": "src/disabled-mixin.js"
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
],
|
|
127
|
+
"mixins": [
|
|
128
|
+
{
|
|
129
|
+
"name": "FocusMixin",
|
|
130
|
+
"module": "src/focus-mixin.js"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"name": "TabindexMixin",
|
|
134
|
+
"module": "src/tabindex-mixin.js"
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"name": "dedupeMixin",
|
|
138
|
+
"package": "@open-wc/dedupe-mixin"
|
|
139
|
+
}
|
|
140
|
+
],
|
|
141
|
+
"parameters": [
|
|
142
|
+
{
|
|
143
|
+
"name": "superclass"
|
|
144
|
+
}
|
|
145
|
+
]
|
|
50
146
|
}
|
|
51
147
|
],
|
|
52
148
|
"exports": [
|
|
@@ -65,9 +161,42 @@
|
|
|
65
161
|
"path": "src/disabled-mixin.js",
|
|
66
162
|
"declarations": [
|
|
67
163
|
{
|
|
68
|
-
"kind": "
|
|
164
|
+
"kind": "mixin",
|
|
165
|
+
"description": "",
|
|
69
166
|
"name": "DisabledMixin",
|
|
70
|
-
"
|
|
167
|
+
"members": [
|
|
168
|
+
{
|
|
169
|
+
"kind": "field",
|
|
170
|
+
"name": "disabled",
|
|
171
|
+
"privacy": "public",
|
|
172
|
+
"type": {
|
|
173
|
+
"text": "boolean"
|
|
174
|
+
},
|
|
175
|
+
"description": "If true, the user cannot interact with this element.",
|
|
176
|
+
"attribute": "disabled"
|
|
177
|
+
}
|
|
178
|
+
],
|
|
179
|
+
"attributes": [
|
|
180
|
+
{
|
|
181
|
+
"name": "disabled",
|
|
182
|
+
"type": {
|
|
183
|
+
"text": "boolean"
|
|
184
|
+
},
|
|
185
|
+
"description": "If true, the user cannot interact with this element.",
|
|
186
|
+
"fieldName": "disabled"
|
|
187
|
+
}
|
|
188
|
+
],
|
|
189
|
+
"parameters": [
|
|
190
|
+
{
|
|
191
|
+
"name": "superclass"
|
|
192
|
+
}
|
|
193
|
+
],
|
|
194
|
+
"mixins": [
|
|
195
|
+
{
|
|
196
|
+
"name": "dedupeMixin",
|
|
197
|
+
"package": "@open-wc/dedupe-mixin"
|
|
198
|
+
}
|
|
199
|
+
]
|
|
71
200
|
}
|
|
72
201
|
],
|
|
73
202
|
"exports": [
|
|
@@ -86,9 +215,21 @@
|
|
|
86
215
|
"path": "src/focus-mixin.js",
|
|
87
216
|
"declarations": [
|
|
88
217
|
{
|
|
89
|
-
"kind": "
|
|
218
|
+
"kind": "mixin",
|
|
219
|
+
"description": "",
|
|
90
220
|
"name": "FocusMixin",
|
|
91
|
-
"
|
|
221
|
+
"members": [],
|
|
222
|
+
"parameters": [
|
|
223
|
+
{
|
|
224
|
+
"name": "superclass"
|
|
225
|
+
}
|
|
226
|
+
],
|
|
227
|
+
"mixins": [
|
|
228
|
+
{
|
|
229
|
+
"name": "dedupeMixin",
|
|
230
|
+
"package": "@open-wc/dedupe-mixin"
|
|
231
|
+
}
|
|
232
|
+
]
|
|
92
233
|
}
|
|
93
234
|
],
|
|
94
235
|
"exports": [
|
|
@@ -140,9 +281,21 @@
|
|
|
140
281
|
"path": "src/keyboard-mixin.js",
|
|
141
282
|
"declarations": [
|
|
142
283
|
{
|
|
143
|
-
"kind": "
|
|
284
|
+
"kind": "mixin",
|
|
285
|
+
"description": "",
|
|
144
286
|
"name": "KeyboardMixin",
|
|
145
|
-
"
|
|
287
|
+
"members": [],
|
|
288
|
+
"parameters": [
|
|
289
|
+
{
|
|
290
|
+
"name": "superclass"
|
|
291
|
+
}
|
|
292
|
+
],
|
|
293
|
+
"mixins": [
|
|
294
|
+
{
|
|
295
|
+
"name": "dedupeMixin",
|
|
296
|
+
"package": "@open-wc/dedupe-mixin"
|
|
297
|
+
}
|
|
298
|
+
]
|
|
146
299
|
}
|
|
147
300
|
],
|
|
148
301
|
"exports": [
|
|
@@ -265,8 +418,36 @@
|
|
|
265
418
|
"kind": "mixin",
|
|
266
419
|
"description": "A mixin to toggle the `tabindex` attribute.\n\nThe attribute is set to -1 whenever the user disables the element\nand restored with the last known value once the element is enabled.",
|
|
267
420
|
"name": "TabindexMixin",
|
|
268
|
-
"members": [
|
|
269
|
-
|
|
421
|
+
"members": [
|
|
422
|
+
{
|
|
423
|
+
"kind": "field",
|
|
424
|
+
"name": "disabled",
|
|
425
|
+
"privacy": "public",
|
|
426
|
+
"type": {
|
|
427
|
+
"text": "boolean"
|
|
428
|
+
},
|
|
429
|
+
"description": "If true, the user cannot interact with this element.",
|
|
430
|
+
"attribute": "disabled",
|
|
431
|
+
"inheritedFrom": {
|
|
432
|
+
"name": "DisabledMixin",
|
|
433
|
+
"module": "src/disabled-mixin.js"
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
],
|
|
437
|
+
"attributes": [
|
|
438
|
+
{
|
|
439
|
+
"name": "disabled",
|
|
440
|
+
"type": {
|
|
441
|
+
"text": "boolean"
|
|
442
|
+
},
|
|
443
|
+
"description": "If true, the user cannot interact with this element.",
|
|
444
|
+
"fieldName": "disabled",
|
|
445
|
+
"inheritedFrom": {
|
|
446
|
+
"name": "DisabledMixin",
|
|
447
|
+
"module": "src/disabled-mixin.js"
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
],
|
|
270
451
|
"mixins": [
|
|
271
452
|
{
|
|
272
453
|
"name": "DisabledMixin",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/a11y-base",
|
|
3
|
-
"version": "25.2.0-
|
|
3
|
+
"version": "25.2.0-beta1",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -32,15 +32,15 @@
|
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@open-wc/dedupe-mixin": "^1.3.0",
|
|
35
|
-
"@vaadin/component-base": "25.2.0-
|
|
35
|
+
"@vaadin/component-base": "25.2.0-beta1",
|
|
36
36
|
"lit": "^3.0.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@vaadin/chai-plugins": "25.2.0-
|
|
40
|
-
"@vaadin/test-runner-commands": "25.2.0-
|
|
39
|
+
"@vaadin/chai-plugins": "25.2.0-beta1",
|
|
40
|
+
"@vaadin/test-runner-commands": "25.2.0-beta1",
|
|
41
41
|
"@vaadin/testing-helpers": "^2.0.0",
|
|
42
42
|
"sinon": "^21.0.2"
|
|
43
43
|
},
|
|
44
44
|
"customElements": "custom-elements.json",
|
|
45
|
-
"gitHead": "
|
|
45
|
+
"gitHead": "471a23f60d1eb725f98a33f62cb9664d9c0a4163"
|
|
46
46
|
}
|
package/src/active-mixin.js
CHANGED
|
@@ -15,10 +15,6 @@ import { KeyboardMixin } from './keyboard-mixin.js';
|
|
|
15
15
|
*
|
|
16
16
|
* The attribute is removed as soon as the element is deactivated
|
|
17
17
|
* by the pointer or by releasing the activation key.
|
|
18
|
-
*
|
|
19
|
-
* @polymerMixin
|
|
20
|
-
* @mixes DisabledMixin
|
|
21
|
-
* @mixes KeyboardMixin
|
|
22
18
|
*/
|
|
23
19
|
export const ActiveMixin = (superclass) =>
|
|
24
20
|
class ActiveMixinClass extends DisabledMixin(KeyboardMixin(superclass)) {
|
package/src/announce.js
CHANGED
|
@@ -23,7 +23,7 @@ let alertDebouncer;
|
|
|
23
23
|
*/
|
|
24
24
|
export function announce(text, options = {}) {
|
|
25
25
|
const mode = options.mode || 'polite';
|
|
26
|
-
const timeout = options.timeout
|
|
26
|
+
const timeout = options.timeout ?? 150;
|
|
27
27
|
|
|
28
28
|
if (mode === 'alert') {
|
|
29
29
|
region.removeAttribute('aria-live');
|
package/src/aria-hidden.js
CHANGED
|
@@ -3,13 +3,12 @@
|
|
|
3
3
|
* Copyright (c) 2021 - 2026 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import type { ReactiveController } from 'lit';
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* A controller for handling modal state on the elements with `dialog` and `alertdialog` role.
|
|
10
9
|
* See https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-modal
|
|
11
10
|
*/
|
|
12
|
-
export class AriaModalController
|
|
11
|
+
export class AriaModalController {
|
|
13
12
|
/**
|
|
14
13
|
* The controller host element.
|
|
15
14
|
*/
|
|
@@ -21,7 +20,7 @@ export class AriaModalController implements ReactiveController {
|
|
|
21
20
|
*/
|
|
22
21
|
callback: () => HTMLElement | HTMLElement[];
|
|
23
22
|
|
|
24
|
-
constructor(
|
|
23
|
+
constructor(host: HTMLElement, callback?: () => HTMLElement | HTMLElement[]);
|
|
25
24
|
|
|
26
25
|
/**
|
|
27
26
|
* Make the controller host element modal by trapping focus inside it and hiding
|
|
@@ -9,237 +9,236 @@ import { TabindexMixin } from './tabindex-mixin.js';
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* A mixin to forward focus to an element in the light DOM.
|
|
12
|
-
*
|
|
13
|
-
* @polymerMixin
|
|
14
|
-
* @mixes FocusMixin
|
|
15
|
-
* @mixes TabindexMixin
|
|
16
12
|
*/
|
|
17
|
-
|
|
18
|
-
(superclass)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
ready()
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
this.setAttribute('focus-ring', '');
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* @protected
|
|
99
|
-
* @override
|
|
100
|
-
*/
|
|
101
|
-
blur() {
|
|
102
|
-
if (this.focusElement) {
|
|
103
|
-
this.focusElement.blur();
|
|
13
|
+
const DelegateFocusMixinImplementation = (superclass) => {
|
|
14
|
+
return class DelegateFocusMixinClass extends FocusMixin(TabindexMixin(superclass)) {
|
|
15
|
+
static get properties() {
|
|
16
|
+
return {
|
|
17
|
+
/**
|
|
18
|
+
* Specify that this control should have input focus when the page loads.
|
|
19
|
+
*/
|
|
20
|
+
autofocus: {
|
|
21
|
+
type: Boolean,
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A reference to the focusable element controlled by the mixin.
|
|
26
|
+
* It can be an input, textarea, button or any element with tabindex > -1.
|
|
27
|
+
*
|
|
28
|
+
* Any component implementing this mixin is expected to provide it
|
|
29
|
+
* by using `this._setFocusElement(input)` Polymer API.
|
|
30
|
+
*
|
|
31
|
+
* Toggling `tabindex` attribute on the host element propagates its value to `focusElement`.
|
|
32
|
+
*
|
|
33
|
+
* @protected
|
|
34
|
+
* @type {!HTMLElement}
|
|
35
|
+
*/
|
|
36
|
+
focusElement: {
|
|
37
|
+
type: Object,
|
|
38
|
+
readOnly: true,
|
|
39
|
+
observer: '_focusElementChanged',
|
|
40
|
+
sync: true,
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Override the property from `TabIndexMixin`
|
|
45
|
+
* to ensure the `tabindex` attribute of the focus element
|
|
46
|
+
* will be restored to `0` after re-enabling the element.
|
|
47
|
+
*
|
|
48
|
+
* @protected
|
|
49
|
+
* @override
|
|
50
|
+
*/
|
|
51
|
+
_lastTabIndex: {
|
|
52
|
+
value: 0,
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
constructor() {
|
|
58
|
+
super();
|
|
59
|
+
|
|
60
|
+
this._boundOnBlur = this._onBlur.bind(this);
|
|
61
|
+
this._boundOnFocus = this._onFocus.bind(this);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** @protected */
|
|
65
|
+
ready() {
|
|
66
|
+
super.ready();
|
|
67
|
+
|
|
68
|
+
if (this.autofocus && !this.disabled) {
|
|
69
|
+
requestAnimationFrame(() => {
|
|
70
|
+
this.focus();
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @param {FocusOptions=} options
|
|
77
|
+
* @protected
|
|
78
|
+
* @override
|
|
79
|
+
*/
|
|
80
|
+
focus(options) {
|
|
81
|
+
if (this.focusElement && !this.disabled) {
|
|
82
|
+
this.focusElement.focus();
|
|
83
|
+
|
|
84
|
+
// Set focus-ring attribute on programmatic focus by default
|
|
85
|
+
// unless explicitly disabled by `{ focusVisible: false }`.
|
|
86
|
+
if (options?.focusVisible !== false) {
|
|
87
|
+
this.setAttribute('focus-ring', '');
|
|
104
88
|
}
|
|
105
89
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @protected
|
|
94
|
+
* @override
|
|
95
|
+
*/
|
|
96
|
+
blur() {
|
|
97
|
+
if (this.focusElement) {
|
|
98
|
+
this.focusElement.blur();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @protected
|
|
104
|
+
* @override
|
|
105
|
+
*/
|
|
106
|
+
click() {
|
|
107
|
+
if (this.focusElement && !this.disabled) {
|
|
108
|
+
this.focusElement.click();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** @protected */
|
|
113
|
+
_focusElementChanged(element, oldElement) {
|
|
114
|
+
if (element) {
|
|
115
|
+
element.disabled = this.disabled;
|
|
116
|
+
this._addFocusListeners(element);
|
|
117
|
+
this.__forwardTabIndex(this.tabindex);
|
|
118
|
+
} else if (oldElement) {
|
|
119
|
+
this._removeFocusListeners(oldElement);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* @param {HTMLElement} element
|
|
125
|
+
* @protected
|
|
126
|
+
*/
|
|
127
|
+
_addFocusListeners(element) {
|
|
128
|
+
element.addEventListener('blur', this._boundOnBlur);
|
|
129
|
+
element.addEventListener('focus', this._boundOnFocus);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* @param {HTMLElement} element
|
|
134
|
+
* @protected
|
|
135
|
+
*/
|
|
136
|
+
_removeFocusListeners(element) {
|
|
137
|
+
element.removeEventListener('blur', this._boundOnBlur);
|
|
138
|
+
element.removeEventListener('focus', this._boundOnFocus);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Focus event does not bubble, so we dispatch it manually
|
|
143
|
+
* on the host element to support adding focus listeners
|
|
144
|
+
* when the focusable element is placed in light DOM.
|
|
145
|
+
* @param {FocusEvent} event
|
|
146
|
+
* @protected
|
|
147
|
+
*/
|
|
148
|
+
_onFocus(event) {
|
|
149
|
+
event.stopPropagation();
|
|
150
|
+
/** @internal to not document it in CEM */
|
|
151
|
+
this.dispatchEvent(new Event('focus'));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Blur event does not bubble, so we dispatch it manually
|
|
156
|
+
* on the host element to support adding blur listeners
|
|
157
|
+
* when the focusable element is placed in light DOM.
|
|
158
|
+
* @param {FocusEvent} event
|
|
159
|
+
* @protected
|
|
160
|
+
*/
|
|
161
|
+
_onBlur(event) {
|
|
162
|
+
event.stopPropagation();
|
|
163
|
+
/** @internal to not document it in CEM */
|
|
164
|
+
this.dispatchEvent(new Event('blur'));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* @param {FocusEvent} event
|
|
169
|
+
* @return {boolean}
|
|
170
|
+
* @protected
|
|
171
|
+
* @override
|
|
172
|
+
*/
|
|
173
|
+
_shouldSetFocus(event) {
|
|
174
|
+
return event.target === this.focusElement;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* @param {FocusEvent} event
|
|
179
|
+
* @return {boolean}
|
|
180
|
+
* @protected
|
|
181
|
+
* @override
|
|
182
|
+
*/
|
|
183
|
+
_shouldRemoveFocus(event) {
|
|
184
|
+
return event.target === this.focusElement;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* @param {boolean} disabled
|
|
189
|
+
* @param {boolean} oldDisabled
|
|
190
|
+
* @protected
|
|
191
|
+
* @override
|
|
192
|
+
*/
|
|
193
|
+
_disabledChanged(disabled, oldDisabled) {
|
|
194
|
+
super._disabledChanged(disabled, oldDisabled);
|
|
195
|
+
|
|
196
|
+
if (this.focusElement) {
|
|
197
|
+
this.focusElement.disabled = disabled;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (disabled) {
|
|
201
|
+
this.blur();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Override an observer from `TabindexMixin`.
|
|
207
|
+
* Do not call super to remove tabindex attribute
|
|
208
|
+
* from the host after it has been forwarded.
|
|
209
|
+
* @param {string} tabindex
|
|
210
|
+
* @protected
|
|
211
|
+
* @override
|
|
212
|
+
*/
|
|
213
|
+
_tabindexChanged(tabindex) {
|
|
214
|
+
this.__forwardTabIndex(tabindex);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** @private */
|
|
218
|
+
__forwardTabIndex(tabindex) {
|
|
219
|
+
if (tabindex !== undefined && this.focusElement) {
|
|
220
|
+
this.focusElement.tabIndex = tabindex;
|
|
221
|
+
|
|
222
|
+
// Preserve tabindex="-1" on the host element
|
|
223
|
+
if (tabindex !== -1) {
|
|
224
|
+
this.tabindex = undefined;
|
|
114
225
|
}
|
|
115
226
|
}
|
|
116
227
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (
|
|
120
|
-
|
|
121
|
-
this._addFocusListeners(element);
|
|
122
|
-
this.__forwardTabIndex(this.tabindex);
|
|
123
|
-
} else if (oldElement) {
|
|
124
|
-
this._removeFocusListeners(oldElement);
|
|
228
|
+
if (this.disabled && tabindex) {
|
|
229
|
+
// If tabindex attribute was changed while component was disabled
|
|
230
|
+
if (tabindex !== -1) {
|
|
231
|
+
this._lastTabIndex = tabindex;
|
|
125
232
|
}
|
|
233
|
+
this.tabindex = undefined;
|
|
126
234
|
}
|
|
127
235
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
*/
|
|
132
|
-
_addFocusListeners(element) {
|
|
133
|
-
element.addEventListener('blur', this._boundOnBlur);
|
|
134
|
-
element.addEventListener('focus', this._boundOnFocus);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* @param {HTMLElement} element
|
|
139
|
-
* @protected
|
|
140
|
-
*/
|
|
141
|
-
_removeFocusListeners(element) {
|
|
142
|
-
element.removeEventListener('blur', this._boundOnBlur);
|
|
143
|
-
element.removeEventListener('focus', this._boundOnFocus);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Focus event does not bubble, so we dispatch it manually
|
|
148
|
-
* on the host element to support adding focus listeners
|
|
149
|
-
* when the focusable element is placed in light DOM.
|
|
150
|
-
* @param {FocusEvent} event
|
|
151
|
-
* @protected
|
|
152
|
-
*/
|
|
153
|
-
_onFocus(event) {
|
|
154
|
-
event.stopPropagation();
|
|
155
|
-
this.dispatchEvent(new Event('focus'));
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Blur event does not bubble, so we dispatch it manually
|
|
160
|
-
* on the host element to support adding blur listeners
|
|
161
|
-
* when the focusable element is placed in light DOM.
|
|
162
|
-
* @param {FocusEvent} event
|
|
163
|
-
* @protected
|
|
164
|
-
*/
|
|
165
|
-
_onBlur(event) {
|
|
166
|
-
event.stopPropagation();
|
|
167
|
-
this.dispatchEvent(new Event('blur'));
|
|
236
|
+
// Lit does not remove attribute when setting property to undefined
|
|
237
|
+
if (tabindex === undefined && this.hasAttribute('tabindex')) {
|
|
238
|
+
this.removeAttribute('tabindex');
|
|
168
239
|
}
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
};
|
|
169
243
|
|
|
170
|
-
|
|
171
|
-
* @param {FocusEvent} event
|
|
172
|
-
* @return {boolean}
|
|
173
|
-
* @protected
|
|
174
|
-
* @override
|
|
175
|
-
*/
|
|
176
|
-
_shouldSetFocus(event) {
|
|
177
|
-
return event.target === this.focusElement;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* @param {FocusEvent} event
|
|
182
|
-
* @return {boolean}
|
|
183
|
-
* @protected
|
|
184
|
-
* @override
|
|
185
|
-
*/
|
|
186
|
-
_shouldRemoveFocus(event) {
|
|
187
|
-
return event.target === this.focusElement;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* @param {boolean} disabled
|
|
192
|
-
* @param {boolean} oldDisabled
|
|
193
|
-
* @protected
|
|
194
|
-
* @override
|
|
195
|
-
*/
|
|
196
|
-
_disabledChanged(disabled, oldDisabled) {
|
|
197
|
-
super._disabledChanged(disabled, oldDisabled);
|
|
198
|
-
|
|
199
|
-
if (this.focusElement) {
|
|
200
|
-
this.focusElement.disabled = disabled;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
if (disabled) {
|
|
204
|
-
this.blur();
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Override an observer from `TabindexMixin`.
|
|
210
|
-
* Do not call super to remove tabindex attribute
|
|
211
|
-
* from the host after it has been forwarded.
|
|
212
|
-
* @param {string} tabindex
|
|
213
|
-
* @protected
|
|
214
|
-
* @override
|
|
215
|
-
*/
|
|
216
|
-
_tabindexChanged(tabindex) {
|
|
217
|
-
this.__forwardTabIndex(tabindex);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/** @private */
|
|
221
|
-
__forwardTabIndex(tabindex) {
|
|
222
|
-
if (tabindex !== undefined && this.focusElement) {
|
|
223
|
-
this.focusElement.tabIndex = tabindex;
|
|
224
|
-
|
|
225
|
-
// Preserve tabindex="-1" on the host element
|
|
226
|
-
if (tabindex !== -1) {
|
|
227
|
-
this.tabindex = undefined;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (this.disabled && tabindex) {
|
|
232
|
-
// If tabindex attribute was changed while component was disabled
|
|
233
|
-
if (tabindex !== -1) {
|
|
234
|
-
this._lastTabIndex = tabindex;
|
|
235
|
-
}
|
|
236
|
-
this.tabindex = undefined;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Lit does not remove attribute when setting property to undefined
|
|
240
|
-
if (tabindex === undefined && this.hasAttribute('tabindex')) {
|
|
241
|
-
this.removeAttribute('tabindex');
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
},
|
|
245
|
-
);
|
|
244
|
+
export const DelegateFocusMixin = dedupeMixin(DelegateFocusMixinImplementation);
|
package/src/disabled-mixin.js
CHANGED
|
@@ -7,57 +7,56 @@ import { dedupeMixin } from '@open-wc/dedupe-mixin';
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* A mixin to provide disabled property for field components.
|
|
10
|
-
*
|
|
11
|
-
* @polymerMixin
|
|
12
10
|
*/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
11
|
+
const DisabledMixinImplementation = (superclass) => {
|
|
12
|
+
return class DisabledMixinClass extends superclass {
|
|
13
|
+
static get properties() {
|
|
14
|
+
return {
|
|
15
|
+
/**
|
|
16
|
+
* If true, the user cannot interact with this element.
|
|
17
|
+
*/
|
|
18
|
+
disabled: {
|
|
19
|
+
type: Boolean,
|
|
20
|
+
value: false,
|
|
21
|
+
observer: '_disabledChanged',
|
|
22
|
+
reflectToAttribute: true,
|
|
23
|
+
sync: true,
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
30
27
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
28
|
+
/**
|
|
29
|
+
* @param {boolean} disabled
|
|
30
|
+
* @protected
|
|
31
|
+
*/
|
|
32
|
+
_disabledChanged(disabled) {
|
|
33
|
+
this._setAriaDisabled(disabled);
|
|
34
|
+
}
|
|
38
35
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
36
|
+
/**
|
|
37
|
+
* @param {boolean} disabled
|
|
38
|
+
* @protected
|
|
39
|
+
*/
|
|
40
|
+
_setAriaDisabled(disabled) {
|
|
41
|
+
if (disabled) {
|
|
42
|
+
this.setAttribute('aria-disabled', 'true');
|
|
43
|
+
} else {
|
|
44
|
+
this.removeAttribute('aria-disabled');
|
|
49
45
|
}
|
|
46
|
+
}
|
|
50
47
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
48
|
+
/**
|
|
49
|
+
* Overrides the default element `click` method in order to prevent
|
|
50
|
+
* firing the `click` event when the element is disabled.
|
|
51
|
+
* @protected
|
|
52
|
+
* @override
|
|
53
|
+
*/
|
|
54
|
+
click() {
|
|
55
|
+
if (!this.disabled) {
|
|
56
|
+
super.click();
|
|
61
57
|
}
|
|
62
|
-
}
|
|
63
|
-
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const DisabledMixin = dedupeMixin(DisabledMixinImplementation);
|
package/src/focus-mixin.js
CHANGED
|
@@ -8,101 +8,100 @@ import { isKeyboardActive } from './focus-utils.js';
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* A mixin to handle `focused` and `focus-ring` attributes based on focus.
|
|
11
|
-
*
|
|
12
|
-
* @polymerMixin
|
|
13
11
|
*/
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
12
|
+
const FocusMixinImplementation = (superclass) => {
|
|
13
|
+
return class FocusMixinClass extends superclass {
|
|
14
|
+
/**
|
|
15
|
+
* @protected
|
|
16
|
+
* @return {boolean}
|
|
17
|
+
*/
|
|
18
|
+
get _keyboardActive() {
|
|
19
|
+
return isKeyboardActive();
|
|
20
|
+
}
|
|
24
21
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
22
|
+
/** @protected */
|
|
23
|
+
ready() {
|
|
24
|
+
this.addEventListener('focusin', (e) => {
|
|
25
|
+
if (this._shouldSetFocus(e)) {
|
|
26
|
+
this._setFocused(true);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
32
29
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
this.addEventListener('focusout', (e) => {
|
|
31
|
+
if (this._shouldRemoveFocus(e)) {
|
|
32
|
+
this._setFocused(false);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
38
35
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
36
|
+
// In super.ready() other 'focusin' and 'focusout' listeners might be
|
|
37
|
+
// added, so we call it after our own ones to ensure they execute first.
|
|
38
|
+
// Issue to watch out: when incorrect, <vaadin-combo-box> refocuses the
|
|
39
|
+
// input field on iOS after "Done" is pressed.
|
|
40
|
+
super.ready();
|
|
41
|
+
}
|
|
45
42
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
/** @protected */
|
|
44
|
+
disconnectedCallback() {
|
|
45
|
+
super.disconnectedCallback();
|
|
49
46
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
47
|
+
// In non-Chrome browsers, blur does not fire on the element when it is disconnected.
|
|
48
|
+
// reproducible in `<vaadin-date-picker>` when closing on `Cancel` or `Today` click.
|
|
49
|
+
if (this.hasAttribute('focused')) {
|
|
50
|
+
this._setFocused(false);
|
|
55
51
|
}
|
|
52
|
+
}
|
|
56
53
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
54
|
+
/**
|
|
55
|
+
* @param {FocusOptions=} options
|
|
56
|
+
* @protected
|
|
57
|
+
* @override
|
|
58
|
+
*/
|
|
59
|
+
focus(options) {
|
|
60
|
+
super.focus(options);
|
|
64
61
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
62
|
+
// Set focus-ring attribute on programmatic focus by default
|
|
63
|
+
// unless explicitly disabled by `{ focusVisible: false }`.
|
|
64
|
+
if (options?.focusVisible !== false) {
|
|
65
|
+
this.setAttribute('focus-ring', '');
|
|
70
66
|
}
|
|
67
|
+
}
|
|
71
68
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Override to change how focused and focus-ring attributes are set.
|
|
71
|
+
*
|
|
72
|
+
* @param {boolean} focused
|
|
73
|
+
* @protected
|
|
74
|
+
*/
|
|
75
|
+
_setFocused(focused) {
|
|
76
|
+
this.toggleAttribute('focused', focused);
|
|
80
77
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
78
|
+
// Focus-ring is true when the element was focused from the keyboard.
|
|
79
|
+
// Focus Ring [A11ycasts]: https://youtu.be/ilj2P5-5CjI
|
|
80
|
+
this.toggleAttribute('focus-ring', focused && this._keyboardActive);
|
|
81
|
+
}
|
|
85
82
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Override to define if the field receives focus based on the event.
|
|
85
|
+
*
|
|
86
|
+
* @param {FocusEvent} _event
|
|
87
|
+
* @return {boolean}
|
|
88
|
+
* @protected
|
|
89
|
+
*/
|
|
90
|
+
_shouldSetFocus(_event) {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
96
93
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Override to define if the field loses focus based on the event.
|
|
96
|
+
*
|
|
97
|
+
* @param {FocusEvent} _event
|
|
98
|
+
* @return {boolean}
|
|
99
|
+
* @protected
|
|
100
|
+
*/
|
|
101
|
+
_shouldRemoveFocus(_event) {
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export const FocusMixin = dedupeMixin(FocusMixinImplementation);
|
|
@@ -8,9 +8,6 @@ import { KeyboardMixin } from './keyboard-mixin.js';
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* A mixin for navigating items with keyboard.
|
|
11
|
-
*
|
|
12
|
-
* @polymerMixin
|
|
13
|
-
* @mixes KeyboardMixin
|
|
14
11
|
*/
|
|
15
12
|
export const KeyboardDirectionMixin = (superclass) =>
|
|
16
13
|
class KeyboardDirectionMixinClass extends KeyboardMixin(superclass) {
|
|
@@ -127,7 +124,7 @@ export const KeyboardDirectionMixin = (superclass) =>
|
|
|
127
124
|
|
|
128
125
|
if (idx >= 0) {
|
|
129
126
|
event.preventDefault();
|
|
130
|
-
this._focus(idx, { focusVisible: true }, true);
|
|
127
|
+
this._focus(idx, { focusVisible: true, preventScroll: true }, true);
|
|
131
128
|
}
|
|
132
129
|
}
|
|
133
130
|
|
package/src/keyboard-mixin.js
CHANGED
|
@@ -9,77 +9,76 @@ import { dedupeMixin } from '@open-wc/dedupe-mixin';
|
|
|
9
9
|
* A mixin that manages keyboard handling.
|
|
10
10
|
* The mixin subscribes to the keyboard events while an actual implementation
|
|
11
11
|
* for the event handlers is left to the client (a component or another mixin).
|
|
12
|
-
*
|
|
13
|
-
* @polymerMixin
|
|
14
12
|
*/
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
ready()
|
|
20
|
-
super.ready();
|
|
13
|
+
const KeyboardMixinImplementation = (superclass) => {
|
|
14
|
+
return class KeyboardMixinClass extends superclass {
|
|
15
|
+
/** @protected */
|
|
16
|
+
ready() {
|
|
17
|
+
super.ready();
|
|
21
18
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
this.addEventListener('keydown', (event) => {
|
|
20
|
+
this._onKeyDown(event);
|
|
21
|
+
});
|
|
25
22
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
this.addEventListener('keyup', (event) => {
|
|
24
|
+
this._onKeyUp(event);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
30
27
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
28
|
+
/**
|
|
29
|
+
* A handler for the `keydown` event. By default, it calls
|
|
30
|
+
* separate methods for handling "Enter" and "Escape" keys.
|
|
31
|
+
* Override the method to implement your own behavior.
|
|
32
|
+
*
|
|
33
|
+
* @param {KeyboardEvent} event
|
|
34
|
+
* @protected
|
|
35
|
+
*/
|
|
36
|
+
_onKeyDown(event) {
|
|
37
|
+
switch (event.key) {
|
|
38
|
+
case 'Enter':
|
|
39
|
+
this._onEnter(event);
|
|
40
|
+
break;
|
|
41
|
+
case 'Escape':
|
|
42
|
+
this._onEscape(event);
|
|
43
|
+
break;
|
|
44
|
+
default:
|
|
45
|
+
break;
|
|
50
46
|
}
|
|
47
|
+
}
|
|
51
48
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
49
|
+
/**
|
|
50
|
+
* A handler for the `keyup` event. By default, it does nothing.
|
|
51
|
+
* Override the method to implement your own behavior.
|
|
52
|
+
*
|
|
53
|
+
* @param {KeyboardEvent} _event
|
|
54
|
+
* @protected
|
|
55
|
+
*/
|
|
56
|
+
_onKeyUp(_event) {
|
|
57
|
+
// To be implemented.
|
|
58
|
+
}
|
|
62
59
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
60
|
+
/**
|
|
61
|
+
* A handler for the "Enter" key. By default, it does nothing.
|
|
62
|
+
* Override the method to implement your own behavior.
|
|
63
|
+
*
|
|
64
|
+
* @param {KeyboardEvent} _event
|
|
65
|
+
* @protected
|
|
66
|
+
*/
|
|
67
|
+
_onEnter(_event) {
|
|
68
|
+
// To be implemented.
|
|
69
|
+
}
|
|
73
70
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
71
|
+
/**
|
|
72
|
+
* A handler for the "Escape" key. By default, it does nothing.
|
|
73
|
+
* Override the method to implement your own behavior.
|
|
74
|
+
*
|
|
75
|
+
* @param {KeyboardEvent} _event
|
|
76
|
+
* @protected
|
|
77
|
+
*/
|
|
78
|
+
_onEscape(_event) {
|
|
79
|
+
// To be implemented.
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export const KeyboardMixin = dedupeMixin(KeyboardMixinImplementation);
|
package/src/list-mixin.js
CHANGED
|
@@ -12,9 +12,6 @@ import { KeyboardDirectionMixin } from './keyboard-direction-mixin.js';
|
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* A mixin for list elements, facilitating navigation and selection of items.
|
|
15
|
-
*
|
|
16
|
-
* @polymerMixin
|
|
17
|
-
* @mixes KeyboardDirectionMixin
|
|
18
15
|
*/
|
|
19
16
|
export const ListMixin = (superClass) =>
|
|
20
17
|
class ListMixinClass extends KeyboardDirectionMixin(superClass) {
|
|
@@ -285,40 +282,19 @@ export const ListMixin = (superClass) =>
|
|
|
285
282
|
});
|
|
286
283
|
this._setFocusable(idx);
|
|
287
284
|
this._scrollToItem(idx);
|
|
288
|
-
super._focus(idx, options);
|
|
285
|
+
super._focus(idx, options ?? { preventScroll: true });
|
|
289
286
|
}
|
|
290
287
|
|
|
291
288
|
/**
|
|
292
|
-
* Scroll the container to have the
|
|
289
|
+
* Scroll the container to have the item visible.
|
|
293
290
|
* @param {number} idx
|
|
294
291
|
* @protected
|
|
295
292
|
*/
|
|
296
293
|
_scrollToItem(idx) {
|
|
297
|
-
const item = this.
|
|
298
|
-
if (
|
|
299
|
-
|
|
294
|
+
const item = this._getItems()[idx];
|
|
295
|
+
if (item) {
|
|
296
|
+
item.scrollIntoView({ block: 'nearest', inline: 'nearest' });
|
|
300
297
|
}
|
|
301
|
-
|
|
302
|
-
const props = this._vertical ? ['top', 'bottom'] : this._isRTL ? ['right', 'left'] : ['left', 'right'];
|
|
303
|
-
|
|
304
|
-
const scrollerRect = this._scrollerElement.getBoundingClientRect();
|
|
305
|
-
const nextItemRect = (this.items[idx + 1] || item).getBoundingClientRect();
|
|
306
|
-
const prevItemRect = (this.items[idx - 1] || item).getBoundingClientRect();
|
|
307
|
-
|
|
308
|
-
let scrollDistance = 0;
|
|
309
|
-
if (
|
|
310
|
-
(!this._isRTL && nextItemRect[props[1]] >= scrollerRect[props[1]]) ||
|
|
311
|
-
(this._isRTL && nextItemRect[props[1]] <= scrollerRect[props[1]])
|
|
312
|
-
) {
|
|
313
|
-
scrollDistance = nextItemRect[props[1]] - scrollerRect[props[1]];
|
|
314
|
-
} else if (
|
|
315
|
-
(!this._isRTL && prevItemRect[props[0]] <= scrollerRect[props[0]]) ||
|
|
316
|
-
(this._isRTL && prevItemRect[props[0]] >= scrollerRect[props[0]])
|
|
317
|
-
) {
|
|
318
|
-
scrollDistance = prevItemRect[props[0]] - scrollerRect[props[0]];
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
this._scroll(scrollDistance);
|
|
322
298
|
}
|
|
323
299
|
|
|
324
300
|
/**
|
|
@@ -350,13 +326,4 @@ export const ListMixin = (superClass) =>
|
|
|
350
326
|
|
|
351
327
|
return super._isItemFocusable(item);
|
|
352
328
|
}
|
|
353
|
-
|
|
354
|
-
/**
|
|
355
|
-
* Fired when the selection is changed.
|
|
356
|
-
* Not fired when used in `multiple` selection mode.
|
|
357
|
-
*
|
|
358
|
-
* @event selected-changed
|
|
359
|
-
* @param {Object} detail
|
|
360
|
-
* @param {Object} detail.value the index of the item selected in the items array.
|
|
361
|
-
*/
|
|
362
329
|
};
|
package/src/tabindex-mixin.js
CHANGED
|
@@ -10,9 +10,6 @@ import { DisabledMixin } from './disabled-mixin.js';
|
|
|
10
10
|
*
|
|
11
11
|
* The attribute is set to -1 whenever the user disables the element
|
|
12
12
|
* and restored with the last known value once the element is enabled.
|
|
13
|
-
*
|
|
14
|
-
* @polymerMixin
|
|
15
|
-
* @mixes DisabledMixin
|
|
16
13
|
*/
|
|
17
14
|
export const TabindexMixin = (superclass) =>
|
|
18
15
|
class TabindexMixinClass extends DisabledMixin(superclass) {
|