@vaadin/component-base 24.1.0-alpha1 → 24.1.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/package.json +2 -2
- package/src/async.js +1 -13
- package/src/dom-utils.d.ts +10 -0
- package/src/dom-utils.js +7 -3
- package/src/element-mixin.js +1 -1
- package/src/virtualizer-iron-list-adapter.js +77 -48
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/component-base",
|
|
3
|
-
"version": "24.1.0-
|
|
3
|
+
"version": "24.1.0-alpha10",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -42,5 +42,5 @@
|
|
|
42
42
|
"@vaadin/testing-helpers": "^0.4.0",
|
|
43
43
|
"sinon": "^13.0.2"
|
|
44
44
|
},
|
|
45
|
-
"gitHead": "
|
|
45
|
+
"gitHead": "12e39be7eb3b49c68708e8ca3de2fb22e91051a1"
|
|
46
46
|
}
|
package/src/async.js
CHANGED
|
@@ -20,13 +20,10 @@
|
|
|
20
20
|
* asynchronous tasks.
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
-
// Microtask implemented using Mutation Observer
|
|
24
23
|
let microtaskCurrHandle = 0;
|
|
25
24
|
let microtaskLastHandle = 0;
|
|
26
25
|
const microtaskCallbacks = [];
|
|
27
|
-
let microtaskNodeContent = 0;
|
|
28
26
|
let microtaskScheduled = false;
|
|
29
|
-
const microtaskNode = document.createTextNode('');
|
|
30
27
|
|
|
31
28
|
function microtaskFlush() {
|
|
32
29
|
microtaskScheduled = false;
|
|
@@ -47,8 +44,6 @@ function microtaskFlush() {
|
|
|
47
44
|
microtaskLastHandle += len;
|
|
48
45
|
}
|
|
49
46
|
|
|
50
|
-
new window.MutationObserver(microtaskFlush).observe(microtaskNode, { characterData: true });
|
|
51
|
-
|
|
52
47
|
/**
|
|
53
48
|
* Async interface wrapper around `setTimeout`.
|
|
54
49
|
*
|
|
@@ -166,12 +161,6 @@ export { idlePeriod };
|
|
|
166
161
|
/**
|
|
167
162
|
* Async interface for enqueuing callbacks that run at microtask timing.
|
|
168
163
|
*
|
|
169
|
-
* Note that microtask timing is achieved via a single `MutationObserver`,
|
|
170
|
-
* and thus callbacks enqueued with this API will all run in a single
|
|
171
|
-
* batch, and not interleaved with other microtasks such as promises.
|
|
172
|
-
* Promises are avoided as an implementation choice for the time being
|
|
173
|
-
* due to Safari bugs that cause Promises to lack microtask guarantees.
|
|
174
|
-
*
|
|
175
164
|
* @namespace
|
|
176
165
|
* @summary Async interface for enqueuing callbacks that run at microtask
|
|
177
166
|
* timing.
|
|
@@ -187,8 +176,7 @@ const microTask = {
|
|
|
187
176
|
run(callback) {
|
|
188
177
|
if (!microtaskScheduled) {
|
|
189
178
|
microtaskScheduled = true;
|
|
190
|
-
|
|
191
|
-
microtaskNodeContent += 1;
|
|
179
|
+
queueMicrotask(() => microtaskFlush());
|
|
192
180
|
}
|
|
193
181
|
microtaskCallbacks.push(callback);
|
|
194
182
|
const result = microtaskCurrHandle;
|
package/src/dom-utils.d.ts
CHANGED
|
@@ -13,6 +13,16 @@
|
|
|
13
13
|
*/
|
|
14
14
|
export function getAncestorRootNodes(node: Node): Node[];
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Takes a string with values separated by space and returns a set the values
|
|
18
|
+
*/
|
|
19
|
+
export function deserializeAttributeValue(value: string): Set<string>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Takes a set of string values and returns a string with values separated by space
|
|
23
|
+
*/
|
|
24
|
+
export function serializeAttributeValue(values: Set<string>): string;
|
|
25
|
+
|
|
16
26
|
/**
|
|
17
27
|
* Adds a value to an attribute containing space-delimited values.
|
|
18
28
|
*/
|
package/src/dom-utils.js
CHANGED
|
@@ -41,10 +41,12 @@ export function getAncestorRootNodes(node) {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
|
+
* Takes a string with values separated by space and returns a set the values
|
|
45
|
+
*
|
|
44
46
|
* @param {string} value
|
|
45
47
|
* @return {Set<string>}
|
|
46
48
|
*/
|
|
47
|
-
function deserializeAttributeValue(value) {
|
|
49
|
+
export function deserializeAttributeValue(value) {
|
|
48
50
|
if (!value) {
|
|
49
51
|
return new Set();
|
|
50
52
|
}
|
|
@@ -53,11 +55,13 @@ function deserializeAttributeValue(value) {
|
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
/**
|
|
58
|
+
* Takes a set of string values and returns a string with values separated by space
|
|
59
|
+
*
|
|
56
60
|
* @param {Set<string>} values
|
|
57
61
|
* @return {string}
|
|
58
62
|
*/
|
|
59
|
-
function serializeAttributeValue(values) {
|
|
60
|
-
return [...values].join(' ');
|
|
63
|
+
export function serializeAttributeValue(values) {
|
|
64
|
+
return values ? [...values].join(' ') : '';
|
|
61
65
|
}
|
|
62
66
|
|
|
63
67
|
/**
|
package/src/element-mixin.js
CHANGED
|
@@ -136,11 +136,15 @@ export class IronListAdapter {
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
update(startIndex = 0, endIndex = this.size - 1) {
|
|
139
|
+
const updatedElements = [];
|
|
139
140
|
this.__getVisibleElements().forEach((el) => {
|
|
140
141
|
if (el.__virtualIndex >= startIndex && el.__virtualIndex <= endIndex) {
|
|
141
142
|
this.__updateElement(el, el.__virtualIndex, true);
|
|
143
|
+
updatedElements.push(el);
|
|
142
144
|
}
|
|
143
145
|
});
|
|
146
|
+
|
|
147
|
+
this.__afterElementsUpdated(updatedElements);
|
|
144
148
|
}
|
|
145
149
|
|
|
146
150
|
/**
|
|
@@ -203,28 +207,40 @@ export class IronListAdapter {
|
|
|
203
207
|
this.updateElement(el, index);
|
|
204
208
|
el.__lastUpdatedIndex = index;
|
|
205
209
|
}
|
|
210
|
+
}
|
|
206
211
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
212
|
+
/**
|
|
213
|
+
* Called synchronously right after elements have been updated.
|
|
214
|
+
* This is a good place to do any post-update work.
|
|
215
|
+
*
|
|
216
|
+
* @param {!Array<!HTMLElement>} updatedElements
|
|
217
|
+
*/
|
|
218
|
+
__afterElementsUpdated(updatedElements) {
|
|
219
|
+
updatedElements.forEach((el) => {
|
|
220
|
+
const elementHeight = el.offsetHeight;
|
|
221
|
+
if (elementHeight === 0) {
|
|
222
|
+
// If the elements have 0 height after update (for example due to lazy rendering),
|
|
223
|
+
// it results in iron-list requesting to create an unlimited count of elements.
|
|
224
|
+
// Assign a temporary placeholder sizing to elements that would otherwise end up having
|
|
225
|
+
// no height.
|
|
226
|
+
el.style.paddingTop = `${this.__placeholderHeight}px`;
|
|
227
|
+
|
|
228
|
+
// Manually schedule the resize handler to make sure the placeholder padding is
|
|
229
|
+
// cleared in case the resize observer never triggers.
|
|
230
|
+
this.__placeholderClearDebouncer = Debouncer.debounce(this.__placeholderClearDebouncer, animationFrame, () =>
|
|
231
|
+
this._resizeHandler(),
|
|
232
|
+
);
|
|
233
|
+
} else {
|
|
234
|
+
// Add element height to the queue
|
|
235
|
+
this.__elementHeightQueue.push(elementHeight);
|
|
236
|
+
this.__elementHeightQueue.shift();
|
|
237
|
+
|
|
238
|
+
// Calculate new placeholder height based on the average of the defined values in the
|
|
239
|
+
// element height queue
|
|
240
|
+
const filteredHeights = this.__elementHeightQueue.filter((h) => h !== undefined);
|
|
241
|
+
this.__placeholderHeight = Math.round(filteredHeights.reduce((a, b) => a + b, 0) / filteredHeights.length);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
228
244
|
}
|
|
229
245
|
|
|
230
246
|
__getIndexScrollOffset(index) {
|
|
@@ -249,42 +265,28 @@ export class IronListAdapter {
|
|
|
249
265
|
this._debouncers._increasePoolIfNeeded.cancel();
|
|
250
266
|
}
|
|
251
267
|
|
|
252
|
-
// Prevent element update while the scroll position is being restored
|
|
253
|
-
this.__preventElementUpdates = true;
|
|
254
|
-
|
|
255
|
-
// Record the scroll position before changing the size
|
|
256
|
-
let fvi; // First visible index
|
|
257
|
-
let fviOffsetBefore; // Scroll offset of the first visible index
|
|
258
|
-
if (size > 0) {
|
|
259
|
-
fvi = this.adjustedFirstVisibleIndex;
|
|
260
|
-
fviOffsetBefore = this.__getIndexScrollOffset(fvi);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
268
|
// Change the size
|
|
264
269
|
this.__size = size;
|
|
265
270
|
|
|
266
|
-
this.
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
if (fviOffsetBefore !== undefined && fviOffsetAfter !== undefined) {
|
|
278
|
-
this._scrollTop += fviOffsetBefore - fviOffsetAfter;
|
|
279
|
-
}
|
|
271
|
+
if (!this._physicalItems) {
|
|
272
|
+
// Not initialized yet
|
|
273
|
+
this._itemsChanged({
|
|
274
|
+
path: 'items',
|
|
275
|
+
});
|
|
276
|
+
this.__preventElementUpdates = true;
|
|
277
|
+
flush();
|
|
278
|
+
this.__preventElementUpdates = false;
|
|
279
|
+
} else {
|
|
280
|
+
// Already initialized, just update _virtualCount
|
|
281
|
+
this._virtualCount = this.items.length;
|
|
280
282
|
}
|
|
281
283
|
|
|
282
284
|
if (!this.elementsContainer.children.length) {
|
|
283
285
|
requestAnimationFrame(() => this._resizeHandler());
|
|
284
286
|
}
|
|
285
287
|
|
|
286
|
-
|
|
287
|
-
//
|
|
288
|
+
// Schedule and flush a resize handler. This will cause a
|
|
289
|
+
// re-render for the elements.
|
|
288
290
|
this._resizeHandler();
|
|
289
291
|
flush();
|
|
290
292
|
}
|
|
@@ -349,16 +351,20 @@ export class IronListAdapter {
|
|
|
349
351
|
|
|
350
352
|
/** @private */
|
|
351
353
|
_assignModels(itemSet) {
|
|
354
|
+
const updatedElements = [];
|
|
352
355
|
this._iterateItems((pidx, vidx) => {
|
|
353
356
|
const el = this._physicalItems[pidx];
|
|
354
357
|
el.hidden = vidx >= this.size;
|
|
355
358
|
if (!el.hidden) {
|
|
356
359
|
el.__virtualIndex = vidx + (this._vidxOffset || 0);
|
|
357
360
|
this.__updateElement(el, el.__virtualIndex);
|
|
361
|
+
updatedElements.push(el);
|
|
358
362
|
} else {
|
|
359
363
|
delete el.__lastUpdatedIndex;
|
|
360
364
|
}
|
|
361
365
|
}, itemSet);
|
|
366
|
+
|
|
367
|
+
this.__afterElementsUpdated(updatedElements);
|
|
362
368
|
}
|
|
363
369
|
|
|
364
370
|
/** @private */
|
|
@@ -557,6 +563,29 @@ export class IronListAdapter {
|
|
|
557
563
|
);
|
|
558
564
|
}
|
|
559
565
|
|
|
566
|
+
/**
|
|
567
|
+
* Increases the pool size.
|
|
568
|
+
* @override
|
|
569
|
+
*/
|
|
570
|
+
_increasePoolIfNeeded(count) {
|
|
571
|
+
if (this._physicalCount > 2 && count) {
|
|
572
|
+
// The iron-list logic has already created some physical items and
|
|
573
|
+
// has decided to create more. Since each item creation round is
|
|
574
|
+
// expensive, let's try to create the remaining items in one go.
|
|
575
|
+
|
|
576
|
+
// Calculate the total item count that would be needed to fill the viewport
|
|
577
|
+
// plus the buffer assuming rest of the items to be of the average size
|
|
578
|
+
// of the items already created.
|
|
579
|
+
const totalItemCount = Math.ceil(this._optPhysicalSize / this._physicalAverage);
|
|
580
|
+
const missingItemCount = totalItemCount - this._physicalCount;
|
|
581
|
+
// Create the remaining items in one go. Use a maximum of 100 items
|
|
582
|
+
// as a safety measure.
|
|
583
|
+
super._increasePoolIfNeeded(Math.max(count, Math.min(100, missingItemCount)));
|
|
584
|
+
} else {
|
|
585
|
+
super._increasePoolIfNeeded(count);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
560
589
|
/**
|
|
561
590
|
* @returns {Number|undefined} - The browser's default font-size in pixels
|
|
562
591
|
* @private
|