@vaadin/grid 24.0.0-alpha1 → 24.0.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.
Files changed (63) hide show
  1. package/all-imports.d.ts +10 -0
  2. package/all-imports.js +11 -0
  3. package/package.json +11 -10
  4. package/src/all-imports.js +1 -1
  5. package/src/array-data-provider.js +1 -1
  6. package/src/lit/column-renderer-directives.d.ts +1 -1
  7. package/src/lit/column-renderer-directives.js +1 -1
  8. package/src/lit/renderer-directives.d.ts +1 -1
  9. package/src/lit/renderer-directives.js +1 -1
  10. package/src/vaadin-grid-a11y-mixin.js +12 -9
  11. package/src/vaadin-grid-active-item-mixin.d.ts +1 -1
  12. package/src/vaadin-grid-active-item-mixin.js +10 -5
  13. package/src/vaadin-grid-array-data-provider-mixin.d.ts +1 -1
  14. package/src/vaadin-grid-array-data-provider-mixin.js +1 -1
  15. package/src/vaadin-grid-column-group.d.ts +1 -1
  16. package/src/vaadin-grid-column-group.js +17 -17
  17. package/src/vaadin-grid-column-reordering-mixin.d.ts +1 -1
  18. package/src/vaadin-grid-column-reordering-mixin.js +8 -10
  19. package/src/vaadin-grid-column-resizing-mixin.js +6 -5
  20. package/src/vaadin-grid-column.d.ts +1 -1
  21. package/src/vaadin-grid-column.js +54 -40
  22. package/src/vaadin-grid-data-provider-mixin.d.ts +1 -1
  23. package/src/vaadin-grid-data-provider-mixin.js +34 -7
  24. package/src/vaadin-grid-drag-and-drop-mixin.d.ts +1 -1
  25. package/src/vaadin-grid-drag-and-drop-mixin.js +23 -18
  26. package/src/vaadin-grid-dynamic-columns-mixin.js +5 -4
  27. package/src/vaadin-grid-event-context-mixin.d.ts +1 -1
  28. package/src/vaadin-grid-event-context-mixin.js +1 -1
  29. package/src/vaadin-grid-filter-column.d.ts +1 -1
  30. package/src/vaadin-grid-filter-column.js +1 -2
  31. package/src/vaadin-grid-filter-mixin.js +1 -1
  32. package/src/vaadin-grid-filter.d.ts +3 -2
  33. package/src/vaadin-grid-filter.js +27 -22
  34. package/src/vaadin-grid-helpers.js +104 -1
  35. package/src/vaadin-grid-keyboard-navigation-mixin.js +48 -35
  36. package/src/vaadin-grid-row-details-mixin.d.ts +2 -6
  37. package/src/vaadin-grid-row-details-mixin.js +5 -4
  38. package/src/vaadin-grid-scroll-mixin.d.ts +1 -1
  39. package/src/vaadin-grid-scroll-mixin.js +18 -10
  40. package/src/vaadin-grid-selection-column.d.ts +1 -1
  41. package/src/vaadin-grid-selection-column.js +1 -1
  42. package/src/vaadin-grid-selection-mixin.d.ts +1 -1
  43. package/src/vaadin-grid-selection-mixin.js +1 -1
  44. package/src/vaadin-grid-sort-column.d.ts +1 -1
  45. package/src/vaadin-grid-sort-column.js +1 -1
  46. package/src/vaadin-grid-sort-mixin.d.ts +10 -1
  47. package/src/vaadin-grid-sort-mixin.js +22 -6
  48. package/src/vaadin-grid-sorter.d.ts +7 -2
  49. package/src/vaadin-grid-sorter.js +18 -2
  50. package/src/vaadin-grid-styles.js +5 -1
  51. package/src/vaadin-grid-styling-mixin.d.ts +38 -2
  52. package/src/vaadin-grid-styling-mixin.js +78 -6
  53. package/src/vaadin-grid-tree-column.d.ts +1 -8
  54. package/src/vaadin-grid-tree-column.js +2 -25
  55. package/src/vaadin-grid-tree-toggle.d.ts +1 -1
  56. package/src/vaadin-grid-tree-toggle.js +2 -2
  57. package/src/vaadin-grid.d.ts +100 -50
  58. package/src/vaadin-grid.js +198 -105
  59. package/theme/lumo/vaadin-grid-styles.js +14 -14
  60. package/theme/lumo/vaadin-grid.js +0 -7
  61. package/theme/material/vaadin-grid-styles.js +1 -1
  62. package/web-types.json +33 -26
  63. package/web-types.lit.json +18 -11
@@ -1,10 +1,11 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2016 - 2022 Vaadin Ltd.
3
+ * Copyright (c) 2016 - 2023 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 { timeOut } from '@vaadin/component-base/src/async.js';
7
7
  import { Debouncer } from '@vaadin/component-base/src/debounce.js';
8
+ import { getBodyRowCells, iterateChildren, updateCellsPart, updateState } from './vaadin-grid-helpers.js';
8
9
 
9
10
  /**
10
11
  * @private
@@ -193,6 +194,7 @@ export const DataProviderMixin = (superClass) =>
193
194
  itemHasChildrenPath: {
194
195
  type: String,
195
196
  value: 'children',
197
+ observer: '__itemHasChildrenPathChanged',
196
198
  },
197
199
 
198
200
  /**
@@ -236,6 +238,15 @@ export const DataProviderMixin = (superClass) =>
236
238
  this._effectiveSize = this._cache.effectiveSize;
237
239
  }
238
240
 
241
+ /** @private */
242
+ __itemHasChildrenPathChanged(value, oldValue) {
243
+ if (!oldValue && value === 'children') {
244
+ // Avoid an unnecessary content update on init.
245
+ return;
246
+ }
247
+ this.requestContentUpdate();
248
+ }
249
+
239
250
  /**
240
251
  * @param {number} index
241
252
  * @param {HTMLElement} el
@@ -250,17 +261,32 @@ export const DataProviderMixin = (superClass) =>
250
261
  const { cache, scaledIndex } = this._cache.getCacheAndIndex(index);
251
262
  const item = cache.items[scaledIndex];
252
263
  if (item) {
253
- el.toggleAttribute('loading', false);
264
+ this.__updateLoading(el, false);
254
265
  this._updateItem(el, item);
255
266
  if (this._isExpanded(item)) {
256
267
  cache.ensureSubCacheForScaledIndex(scaledIndex);
257
268
  }
258
269
  } else {
259
- el.toggleAttribute('loading', true);
270
+ this.__updateLoading(el, true);
260
271
  this._loadPage(this._getPageForIndex(scaledIndex), cache);
261
272
  }
262
273
  }
263
274
 
275
+ /**
276
+ * @param {!HTMLElement} row
277
+ * @param {boolean} loading
278
+ * @private
279
+ */
280
+ __updateLoading(row, loading) {
281
+ const cells = getBodyRowCells(row);
282
+
283
+ // Row state attribute
284
+ updateState(row, 'loading', loading);
285
+
286
+ // Cells part attribute
287
+ updateCellsPart(cells, 'loading-row-cell', loading);
288
+ }
289
+
264
290
  /**
265
291
  * Returns a value that identifies the item. Uses `itemIdPath` if available.
266
292
  * Can be customized by overriding.
@@ -379,14 +405,14 @@ export const DataProviderMixin = (superClass) =>
379
405
  this._cache.updateSize();
380
406
  this._effectiveSize = this._cache.effectiveSize;
381
407
 
382
- Array.from(this.$.items.children)
383
- .filter((row) => !row.hidden)
384
- .forEach((row) => {
408
+ iterateChildren(this.$.items, (row) => {
409
+ if (!row.hidden) {
385
410
  const cachedItem = this._cache.getItemForIndex(row.index);
386
411
  if (cachedItem) {
387
412
  this._getItem(row.index, row);
388
413
  }
389
- });
414
+ }
415
+ });
390
416
 
391
417
  this.__scrollToPendingIndex();
392
418
  });
@@ -500,6 +526,7 @@ export const DataProviderMixin = (superClass) =>
500
526
  }
501
527
  }
502
528
 
529
+ /** @private */
503
530
  __scrollToPendingIndex() {
504
531
  if (this.__pendingScrollToIndex && this.$.items.children.length) {
505
532
  const index = this.__pendingScrollToIndex;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2016 - 2022 Vaadin Ltd.
3
+ * Copyright (c) 2016 - 2023 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,8 +1,10 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2016 - 2022 Vaadin Ltd.
3
+ * Copyright (c) 2016 - 2023 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
+ import { iterateChildren, updateRowStates } from './vaadin-grid-helpers.js';
7
+
6
8
  const DropMode = {
7
9
  BETWEEN: 'between',
8
10
  ON_TOP: 'on-top',
@@ -126,7 +128,7 @@ export const DragAndDropMixin = (superClass) =>
126
128
  // Safari doesn't position drag images from transformed
127
129
  // elements properly so we need to switch to use top temporarily
128
130
  const transform = row.style.transform;
129
- row.style.top = /translateY\((.*)\)/.exec(transform)[1];
131
+ row.style.top = /translateY\((.*)\)/u.exec(transform)[1];
130
132
  row.style.transform = 'none';
131
133
  requestAnimationFrame(() => {
132
134
  row.style.top = '';
@@ -154,13 +156,14 @@ export const DragAndDropMixin = (superClass) =>
154
156
  // Set the default transfer data
155
157
  e.dataTransfer.setData('text', this.__formatDefaultTransferData(rows));
156
158
 
157
- row.setAttribute('dragstart', rows.length > 1 ? rows.length : '');
159
+ updateRowStates(row, { dragstart: rows.length > 1 ? `${rows.length}` : '' });
158
160
  this.style.setProperty('--_grid-drag-start-x', `${e.clientX - rowRect.left + 20}px`);
159
161
  this.style.setProperty('--_grid-drag-start-y', `${e.clientY - rowRect.top + 10}px`);
160
162
 
161
163
  requestAnimationFrame(() => {
162
- row.removeAttribute('dragstart');
163
- this.updateStyles({ '--_grid-drag-start-x': '', '--_grid-drag-start-y': '' });
164
+ updateRowStates(row, { dragstart: null });
165
+ this.style.setProperty('--_grid-drag-start-x', '');
166
+ this.style.setProperty('--_grid-drag-start-y', '');
164
167
  });
165
168
 
166
169
  const event = new CustomEvent('grid-dragstart', {
@@ -252,7 +255,7 @@ export const DragAndDropMixin = (superClass) =>
252
255
  } else if (row) {
253
256
  this._dragOverItem = row._item;
254
257
  if (row.getAttribute('dragover') !== this._dropLocation) {
255
- row.setAttribute('dragover', this._dropLocation);
258
+ updateRowStates(row, { dragover: this._dropLocation }, true);
256
259
  }
257
260
  } else {
258
261
  this._clearDragStyles();
@@ -306,7 +309,9 @@ export const DragAndDropMixin = (superClass) =>
306
309
  /** @protected */
307
310
  _clearDragStyles() {
308
311
  this.removeAttribute('dragover');
309
- Array.from(this.$.items.children).forEach((row) => row.removeAttribute('dragover'));
312
+ iterateChildren(this.$.items, (row) => {
313
+ updateRowStates(row, { dragover: null }, true);
314
+ });
310
315
  }
311
316
 
312
317
  /** @private */
@@ -368,11 +373,11 @@ export const DragAndDropMixin = (superClass) =>
368
373
  * the conditions change.
369
374
  */
370
375
  filterDragAndDrop() {
371
- Array.from(this.$.items.children)
372
- .filter((row) => !row.hidden)
373
- .forEach((row) => {
376
+ iterateChildren(this.$.items, (row) => {
377
+ if (!row.hidden) {
374
378
  this._filterDragAndDrop(row, this.__getRowModel(row));
375
- });
379
+ }
380
+ });
376
381
  }
377
382
 
378
383
  /**
@@ -385,18 +390,18 @@ export const DragAndDropMixin = (superClass) =>
385
390
  const dragDisabled = !this.rowsDraggable || loading || (this.dragFilter && !this.dragFilter(model));
386
391
  const dropDisabled = !this.dropMode || loading || (this.dropFilter && !this.dropFilter(model));
387
392
 
388
- const draggableElements = Array.from(row.children).map((cell) => cell._content);
389
-
390
- draggableElements.forEach((e) => {
393
+ iterateChildren(row, (cell) => {
391
394
  if (dragDisabled) {
392
- e.removeAttribute('draggable');
395
+ cell._content.removeAttribute('draggable');
393
396
  } else {
394
- e.setAttribute('draggable', true);
397
+ cell._content.setAttribute('draggable', true);
395
398
  }
396
399
  });
397
400
 
398
- row.toggleAttribute('drag-disabled', !!dragDisabled);
399
- row.toggleAttribute('drop-disabled', !!dropDisabled);
401
+ updateRowStates(row, {
402
+ 'drag-disabled': !!dragDisabled,
403
+ 'drop-disabled': !!dropDisabled,
404
+ });
400
405
  }
401
406
 
402
407
  /**
@@ -1,12 +1,13 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2016 - 2022 Vaadin Ltd.
3
+ * Copyright (c) 2016 - 2023 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 { FlattenedNodesObserver } from '@polymer/polymer/lib/utils/flattened-nodes-observer.js';
7
7
  import { PolymerElement } from '@polymer/polymer/polymer-element.js';
8
8
  import { timeOut } from '@vaadin/component-base/src/async.js';
9
9
  import { Debouncer } from '@vaadin/component-base/src/debounce.js';
10
+ import { updateCellState } from './vaadin-grid-helpers.js';
10
11
 
11
12
  function arrayEquals(arr1, arr2) {
12
13
  if (!arr1 || !arr2 || arr1.length !== arr2.length) {
@@ -160,8 +161,8 @@ export const DynamicColumnsMixin = (superClass) =>
160
161
  return a._column._order - b._column._order;
161
162
  })
162
163
  .forEach((cell, cellIndex, children) => {
163
- cell.toggleAttribute('first-column', cellIndex === 0);
164
- cell.toggleAttribute('last-column', cellIndex === children.length - 1);
164
+ updateCellState(cell, 'first-column', cellIndex === 0);
165
+ updateCellState(cell, 'last-column', cellIndex === children.length - 1);
165
166
  });
166
167
  }
167
168
 
@@ -171,6 +172,6 @@ export const DynamicColumnsMixin = (superClass) =>
171
172
  * @protected
172
173
  */
173
174
  _isColumnElement(node) {
174
- return node.nodeType === Node.ELEMENT_NODE && /\bcolumn\b/.test(node.localName);
175
+ return node.nodeType === Node.ELEMENT_NODE && /\bcolumn\b/u.test(node.localName);
175
176
  }
176
177
  };
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2016 - 2022 Vaadin Ltd.
3
+ * Copyright (c) 2016 - 2023 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) 2016 - 2022 Vaadin Ltd.
3
+ * Copyright (c) 2016 - 2023 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) 2016 - 2022 Vaadin Ltd.
3
+ * Copyright (c) 2016 - 2023 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 { GridDefaultItem } from './vaadin-grid.js';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2016 - 2022 Vaadin Ltd.
3
+ * Copyright (c) 2016 - 2023 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 './vaadin-grid-filter.js';
@@ -60,7 +60,6 @@ class GridFilterColumn extends GridColumn {
60
60
  if (!filter) {
61
61
  filter = document.createElement('vaadin-grid-filter');
62
62
  textField = document.createElement('vaadin-text-field');
63
- textField.setAttribute('slot', 'filter');
64
63
  textField.setAttribute('theme', 'small');
65
64
  textField.setAttribute('style', 'max-width: 100%;');
66
65
  textField.setAttribute('focus-target', '');
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2016 - 2022 Vaadin Ltd.
3
+ * Copyright (c) 2016 - 2023 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,8 +1,9 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2016 - 2022 Vaadin Ltd.
3
+ * Copyright (c) 2016 - 2023 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
+ import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
6
7
 
7
8
  /**
8
9
  * Fired when the `value` property changes.
@@ -40,7 +41,7 @@ export interface GridFilterEventMap extends HTMLElementEventMap, GridFilterCusto
40
41
  *
41
42
  * @fires {CustomEvent} value-changed - Fired when the `value` property changes.
42
43
  */
43
- declare class GridFilter extends HTMLElement {
44
+ declare class GridFilter extends ControllerMixin(HTMLElement) {
44
45
  /**
45
46
  * JS Path of the property in the item used for filtering the data.
46
47
  */
@@ -1,12 +1,14 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2016 - 2022 Vaadin Ltd.
3
+ * Copyright (c) 2016 - 2023 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 '@vaadin/text-field/src/vaadin-text-field.js';
7
7
  import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
8
8
  import { timeOut } from '@vaadin/component-base/src/async.js';
9
+ import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
9
10
  import { Debouncer } from '@vaadin/component-base/src/debounce.js';
11
+ import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
10
12
 
11
13
  /**
12
14
  * `<vaadin-grid-filter>` is a helper element for the `<vaadin-grid>` that provides out-of-the-box UI controls,
@@ -35,7 +37,7 @@ import { Debouncer } from '@vaadin/component-base/src/debounce.js';
35
37
  *
36
38
  * @extends HTMLElement
37
39
  */
38
- class GridFilter extends class extends PolymerElement {} {
40
+ class GridFilter extends ControllerMixin(PolymerElement) {
39
41
  static get template() {
40
42
  return html`
41
43
  <style>
@@ -44,14 +46,12 @@ class GridFilter extends class extends PolymerElement {} {
44
46
  max-width: 100%;
45
47
  }
46
48
 
47
- #filter {
49
+ ::slotted(*) {
48
50
  width: 100%;
49
51
  box-sizing: border-box;
50
52
  }
51
53
  </style>
52
- <slot name="filter">
53
- <vaadin-text-field id="filter" value="{{value}}"></vaadin-text-field>
54
- </slot>
54
+ <slot></slot>
55
55
  `;
56
56
  }
57
57
 
@@ -75,39 +75,42 @@ class GridFilter extends class extends PolymerElement {} {
75
75
  },
76
76
 
77
77
  /** @private */
78
- _connected: Boolean,
78
+ _textField: {
79
+ type: Object,
80
+ },
79
81
  };
80
82
  }
81
83
 
82
- /** @protected */
83
- connectedCallback() {
84
- super.connectedCallback();
85
- this._connected = true;
86
- }
87
-
88
84
  static get observers() {
89
- return ['_filterChanged(path, value, _connected)'];
85
+ return ['_filterChanged(path, value, _textField)'];
90
86
  }
91
87
 
92
88
  /** @protected */
93
89
  ready() {
94
90
  super.ready();
95
91
 
96
- const child = this.firstElementChild;
97
- if (child && child.getAttribute('slot') !== 'filter') {
98
- console.warn('Make sure you have assigned slot="filter" to the child elements of <vaadin-grid-filter>');
99
- child.setAttribute('slot', 'filter');
100
- }
92
+ this._filterController = new SlotController(this, '', 'vaadin-text-field', {
93
+ initializer: (field) => {
94
+ field.addEventListener('value-changed', (e) => {
95
+ this.value = e.detail.value;
96
+ });
97
+
98
+ this._textField = field;
99
+ },
100
+ });
101
+ this.addController(this._filterController);
101
102
  }
102
103
 
103
104
  /** @private */
104
- _filterChanged(path, value, connected) {
105
- if (path === undefined || value === undefined || !connected) {
105
+ _filterChanged(path, value, textField) {
106
+ if (path === undefined || value === undefined || !textField) {
106
107
  return;
107
108
  }
108
109
  if (this._previousValue === undefined && value === '') {
109
110
  return;
110
111
  }
112
+
113
+ textField.value = value;
111
114
  this._previousValue = value;
112
115
 
113
116
  this._debouncerFilterChanged = Debouncer.debounce(this._debouncerFilterChanged, timeOut.after(200), () => {
@@ -116,7 +119,9 @@ class GridFilter extends class extends PolymerElement {} {
116
119
  }
117
120
 
118
121
  focus() {
119
- this.$.filter.focus();
122
+ if (this._textField) {
123
+ this._textField.focus();
124
+ }
120
125
  }
121
126
  }
122
127
 
@@ -1,8 +1,25 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2016 - 2022 Vaadin Ltd.
3
+ * Copyright (c) 2016 - 2023 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
+ import { addValueToAttribute, removeValueFromAttribute } from '@vaadin/component-base/src/dom-utils.js';
7
+
8
+ /**
9
+ * @param {HTMLTableRowElement} row the table row
10
+ * @return {HTMLTableCellElement[]} array of cells
11
+ */
12
+ export function getBodyRowCells(row) {
13
+ return Array.from(row.querySelectorAll('[part~="cell"]:not([part~="details-cell"])'));
14
+ }
15
+
16
+ /**
17
+ * @param {HTMLElement} container the DOM element with children
18
+ * @param {Function} callback function to call on each child
19
+ */
20
+ export function iterateChildren(container, callback) {
21
+ [...container.children].forEach(callback);
22
+ }
6
23
 
7
24
  /**
8
25
  * @param {Array<Object>} columns array of columns to be modified
@@ -21,3 +38,89 @@ export function updateColumnOrders(columns, scope, baseOrder) {
21
38
  c += 1;
22
39
  });
23
40
  }
41
+
42
+ /**
43
+ * @param {!HTMLElement} element
44
+ * @param {string} attribute
45
+ * @param {boolean | string | null | undefined} value
46
+ */
47
+ export function updateState(element, attribute, value) {
48
+ switch (typeof value) {
49
+ case 'boolean':
50
+ element.toggleAttribute(attribute, value);
51
+ break;
52
+ case 'string':
53
+ element.setAttribute(attribute, value);
54
+ break;
55
+ default:
56
+ // Value set to null / undefined
57
+ element.removeAttribute(attribute);
58
+ break;
59
+ }
60
+ }
61
+
62
+ /**
63
+ * @param {!HTMLElement} element
64
+ * @param {boolean | string | null | undefined} value
65
+ * @param {string} part
66
+ */
67
+ export function updatePart(element, value, part) {
68
+ if (value || value === '') {
69
+ addValueToAttribute(element, 'part', part);
70
+ } else {
71
+ removeValueFromAttribute(element, 'part', part);
72
+ }
73
+ }
74
+
75
+ /**
76
+ * @param {HTMLTableCellElement[]} cells
77
+ * @param {string} part
78
+ * @param {boolean | string | null | undefined} value
79
+ */
80
+ export function updateCellsPart(cells, part, value) {
81
+ cells.forEach((cell) => {
82
+ updatePart(cell, value, part);
83
+ });
84
+ }
85
+
86
+ /**
87
+ * @param {!HTMLElement} row
88
+ * @param {Object} states
89
+ * @param {boolean} appendValue
90
+ */
91
+ export function updateRowStates(row, states, appendValue) {
92
+ const cells = getBodyRowCells(row);
93
+
94
+ Object.entries(states).forEach(([state, value]) => {
95
+ // Row state attribute
96
+ updateState(row, state, value);
97
+
98
+ const rowPart = appendValue ? `${state}-${value}-row` : `${state}-row`;
99
+
100
+ // Row part attribute
101
+ updatePart(row, value, rowPart);
102
+
103
+ // Cells part attribute
104
+ updateCellsPart(cells, `${rowPart}-cell`, value);
105
+ });
106
+ }
107
+
108
+ /**
109
+ * @param {!HTMLElement} cell
110
+ * @param {string} attribute
111
+ * @param {boolean | string | null | undefined} value
112
+ * @param {string} part
113
+ * @param {?string} oldPart
114
+ */
115
+ export function updateCellState(cell, attribute, value, part, oldPart) {
116
+ // Toggle state attribute on the cell
117
+ updateState(cell, attribute, value);
118
+
119
+ // Remove old part from the attribute
120
+ if (oldPart) {
121
+ updatePart(cell, false, oldPart);
122
+ }
123
+
124
+ // Add new part to the cell attribute
125
+ updatePart(cell, value, part || `${attribute}-cell`);
126
+ }