@vaadin/component-base 24.4.0-dev.b3e1d14600 → 24.4.0-rc2

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.
Files changed (50) hide show
  1. package/package.json +2 -2
  2. package/src/browser-utils.js +1 -1
  3. package/src/controller-mixin.d.ts +1 -1
  4. package/src/controller-mixin.js +1 -1
  5. package/src/data-provider-controller/cache.d.ts +1 -1
  6. package/src/data-provider-controller/cache.js +9 -53
  7. package/src/data-provider-controller/data-provider-controller.d.ts +1 -1
  8. package/src/data-provider-controller/data-provider-controller.js +4 -28
  9. package/src/data-provider-controller/helpers.d.ts +1 -1
  10. package/src/data-provider-controller/helpers.js +1 -1
  11. package/src/define.d.ts +1 -1
  12. package/src/define.js +2 -2
  13. package/src/delegate-state-mixin.d.ts +1 -1
  14. package/src/delegate-state-mixin.js +1 -1
  15. package/src/dir-mixin.d.ts +1 -1
  16. package/src/dir-mixin.js +1 -1
  17. package/src/dir-utils.d.ts +1 -1
  18. package/src/dir-utils.js +1 -1
  19. package/src/dom-utils.d.ts +1 -1
  20. package/src/dom-utils.js +1 -1
  21. package/src/element-mixin.d.ts +1 -1
  22. package/src/element-mixin.js +1 -1
  23. package/src/media-query-controller.d.ts +1 -1
  24. package/src/media-query-controller.js +1 -1
  25. package/src/overflow-controller.d.ts +1 -1
  26. package/src/overflow-controller.js +1 -1
  27. package/src/overlay-class-mixin.d.ts +1 -1
  28. package/src/overlay-class-mixin.js +2 -2
  29. package/src/path-utils.d.ts +1 -1
  30. package/src/path-utils.js +1 -1
  31. package/src/polylit-mixin.d.ts +1 -1
  32. package/src/polylit-mixin.js +24 -1
  33. package/src/resize-mixin.d.ts +1 -1
  34. package/src/resize-mixin.js +1 -1
  35. package/src/slot-child-observe-controller.d.ts +1 -1
  36. package/src/slot-child-observe-controller.js +1 -1
  37. package/src/slot-controller.d.ts +1 -1
  38. package/src/slot-controller.js +2 -2
  39. package/src/slot-observer.d.ts +1 -1
  40. package/src/slot-observer.js +1 -1
  41. package/src/slot-styles-mixin.d.ts +1 -1
  42. package/src/slot-styles-mixin.js +1 -1
  43. package/src/templates.js +1 -1
  44. package/src/tooltip-controller.d.ts +1 -1
  45. package/src/tooltip-controller.js +1 -1
  46. package/src/unique-id-utils.d.ts +1 -1
  47. package/src/unique-id-utils.js +1 -1
  48. package/src/url-utils.d.ts +8 -4
  49. package/src/url-utils.js +31 -9
  50. package/src/virtualizer-iron-list-adapter.js +124 -35
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/component-base",
3
- "version": "24.4.0-dev.b3e1d14600",
3
+ "version": "24.4.0-rc2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -42,5 +42,5 @@
42
42
  "@vaadin/testing-helpers": "^0.6.0",
43
43
  "sinon": "^13.0.2"
44
44
  },
45
- "gitHead": "502d4f5b03f770a83d270d98078cde230254dd0e"
45
+ "gitHead": "ea3d99e8cf67a337e959d5cab849f80464c7c7e5"
46
46
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import type { Constructor } from '@open-wc/dedupe-mixin';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import { dedupingMixin } from '@polymer/polymer/lib/utils/mixin.js';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import type { DataProviderCallback } from './data-provider-controller.js';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import { getFlatIndexContext } from './helpers.js';
@@ -16,6 +16,13 @@ export class Cache {
16
16
  */
17
17
  context;
18
18
 
19
+ /**
20
+ * The number of items.
21
+ *
22
+ * @type {number}
23
+ */
24
+ size = 0;
25
+
19
26
  /**
20
27
  * The number of items to display per page.
21
28
  *
@@ -51,14 +58,6 @@ export class Cache {
51
58
  */
52
59
  __subCacheByIndex = {};
53
60
 
54
- /**
55
- * The number of items.
56
- *
57
- * @type {number}
58
- * @private
59
- */
60
- __size = 0;
61
-
62
61
  /**
63
62
  * The total number of items, including items from expanded sub-caches.
64
63
  *
@@ -137,46 +136,6 @@ export class Cache {
137
136
  return this.flatSize;
138
137
  }
139
138
 
140
- /**
141
- * The number of items.
142
- *
143
- * @type {number}
144
- * @private
145
- */
146
- get size() {
147
- return this.__size;
148
- }
149
-
150
- /**
151
- * Sets the number of items.
152
- *
153
- * @type {number}
154
- * @private
155
- */
156
- set size(size) {
157
- const oldSize = this.__size;
158
- if (oldSize === size) {
159
- return;
160
- }
161
-
162
- this.__size = size;
163
-
164
- if (this.context.placeholder !== undefined) {
165
- this.items.length = size;
166
- for (let i = 0; i < size; i++) {
167
- // eslint-disable-next-line logical-assignment-operators
168
- this.items[i] = this.items[i] || this.context.placeholder;
169
- }
170
- }
171
-
172
- Object.keys(this.pendingRequests).forEach((page) => {
173
- const startIndex = parseInt(page) * this.pageSize;
174
- if (startIndex >= this.size) {
175
- delete this.pendingRequests[page];
176
- }
177
- });
178
- }
179
-
180
139
  /**
181
140
  * Recalculates the flattened size for the cache and its descendant caches recursively.
182
141
  */
@@ -201,10 +160,7 @@ export class Cache {
201
160
  setPage(page, items) {
202
161
  const startIndex = page * this.pageSize;
203
162
  items.forEach((item, i) => {
204
- const itemIndex = startIndex + i;
205
- if (itemIndex < this.size) {
206
- this.items[itemIndex] = item;
207
- }
163
+ this.items[startIndex + i] = item;
208
164
  });
209
165
  }
210
166
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import type { ReactiveController } from 'lit';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import { Cache } from './cache.js';
@@ -65,27 +65,12 @@ export class DataProviderController extends EventTarget {
65
65
  */
66
66
  rootCache;
67
67
 
68
- /**
69
- * Indicates whether any data has been loaded since the last cache clear.
70
- *
71
- * @type {boolean}
72
- */
73
- hasData = false;
74
-
75
- /**
76
- * A placeholder item that is used to indicate that the item is not loaded yet.
77
- *
78
- * @type {undefined | object}
79
- */
80
- placeholder;
81
-
82
- constructor(host, { size, pageSize, isExpanded, getItemId, placeholder, dataProvider, dataProviderParams }) {
68
+ constructor(host, { size, pageSize, isExpanded, getItemId, dataProvider, dataProviderParams }) {
83
69
  super();
84
70
  this.host = host;
85
71
  this.pageSize = pageSize;
86
72
  this.getItemId = getItemId;
87
73
  this.isExpanded = isExpanded;
88
- this.placeholder = placeholder;
89
74
  this.dataProvider = dataProvider;
90
75
  this.dataProviderParams = dataProviderParams;
91
76
  this.rootCache = this.__createRootCache(size);
@@ -102,7 +87,6 @@ export class DataProviderController extends EventTarget {
102
87
  get __cacheContext() {
103
88
  return {
104
89
  isExpanded: this.isExpanded,
105
- placeholder: this.placeholder,
106
90
  // The controller instance is needed to ensure deprecated cache methods work.
107
91
  __controller: this,
108
92
  };
@@ -149,7 +133,6 @@ export class DataProviderController extends EventTarget {
149
133
  */
150
134
  clearCache() {
151
135
  this.rootCache = this.__createRootCache(this.rootCache.size);
152
- this.hasData = false;
153
136
  }
154
137
 
155
138
  /**
@@ -204,7 +187,7 @@ export class DataProviderController extends EventTarget {
204
187
  ensureFlatIndexLoaded(flatIndex) {
205
188
  const { cache, page, item } = this.getFlatIndexContext(flatIndex);
206
189
 
207
- if (this.__isPlaceholder(item)) {
190
+ if (!item) {
208
191
  this.__loadCachePage(cache, page);
209
192
  }
210
193
  }
@@ -219,7 +202,7 @@ export class DataProviderController extends EventTarget {
219
202
  ensureFlatIndexHierarchy(flatIndex) {
220
203
  const { cache, item, index } = this.getFlatIndexContext(flatIndex);
221
204
 
222
- if (!this.__isPlaceholder(item) && this.isExpanded(item) && !cache.getSubCache(index)) {
205
+ if (item && this.isExpanded(item) && !cache.getSubCache(index)) {
223
206
  const subCache = cache.createSubCache(index);
224
207
  this.__loadCachePage(subCache, 0);
225
208
  }
@@ -268,8 +251,6 @@ export class DataProviderController extends EventTarget {
268
251
 
269
252
  this.recalculateFlatSize();
270
253
 
271
- this.hasData = true;
272
-
273
254
  this.dispatchEvent(new CustomEvent('page-received'));
274
255
 
275
256
  delete cache.pendingRequests[page];
@@ -283,9 +264,4 @@ export class DataProviderController extends EventTarget {
283
264
 
284
265
  this.dataProvider(params, callback);
285
266
  }
286
-
287
- /** @private */
288
- __isPlaceholder(item) {
289
- return item === this.placeholder;
290
- }
291
267
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import type { Cache } from './cache.js';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
 
package/src/define.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  export interface CustomElementType extends CustomElementConstructor {
package/src/define.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
 
@@ -9,7 +9,7 @@ export function defineCustomElement(CustomElement) {
9
9
  if (!defined) {
10
10
  Object.defineProperty(CustomElement, 'version', {
11
11
  get() {
12
- return '24.4.0-alpha1';
12
+ return '24.4.0-rc2';
13
13
  },
14
14
  });
15
15
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import type { Constructor } from '@open-wc/dedupe-mixin';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import { dedupingMixin } from '@polymer/polymer/lib/utils/mixin.js';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import type { Constructor } from '@open-wc/dedupe-mixin';
package/src/dir-mixin.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
 
package/src/dir-utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
 
package/src/dom-utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import '../custom_typings/vaadin-usage-statistics.js';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import { setCancelSyntheticClickEvents } from '@polymer/polymer/lib/utils/settings.js';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import type { ReactiveController } from 'lit';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import type { ReactiveController } from 'lit';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import { animationFrame } from './async.js';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2023 Vaadin Ltd.
3
+ * Copyright (c) 2023 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import type { Constructor } from '@open-wc/dedupe-mixin';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2023 Vaadin Ltd.
3
+ * Copyright (c) 2023 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
 
@@ -69,7 +69,7 @@ export const OverlayClassMixin = (superclass) =>
69
69
  }
70
70
 
71
71
  // Add new classes based on the overlayClass
72
- const classesToAdd = typeof overlayClass === 'string' ? overlayClass.split(' ') : [];
72
+ const classesToAdd = typeof overlayClass === 'string' ? overlayClass.split(' ').filter(Boolean) : [];
73
73
  if (classesToAdd.length > 0) {
74
74
  classList.add(...classesToAdd);
75
75
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2023 Vaadin Ltd.
3
+ * Copyright (c) 2023 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
 
package/src/path-utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2023 Vaadin Ltd.
3
+ * Copyright (c) 2023 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import type { Constructor } from '@open-wc/dedupe-mixin';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import { dedupeMixin } from '@open-wc/dedupe-mixin';
@@ -93,6 +93,9 @@ const PolylitMixinImplementation = (superclass) => {
93
93
 
94
94
  let result = defaultDescriptor;
95
95
 
96
+ // Set the key for this property
97
+ this.getOrCreateMap('__propKeys').set(name, key);
98
+
96
99
  if (options.sync) {
97
100
  result = {
98
101
  get: defaultDescriptor.get,
@@ -232,6 +235,26 @@ const PolylitMixinImplementation = (superclass) => {
232
235
  }
233
236
  }
234
237
 
238
+ /**
239
+ * Set several properties at once and perform synchronous update.
240
+ * @protected
241
+ */
242
+ setProperties(props) {
243
+ Object.entries(props).forEach(([name, value]) => {
244
+ // Use private key and not setter to not trigger
245
+ // update for properties marked as `sync: true`.
246
+ const key = this.constructor.__propKeys.get(name);
247
+ const oldValue = this[key];
248
+ this[key] = value;
249
+ this.requestUpdate(name, oldValue);
250
+ });
251
+
252
+ // Perform sync update
253
+ if (this.hasUpdated) {
254
+ this.performUpdate();
255
+ }
256
+ }
257
+
235
258
  /** @protected */
236
259
  _createMethodObserver(observer) {
237
260
  const dynamicObservers = getOrCreateMap(this, '__dynamicObservers');
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import type { Constructor } from '@open-wc/dedupe-mixin';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import { dedupingMixin } from '@polymer/polymer/lib/utils/mixin.js';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2022 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2022 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import { SlotController } from './slot-controller.js';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2022 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2022 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import { SlotController } from './slot-controller.js';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import type { ReactiveController } from 'lit';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import { isEmptyTextNode } from './dom-utils.js';
@@ -111,12 +111,12 @@ export class SlotController extends EventTarget {
111
111
  if (slotName !== '') {
112
112
  node.setAttribute('slot', slotName);
113
113
  }
114
- this.node = node;
115
114
  this.defaultNode = node;
116
115
  }
117
116
  }
118
117
 
119
118
  if (node) {
119
+ this.node = node;
120
120
  host.appendChild(node);
121
121
  }
122
122
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2023 Vaadin Ltd.
3
+ * Copyright (c) 2023 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2023 Vaadin Ltd.
3
+ * Copyright (c) 2023 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import type { Constructor } from '@open-wc/dedupe-mixin';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import { dedupingMixin } from '@polymer/polymer/lib/utils/mixin.js';
package/src/templates.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2022 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2022 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import { SlotController } from './slot-controller.js';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2022 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2022 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import { SlotController } from './slot-controller.js';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
 
@@ -1,11 +1,15 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2023 Vaadin Ltd.
3
+ * Copyright (c) 2023 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
 
7
7
  /**
8
- * Check if two paths can be resolved as URLs
9
- * with the same origin and pathname.
8
+ * Checks if two paths match based on their origin, pathname, and query parameters.
9
+ *
10
+ * The function matches an actual URL against an expected URL to see if they share
11
+ * the same base origin (like https://example.com), the same path (like /path/to/page),
12
+ * and if the actual URL contains at least all the query parameters with the same values
13
+ * from the expected URL.
10
14
  */
11
- export declare function matchPaths(path1: string, path2: string): boolean;
15
+ export declare function matchPaths(actual: string, expected: string): boolean;
package/src/url-utils.js CHANGED
@@ -1,19 +1,41 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2023 Vaadin Ltd.
3
+ * Copyright (c) 2023 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
 
7
7
  /**
8
- * Check if two paths can be resolved as URLs
9
- * with the same origin and pathname.
8
+ * Checks if one set of URL parameters contains all the parameters
9
+ * with the same values from another set.
10
10
  *
11
- * @param {string} path1
12
- * @param {string} path2
11
+ * @param {URLSearchParams} actual
12
+ * @param {URLSearchParams} expected
13
13
  */
14
- export function matchPaths(path1, path2) {
14
+ function containsQueryParams(actual, expected) {
15
+ return [...expected.entries()].every(([key, value]) => {
16
+ return actual.getAll(key).includes(value);
17
+ });
18
+ }
19
+
20
+ /**
21
+ * Checks if two paths match based on their origin, pathname, and query parameters.
22
+ *
23
+ * The function matches an actual URL against an expected URL to see if they share
24
+ * the same base origin (like https://example.com), the same path (like /path/to/page),
25
+ * and if the actual URL contains at least all the query parameters with the same values
26
+ * from the expected URL.
27
+ *
28
+ * @param {string} actual The actual URL to match.
29
+ * @param {string} expected The expected URL to match.
30
+ */
31
+ export function matchPaths(actual, expected) {
15
32
  const base = document.baseURI;
16
- const url1 = new URL(path1, base);
17
- const url2 = new URL(path2, base);
18
- return url1.origin === url2.origin && url1.pathname === url2.pathname;
33
+ const actualUrl = new URL(actual, base);
34
+ const expectedUrl = new URL(expected, base);
35
+
36
+ return (
37
+ actualUrl.origin === expectedUrl.origin &&
38
+ actualUrl.pathname === expectedUrl.pathname &&
39
+ containsQueryParams(actualUrl.searchParams, expectedUrl.searchParams)
40
+ );
19
41
  }
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2021 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2021 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  /* eslint-disable @typescript-eslint/member-ordering */
7
7
  // https://github.com/vaadin/eslint-config-vaadin/issues/33
8
- import { animationFrame, timeOut } from './async.js';
8
+ import { animationFrame, microTask, timeOut } from './async.js';
9
9
  import { isSafari } from './browser-utils.js';
10
10
  import { Debouncer, flush } from './debounce.js';
11
11
  import { ironList } from './iron-list-core.js';
@@ -55,6 +55,13 @@ export class IronListAdapter {
55
55
  this._scrollLineHeight = this._getScrollLineHeight();
56
56
  this.scrollTarget.addEventListener('wheel', (e) => this.__onWheel(e));
57
57
 
58
+ this.scrollTarget.addEventListener('virtualizer-element-focused', (e) => this.__onElementFocused(e));
59
+ this.elementsContainer.addEventListener('focusin', (e) => {
60
+ this.scrollTarget.dispatchEvent(
61
+ new CustomEvent('virtualizer-element-focused', { detail: { element: this.__getFocusedElement() } }),
62
+ );
63
+ });
64
+
58
65
  if (this.reorderElements) {
59
66
  // Reordering the physical elements cancels the user's grab of the scroll bar handle on Safari.
60
67
  // Need to defer reordering until the user lets go of the scroll bar handle.
@@ -82,6 +89,10 @@ export class IronListAdapter {
82
89
  return this.lastVisibleIndex + this._vidxOffset;
83
90
  }
84
91
 
92
+ get _maxVirtualIndexOffset() {
93
+ return this.size - this._virtualCount;
94
+ }
95
+
85
96
  __hasPlaceholders() {
86
97
  return this.__getVisibleElements().some((el) => el.__virtualizerPlaceholder);
87
98
  }
@@ -106,7 +117,7 @@ export class IronListAdapter {
106
117
  let targetVirtualIndex = Math.floor((index / this.size) * this._virtualCount);
107
118
  if (this._virtualCount - targetVirtualIndex < visibleElementCount) {
108
119
  targetVirtualIndex = this._virtualCount - (this.size - index);
109
- this._vidxOffset = this.size - this._virtualCount;
120
+ this._vidxOffset = this._maxVirtualIndexOffset;
110
121
  } else if (targetVirtualIndex < visibleElementCount) {
111
122
  if (index < OFFSET_ADJUST_MIN_THRESHOLD) {
112
123
  targetVirtualIndex = index;
@@ -136,7 +147,6 @@ export class IronListAdapter {
136
147
  }
137
148
 
138
149
  flush() {
139
- const startPhysicalCount = this._physicalCount;
140
150
  // The scroll target is hidden.
141
151
  if (this.scrollTarget.offsetHeight === 0) {
142
152
  return;
@@ -154,11 +164,6 @@ export class IronListAdapter {
154
164
  if (this.__debouncerWheelAnimationFrame) {
155
165
  this.__debouncerWheelAnimationFrame.flush();
156
166
  }
157
-
158
- if (this._physicalCount !== startPhysicalCount) {
159
- // Flushing again until physical count stabilizes fixes https://github.com/vaadin/flow-components/issues/5595#issuecomment-1770278913
160
- this.flush();
161
- }
162
167
  }
163
168
 
164
169
  update(startIndex = 0, endIndex = this.size - 1) {
@@ -227,6 +232,7 @@ export class IronListAdapter {
227
232
  // Clean up temporary placeholder sizing
228
233
  if (el.__virtualizerPlaceholder) {
229
234
  el.style.paddingTop = '';
235
+ el.style.opacity = '';
230
236
  el.__virtualizerPlaceholder = false;
231
237
  }
232
238
 
@@ -251,6 +257,7 @@ export class IronListAdapter {
251
257
  // Assign a temporary placeholder sizing to elements that would otherwise end up having
252
258
  // no height.
253
259
  el.style.paddingTop = `${this.__placeholderHeight}px`;
260
+ el.style.opacity = '0';
254
261
  el.__virtualizerPlaceholder = true;
255
262
 
256
263
  // Manually schedule the resize handler to make sure the placeholder padding is
@@ -297,24 +304,41 @@ export class IronListAdapter {
297
304
  this._debouncers._increasePoolIfNeeded.cancel();
298
305
  }
299
306
 
307
+ // Prevent element update while the scroll position is being restored
308
+ this.__preventElementUpdates = true;
309
+
310
+ // Record the scroll position before changing the size
311
+ let fvi; // First visible index
312
+ let fviOffsetBefore; // Scroll offset of the first visible index
313
+ if (size > 0) {
314
+ fvi = this.adjustedFirstVisibleIndex;
315
+ fviOffsetBefore = this.__getIndexScrollOffset(fvi);
316
+ }
317
+
300
318
  // Change the size
301
319
  this.__size = size;
302
320
 
303
- if (!this._physicalItems) {
304
- // Not initialized yet
305
- this._itemsChanged({
306
- path: 'items',
307
- });
308
- this.__preventElementUpdates = true;
309
- flush();
310
- this.__preventElementUpdates = false;
311
- } else {
312
- // Already initialized, just update _virtualCount
313
- this._updateScrollerSize();
314
- this._virtualCount = this.items.length;
315
- this._render();
321
+ this._itemsChanged({
322
+ path: 'items',
323
+ });
324
+ flush();
325
+
326
+ // Try to restore the scroll position if the new size is larger than 0
327
+ if (size > 0) {
328
+ fvi = Math.min(fvi, size - 1);
329
+ // Note, calling scrollToIndex also updates the virtual index offset,
330
+ // causing the virtualizer to add more items when size is increased,
331
+ // and remove exceeding items when size is decreased.
332
+ this.scrollToIndex(fvi);
333
+
334
+ const fviOffsetAfter = this.__getIndexScrollOffset(fvi);
335
+ if (fviOffsetBefore !== undefined && fviOffsetAfter !== undefined) {
336
+ this._scrollTop += fviOffsetBefore - fviOffsetAfter;
337
+ }
316
338
  }
317
339
 
340
+ this.__preventElementUpdates = false;
341
+
318
342
  // When reducing size while invisible, iron-list does not update items, so
319
343
  // their hidden state is not updated and their __lastUpdatedIndex is not
320
344
  // reset. In that case force an update here.
@@ -326,10 +350,12 @@ export class IronListAdapter {
326
350
  requestAnimationFrame(() => this._resizeHandler());
327
351
  }
328
352
 
329
- // Schedule and flush a resize handler. This will cause a
330
- // re-render for the elements.
353
+ // Schedule and flush a resize handler
331
354
  this._resizeHandler();
332
355
  flush();
356
+ // Schedule an update to ensure item positions are correct after subsequent size changes
357
+ // Fix for https://github.com/vaadin/flow-components/issues/6269
358
+ this._debounce('_update', this._update, microTask);
333
359
  }
334
360
 
335
361
  /** @private */
@@ -425,6 +451,75 @@ export class IronListAdapter {
425
451
  /** @private */
426
452
  toggleScrollListener() {}
427
453
 
454
+ /** @private */
455
+ __getFocusedElement(visibleElements = this.__getVisibleElements()) {
456
+ return visibleElements.find(
457
+ (element) =>
458
+ element.contains(this.elementsContainer.getRootNode().activeElement) ||
459
+ element.contains(this.scrollTarget.getRootNode().activeElement),
460
+ );
461
+ }
462
+
463
+ /** @private */
464
+ __nextFocusableSiblingMissing(focusedElement, visibleElements) {
465
+ return (
466
+ // Check if focused element is the last visible DOM element
467
+ visibleElements.indexOf(focusedElement) === visibleElements.length - 1 &&
468
+ // ...while there are more items available
469
+ this.size > focusedElement.__virtualIndex + 1
470
+ );
471
+ }
472
+
473
+ /** @private */
474
+ __previousFocusableSiblingMissing(focusedElement, visibleElements) {
475
+ return (
476
+ // Check if focused element is the first visible DOM element
477
+ visibleElements.indexOf(focusedElement) === 0 &&
478
+ // ...while there are preceding items available
479
+ focusedElement.__virtualIndex > 0
480
+ );
481
+ }
482
+
483
+ /** @private */
484
+ __onElementFocused(e) {
485
+ if (!this.reorderElements) {
486
+ return;
487
+ }
488
+
489
+ const focusedElement = e.detail.element;
490
+ if (!focusedElement) {
491
+ return;
492
+ }
493
+
494
+ // User has tabbed to or within a virtualizer element.
495
+ // Check if a next or previous focusable sibling is missing while it should be there (so the user can continue tabbing).
496
+ // The focusable sibling might be missing due to the elements not yet being in the correct DOM order.
497
+ // First try flushing (which also flushes any active __scrollReorderDebouncer).
498
+ const visibleElements = this.__getVisibleElements();
499
+ if (
500
+ this.__previousFocusableSiblingMissing(focusedElement, visibleElements) ||
501
+ this.__nextFocusableSiblingMissing(focusedElement, visibleElements)
502
+ ) {
503
+ this.flush();
504
+ }
505
+
506
+ // If the focusable sibling is still missing (because the focused element is at the edge of the viewport and
507
+ // the virtual scrolling logic hasn't had the need to recycle elements), scroll the virtualizer just enough to
508
+ // have the focusable sibling inside the visible viewport to force the virtualizer to recycle.
509
+ const reorderedVisibleElements = this.__getVisibleElements();
510
+ if (this.__nextFocusableSiblingMissing(focusedElement, reorderedVisibleElements)) {
511
+ this._scrollTop +=
512
+ Math.ceil(focusedElement.getBoundingClientRect().bottom) -
513
+ Math.floor(this.scrollTarget.getBoundingClientRect().bottom - 1);
514
+ this.flush();
515
+ } else if (this.__previousFocusableSiblingMissing(focusedElement, reorderedVisibleElements)) {
516
+ this._scrollTop -=
517
+ Math.ceil(this.scrollTarget.getBoundingClientRect().top + 1) -
518
+ Math.floor(focusedElement.getBoundingClientRect().top);
519
+ this.flush();
520
+ }
521
+ }
522
+
428
523
  _scrollHandler() {
429
524
  // The scroll target is hidden.
430
525
  if (this.scrollTarget.offsetHeight === 0) {
@@ -662,13 +757,7 @@ export class IronListAdapter {
662
757
 
663
758
  // Which row to use as a target?
664
759
  const visibleElements = this.__getVisibleElements();
665
-
666
- const elementWithFocus = visibleElements.find(
667
- (element) =>
668
- element.contains(this.elementsContainer.getRootNode().activeElement) ||
669
- element.contains(this.scrollTarget.getRootNode().activeElement),
670
- );
671
- const targetElement = elementWithFocus || visibleElements[0];
760
+ const targetElement = this.__getFocusedElement(visibleElements) || visibleElements[0];
672
761
  if (!targetElement) {
673
762
  // All elements are hidden, don't reorder
674
763
  return;
@@ -703,15 +792,16 @@ export class IronListAdapter {
703
792
 
704
793
  /** @private */
705
794
  _adjustVirtualIndexOffset(delta) {
795
+ const maxOffset = this._maxVirtualIndexOffset;
796
+
706
797
  if (this._virtualCount >= this.size) {
707
798
  this._vidxOffset = 0;
708
799
  } else if (this.__skipNextVirtualIndexAdjust) {
709
800
  this.__skipNextVirtualIndexAdjust = false;
710
801
  } else if (Math.abs(delta) > 10000) {
711
802
  // Process a large scroll position change
712
- const scale = this._scrollTop / (this.scrollTarget.scrollHeight - this.scrollTarget.offsetHeight);
713
- const offset = scale * this.size;
714
- this._vidxOffset = Math.round(offset - scale * this._virtualCount);
803
+ const scale = this._scrollTop / (this.scrollTarget.scrollHeight - this.scrollTarget.clientHeight);
804
+ this._vidxOffset = Math.round(scale * maxOffset);
715
805
  } else {
716
806
  // Make sure user can always swipe/wheel scroll to the start and end
717
807
  const oldOffset = this._vidxOffset;
@@ -730,7 +820,6 @@ export class IronListAdapter {
730
820
  }
731
821
 
732
822
  // Near end
733
- const maxOffset = this.size - this._virtualCount;
734
823
  if (this._scrollTop >= this._maxScrollTop && this._maxScrollTop > 0) {
735
824
  this._vidxOffset = maxOffset;
736
825
  if (oldOffset !== this._vidxOffset) {