@vaadin/a11y-base 25.2.0-alpha9 → 25.2.0-beta2
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 +6 -6
- package/src/active-mixin.d.ts +1 -1
- 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
|
@@ -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);
|