@vaadin/component-base 24.5.0-alpha1 → 24.5.0-alpha11

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/README.md CHANGED
@@ -4,7 +4,7 @@ A set of helpers and mixins used by Vaadin components.
4
4
 
5
5
  ## Contributing
6
6
 
7
- Read the [contributing guide](https://vaadin.com/docs/latest/contributing/overview) to learn about our development process, how to propose bugfixes and improvements, and how to test your changes to Vaadin components.
7
+ Read the [contributing guide](https://vaadin.com/docs/latest/contributing) to learn about our development process, how to propose bugfixes and improvements, and how to test your changes to Vaadin components.
8
8
 
9
9
  ## License
10
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/component-base",
3
- "version": "24.5.0-alpha1",
3
+ "version": "24.5.0-alpha11",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -38,9 +38,9 @@
38
38
  "lit": "^3.0.0"
39
39
  },
40
40
  "devDependencies": {
41
- "@esm-bundle/chai": "^4.3.4",
42
- "@vaadin/testing-helpers": "^0.6.0",
43
- "sinon": "^13.0.2"
41
+ "@vaadin/chai-plugins": "24.5.0-alpha11",
42
+ "@vaadin/testing-helpers": "^1.0.0",
43
+ "sinon": "^18.0.0"
44
44
  },
45
- "gitHead": "57806caac5468532a3b4e3dbdda730cd0fca193a"
45
+ "gitHead": "8426cea2803a10db518fc85752eeea4c5c755687"
46
46
  }
@@ -29,7 +29,7 @@ export const isTouch = (() => {
29
29
  try {
30
30
  document.createEvent('TouchEvent');
31
31
  return true;
32
- } catch (e) {
32
+ } catch (_) {
33
33
  return false;
34
34
  }
35
35
  })();
@@ -16,13 +16,6 @@ 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
-
26
19
  /**
27
20
  * The number of items to display per page.
28
21
  *
@@ -58,6 +51,14 @@ export class Cache {
58
51
  */
59
52
  __subCacheByIndex = {};
60
53
 
54
+ /**
55
+ * The number of items.
56
+ *
57
+ * @type {number}
58
+ * @private
59
+ */
60
+ __size = 0;
61
+
61
62
  /**
62
63
  * The total number of items, including items from expanded sub-caches.
63
64
  *
@@ -76,7 +77,7 @@ export class Cache {
76
77
  constructor(context, pageSize, size, parentCache, parentCacheIndex) {
77
78
  this.context = context;
78
79
  this.pageSize = pageSize;
79
- this.size = size || 0;
80
+ this.size = size;
80
81
  this.parentCache = parentCache;
81
82
  this.parentCacheIndex = parentCacheIndex;
82
83
  this.__flatSize = size || 0;
@@ -136,6 +137,43 @@ export class Cache {
136
137
  return this.flatSize;
137
138
  }
138
139
 
140
+ /**
141
+ * The number of items.
142
+ *
143
+ * @return {number}
144
+ */
145
+ get size() {
146
+ return this.__size;
147
+ }
148
+
149
+ /**
150
+ * Sets the number of items.
151
+ *
152
+ * @param {number} size
153
+ */
154
+ set size(size) {
155
+ const oldSize = this.__size;
156
+ if (oldSize === size) {
157
+ return;
158
+ }
159
+
160
+ this.__size = size;
161
+
162
+ if (this.context.placeholder !== undefined) {
163
+ this.items.length = size || 0;
164
+ for (let i = 0; i < size || 0; i++) {
165
+ this.items[i] ||= this.context.placeholder;
166
+ }
167
+ }
168
+
169
+ Object.keys(this.pendingRequests).forEach((page) => {
170
+ const startIndex = parseInt(page) * this.pageSize;
171
+ if (startIndex >= this.size || 0) {
172
+ delete this.pendingRequests[page];
173
+ }
174
+ });
175
+ }
176
+
139
177
  /**
140
178
  * Recalculates the flattened size for the cache and its descendant caches recursively.
141
179
  */
@@ -160,7 +198,10 @@ export class Cache {
160
198
  setPage(page, items) {
161
199
  const startIndex = page * this.pageSize;
162
200
  items.forEach((item, i) => {
163
- this.items[startIndex + i] = item;
201
+ const itemIndex = startIndex + i;
202
+ if (this.size === undefined || itemIndex < this.size) {
203
+ this.items[itemIndex] = item;
204
+ }
164
205
  });
165
206
  }
166
207
 
@@ -43,11 +43,6 @@ export class DataProviderController<TItem, TDataProviderParams extends Record<st
43
43
  */
44
44
  dataProviderParams: () => TDataProviderParams;
45
45
 
46
- /**
47
- * A number of items in the root cache.
48
- */
49
- size?: number;
50
-
51
46
  /**
52
47
  * A number of items to display per page.
53
48
  */
@@ -69,11 +64,23 @@ export class DataProviderController<TItem, TDataProviderParams extends Record<st
69
64
  */
70
65
  rootCache: Cache<TItem>;
71
66
 
67
+ /**
68
+ * A placeholder item that is used to indicate that the item is not loaded yet.
69
+ */
70
+ placeholder?: unknown;
71
+
72
+ /**
73
+ * A callback that returns whether the given item is a placeholder.
74
+ */
75
+ isPlaceholder?: (item: unknown) => boolean;
76
+
72
77
  constructor(
73
78
  host: HTMLElement,
74
79
  config: {
75
80
  size?: number;
76
81
  pageSize: number;
82
+ placeholder?: unknown;
83
+ isPlaceholder?(item: unknown): boolean;
77
84
  getItemId(item: TItem): unknown;
78
85
  isExpanded(item: TItem): boolean;
79
86
  dataProvider: DataProvider<TItem, TDataProviderParams>;
@@ -29,13 +29,6 @@ export class DataProviderController extends EventTarget {
29
29
  */
30
30
  dataProviderParams;
31
31
 
32
- /**
33
- * A number of items in the root cache.
34
- *
35
- * @type {number}
36
- */
37
- size;
38
-
39
32
  /**
40
33
  * A number of items to display per page.
41
34
  *
@@ -65,12 +58,31 @@ export class DataProviderController extends EventTarget {
65
58
  */
66
59
  rootCache;
67
60
 
68
- constructor(host, { size, pageSize, isExpanded, getItemId, dataProvider, dataProviderParams }) {
61
+ /**
62
+ * A placeholder item that is used to indicate that the item is not loaded yet.
63
+ *
64
+ * @type {unknown}
65
+ */
66
+ placeholder;
67
+
68
+ /**
69
+ * A callback that returns whether the given item is a placeholder.
70
+ *
71
+ * @type {(item: unknown) => boolean}
72
+ */
73
+ isPlaceholder;
74
+
75
+ constructor(
76
+ host,
77
+ { size, pageSize, isExpanded, getItemId, isPlaceholder, placeholder, dataProvider, dataProviderParams },
78
+ ) {
69
79
  super();
70
80
  this.host = host;
71
81
  this.pageSize = pageSize;
72
82
  this.getItemId = getItemId;
73
83
  this.isExpanded = isExpanded;
84
+ this.placeholder = placeholder;
85
+ this.isPlaceholder = isPlaceholder;
74
86
  this.dataProvider = dataProvider;
75
87
  this.dataProviderParams = dataProviderParams;
76
88
  this.rootCache = this.__createRootCache(size);
@@ -87,6 +99,7 @@ export class DataProviderController extends EventTarget {
87
99
  get __cacheContext() {
88
100
  return {
89
101
  isExpanded: this.isExpanded,
102
+ placeholder: this.placeholder,
90
103
  // The controller instance is needed to ensure deprecated cache methods work.
91
104
  __controller: this,
92
105
  };
@@ -187,7 +200,7 @@ export class DataProviderController extends EventTarget {
187
200
  ensureFlatIndexLoaded(flatIndex) {
188
201
  const { cache, page, item } = this.getFlatIndexContext(flatIndex);
189
202
 
190
- if (!item) {
203
+ if (!this.__isItemLoaded(item)) {
191
204
  this.__loadCachePage(cache, page);
192
205
  }
193
206
  }
@@ -202,7 +215,7 @@ export class DataProviderController extends EventTarget {
202
215
  ensureFlatIndexHierarchy(flatIndex) {
203
216
  const { cache, item, index } = this.getFlatIndexContext(flatIndex);
204
217
 
205
- if (item && this.isExpanded(item) && !cache.getSubCache(index)) {
218
+ if (this.__isItemLoaded(item) && this.isExpanded(item) && !cache.getSubCache(index)) {
206
219
  const subCache = cache.createSubCache(index);
207
220
  this.__loadCachePage(subCache, 0);
208
221
  }
@@ -264,4 +277,14 @@ export class DataProviderController extends EventTarget {
264
277
 
265
278
  this.dataProvider(params, callback);
266
279
  }
280
+
281
+ /** @private */
282
+ __isItemLoaded(item) {
283
+ if (this.isPlaceholder) {
284
+ return !this.isPlaceholder(item);
285
+ } else if (this.placeholder) {
286
+ return item !== this.placeholder;
287
+ }
288
+ return !!item;
289
+ }
267
290
  }
package/src/define.js CHANGED
@@ -9,7 +9,7 @@ export function defineCustomElement(CustomElement) {
9
9
  if (!defined) {
10
10
  Object.defineProperty(CustomElement, 'version', {
11
11
  get() {
12
- return '24.5.0-alpha1';
12
+ return '24.5.0-alpha11';
13
13
  },
14
14
  });
15
15
 
package/src/gestures.js CHANGED
@@ -45,7 +45,7 @@ const MOUSE_WHICH_TO_BUTTONS = [0, 1, 4, 2];
45
45
  const MOUSE_HAS_BUTTONS = (function () {
46
46
  try {
47
47
  return new MouseEvent('test', { buttons: 1 }).buttons === 1;
48
- } catch (e) {
48
+ } catch (_) {
49
49
  return false;
50
50
  }
51
51
  })();
@@ -71,7 +71,7 @@ let supportsPassive = false;
71
71
  });
72
72
  window.addEventListener('test', null, opts);
73
73
  window.removeEventListener('test', null, opts);
74
- } catch (e) {}
74
+ } catch (_) {}
75
75
  })();
76
76
 
77
77
  /**
@@ -48,7 +48,7 @@ export class OverflowController {
48
48
  observe() {
49
49
  const { host } = this;
50
50
 
51
- this.__resizeObserver = new ResizeObserver((entries) => {
51
+ this.__resizeObserver = new ResizeObserver(() => {
52
52
  this.__debounceOverflow = Debouncer.debounce(this.__debounceOverflow, animationFrame, () => {
53
53
  this.__updateOverflow();
54
54
  });
@@ -38,6 +38,7 @@ export class SlotController extends EventTarget implements ReactiveController {
38
38
  multiple?: boolean;
39
39
  observe?: boolean;
40
40
  useUniqueId?: boolean;
41
+ uniqueIdPrefix?: string;
41
42
  initializer?(host: HTMLElement, node: HTMLElement): void;
42
43
  },
43
44
  );
@@ -19,15 +19,14 @@ export class SlotController extends EventTarget {
19
19
  * @return {string}
20
20
  * @protected
21
21
  */
22
- static generateId(host, slotName) {
23
- const prefix = slotName || 'default';
22
+ static generateId(host, prefix = 'default') {
24
23
  return `${prefix}-${host.localName}-${generateUniqueId()}`;
25
24
  }
26
25
 
27
26
  constructor(host, slotName, tagName, config = {}) {
28
27
  super();
29
28
 
30
- const { initializer, multiple, observe, useUniqueId } = config;
29
+ const { initializer, multiple, observe, useUniqueId, uniqueIdPrefix } = config;
31
30
 
32
31
  this.host = host;
33
32
  this.slotName = slotName;
@@ -42,7 +41,7 @@ export class SlotController extends EventTarget {
42
41
 
43
42
  // Only generate the default ID if requested by the controller.
44
43
  if (useUniqueId) {
45
- this.defaultId = this.constructor.generateId(host, slotName);
44
+ this.defaultId = this.constructor.generateId(host, uniqueIdPrefix || slotName);
46
45
  }
47
46
  }
48
47
 
package/src/url-utils.js CHANGED
@@ -27,15 +27,17 @@ function containsQueryParams(actual, expected) {
27
27
  *
28
28
  * @param {string} actual The actual URL to match.
29
29
  * @param {string} expected The expected URL to match.
30
+ * @param {Object} matchOptions Options for path matching.
30
31
  */
31
- export function matchPaths(actual, expected) {
32
+ export function matchPaths(actual, expected, matchOptions = { matchNested: false }) {
32
33
  const base = document.baseURI;
33
34
  const actualUrl = new URL(actual, base);
34
35
  const expectedUrl = new URL(expected, base);
35
36
 
36
- return (
37
- actualUrl.origin === expectedUrl.origin &&
38
- actualUrl.pathname === expectedUrl.pathname &&
39
- containsQueryParams(actualUrl.searchParams, expectedUrl.searchParams)
40
- );
37
+ const matchesOrigin = actualUrl.origin === expectedUrl.origin;
38
+ const matchesPath = matchOptions.matchNested
39
+ ? actualUrl.pathname === expectedUrl.pathname || actualUrl.pathname.startsWith(`${expectedUrl.pathname}/`)
40
+ : actualUrl.pathname === expectedUrl.pathname;
41
+
42
+ return matchesOrigin && matchesPath && containsQueryParams(actualUrl.searchParams, expectedUrl.searchParams);
41
43
  }
@@ -56,7 +56,7 @@ export class IronListAdapter {
56
56
  this.scrollTarget.addEventListener('wheel', (e) => this.__onWheel(e));
57
57
 
58
58
  this.scrollTarget.addEventListener('virtualizer-element-focused', (e) => this.__onElementFocused(e));
59
- this.elementsContainer.addEventListener('focusin', (e) => {
59
+ this.elementsContainer.addEventListener('focusin', () => {
60
60
  this.scrollTarget.dispatchEvent(
61
61
  new CustomEvent('virtualizer-element-focused', { detail: { element: this.__getFocusedElement() } }),
62
62
  );