@vaadin/grid 24.2.0-alpha4 → 24.2.0-alpha6

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/grid",
3
- "version": "24.2.0-alpha4",
3
+ "version": "24.2.0-alpha6",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -46,18 +46,18 @@
46
46
  "dependencies": {
47
47
  "@open-wc/dedupe-mixin": "^1.3.0",
48
48
  "@polymer/polymer": "^3.0.0",
49
- "@vaadin/a11y-base": "24.2.0-alpha4",
50
- "@vaadin/checkbox": "24.2.0-alpha4",
51
- "@vaadin/component-base": "24.2.0-alpha4",
52
- "@vaadin/lit-renderer": "24.2.0-alpha4",
53
- "@vaadin/text-field": "24.2.0-alpha4",
54
- "@vaadin/vaadin-lumo-styles": "24.2.0-alpha4",
55
- "@vaadin/vaadin-material-styles": "24.2.0-alpha4",
56
- "@vaadin/vaadin-themable-mixin": "24.2.0-alpha4"
49
+ "@vaadin/a11y-base": "24.2.0-alpha6",
50
+ "@vaadin/checkbox": "24.2.0-alpha6",
51
+ "@vaadin/component-base": "24.2.0-alpha6",
52
+ "@vaadin/lit-renderer": "24.2.0-alpha6",
53
+ "@vaadin/text-field": "24.2.0-alpha6",
54
+ "@vaadin/vaadin-lumo-styles": "24.2.0-alpha6",
55
+ "@vaadin/vaadin-material-styles": "24.2.0-alpha6",
56
+ "@vaadin/vaadin-themable-mixin": "24.2.0-alpha6"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@esm-bundle/chai": "^4.3.4",
60
- "@vaadin/testing-helpers": "^0.4.2",
60
+ "@vaadin/testing-helpers": "^0.4.3",
61
61
  "lit": "^2.0.0",
62
62
  "sinon": "^13.0.2"
63
63
  },
@@ -65,5 +65,5 @@
65
65
  "web-types.json",
66
66
  "web-types.lit.json"
67
67
  ],
68
- "gitHead": "aaf7c5ebfea62628210eead4229be1718ac6b129"
68
+ "gitHead": "3ef6e6cd66919b3ef7637e42916e4c54656beb51"
69
69
  }
@@ -4,9 +4,8 @@
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  /* eslint-disable max-classes-per-file */
7
- import type { TemplateResult } from 'lit';
8
7
  import type { DirectiveResult } from 'lit/directive';
9
- import type { LitRenderer } from '@vaadin/lit-renderer';
8
+ import type { LitRenderer, LitRendererResult } from '@vaadin/lit-renderer';
10
9
  import { LitRendererDirective } from '@vaadin/lit-renderer';
11
10
  import type { GridItemModel } from '../vaadin-grid.js';
12
11
  import type { GridColumn } from '../vaadin-grid-column.js';
@@ -15,10 +14,10 @@ export type GridColumnBodyLitRenderer<TItem> = (
15
14
  item: TItem,
16
15
  model: GridItemModel<TItem>,
17
16
  column: GridColumn,
18
- ) => TemplateResult;
17
+ ) => LitRendererResult;
19
18
 
20
- export type GridColumnHeaderLitRenderer = (column: GridColumn) => TemplateResult;
21
- export type GridColumnFooterLitRenderer = (column: GridColumn) => TemplateResult;
19
+ export type GridColumnHeaderLitRenderer = (column: GridColumn) => LitRendererResult;
20
+ export type GridColumnFooterLitRenderer = (column: GridColumn) => LitRendererResult;
22
21
 
23
22
  declare abstract class AbstractGridColumnRendererDirective<R extends LitRenderer> extends LitRendererDirective<
24
23
  GridColumn,
@@ -3,12 +3,16 @@
3
3
  * Copyright (c) 2017 - 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 type { TemplateResult } from 'lit';
7
6
  import type { DirectiveResult } from 'lit/directive';
7
+ import type { LitRendererResult } from '@vaadin/lit-renderer';
8
8
  import { LitRendererDirective } from '@vaadin/lit-renderer';
9
9
  import type { Grid, GridItemModel } from '../vaadin-grid.js';
10
10
 
11
- export type GridRowDetailsLitRenderer<TItem> = (item: TItem, model: GridItemModel<TItem>, grid: Grid) => TemplateResult;
11
+ export type GridRowDetailsLitRenderer<TItem> = (
12
+ item: TItem,
13
+ model: GridItemModel<TItem>,
14
+ grid: Grid,
15
+ ) => LitRendererResult;
12
16
 
13
17
  export declare class GridRowDetailsRendererDirective<TItem> extends LitRendererDirective<
14
18
  Grid,
@@ -43,6 +43,17 @@ export declare class ColumnBaseMixinClass<TItem> {
43
43
  */
44
44
  frozenToEnd: boolean;
45
45
 
46
+ /**
47
+ * When true, the cells for this column will be rendered with the `role` attribute
48
+ * set as `rowheader`, instead of the `gridcell` role value used by default.
49
+ *
50
+ * When a column is set as row header, its cells will be announced by screen readers
51
+ * while navigating to help user identify the current row as uniquely as possible.
52
+ *
53
+ * @attr {boolean} row-header
54
+ */
55
+ rowHeader: boolean;
56
+
46
57
  /**
47
58
  * When set to true, the cells for this column are hidden.
48
59
  */
@@ -61,6 +61,21 @@ export const ColumnBaseMixin = (superClass) =>
61
61
  value: false,
62
62
  },
63
63
 
64
+ /**
65
+ * When true, the cells for this column will be rendered with the `role` attribute
66
+ * set as `rowheader`, instead of the `gridcell` role value used by default.
67
+ *
68
+ * When a column is set as row header, its cells will be announced by screen readers
69
+ * while navigating to help user identify the current row as uniquely as possible.
70
+ *
71
+ * @attr {boolean} row-header
72
+ * @type {boolean}
73
+ */
74
+ rowHeader: {
75
+ type: Boolean,
76
+ value: false,
77
+ },
78
+
64
79
  /**
65
80
  * When set to true, the cells for this column are hidden.
66
81
  */
@@ -222,6 +237,7 @@ export const ColumnBaseMixin = (superClass) =>
222
237
  '_resizableChanged(resizable, _headerCell)',
223
238
  '_reorderStatusChanged(_reorderStatus, _headerCell, _footerCell, _cells.*)',
224
239
  '_hiddenChanged(hidden, _headerCell, _footerCell, _cells.*)',
240
+ '_rowHeaderChanged(rowHeader, _cells.*)',
225
241
  ];
226
242
  }
227
243
 
@@ -407,6 +423,17 @@ export const ColumnBaseMixin = (superClass) =>
407
423
  }
408
424
  }
409
425
 
426
+ /** @private */
427
+ _rowHeaderChanged(rowHeader, cells) {
428
+ if (!cells.value) {
429
+ return;
430
+ }
431
+
432
+ cells.value.forEach((cell) => {
433
+ cell.setAttribute('role', rowHeader ? 'rowheader' : 'gridcell');
434
+ });
435
+ }
436
+
410
437
  /**
411
438
  * @param {string} path
412
439
  * @return {string}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2016 - 2023 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import type { Constructor } from '@open-wc/dedupe-mixin';
7
+
8
+ export declare function GridSelectionColumnBaseMixin<TItem, T extends Constructor<HTMLElement>>(
9
+ base: T,
10
+ ): Constructor<GridSelectionColumnBaseMixinClass<TItem>> & T;
11
+
12
+ /**
13
+ * A mixin that provides basic functionality for the
14
+ * `<vaadin-grid-selection-column>`. This includes properties, cell rendering,
15
+ * and overridable methods for handling changes to the selection state.
16
+ *
17
+ * **NOTE**: This mixin is re-used by the Flow component, and as such must not
18
+ * implement any selection state updates for the column element or the grid.
19
+ * Web component-specific selection state updates must be implemented in the
20
+ * `<vaadin-grid-selection-column>` itself, by overriding the protected methods
21
+ * provided by this mixin.
22
+ *
23
+ * @polymerMixin
24
+ */
25
+ export declare class GridSelectionColumnBaseMixinClass<TItem> {
26
+ /**
27
+ * When true, all the items are selected.
28
+ * @attr {boolean} select-all
29
+ */
30
+ selectAll: boolean;
31
+
32
+ /**
33
+ * When true, the active gets automatically selected.
34
+ * @attr {boolean} auto-select
35
+ */
36
+ autoSelect: boolean;
37
+
38
+ /**
39
+ * When true, rows can be selected by dragging over the selection column.
40
+ * @attr {boolean} drag-select
41
+ */
42
+ dragSelect: boolean;
43
+
44
+ /**
45
+ * Override to handle the user selecting all items.
46
+ */
47
+ protected _selectAll(): void;
48
+
49
+ /**
50
+ * Override to handle the user deselecting all items.
51
+ */
52
+ protected _deselectAll(): void;
53
+
54
+ /**
55
+ * Override to handle the user selecting an item.
56
+ */
57
+ protected _selectItem(item: TItem): void;
58
+
59
+ /**
60
+ * Override to handle the user deselecting an item.
61
+ */
62
+ protected _deselectItem(item: TItem): void;
63
+ }
@@ -0,0 +1,342 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2016 - 2023 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import { addListener } from '@vaadin/component-base/src/gestures.js';
7
+
8
+ /**
9
+ * A mixin that provides basic functionality for the
10
+ * `<vaadin-grid-selection-column>`. This includes properties, cell rendering,
11
+ * and overridable methods for handling changes to the selection state.
12
+ *
13
+ * **NOTE**: This mixin is re-used by the Flow component, and as such must not
14
+ * implement any selection state updates for the column element or the grid.
15
+ * Web component-specific selection state updates must be implemented in the
16
+ * `<vaadin-grid-selection-column>` itself, by overriding the protected methods
17
+ * provided by this mixin.
18
+ *
19
+ * @polymerMixin
20
+ */
21
+ export const GridSelectionColumnBaseMixin = (superClass) =>
22
+ class GridSelectionColumnBaseMixin extends superClass {
23
+ static get properties() {
24
+ return {
25
+ /**
26
+ * Width of the cells for this column.
27
+ */
28
+ width: {
29
+ type: String,
30
+ value: '58px',
31
+ },
32
+
33
+ /**
34
+ * Flex grow ratio for the cell widths. When set to 0, cell width is fixed.
35
+ * @attr {number} flex-grow
36
+ * @type {number}
37
+ */
38
+ flexGrow: {
39
+ type: Number,
40
+ value: 0,
41
+ },
42
+
43
+ /**
44
+ * When true, all the items are selected.
45
+ * @attr {boolean} select-all
46
+ * @type {boolean}
47
+ */
48
+ selectAll: {
49
+ type: Boolean,
50
+ value: false,
51
+ notify: true,
52
+ },
53
+
54
+ /**
55
+ * When true, the active gets automatically selected.
56
+ * @attr {boolean} auto-select
57
+ * @type {boolean}
58
+ */
59
+ autoSelect: {
60
+ type: Boolean,
61
+ value: false,
62
+ },
63
+
64
+ /**
65
+ * When true, rows can be selected by dragging over the selection column.
66
+ * @attr {boolean} drag-select
67
+ * @type {boolean}
68
+ */
69
+ dragSelect: {
70
+ type: Boolean,
71
+ value: false,
72
+ },
73
+
74
+ /** @protected */
75
+ _indeterminate: Boolean,
76
+
77
+ /** @protected */
78
+ _selectAllHidden: Boolean,
79
+ };
80
+ }
81
+
82
+ static get observers() {
83
+ return [
84
+ '_onHeaderRendererOrBindingChanged(_headerRenderer, _headerCell, path, header, selectAll, _indeterminate, _selectAllHidden)',
85
+ ];
86
+ }
87
+
88
+ /**
89
+ * Renders the Select All checkbox to the header cell.
90
+ *
91
+ * @override
92
+ */
93
+ _defaultHeaderRenderer(root, _column) {
94
+ let checkbox = root.firstElementChild;
95
+ if (!checkbox) {
96
+ checkbox = document.createElement('vaadin-checkbox');
97
+ checkbox.setAttribute('aria-label', 'Select All');
98
+ checkbox.classList.add('vaadin-grid-select-all-checkbox');
99
+ root.appendChild(checkbox);
100
+ // Add listener after appending, so we can skip the initial change event
101
+ checkbox.addEventListener('checked-changed', this.__onSelectAllCheckedChanged.bind(this));
102
+ }
103
+
104
+ const checked = this.__isChecked(this.selectAll, this._indeterminate);
105
+ checkbox.__rendererChecked = checked;
106
+ checkbox.checked = checked;
107
+ checkbox.hidden = this._selectAllHidden;
108
+ checkbox.indeterminate = this._indeterminate;
109
+ }
110
+
111
+ /**
112
+ * Renders the Select Row checkbox to the body cell.
113
+ *
114
+ * @override
115
+ */
116
+ _defaultRenderer(root, _column, { item, selected }) {
117
+ let checkbox = root.firstElementChild;
118
+ if (!checkbox) {
119
+ checkbox = document.createElement('vaadin-checkbox');
120
+ checkbox.setAttribute('aria-label', 'Select Row');
121
+ root.appendChild(checkbox);
122
+ // Add listener after appending, so we can skip the initial change event
123
+ checkbox.addEventListener('checked-changed', this.__onSelectRowCheckedChanged.bind(this));
124
+ addListener(root, 'track', this.__onCellTrack.bind(this));
125
+ root.addEventListener('mousedown', this.__onCellMouseDown.bind(this));
126
+ root.addEventListener('click', this.__onCellClick.bind(this));
127
+ }
128
+
129
+ checkbox.__item = item;
130
+ checkbox.__rendererChecked = selected;
131
+ checkbox.checked = selected;
132
+ }
133
+
134
+ /**
135
+ * Updates the select all state when the Select All checkbox is switched.
136
+ * The listener handles only user-fired events.
137
+ *
138
+ * @private
139
+ */
140
+ __onSelectAllCheckedChanged(e) {
141
+ // Skip if the state is changed by the renderer.
142
+ if (e.target.checked === e.target.__rendererChecked) {
143
+ return;
144
+ }
145
+
146
+ if (this._indeterminate || e.target.checked) {
147
+ this._selectAll();
148
+ } else {
149
+ this._deselectAll();
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Selects or deselects the row when the Select Row checkbox is switched.
155
+ * The listener handles only user-fired events.
156
+ *
157
+ * @private
158
+ */
159
+ __onSelectRowCheckedChanged(e) {
160
+ // Skip if the state is changed by the renderer.
161
+ if (e.target.checked === e.target.__rendererChecked) {
162
+ return;
163
+ }
164
+
165
+ if (e.target.checked) {
166
+ this._selectItem(e.target.__item);
167
+ } else {
168
+ this._deselectItem(e.target.__item);
169
+ }
170
+ }
171
+
172
+ /** @private */
173
+ __onCellTrack(event) {
174
+ if (!this.dragSelect) {
175
+ return;
176
+ }
177
+ this.__dragCurrentY = event.detail.y;
178
+ this.__dragDy = event.detail.dy;
179
+ if (event.detail.state === 'start') {
180
+ const renderedRows = this._grid._getRenderedRows();
181
+ // Get the row where the drag started
182
+ const dragStartRow = renderedRows.find((row) => row.contains(event.currentTarget.assignedSlot));
183
+ // Whether to select or deselect the items on drag
184
+ this.__dragSelect = !this._grid._isSelected(dragStartRow._item);
185
+ // Store the index of the row where the drag started
186
+ this.__dragStartIndex = dragStartRow.index;
187
+ // Store the item of the row where the drag started
188
+ this.__dragStartItem = dragStartRow._item;
189
+ // Start the auto scroller
190
+ this.__dragAutoScroller();
191
+ } else if (event.detail.state === 'end') {
192
+ // if drag start and end stays within the same item, then toggle its state
193
+ if (this.__dragStartItem) {
194
+ if (this.__dragSelect) {
195
+ this._selectItem(this.__dragStartItem);
196
+ } else {
197
+ this._deselectItem(this.__dragStartItem);
198
+ }
199
+ }
200
+ // clear drag state after timeout, which allows preventing the
201
+ // subsequent click event if drag started and ended on the same item
202
+ setTimeout(() => {
203
+ this.__dragStartIndex = undefined;
204
+ });
205
+ }
206
+ }
207
+
208
+ /** @private */
209
+ __onCellMouseDown(e) {
210
+ if (this.dragSelect) {
211
+ // Prevent text selection when starting to drag
212
+ e.preventDefault();
213
+ }
214
+ }
215
+
216
+ /** @private */
217
+ __onCellClick(e) {
218
+ if (this.__dragStartIndex !== undefined) {
219
+ // Stop the click event if drag was enabled. This click event should
220
+ // only occur if drag started and stopped on the same item. In that case
221
+ // the selection state has already been toggled on drag end, and we
222
+ // don't want to toggle it again from clicking the checkbox or changing
223
+ // the active item.
224
+ e.preventDefault();
225
+ }
226
+ }
227
+
228
+ /** @private */
229
+ __dragAutoScroller() {
230
+ if (this.__dragStartIndex === undefined) {
231
+ return;
232
+ }
233
+ // Get the row being hovered over
234
+ const renderedRows = this._grid._getRenderedRows();
235
+ const hoveredRow = renderedRows.find((row) => {
236
+ const rowRect = row.getBoundingClientRect();
237
+ return this.__dragCurrentY >= rowRect.top && this.__dragCurrentY <= rowRect.bottom;
238
+ });
239
+
240
+ // Get the index of the row being hovered over or the first/last
241
+ // visible row if hovering outside the grid
242
+ let hoveredIndex = hoveredRow ? hoveredRow.index : undefined;
243
+ const scrollableArea = this.__getScrollableArea();
244
+ if (this.__dragCurrentY < scrollableArea.top) {
245
+ hoveredIndex = this._grid._firstVisibleIndex;
246
+ } else if (this.__dragCurrentY > scrollableArea.bottom) {
247
+ hoveredIndex = this._grid._lastVisibleIndex;
248
+ }
249
+
250
+ if (hoveredIndex !== undefined) {
251
+ // Select all items between the start and the current row
252
+ renderedRows.forEach((row) => {
253
+ if (
254
+ (hoveredIndex > this.__dragStartIndex && row.index >= this.__dragStartIndex && row.index <= hoveredIndex) ||
255
+ (hoveredIndex < this.__dragStartIndex && row.index <= this.__dragStartIndex && row.index >= hoveredIndex)
256
+ ) {
257
+ if (this.__dragSelect) {
258
+ this._selectItem(row._item);
259
+ } else {
260
+ this._deselectItem(row._item);
261
+ }
262
+ this.__dragStartItem = undefined;
263
+ }
264
+ });
265
+ }
266
+
267
+ // Start scrolling in the top/bottom 15% of the scrollable area
268
+ const scrollTriggerArea = scrollableArea.height * 0.15;
269
+ // Maximum number of pixels to scroll per iteration
270
+ const maxScrollAmount = 10;
271
+
272
+ if (this.__dragDy < 0 && this.__dragCurrentY < scrollableArea.top + scrollTriggerArea) {
273
+ const dy = scrollableArea.top + scrollTriggerArea - this.__dragCurrentY;
274
+ const percentage = Math.min(1, dy / scrollTriggerArea);
275
+ this._grid.$.table.scrollTop -= percentage * maxScrollAmount;
276
+ }
277
+ if (this.__dragDy > 0 && this.__dragCurrentY > scrollableArea.bottom - scrollTriggerArea) {
278
+ const dy = this.__dragCurrentY - (scrollableArea.bottom - scrollTriggerArea);
279
+ const percentage = Math.min(1, dy / scrollTriggerArea);
280
+ this._grid.$.table.scrollTop += percentage * maxScrollAmount;
281
+ }
282
+
283
+ // Schedule the next auto scroll
284
+ setTimeout(() => this.__dragAutoScroller(), 10);
285
+ }
286
+
287
+ /**
288
+ * Gets the scrollable area of the grid as a bounding client rect. The
289
+ * scrollable area is the bounding rect of the grid minus the header and
290
+ * footer.
291
+ *
292
+ * @private
293
+ */
294
+ __getScrollableArea() {
295
+ const gridRect = this._grid.$.table.getBoundingClientRect();
296
+ const headerRect = this._grid.$.header.getBoundingClientRect();
297
+ const footerRect = this._grid.$.footer.getBoundingClientRect();
298
+
299
+ return {
300
+ top: gridRect.top + headerRect.height,
301
+ bottom: gridRect.bottom - footerRect.height,
302
+ left: gridRect.left,
303
+ right: gridRect.right,
304
+ height: gridRect.height - headerRect.height - footerRect.height,
305
+ width: gridRect.width,
306
+ };
307
+ }
308
+
309
+ /**
310
+ * Override to handle the user selecting all items.
311
+ * @protected
312
+ */
313
+ _selectAll() {}
314
+
315
+ /**
316
+ * Override to handle the user deselecting all items.
317
+ * @protected
318
+ */
319
+ _deselectAll() {}
320
+
321
+ /**
322
+ * Override to handle the user selecting an item.
323
+ * @param {Object} item the item to select
324
+ * @protected
325
+ */
326
+ _selectItem(item) {}
327
+
328
+ /**
329
+ * Override to handle the user deselecting an item.
330
+ * @param {Object} item the item to deselect
331
+ * @protected
332
+ */
333
+ _deselectItem(item) {}
334
+
335
+ /**
336
+ * IOS needs indeterminate + checked at the same time
337
+ * @private
338
+ */
339
+ __isChecked(selectAll, indeterminate) {
340
+ return indeterminate || selectAll;
341
+ }
342
+ };
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import type { GridDefaultItem } from './vaadin-grid.js';
7
7
  import { GridColumn } from './vaadin-grid-column.js';
8
+ import type { GridSelectionColumnBaseMixinClass } from './vaadin-grid-selection-column-base-mixin.js';
8
9
 
9
10
  /**
10
11
  * Fired when the `selectAll` property changes.
@@ -42,18 +43,6 @@ export interface GridSelectionColumnEventMap extends HTMLElementEventMap, GridSe
42
43
  * @fires {CustomEvent} select-all-changed - Fired when the `selectAll` property changes.
43
44
  */
44
45
  declare class GridSelectionColumn<TItem = GridDefaultItem> extends GridColumn<TItem> {
45
- /**
46
- * When true, all the items are selected.
47
- * @attr {boolean} select-all
48
- */
49
- selectAll: boolean;
50
-
51
- /**
52
- * When true, the active gets automatically selected.
53
- * @attr {boolean} auto-select
54
- */
55
- autoSelect: boolean;
56
-
57
46
  addEventListener<K extends keyof GridSelectionColumnEventMap>(
58
47
  type: K,
59
48
  listener: (this: GridSelectionColumn<TItem>, ev: GridSelectionColumnEventMap[K]) => void,
@@ -67,6 +56,8 @@ declare class GridSelectionColumn<TItem = GridDefaultItem> extends GridColumn<TI
67
56
  ): void;
68
57
  }
69
58
 
59
+ interface GridSelectionColumn<TItem = GridDefaultItem> extends GridSelectionColumnBaseMixinClass<TItem> {}
60
+
70
61
  declare global {
71
62
  interface HTMLElementTagNameMap {
72
63
  'vaadin-grid-selection-column': GridSelectionColumn<GridDefaultItem>;