@vaadin/a11y-base 25.2.0-alpha1 → 25.2.0-alpha10
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/aria-hidden.js +1 -1
- package/src/delegate-focus-mixin.js +222 -219
- package/src/disabled-mixin.js +47 -46
- package/src/focus-mixin.js +83 -82
- package/src/keyboard-mixin.js +65 -64
- package/src/list-mixin.js +12 -5
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-alpha10",
|
|
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-alpha10",
|
|
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-alpha10",
|
|
40
|
+
"@vaadin/test-runner-commands": "25.2.0-alpha10",
|
|
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": "1303b6a3eeecb44a9d26f2b53cb56d9e906febdf"
|
|
46
46
|
}
|
package/src/aria-hidden.js
CHANGED
|
@@ -14,232 +14,235 @@ import { TabindexMixin } from './tabindex-mixin.js';
|
|
|
14
14
|
* @mixes FocusMixin
|
|
15
15
|
* @mixes TabindexMixin
|
|
16
16
|
*/
|
|
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();
|
|
17
|
+
const DelegateFocusMixinImplementation = (superclass) => {
|
|
18
|
+
return class DelegateFocusMixinClass extends FocusMixin(TabindexMixin(superclass)) {
|
|
19
|
+
static get properties() {
|
|
20
|
+
return {
|
|
21
|
+
/**
|
|
22
|
+
* Specify that this control should have input focus when the page loads.
|
|
23
|
+
*/
|
|
24
|
+
autofocus: {
|
|
25
|
+
type: Boolean,
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* A reference to the focusable element controlled by the mixin.
|
|
30
|
+
* It can be an input, textarea, button or any element with tabindex > -1.
|
|
31
|
+
*
|
|
32
|
+
* Any component implementing this mixin is expected to provide it
|
|
33
|
+
* by using `this._setFocusElement(input)` Polymer API.
|
|
34
|
+
*
|
|
35
|
+
* Toggling `tabindex` attribute on the host element propagates its value to `focusElement`.
|
|
36
|
+
*
|
|
37
|
+
* @protected
|
|
38
|
+
* @type {!HTMLElement}
|
|
39
|
+
*/
|
|
40
|
+
focusElement: {
|
|
41
|
+
type: Object,
|
|
42
|
+
readOnly: true,
|
|
43
|
+
observer: '_focusElementChanged',
|
|
44
|
+
sync: true,
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Override the property from `TabIndexMixin`
|
|
49
|
+
* to ensure the `tabindex` attribute of the focus element
|
|
50
|
+
* will be restored to `0` after re-enabling the element.
|
|
51
|
+
*
|
|
52
|
+
* @protected
|
|
53
|
+
* @override
|
|
54
|
+
*/
|
|
55
|
+
_lastTabIndex: {
|
|
56
|
+
value: 0,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
constructor() {
|
|
62
|
+
super();
|
|
63
|
+
|
|
64
|
+
this._boundOnBlur = this._onBlur.bind(this);
|
|
65
|
+
this._boundOnFocus = this._onFocus.bind(this);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** @protected */
|
|
69
|
+
ready() {
|
|
70
|
+
super.ready();
|
|
71
|
+
|
|
72
|
+
if (this.autofocus && !this.disabled) {
|
|
73
|
+
requestAnimationFrame(() => {
|
|
74
|
+
this.focus();
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* @param {FocusOptions=} options
|
|
81
|
+
* @protected
|
|
82
|
+
* @override
|
|
83
|
+
*/
|
|
84
|
+
focus(options) {
|
|
85
|
+
if (this.focusElement && !this.disabled) {
|
|
86
|
+
this.focusElement.focus();
|
|
87
|
+
|
|
88
|
+
// Set focus-ring attribute on programmatic focus by default
|
|
89
|
+
// unless explicitly disabled by `{ focusVisible: false }`.
|
|
90
|
+
if (options?.focusVisible !== false) {
|
|
91
|
+
this.setAttribute('focus-ring', '');
|
|
104
92
|
}
|
|
105
93
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @protected
|
|
98
|
+
* @override
|
|
99
|
+
*/
|
|
100
|
+
blur() {
|
|
101
|
+
if (this.focusElement) {
|
|
102
|
+
this.focusElement.blur();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @protected
|
|
108
|
+
* @override
|
|
109
|
+
*/
|
|
110
|
+
click() {
|
|
111
|
+
if (this.focusElement && !this.disabled) {
|
|
112
|
+
this.focusElement.click();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** @protected */
|
|
117
|
+
_focusElementChanged(element, oldElement) {
|
|
118
|
+
if (element) {
|
|
119
|
+
element.disabled = this.disabled;
|
|
120
|
+
this._addFocusListeners(element);
|
|
121
|
+
this.__forwardTabIndex(this.tabindex);
|
|
122
|
+
} else if (oldElement) {
|
|
123
|
+
this._removeFocusListeners(oldElement);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @param {HTMLElement} element
|
|
129
|
+
* @protected
|
|
130
|
+
*/
|
|
131
|
+
_addFocusListeners(element) {
|
|
132
|
+
element.addEventListener('blur', this._boundOnBlur);
|
|
133
|
+
element.addEventListener('focus', this._boundOnFocus);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* @param {HTMLElement} element
|
|
138
|
+
* @protected
|
|
139
|
+
*/
|
|
140
|
+
_removeFocusListeners(element) {
|
|
141
|
+
element.removeEventListener('blur', this._boundOnBlur);
|
|
142
|
+
element.removeEventListener('focus', this._boundOnFocus);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Focus event does not bubble, so we dispatch it manually
|
|
147
|
+
* on the host element to support adding focus listeners
|
|
148
|
+
* when the focusable element is placed in light DOM.
|
|
149
|
+
* @param {FocusEvent} event
|
|
150
|
+
* @protected
|
|
151
|
+
*/
|
|
152
|
+
_onFocus(event) {
|
|
153
|
+
event.stopPropagation();
|
|
154
|
+
/** @internal to not document it in CEM */
|
|
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
|
+
/** @internal to not document it in CEM */
|
|
168
|
+
this.dispatchEvent(new Event('blur'));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* @param {FocusEvent} event
|
|
173
|
+
* @return {boolean}
|
|
174
|
+
* @protected
|
|
175
|
+
* @override
|
|
176
|
+
*/
|
|
177
|
+
_shouldSetFocus(event) {
|
|
178
|
+
return event.target === this.focusElement;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* @param {FocusEvent} event
|
|
183
|
+
* @return {boolean}
|
|
184
|
+
* @protected
|
|
185
|
+
* @override
|
|
186
|
+
*/
|
|
187
|
+
_shouldRemoveFocus(event) {
|
|
188
|
+
return event.target === this.focusElement;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* @param {boolean} disabled
|
|
193
|
+
* @param {boolean} oldDisabled
|
|
194
|
+
* @protected
|
|
195
|
+
* @override
|
|
196
|
+
*/
|
|
197
|
+
_disabledChanged(disabled, oldDisabled) {
|
|
198
|
+
super._disabledChanged(disabled, oldDisabled);
|
|
199
|
+
|
|
200
|
+
if (this.focusElement) {
|
|
201
|
+
this.focusElement.disabled = disabled;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (disabled) {
|
|
205
|
+
this.blur();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Override an observer from `TabindexMixin`.
|
|
211
|
+
* Do not call super to remove tabindex attribute
|
|
212
|
+
* from the host after it has been forwarded.
|
|
213
|
+
* @param {string} tabindex
|
|
214
|
+
* @protected
|
|
215
|
+
* @override
|
|
216
|
+
*/
|
|
217
|
+
_tabindexChanged(tabindex) {
|
|
218
|
+
this.__forwardTabIndex(tabindex);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** @private */
|
|
222
|
+
__forwardTabIndex(tabindex) {
|
|
223
|
+
if (tabindex !== undefined && this.focusElement) {
|
|
224
|
+
this.focusElement.tabIndex = tabindex;
|
|
225
|
+
|
|
226
|
+
// Preserve tabindex="-1" on the host element
|
|
227
|
+
if (tabindex !== -1) {
|
|
228
|
+
this.tabindex = undefined;
|
|
114
229
|
}
|
|
115
230
|
}
|
|
116
231
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (
|
|
120
|
-
|
|
121
|
-
this._addFocusListeners(element);
|
|
122
|
-
this.__forwardTabIndex(this.tabindex);
|
|
123
|
-
} else if (oldElement) {
|
|
124
|
-
this._removeFocusListeners(oldElement);
|
|
232
|
+
if (this.disabled && tabindex) {
|
|
233
|
+
// If tabindex attribute was changed while component was disabled
|
|
234
|
+
if (tabindex !== -1) {
|
|
235
|
+
this._lastTabIndex = tabindex;
|
|
125
236
|
}
|
|
237
|
+
this.tabindex = undefined;
|
|
126
238
|
}
|
|
127
239
|
|
|
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'));
|
|
240
|
+
// Lit does not remove attribute when setting property to undefined
|
|
241
|
+
if (tabindex === undefined && this.hasAttribute('tabindex')) {
|
|
242
|
+
this.removeAttribute('tabindex');
|
|
168
243
|
}
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
};
|
|
169
247
|
|
|
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
|
-
);
|
|
248
|
+
export const DelegateFocusMixin = dedupeMixin(DelegateFocusMixinImplementation);
|
package/src/disabled-mixin.js
CHANGED
|
@@ -10,54 +10,55 @@ import { dedupeMixin } from '@open-wc/dedupe-mixin';
|
|
|
10
10
|
*
|
|
11
11
|
* @polymerMixin
|
|
12
12
|
*/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
13
|
+
const DisabledMixinImplementation = (superclass) => {
|
|
14
|
+
return class DisabledMixinClass extends superclass {
|
|
15
|
+
static get properties() {
|
|
16
|
+
return {
|
|
17
|
+
/**
|
|
18
|
+
* If true, the user cannot interact with this element.
|
|
19
|
+
*/
|
|
20
|
+
disabled: {
|
|
21
|
+
type: Boolean,
|
|
22
|
+
value: false,
|
|
23
|
+
observer: '_disabledChanged',
|
|
24
|
+
reflectToAttribute: true,
|
|
25
|
+
sync: true,
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
/**
|
|
31
|
+
* @param {boolean} disabled
|
|
32
|
+
* @protected
|
|
33
|
+
*/
|
|
34
|
+
_disabledChanged(disabled) {
|
|
35
|
+
this._setAriaDisabled(disabled);
|
|
36
|
+
}
|
|
38
37
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
38
|
+
/**
|
|
39
|
+
* @param {boolean} disabled
|
|
40
|
+
* @protected
|
|
41
|
+
*/
|
|
42
|
+
_setAriaDisabled(disabled) {
|
|
43
|
+
if (disabled) {
|
|
44
|
+
this.setAttribute('aria-disabled', 'true');
|
|
45
|
+
} else {
|
|
46
|
+
this.removeAttribute('aria-disabled');
|
|
49
47
|
}
|
|
48
|
+
}
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
50
|
+
/**
|
|
51
|
+
* Overrides the default element `click` method in order to prevent
|
|
52
|
+
* firing the `click` event when the element is disabled.
|
|
53
|
+
* @protected
|
|
54
|
+
* @override
|
|
55
|
+
*/
|
|
56
|
+
click() {
|
|
57
|
+
if (!this.disabled) {
|
|
58
|
+
super.click();
|
|
61
59
|
}
|
|
62
|
-
}
|
|
63
|
-
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const DisabledMixin = dedupeMixin(DisabledMixinImplementation);
|
package/src/focus-mixin.js
CHANGED
|
@@ -11,98 +11,99 @@ import { isKeyboardActive } from './focus-utils.js';
|
|
|
11
11
|
*
|
|
12
12
|
* @polymerMixin
|
|
13
13
|
*/
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
14
|
+
const FocusMixinImplementation = (superclass) => {
|
|
15
|
+
return class FocusMixinClass extends superclass {
|
|
16
|
+
/**
|
|
17
|
+
* @protected
|
|
18
|
+
* @return {boolean}
|
|
19
|
+
*/
|
|
20
|
+
get _keyboardActive() {
|
|
21
|
+
return isKeyboardActive();
|
|
22
|
+
}
|
|
24
23
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
/** @protected */
|
|
25
|
+
ready() {
|
|
26
|
+
this.addEventListener('focusin', (e) => {
|
|
27
|
+
if (this._shouldSetFocus(e)) {
|
|
28
|
+
this._setFocused(true);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
32
31
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
this.addEventListener('focusout', (e) => {
|
|
33
|
+
if (this._shouldRemoveFocus(e)) {
|
|
34
|
+
this._setFocused(false);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
38
37
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
38
|
+
// In super.ready() other 'focusin' and 'focusout' listeners might be
|
|
39
|
+
// added, so we call it after our own ones to ensure they execute first.
|
|
40
|
+
// Issue to watch out: when incorrect, <vaadin-combo-box> refocuses the
|
|
41
|
+
// input field on iOS after "Done" is pressed.
|
|
42
|
+
super.ready();
|
|
43
|
+
}
|
|
45
44
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
/** @protected */
|
|
46
|
+
disconnectedCallback() {
|
|
47
|
+
super.disconnectedCallback();
|
|
49
48
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
49
|
+
// In non-Chrome browsers, blur does not fire on the element when it is disconnected.
|
|
50
|
+
// reproducible in `<vaadin-date-picker>` when closing on `Cancel` or `Today` click.
|
|
51
|
+
if (this.hasAttribute('focused')) {
|
|
52
|
+
this._setFocused(false);
|
|
55
53
|
}
|
|
54
|
+
}
|
|
56
55
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
56
|
+
/**
|
|
57
|
+
* @param {FocusOptions=} options
|
|
58
|
+
* @protected
|
|
59
|
+
* @override
|
|
60
|
+
*/
|
|
61
|
+
focus(options) {
|
|
62
|
+
super.focus(options);
|
|
64
63
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
64
|
+
// Set focus-ring attribute on programmatic focus by default
|
|
65
|
+
// unless explicitly disabled by `{ focusVisible: false }`.
|
|
66
|
+
if (options?.focusVisible !== false) {
|
|
67
|
+
this.setAttribute('focus-ring', '');
|
|
70
68
|
}
|
|
69
|
+
}
|
|
71
70
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
71
|
+
/**
|
|
72
|
+
* Override to change how focused and focus-ring attributes are set.
|
|
73
|
+
*
|
|
74
|
+
* @param {boolean} focused
|
|
75
|
+
* @protected
|
|
76
|
+
*/
|
|
77
|
+
_setFocused(focused) {
|
|
78
|
+
this.toggleAttribute('focused', focused);
|
|
80
79
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
80
|
+
// Focus-ring is true when the element was focused from the keyboard.
|
|
81
|
+
// Focus Ring [A11ycasts]: https://youtu.be/ilj2P5-5CjI
|
|
82
|
+
this.toggleAttribute('focus-ring', focused && this._keyboardActive);
|
|
83
|
+
}
|
|
85
84
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
85
|
+
/**
|
|
86
|
+
* Override to define if the field receives focus based on the event.
|
|
87
|
+
*
|
|
88
|
+
* @param {FocusEvent} _event
|
|
89
|
+
* @return {boolean}
|
|
90
|
+
* @protected
|
|
91
|
+
*/
|
|
92
|
+
_shouldSetFocus(_event) {
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
96
95
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
96
|
+
/**
|
|
97
|
+
* Override to define if the field loses focus based on the event.
|
|
98
|
+
*
|
|
99
|
+
* @param {FocusEvent} _event
|
|
100
|
+
* @return {boolean}
|
|
101
|
+
* @protected
|
|
102
|
+
*/
|
|
103
|
+
_shouldRemoveFocus(_event) {
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export const FocusMixin = dedupeMixin(FocusMixinImplementation);
|
package/src/keyboard-mixin.js
CHANGED
|
@@ -12,74 +12,75 @@ import { dedupeMixin } from '@open-wc/dedupe-mixin';
|
|
|
12
12
|
*
|
|
13
13
|
* @polymerMixin
|
|
14
14
|
*/
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
ready()
|
|
20
|
-
super.ready();
|
|
15
|
+
const KeyboardMixinImplementation = (superclass) => {
|
|
16
|
+
return class KeyboardMixinClass extends superclass {
|
|
17
|
+
/** @protected */
|
|
18
|
+
ready() {
|
|
19
|
+
super.ready();
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
this.addEventListener('keydown', (event) => {
|
|
22
|
+
this._onKeyDown(event);
|
|
23
|
+
});
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
this.addEventListener('keyup', (event) => {
|
|
26
|
+
this._onKeyUp(event);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
30
|
+
/**
|
|
31
|
+
* A handler for the `keydown` event. By default, it calls
|
|
32
|
+
* separate methods for handling "Enter" and "Escape" keys.
|
|
33
|
+
* Override the method to implement your own behavior.
|
|
34
|
+
*
|
|
35
|
+
* @param {KeyboardEvent} event
|
|
36
|
+
* @protected
|
|
37
|
+
*/
|
|
38
|
+
_onKeyDown(event) {
|
|
39
|
+
switch (event.key) {
|
|
40
|
+
case 'Enter':
|
|
41
|
+
this._onEnter(event);
|
|
42
|
+
break;
|
|
43
|
+
case 'Escape':
|
|
44
|
+
this._onEscape(event);
|
|
45
|
+
break;
|
|
46
|
+
default:
|
|
47
|
+
break;
|
|
50
48
|
}
|
|
49
|
+
}
|
|
51
50
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
51
|
+
/**
|
|
52
|
+
* A handler for the `keyup` event. By default, it does nothing.
|
|
53
|
+
* Override the method to implement your own behavior.
|
|
54
|
+
*
|
|
55
|
+
* @param {KeyboardEvent} _event
|
|
56
|
+
* @protected
|
|
57
|
+
*/
|
|
58
|
+
_onKeyUp(_event) {
|
|
59
|
+
// To be implemented.
|
|
60
|
+
}
|
|
62
61
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
62
|
+
/**
|
|
63
|
+
* A handler for the "Enter" key. By default, it does nothing.
|
|
64
|
+
* Override the method to implement your own behavior.
|
|
65
|
+
*
|
|
66
|
+
* @param {KeyboardEvent} _event
|
|
67
|
+
* @protected
|
|
68
|
+
*/
|
|
69
|
+
_onEnter(_event) {
|
|
70
|
+
// To be implemented.
|
|
71
|
+
}
|
|
73
72
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
73
|
+
/**
|
|
74
|
+
* A handler for the "Escape" key. By default, it does nothing.
|
|
75
|
+
* Override the method to implement your own behavior.
|
|
76
|
+
*
|
|
77
|
+
* @param {KeyboardEvent} _event
|
|
78
|
+
* @protected
|
|
79
|
+
*/
|
|
80
|
+
_onEscape(_event) {
|
|
81
|
+
// To be implemented.
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const KeyboardMixin = dedupeMixin(KeyboardMixinImplementation);
|
package/src/list-mixin.js
CHANGED
|
@@ -336,11 +336,18 @@ export const ListMixin = (superClass) =>
|
|
|
336
336
|
}
|
|
337
337
|
|
|
338
338
|
/**
|
|
339
|
-
*
|
|
340
|
-
*
|
|
339
|
+
* Override method inherited from `KeyboardDirectionMixin` to allow
|
|
340
|
+
* focusing disabled items that are configured so.
|
|
341
341
|
*
|
|
342
|
-
* @
|
|
343
|
-
* @
|
|
344
|
-
* @
|
|
342
|
+
* @param {Element} item
|
|
343
|
+
* @protected
|
|
344
|
+
* @override
|
|
345
345
|
*/
|
|
346
|
+
_isItemFocusable(item) {
|
|
347
|
+
if (item.disabled && item.__shouldAllowFocusWhenDisabled) {
|
|
348
|
+
return item.__shouldAllowFocusWhenDisabled();
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return super._isItemFocusable(item);
|
|
352
|
+
}
|
|
346
353
|
};
|