@vaadin/component-base 24.0.0-alpha9 → 24.0.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_typings/vaadin-usage-statistics.d.ts +2 -2
- package/package.json +3 -3
- package/src/async.d.ts +3 -0
- package/src/async.js +2 -1
- package/src/debounce.js +2 -2
- package/src/dir-mixin.js +18 -18
- package/src/element-mixin.js +1 -1
- package/src/focus-mixin.js +1 -1
- package/src/focus-trap-controller.js +21 -21
- package/src/focus-utils.js +56 -56
- package/src/gestures.d.ts +6 -12
- package/src/gestures.js +5 -3
- package/src/iron-list-core.js +1 -33
- package/src/keyboard-direction-mixin.js +11 -11
- package/src/list-mixin.d.ts +7 -0
- package/src/list-mixin.js +47 -36
- package/src/polylit-mixin.d.ts +2 -2
- package/src/resize-mixin.js +10 -10
- package/src/virtualizer-iron-list-adapter.js +52 -0
- package/src/virtualizer.js +18 -18
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/component-base",
|
|
3
|
-
"version": "24.0.0-
|
|
3
|
+
"version": "24.0.0-beta2",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@esm-bundle/chai": "^4.3.4",
|
|
42
|
-
"@vaadin/testing-helpers": "^0.
|
|
42
|
+
"@vaadin/testing-helpers": "^0.4.0",
|
|
43
43
|
"sinon": "^13.0.2"
|
|
44
44
|
},
|
|
45
|
-
"gitHead": "
|
|
45
|
+
"gitHead": "00086f1f6d487f042f189c9b9ecd7ba736960888"
|
|
46
46
|
}
|
package/src/async.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
export interface AsyncInterface {
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
12
13
|
run(fn: Function, delay?: number): number;
|
|
13
14
|
cancel(handle: number): void;
|
|
14
15
|
}
|
|
@@ -39,6 +40,7 @@ declare namespace timeOut {
|
|
|
39
40
|
*
|
|
40
41
|
* @returns Handle used for canceling task
|
|
41
42
|
*/
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
42
44
|
function run(fn: Function, delay?: number): number;
|
|
43
45
|
|
|
44
46
|
/**
|
|
@@ -103,6 +105,7 @@ declare namespace microTask {
|
|
|
103
105
|
*
|
|
104
106
|
* @returns Handle used for canceling task
|
|
105
107
|
*/
|
|
108
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
106
109
|
function run(callback?: Function): number;
|
|
107
110
|
|
|
108
111
|
/**
|
package/src/async.js
CHANGED
|
@@ -27,7 +27,6 @@ const microtaskCallbacks = [];
|
|
|
27
27
|
let microtaskNodeContent = 0;
|
|
28
28
|
let microtaskScheduled = false;
|
|
29
29
|
const microtaskNode = document.createTextNode('');
|
|
30
|
-
new window.MutationObserver(microtaskFlush).observe(microtaskNode, { characterData: true });
|
|
31
30
|
|
|
32
31
|
function microtaskFlush() {
|
|
33
32
|
microtaskScheduled = false;
|
|
@@ -48,6 +47,8 @@ function microtaskFlush() {
|
|
|
48
47
|
microtaskLastHandle += len;
|
|
49
48
|
}
|
|
50
49
|
|
|
50
|
+
new window.MutationObserver(microtaskFlush).observe(microtaskNode, { characterData: true });
|
|
51
|
+
|
|
51
52
|
/**
|
|
52
53
|
* Async interface wrapper around `setTimeout`.
|
|
53
54
|
*
|
package/src/debounce.js
CHANGED
|
@@ -8,6 +8,8 @@ Code distributed by Google as part of the polymer project is also
|
|
|
8
8
|
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
const debouncerQueue = new Set();
|
|
12
|
+
|
|
11
13
|
/**
|
|
12
14
|
* @summary Collapse multiple callbacks into one invocation after a timer.
|
|
13
15
|
*/
|
|
@@ -133,8 +135,6 @@ export class Debouncer {
|
|
|
133
135
|
}
|
|
134
136
|
}
|
|
135
137
|
|
|
136
|
-
let debouncerQueue = new Set();
|
|
137
|
-
|
|
138
138
|
/**
|
|
139
139
|
* Adds a `Debouncer` to a list of globally flushable tasks.
|
|
140
140
|
*
|
package/src/dir-mixin.js
CHANGED
|
@@ -9,16 +9,6 @@
|
|
|
9
9
|
*/
|
|
10
10
|
const directionSubscribers = [];
|
|
11
11
|
|
|
12
|
-
function directionUpdater() {
|
|
13
|
-
const documentDir = getDocumentDir();
|
|
14
|
-
directionSubscribers.forEach((element) => {
|
|
15
|
-
alignDirs(element, documentDir);
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const directionObserver = new MutationObserver(directionUpdater);
|
|
20
|
-
directionObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['dir'] });
|
|
21
|
-
|
|
22
12
|
function alignDirs(element, documentDir, elementDir = element.getAttribute('dir')) {
|
|
23
13
|
if (documentDir) {
|
|
24
14
|
element.setAttribute('dir', documentDir);
|
|
@@ -31,6 +21,16 @@ function getDocumentDir() {
|
|
|
31
21
|
return document.documentElement.getAttribute('dir');
|
|
32
22
|
}
|
|
33
23
|
|
|
24
|
+
function directionUpdater() {
|
|
25
|
+
const documentDir = getDocumentDir();
|
|
26
|
+
directionSubscribers.forEach((element) => {
|
|
27
|
+
alignDirs(element, documentDir);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const directionObserver = new MutationObserver(directionUpdater);
|
|
32
|
+
directionObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['dir'] });
|
|
33
|
+
|
|
34
34
|
/**
|
|
35
35
|
* A mixin to handle `dir` attribute based on the one set on the `<html>` element.
|
|
36
36
|
*
|
|
@@ -59,6 +59,14 @@ export const DirMixin = (superClass) =>
|
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* @return {boolean}
|
|
64
|
+
* @protected
|
|
65
|
+
*/
|
|
66
|
+
get __isRTL() {
|
|
67
|
+
return this.getAttribute('dir') === 'rtl';
|
|
68
|
+
}
|
|
69
|
+
|
|
62
70
|
/** @protected */
|
|
63
71
|
connectedCallback() {
|
|
64
72
|
super.connectedCallback();
|
|
@@ -100,14 +108,6 @@ export const DirMixin = (superClass) =>
|
|
|
100
108
|
this.__unsubscribe();
|
|
101
109
|
}
|
|
102
110
|
|
|
103
|
-
/**
|
|
104
|
-
* @return {boolean}
|
|
105
|
-
* @protected
|
|
106
|
-
*/
|
|
107
|
-
get __isRTL() {
|
|
108
|
-
return this.getAttribute('dir') === 'rtl';
|
|
109
|
-
}
|
|
110
|
-
|
|
111
111
|
/** @protected */
|
|
112
112
|
_valueToNodeAttribute(node, value, attribute) {
|
|
113
113
|
// Override default Polymer attribute reflection to match native behavior of HTMLElement.dir property
|
package/src/element-mixin.js
CHANGED
package/src/focus-mixin.js
CHANGED
|
@@ -39,7 +39,7 @@ export const FocusMixin = dedupingMixin(
|
|
|
39
39
|
// In super.ready() other 'focusin' and 'focusout' listeners might be
|
|
40
40
|
// added, so we call it after our own ones to ensure they execute first.
|
|
41
41
|
// Issue to watch out: when incorrect, <vaadin-combo-box> refocuses the
|
|
42
|
-
// input field on iOS after
|
|
42
|
+
// input field on iOS after "Done" is pressed.
|
|
43
43
|
super.ready();
|
|
44
44
|
}
|
|
45
45
|
|
|
@@ -33,6 +33,27 @@ export class FocusTrapController {
|
|
|
33
33
|
this.__onKeyDown = this.__onKeyDown.bind(this);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* An array of tab-ordered focusable elements inside the trap node.
|
|
38
|
+
*
|
|
39
|
+
* @return {HTMLElement[]}
|
|
40
|
+
* @private
|
|
41
|
+
*/
|
|
42
|
+
get __focusableElements() {
|
|
43
|
+
return getFocusableElements(this.__trapNode);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The index of the element inside the trap node that currently has focus.
|
|
48
|
+
*
|
|
49
|
+
* @return {HTMLElement | undefined}
|
|
50
|
+
* @private
|
|
51
|
+
*/
|
|
52
|
+
get __focusedElementIndex() {
|
|
53
|
+
const focusableElements = this.__focusableElements;
|
|
54
|
+
return focusableElements.indexOf(focusableElements.filter(isElementFocused).pop());
|
|
55
|
+
}
|
|
56
|
+
|
|
36
57
|
hostConnected() {
|
|
37
58
|
document.addEventListener('keydown', this.__onKeyDown);
|
|
38
59
|
}
|
|
@@ -131,25 +152,4 @@ export class FocusTrapController {
|
|
|
131
152
|
element.select();
|
|
132
153
|
}
|
|
133
154
|
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* An array of tab-ordered focusable elements inside the trap node.
|
|
137
|
-
*
|
|
138
|
-
* @return {HTMLElement[]}
|
|
139
|
-
* @private
|
|
140
|
-
*/
|
|
141
|
-
get __focusableElements() {
|
|
142
|
-
return getFocusableElements(this.__trapNode);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* The index of the element inside the trap node that currently has focus.
|
|
147
|
-
*
|
|
148
|
-
* @return {HTMLElement | undefined}
|
|
149
|
-
* @private
|
|
150
|
-
*/
|
|
151
|
-
get __focusedElementIndex() {
|
|
152
|
-
const focusableElements = this.__focusableElements;
|
|
153
|
-
return focusableElements.indexOf(focusableElements.filter(isElementFocused).pop());
|
|
154
|
-
}
|
|
155
155
|
}
|
package/src/focus-utils.js
CHANGED
|
@@ -61,24 +61,6 @@ function isElementHiddenDirectly(element) {
|
|
|
61
61
|
return false;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
/**
|
|
65
|
-
* Returns the normalized element tabindex. If not focusable, returns -1.
|
|
66
|
-
* It checks for the attribute "tabindex" instead of the element property
|
|
67
|
-
* `tabIndex` since browsers assign different values to it.
|
|
68
|
-
* e.g. in Firefox `<div contenteditable>` has `tabIndex = -1`
|
|
69
|
-
*
|
|
70
|
-
* @param {HTMLElement} element
|
|
71
|
-
* @return {number}
|
|
72
|
-
*/
|
|
73
|
-
function normalizeTabIndex(element) {
|
|
74
|
-
if (!isElementFocusable(element)) {
|
|
75
|
-
return -1;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const tabIndex = element.getAttribute('tabindex') || 0;
|
|
79
|
-
return Number(tabIndex);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
64
|
/**
|
|
83
65
|
* Returns if element `a` has lower tab order compared to element `b`
|
|
84
66
|
* (both elements are assumed to be focusable and tabbable).
|
|
@@ -138,42 +120,6 @@ function sortElementsByTabIndex(elements) {
|
|
|
138
120
|
return mergeSortByTabIndex(left, right);
|
|
139
121
|
}
|
|
140
122
|
|
|
141
|
-
/**
|
|
142
|
-
* Searches for nodes that are tabbable and adds them to the `result` array.
|
|
143
|
-
* Returns if the `result` array needs to be sorted by tabindex.
|
|
144
|
-
*
|
|
145
|
-
* @param {Node} node The starting point for the search; added to `result` if tabbable.
|
|
146
|
-
* @param {HTMLElement[]} result
|
|
147
|
-
* @return {boolean}
|
|
148
|
-
* @private
|
|
149
|
-
*/
|
|
150
|
-
function collectFocusableNodes(node, result) {
|
|
151
|
-
if (node.nodeType !== Node.ELEMENT_NODE || isElementHiddenDirectly(node)) {
|
|
152
|
-
// Don't traverse children if the node is not an HTML element or not visible.
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const element = /** @type {HTMLElement} */ (node);
|
|
157
|
-
const tabIndex = normalizeTabIndex(element);
|
|
158
|
-
let needsSort = tabIndex > 0;
|
|
159
|
-
if (tabIndex >= 0) {
|
|
160
|
-
result.push(element);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
let children = [];
|
|
164
|
-
if (element.localName === 'slot') {
|
|
165
|
-
children = element.assignedNodes({ flatten: true });
|
|
166
|
-
} else {
|
|
167
|
-
// Use shadow root if possible, will check for distributed nodes.
|
|
168
|
-
children = (element.shadowRoot || element).children;
|
|
169
|
-
}
|
|
170
|
-
[...children].forEach((child) => {
|
|
171
|
-
// Ensure method is always invoked to collect focusable children.
|
|
172
|
-
needsSort = collectFocusableNodes(child, result) || needsSort;
|
|
173
|
-
});
|
|
174
|
-
return needsSort;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
123
|
/**
|
|
178
124
|
* Returns true if the element is hidden, false otherwise.
|
|
179
125
|
*
|
|
@@ -239,6 +185,60 @@ export function isElementFocused(element) {
|
|
|
239
185
|
return element.getRootNode().activeElement === element;
|
|
240
186
|
}
|
|
241
187
|
|
|
188
|
+
/**
|
|
189
|
+
* Returns the normalized element tabindex. If not focusable, returns -1.
|
|
190
|
+
* It checks for the attribute "tabindex" instead of the element property
|
|
191
|
+
* `tabIndex` since browsers assign different values to it.
|
|
192
|
+
* e.g. in Firefox `<div contenteditable>` has `tabIndex = -1`
|
|
193
|
+
*
|
|
194
|
+
* @param {HTMLElement} element
|
|
195
|
+
* @return {number}
|
|
196
|
+
*/
|
|
197
|
+
function normalizeTabIndex(element) {
|
|
198
|
+
if (!isElementFocusable(element)) {
|
|
199
|
+
return -1;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const tabIndex = element.getAttribute('tabindex') || 0;
|
|
203
|
+
return Number(tabIndex);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Searches for nodes that are tabbable and adds them to the `result` array.
|
|
208
|
+
* Returns if the `result` array needs to be sorted by tabindex.
|
|
209
|
+
*
|
|
210
|
+
* @param {Node} node The starting point for the search; added to `result` if tabbable.
|
|
211
|
+
* @param {HTMLElement[]} result
|
|
212
|
+
* @return {boolean}
|
|
213
|
+
* @private
|
|
214
|
+
*/
|
|
215
|
+
function collectFocusableNodes(node, result) {
|
|
216
|
+
if (node.nodeType !== Node.ELEMENT_NODE || isElementHiddenDirectly(node)) {
|
|
217
|
+
// Don't traverse children if the node is not an HTML element or not visible.
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const element = /** @type {HTMLElement} */ (node);
|
|
222
|
+
const tabIndex = normalizeTabIndex(element);
|
|
223
|
+
let needsSort = tabIndex > 0;
|
|
224
|
+
if (tabIndex >= 0) {
|
|
225
|
+
result.push(element);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
let children = [];
|
|
229
|
+
if (element.localName === 'slot') {
|
|
230
|
+
children = element.assignedNodes({ flatten: true });
|
|
231
|
+
} else {
|
|
232
|
+
// Use shadow root if possible, will check for distributed nodes.
|
|
233
|
+
children = (element.shadowRoot || element).children;
|
|
234
|
+
}
|
|
235
|
+
[...children].forEach((child) => {
|
|
236
|
+
// Ensure method is always invoked to collect focusable children.
|
|
237
|
+
needsSort = collectFocusableNodes(child, result) || needsSort;
|
|
238
|
+
});
|
|
239
|
+
return needsSort;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
242
|
/**
|
|
243
243
|
* Returns a tab-ordered array of focusable elements for a root element.
|
|
244
244
|
* The resulting array will include the root element if it is focusable.
|
|
@@ -251,8 +251,8 @@ export function isElementFocused(element) {
|
|
|
251
251
|
export function getFocusableElements(element) {
|
|
252
252
|
const focusableElements = [];
|
|
253
253
|
const needsSortByTabIndex = collectFocusableNodes(element, focusableElements);
|
|
254
|
-
// If there is at least one element with tabindex > 0,
|
|
255
|
-
// the final array by tabindex
|
|
254
|
+
// If there is at least one element with tabindex > 0,
|
|
255
|
+
// we need to sort the final array by tabindex.
|
|
256
256
|
if (needsSortByTabIndex) {
|
|
257
257
|
return sortElementsByTabIndex(focusableElements);
|
|
258
258
|
}
|
package/src/gestures.d.ts
CHANGED
|
@@ -8,8 +8,6 @@ Code distributed by Google as part of the polymer project is also
|
|
|
8
8
|
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
export { deepTargetFind };
|
|
12
|
-
|
|
13
11
|
/**
|
|
14
12
|
* Finds the element rendered on the screen at the provided coordinates.
|
|
15
13
|
*
|
|
@@ -20,8 +18,7 @@ export { deepTargetFind };
|
|
|
20
18
|
* found at the screen position given.
|
|
21
19
|
*/
|
|
22
20
|
declare function deepTargetFind(x: number, y: number): Element | null;
|
|
23
|
-
|
|
24
|
-
export { addListener };
|
|
21
|
+
export { deepTargetFind };
|
|
25
22
|
|
|
26
23
|
/**
|
|
27
24
|
* Adds an event listener to a node for the given gesture type.
|
|
@@ -29,8 +26,7 @@ export { addListener };
|
|
|
29
26
|
* @returns Returns true if a gesture event listener was added.
|
|
30
27
|
*/
|
|
31
28
|
declare function addListener(node: EventTarget, evType: string, handler: (p0: Event) => void): boolean;
|
|
32
|
-
|
|
33
|
-
export { removeListener };
|
|
29
|
+
export { addListener };
|
|
34
30
|
|
|
35
31
|
/**
|
|
36
32
|
* Removes an event listener from a node for the given gesture type.
|
|
@@ -38,16 +34,14 @@ export { removeListener };
|
|
|
38
34
|
* @returns Returns true if a gesture event listener was removed.
|
|
39
35
|
*/
|
|
40
36
|
declare function removeListener(node: EventTarget, evType: string, handler: (p0: Event) => void): boolean;
|
|
41
|
-
|
|
42
|
-
export { register };
|
|
37
|
+
export { removeListener };
|
|
43
38
|
|
|
44
39
|
/**
|
|
45
40
|
* Registers a new gesture event recognizer for adding new custom
|
|
46
41
|
* gesture event types.
|
|
47
42
|
*/
|
|
48
43
|
declare function register(recog: GestureRecognizer): void;
|
|
49
|
-
|
|
50
|
-
export { setTouchAction };
|
|
44
|
+
export { register };
|
|
51
45
|
|
|
52
46
|
/**
|
|
53
47
|
* Sets scrolling direction on node.
|
|
@@ -56,13 +50,13 @@ export { setTouchAction };
|
|
|
56
50
|
* adding event listeners.
|
|
57
51
|
*/
|
|
58
52
|
declare function setTouchAction(node: EventTarget, value: string): void;
|
|
59
|
-
|
|
60
|
-
export { prevent };
|
|
53
|
+
export { setTouchAction };
|
|
61
54
|
|
|
62
55
|
/**
|
|
63
56
|
* Prevents the dispatch and default action of the given event name.
|
|
64
57
|
*/
|
|
65
58
|
declare function prevent(evName: string): void;
|
|
59
|
+
export { prevent };
|
|
66
60
|
|
|
67
61
|
export interface GestureRecognizer {
|
|
68
62
|
reset(): void;
|
package/src/gestures.js
CHANGED
|
@@ -8,6 +8,8 @@ Code distributed by Google as part of the polymer project is also
|
|
|
8
8
|
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
/* eslint-disable @typescript-eslint/no-use-before-define */
|
|
12
|
+
|
|
11
13
|
/**
|
|
12
14
|
* @fileoverview
|
|
13
15
|
*
|
|
@@ -468,9 +470,9 @@ function _remove(node, evType, handler) {
|
|
|
468
470
|
*/
|
|
469
471
|
export function register(recog) {
|
|
470
472
|
recognizers.push(recog);
|
|
471
|
-
|
|
472
|
-
gestures[
|
|
473
|
-
}
|
|
473
|
+
recog.emits.forEach((emit) => {
|
|
474
|
+
gestures[emit] = recog;
|
|
475
|
+
});
|
|
474
476
|
}
|
|
475
477
|
|
|
476
478
|
/**
|
package/src/iron-list-core.js
CHANGED
|
@@ -23,6 +23,7 @@ const DEFAULT_PHYSICAL_COUNT = 3;
|
|
|
23
23
|
* If something in the scrolling engine needs to be changed
|
|
24
24
|
* for the virtualizer's purposes, override a function
|
|
25
25
|
* in virtualizer-iron-list-adapter.js instead of changing it here.
|
|
26
|
+
* If a function on this file is no longer needed, the code can be safely deleted.
|
|
26
27
|
*
|
|
27
28
|
* This will allow us to keep the iron-list code here as close to
|
|
28
29
|
* the original as possible.
|
|
@@ -563,39 +564,6 @@ export const ironList = {
|
|
|
563
564
|
return this._virtualStart + (this._physicalCount - this._physicalStart) + pidx;
|
|
564
565
|
},
|
|
565
566
|
|
|
566
|
-
/**
|
|
567
|
-
* Updates the height for a given set of items.
|
|
568
|
-
*
|
|
569
|
-
* @param {!Array<number>=} itemSet
|
|
570
|
-
*/
|
|
571
|
-
_updateMetrics(itemSet) {
|
|
572
|
-
// Make sure we distributed all the physical items
|
|
573
|
-
// so we can measure them.
|
|
574
|
-
flush();
|
|
575
|
-
|
|
576
|
-
let newPhysicalSize = 0;
|
|
577
|
-
let oldPhysicalSize = 0;
|
|
578
|
-
const prevAvgCount = this._physicalAverageCount;
|
|
579
|
-
const prevPhysicalAvg = this._physicalAverage;
|
|
580
|
-
|
|
581
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
582
|
-
this._iterateItems((pidx, vidx) => {
|
|
583
|
-
oldPhysicalSize += this._physicalSizes[pidx];
|
|
584
|
-
this._physicalSizes[pidx] = this._physicalItems[pidx].offsetHeight;
|
|
585
|
-
newPhysicalSize += this._physicalSizes[pidx];
|
|
586
|
-
this._physicalAverageCount += this._physicalSizes[pidx] ? 1 : 0;
|
|
587
|
-
}, itemSet);
|
|
588
|
-
|
|
589
|
-
this._physicalSize = this._physicalSize + newPhysicalSize - oldPhysicalSize;
|
|
590
|
-
|
|
591
|
-
// Update the average if it measured something.
|
|
592
|
-
if (this._physicalAverageCount !== prevAvgCount) {
|
|
593
|
-
this._physicalAverage = Math.round(
|
|
594
|
-
(prevPhysicalAvg * prevAvgCount + newPhysicalSize) / this._physicalAverageCount,
|
|
595
|
-
);
|
|
596
|
-
}
|
|
597
|
-
},
|
|
598
|
-
|
|
599
567
|
/**
|
|
600
568
|
* Updates the position of the physical items.
|
|
601
569
|
*/
|
|
@@ -14,17 +14,6 @@ import { KeyboardMixin } from './keyboard-mixin.js';
|
|
|
14
14
|
*/
|
|
15
15
|
export const KeyboardDirectionMixin = (superclass) =>
|
|
16
16
|
class KeyboardDirectionMixinClass extends KeyboardMixin(superclass) {
|
|
17
|
-
/** @protected */
|
|
18
|
-
focus() {
|
|
19
|
-
const items = this._getItems();
|
|
20
|
-
if (Array.isArray(items)) {
|
|
21
|
-
const idx = this._getAvailableIndex(items, 0, null, (item) => !isElementHidden(item));
|
|
22
|
-
if (idx >= 0) {
|
|
23
|
-
items[idx].focus();
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
17
|
/**
|
|
29
18
|
* @return {Element | null}
|
|
30
19
|
* @protected
|
|
@@ -41,6 +30,17 @@ export const KeyboardDirectionMixin = (superclass) =>
|
|
|
41
30
|
return true;
|
|
42
31
|
}
|
|
43
32
|
|
|
33
|
+
/** @protected */
|
|
34
|
+
focus() {
|
|
35
|
+
const items = this._getItems();
|
|
36
|
+
if (Array.isArray(items)) {
|
|
37
|
+
const idx = this._getAvailableIndex(items, 0, null, (item) => !isElementHidden(item));
|
|
38
|
+
if (idx >= 0) {
|
|
39
|
+
items[idx].focus();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
44
|
/**
|
|
45
45
|
* Get the list of items participating in keyboard navigation.
|
|
46
46
|
* By default, it treats all the light DOM children as items.
|
package/src/list-mixin.d.ts
CHANGED
|
@@ -20,6 +20,13 @@ export declare class ListMixinClass {
|
|
|
20
20
|
*/
|
|
21
21
|
_hasVaadinListMixin: boolean;
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* If true, the user cannot interact with this element.
|
|
25
|
+
* When the element is disabled, the selected item is
|
|
26
|
+
* not updated when `selected` property is changed.
|
|
27
|
+
*/
|
|
28
|
+
disabled: boolean;
|
|
29
|
+
|
|
23
30
|
/**
|
|
24
31
|
* The index of the item selected in the items array.
|
|
25
32
|
* Note: Not updated when used in `multiple` selection mode.
|
package/src/list-mixin.js
CHANGED
|
@@ -27,6 +27,17 @@ export const ListMixin = (superClass) =>
|
|
|
27
27
|
value: true,
|
|
28
28
|
},
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* If true, the user cannot interact with this element.
|
|
32
|
+
* When the element is disabled, the selected item is
|
|
33
|
+
* not updated when `selected` property is changed.
|
|
34
|
+
*/
|
|
35
|
+
disabled: {
|
|
36
|
+
type: Boolean,
|
|
37
|
+
value: false,
|
|
38
|
+
reflectToAttribute: true,
|
|
39
|
+
},
|
|
40
|
+
|
|
30
41
|
/**
|
|
31
42
|
* The index of the item selected in the items array.
|
|
32
43
|
* Note: Not updated when used in `multiple` selection mode.
|
|
@@ -82,6 +93,41 @@ export const ListMixin = (superClass) =>
|
|
|
82
93
|
return ['_enhanceItems(items, orientation, selected, disabled)'];
|
|
83
94
|
}
|
|
84
95
|
|
|
96
|
+
/**
|
|
97
|
+
* @return {boolean}
|
|
98
|
+
* @protected
|
|
99
|
+
*/
|
|
100
|
+
get _isRTL() {
|
|
101
|
+
return !this._vertical && this.getAttribute('dir') === 'rtl';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @return {!HTMLElement}
|
|
106
|
+
* @protected
|
|
107
|
+
*/
|
|
108
|
+
get _scrollerElement() {
|
|
109
|
+
// Returning scroller element of the component
|
|
110
|
+
console.warn(`Please implement the '_scrollerElement' property in <${this.localName}>`);
|
|
111
|
+
return this;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @return {boolean}
|
|
116
|
+
* @protected
|
|
117
|
+
*/
|
|
118
|
+
get _vertical() {
|
|
119
|
+
return this.orientation !== 'horizontal';
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
focus() {
|
|
123
|
+
// In initialization (e.g vaadin-select) observer might not been run yet.
|
|
124
|
+
if (this._observer) {
|
|
125
|
+
this._observer.flush();
|
|
126
|
+
}
|
|
127
|
+
const firstItem = this.querySelector('[tabindex="0"]') || (this.items ? this.items[0] : null);
|
|
128
|
+
this._focusItem(firstItem);
|
|
129
|
+
}
|
|
130
|
+
|
|
85
131
|
/** @protected */
|
|
86
132
|
ready() {
|
|
87
133
|
super.ready();
|
|
@@ -110,7 +156,7 @@ export const ListMixin = (superClass) =>
|
|
|
110
156
|
if (!disabled) {
|
|
111
157
|
if (items) {
|
|
112
158
|
this.setAttribute('aria-orientation', orientation || 'vertical');
|
|
113
|
-
|
|
159
|
+
items.forEach((item) => {
|
|
114
160
|
if (orientation) {
|
|
115
161
|
item.setAttribute('orientation', orientation);
|
|
116
162
|
} else {
|
|
@@ -189,14 +235,6 @@ export const ListMixin = (superClass) =>
|
|
|
189
235
|
.startsWith(this._searchBuf);
|
|
190
236
|
}
|
|
191
237
|
|
|
192
|
-
/**
|
|
193
|
-
* @return {boolean}
|
|
194
|
-
* @protected
|
|
195
|
-
*/
|
|
196
|
-
get _isRTL() {
|
|
197
|
-
return !this._vertical && this.getAttribute('dir') === 'rtl';
|
|
198
|
-
}
|
|
199
|
-
|
|
200
238
|
/**
|
|
201
239
|
* Override an event listener from `KeyboardMixin`
|
|
202
240
|
* to search items by key.
|
|
@@ -258,25 +296,6 @@ export const ListMixin = (superClass) =>
|
|
|
258
296
|
super._focus(idx);
|
|
259
297
|
}
|
|
260
298
|
|
|
261
|
-
focus() {
|
|
262
|
-
// In initialization (e.g vaadin-select) observer might not been run yet.
|
|
263
|
-
if (this._observer) {
|
|
264
|
-
this._observer.flush();
|
|
265
|
-
}
|
|
266
|
-
const firstItem = this.querySelector('[tabindex="0"]') || (this.items ? this.items[0] : null);
|
|
267
|
-
this._focusItem(firstItem);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* @return {!HTMLElement}
|
|
272
|
-
* @protected
|
|
273
|
-
*/
|
|
274
|
-
get _scrollerElement() {
|
|
275
|
-
// Returning scroller element of the component
|
|
276
|
-
console.warn(`Please implement the '_scrollerElement' property in <${this.localName}>`);
|
|
277
|
-
return this;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
299
|
/**
|
|
281
300
|
* Scroll the container to have the next item by the edge of the viewport.
|
|
282
301
|
* @param {number} idx
|
|
@@ -310,14 +329,6 @@ export const ListMixin = (superClass) =>
|
|
|
310
329
|
this._scroll(scrollDistance);
|
|
311
330
|
}
|
|
312
331
|
|
|
313
|
-
/**
|
|
314
|
-
* @return {boolean}
|
|
315
|
-
* @protected
|
|
316
|
-
*/
|
|
317
|
-
get _vertical() {
|
|
318
|
-
return this.orientation !== 'horizontal';
|
|
319
|
-
}
|
|
320
|
-
|
|
321
332
|
/**
|
|
322
333
|
* @param {number} pixels
|
|
323
334
|
* @protected
|
package/src/polylit-mixin.d.ts
CHANGED
|
@@ -14,10 +14,10 @@ export declare class PolylitMixinClass {
|
|
|
14
14
|
/**
|
|
15
15
|
* Reads a value from a path.
|
|
16
16
|
*/
|
|
17
|
-
protected _get(root:
|
|
17
|
+
protected _get(root: object, path: string): any;
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Sets a value to a path.
|
|
21
21
|
*/
|
|
22
|
-
protected _set(root:
|
|
22
|
+
protected _set(root: object, path: string, value: any): void;
|
|
23
23
|
}
|
package/src/resize-mixin.js
CHANGED
|
@@ -28,6 +28,16 @@ const observer = new ResizeObserver((entries) => {
|
|
|
28
28
|
export const ResizeMixin = dedupingMixin(
|
|
29
29
|
(superclass) =>
|
|
30
30
|
class ResizeMixinClass extends superclass {
|
|
31
|
+
/**
|
|
32
|
+
* When true, the parent element resize will be also observed.
|
|
33
|
+
* Override this getter and return `true` to enable this.
|
|
34
|
+
*
|
|
35
|
+
* @protected
|
|
36
|
+
*/
|
|
37
|
+
get _observeParent() {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
31
41
|
/** @protected */
|
|
32
42
|
connectedCallback() {
|
|
33
43
|
super.connectedCallback();
|
|
@@ -67,16 +77,6 @@ export const ResizeMixin = dedupingMixin(
|
|
|
67
77
|
}
|
|
68
78
|
}
|
|
69
79
|
|
|
70
|
-
/**
|
|
71
|
-
* When true, the parent element resize will be also observed.
|
|
72
|
-
* Override this getter and return `true` to enable this.
|
|
73
|
-
*
|
|
74
|
-
* @protected
|
|
75
|
-
*/
|
|
76
|
-
get _observeParent() {
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
80
|
/**
|
|
81
81
|
* A handler invoked on host resize. By default, it does nothing.
|
|
82
82
|
* Override the method to implement your own behavior.
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* Copyright (c) 2021 - 2023 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
+
/* eslint-disable @typescript-eslint/member-ordering */
|
|
7
|
+
// https://github.com/vaadin/eslint-config-vaadin/issues/33
|
|
6
8
|
import { animationFrame, timeOut } from './async.js';
|
|
7
9
|
import { isSafari } from './browser-utils.js';
|
|
8
10
|
import { Debouncer, flush } from './debounce.js';
|
|
@@ -141,6 +143,56 @@ export class IronListAdapter {
|
|
|
141
143
|
});
|
|
142
144
|
}
|
|
143
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Updates the height for a given set of items.
|
|
148
|
+
*
|
|
149
|
+
* @param {!Array<number>=} itemSet
|
|
150
|
+
*/
|
|
151
|
+
_updateMetrics(itemSet) {
|
|
152
|
+
// Make sure we distributed all the physical items
|
|
153
|
+
// so we can measure them.
|
|
154
|
+
flush();
|
|
155
|
+
|
|
156
|
+
let newPhysicalSize = 0;
|
|
157
|
+
let oldPhysicalSize = 0;
|
|
158
|
+
const prevAvgCount = this._physicalAverageCount;
|
|
159
|
+
const prevPhysicalAvg = this._physicalAverage;
|
|
160
|
+
|
|
161
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
162
|
+
this._iterateItems((pidx, vidx) => {
|
|
163
|
+
oldPhysicalSize += this._physicalSizes[pidx];
|
|
164
|
+
this._physicalSizes[pidx] = Math.ceil(this.__getBorderBoxHeight(this._physicalItems[pidx]));
|
|
165
|
+
newPhysicalSize += this._physicalSizes[pidx];
|
|
166
|
+
this._physicalAverageCount += this._physicalSizes[pidx] ? 1 : 0;
|
|
167
|
+
}, itemSet);
|
|
168
|
+
|
|
169
|
+
this._physicalSize = this._physicalSize + newPhysicalSize - oldPhysicalSize;
|
|
170
|
+
|
|
171
|
+
// Update the average if it measured something.
|
|
172
|
+
if (this._physicalAverageCount !== prevAvgCount) {
|
|
173
|
+
this._physicalAverage = Math.round(
|
|
174
|
+
(prevPhysicalAvg * prevAvgCount + newPhysicalSize) / this._physicalAverageCount,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
__getBorderBoxHeight(el) {
|
|
180
|
+
const style = getComputedStyle(el);
|
|
181
|
+
|
|
182
|
+
const itemHeight = parseFloat(style.height) || 0;
|
|
183
|
+
|
|
184
|
+
if (style.boxSizing === 'border-box') {
|
|
185
|
+
return itemHeight;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const paddingBottom = parseFloat(style.paddingBottom) || 0;
|
|
189
|
+
const paddingTop = parseFloat(style.paddingTop) || 0;
|
|
190
|
+
const borderBottomWidth = parseFloat(style.borderBottomWidth) || 0;
|
|
191
|
+
const borderTopWidth = parseFloat(style.borderTopWidth) || 0;
|
|
192
|
+
|
|
193
|
+
return itemHeight + paddingBottom + paddingTop + borderBottomWidth + borderTopWidth;
|
|
194
|
+
}
|
|
195
|
+
|
|
144
196
|
__updateElement(el, index, forceSameIndexUpdates) {
|
|
145
197
|
// Clean up temporary placeholder sizing
|
|
146
198
|
if (el.style.paddingTop) {
|
package/src/virtualizer.js
CHANGED
|
@@ -15,6 +15,24 @@ export class Virtualizer {
|
|
|
15
15
|
this.__adapter = new IronListAdapter(config);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Gets the index of the first visible item in the viewport.
|
|
20
|
+
*
|
|
21
|
+
* @return {number}
|
|
22
|
+
*/
|
|
23
|
+
get firstVisibleIndex() {
|
|
24
|
+
return this.__adapter.adjustedFirstVisibleIndex;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Gets the index of the last visible item in the viewport.
|
|
29
|
+
*
|
|
30
|
+
* @return {number}
|
|
31
|
+
*/
|
|
32
|
+
get lastVisibleIndex() {
|
|
33
|
+
return this.__adapter.adjustedLastVisibleIndex;
|
|
34
|
+
}
|
|
35
|
+
|
|
18
36
|
/**
|
|
19
37
|
* The size of the virtualizer
|
|
20
38
|
* @return {number | undefined} The size of the virtualizer
|
|
@@ -62,22 +80,4 @@ export class Virtualizer {
|
|
|
62
80
|
flush() {
|
|
63
81
|
this.__adapter.flush();
|
|
64
82
|
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Gets the index of the first visible item in the viewport.
|
|
68
|
-
*
|
|
69
|
-
* @return {number}
|
|
70
|
-
*/
|
|
71
|
-
get firstVisibleIndex() {
|
|
72
|
-
return this.__adapter.adjustedFirstVisibleIndex;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Gets the index of the last visible item in the viewport.
|
|
77
|
-
*
|
|
78
|
-
* @return {number}
|
|
79
|
-
*/
|
|
80
|
-
get lastVisibleIndex() {
|
|
81
|
-
return this.__adapter.adjustedLastVisibleIndex;
|
|
82
|
-
}
|
|
83
83
|
}
|