@vaadin/grid
Advanced tools
Comparing version 24.1.0-alpha1 to 24.1.0-alpha10
{ | ||
"name": "@vaadin/grid", | ||
"version": "24.1.0-alpha1", | ||
"version": "24.1.0-alpha10", | ||
"publishConfig": { | ||
@@ -49,10 +49,10 @@ "access": "public" | ||
"@polymer/polymer": "^3.0.0", | ||
"@vaadin/a11y-base": "24.1.0-alpha1", | ||
"@vaadin/checkbox": "24.1.0-alpha1", | ||
"@vaadin/component-base": "24.1.0-alpha1", | ||
"@vaadin/lit-renderer": "24.1.0-alpha1", | ||
"@vaadin/text-field": "24.1.0-alpha1", | ||
"@vaadin/vaadin-lumo-styles": "24.1.0-alpha1", | ||
"@vaadin/vaadin-material-styles": "24.1.0-alpha1", | ||
"@vaadin/vaadin-themable-mixin": "24.1.0-alpha1" | ||
"@vaadin/a11y-base": "24.1.0-alpha10", | ||
"@vaadin/checkbox": "24.1.0-alpha10", | ||
"@vaadin/component-base": "24.1.0-alpha10", | ||
"@vaadin/lit-renderer": "24.1.0-alpha10", | ||
"@vaadin/text-field": "24.1.0-alpha10", | ||
"@vaadin/vaadin-lumo-styles": "24.1.0-alpha10", | ||
"@vaadin/vaadin-material-styles": "24.1.0-alpha10", | ||
"@vaadin/vaadin-themable-mixin": "24.1.0-alpha10" | ||
}, | ||
@@ -69,3 +69,3 @@ "devDependencies": { | ||
], | ||
"gitHead": "599a339181595923b9ad6373d6888d8a79540141" | ||
"gitHead": "12e39be7eb3b49c68708e8ca3de2fb22e91051a1" | ||
} |
@@ -102,2 +102,11 @@ /** | ||
*/ | ||
_bodyContentHidden: { | ||
type: Boolean, | ||
value: false, | ||
}, | ||
/** | ||
* @type {boolean} | ||
* @protected | ||
*/ | ||
_firstFrozenToEnd: { | ||
@@ -211,3 +220,3 @@ type: Boolean, | ||
'_firstFrozenToEndChanged(_firstFrozenToEnd)', | ||
'_onRendererOrBindingChanged(_renderer, _cells, _cells.*, path)', | ||
'_onRendererOrBindingChanged(_renderer, _cells, _bodyContentHidden, _cells.*, path)', | ||
'_onHeaderRendererOrBindingChanged(_headerRenderer, _headerCell, path, header)', | ||
@@ -214,0 +223,0 @@ '_onFooterRendererOrBindingChanged(_footerRenderer, _footerCell)', |
@@ -57,2 +57,4 @@ /** | ||
getCacheAndIndex(index: number): { cache: ItemCache<TItem>; scaledIndex: number }; | ||
getFlatIndex(scaledIndex: number): number; | ||
} | ||
@@ -142,2 +144,17 @@ | ||
clearCache(): void; | ||
/** | ||
* Scroll to a specific row index in the virtual list. Note that the row index is | ||
* not always the same for any particular item. For example, sorting or filtering | ||
* items can affect the row index related to an item. | ||
* | ||
* The `indexes` parameter can be either a single number or multiple numbers. | ||
* The grid will first try to scroll to the item at the first index on the top level. | ||
* In case the item at the first index is expanded, the grid will then try scroll to the | ||
* item at the second index within the children of the expanded first item, and so on. | ||
* Each given index points to a child of the item at the previous index. | ||
* | ||
* Using `Infinity` as an index will point to the last item on the level. | ||
*/ | ||
scrollToIndex(...indexes: number[]): void; | ||
} |
@@ -8,3 +8,3 @@ /** | ||
import { Debouncer } from '@vaadin/component-base/src/debounce.js'; | ||
import { getBodyRowCells, iterateChildren, updateCellsPart, updateState } from './vaadin-grid-helpers.js'; | ||
import { getBodyRowCells, updateCellsPart, updateState } from './vaadin-grid-helpers.js'; | ||
@@ -100,2 +100,17 @@ /** | ||
} | ||
/** | ||
* Gets the scaled index as flattened index on this cache level. | ||
* In practice, this means that the effective size of any expanded | ||
* subcaches preceding the index are added to the value. | ||
* @param {number} scaledIndex | ||
* @return {number} The flat index on this cache level. | ||
*/ | ||
getFlatIndex(scaledIndex) { | ||
const clampedIndex = Math.max(0, Math.min(this.size - 1, scaledIndex)); | ||
return Object.entries(this.itemCaches).reduce((prev, [index, subCache]) => { | ||
return clampedIndex > Number(index) ? prev + subCache.effectiveSize : prev; | ||
}, clampedIndex); | ||
} | ||
}; | ||
@@ -387,4 +402,2 @@ | ||
const currentItems = Array.from(this.$.items.children).map((row) => row._item); | ||
// Populate the cache with new items | ||
@@ -394,5 +407,14 @@ items.forEach((item, itemsIndex) => { | ||
cache.items[itemIndex] = item; | ||
if (this._isExpanded(item) && currentItems.indexOf(item) > -1) { | ||
// Force synchronous data request for expanded item sub-cache | ||
cache.ensureSubCacheForScaledIndex(itemIndex); | ||
}); | ||
// With the new items added, update the cache size and the grid's effective size | ||
this._cache.updateSize(); | ||
this._effectiveSize = this._cache.effectiveSize; | ||
// After updating the cache, check if some of the expanded items should have sub-caches loaded | ||
this._getVisibleRows().forEach((row) => { | ||
const { cache, scaledIndex } = this._cache.getCacheAndIndex(row.index); | ||
const item = cache.items[scaledIndex]; | ||
if (item && this._isExpanded(item)) { | ||
cache.ensureSubCacheForScaledIndex(scaledIndex); | ||
} | ||
@@ -403,21 +425,20 @@ }); | ||
// Remove the pending request | ||
delete cache.pendingRequests[page]; | ||
// Schedule a debouncer to update the visible rows | ||
this._debouncerApplyCachedData = Debouncer.debounce(this._debouncerApplyCachedData, timeOut.after(0), () => { | ||
this._setLoading(false); | ||
this._cache.updateSize(); | ||
this._effectiveSize = this._cache.effectiveSize; | ||
iterateChildren(this.$.items, (row) => { | ||
if (!row.hidden) { | ||
const cachedItem = this._cache.getItemForIndex(row.index); | ||
if (cachedItem) { | ||
this._getItem(row.index, row); | ||
} | ||
this._getVisibleRows().forEach((row) => { | ||
const cachedItem = this._cache.getItemForIndex(row.index); | ||
if (cachedItem) { | ||
this._getItem(row.index, row); | ||
} | ||
}); | ||
this.__scrollToPendingIndex(); | ||
this.__scrollToPendingIndexes(); | ||
}); | ||
// If the grid is not loading anything, flush the debouncer immediately | ||
if (!this._cache.isLoading()) { | ||
@@ -427,2 +448,3 @@ this._debouncerApplyCachedData.flush(); | ||
// Notify that new data has been received | ||
this.__itemsReceived(); | ||
@@ -526,15 +548,61 @@ }); | ||
scrollToIndex(index) { | ||
super.scrollToIndex(index); | ||
if (!isNaN(index) && (this._cache.isLoading() || !this.clientHeight)) { | ||
this.__pendingScrollToIndex = index; | ||
/** | ||
* Scroll to a specific row index in the virtual list. Note that the row index is | ||
* not always the same for any particular item. For example, sorting or filtering | ||
* items can affect the row index related to an item. | ||
* | ||
* The `indexes` parameter can be either a single number or multiple numbers. | ||
* The grid will first try to scroll to the item at the first index on the top level. | ||
* In case the item at the first index is expanded, the grid will then try scroll to the | ||
* item at the second index within the children of the expanded first item, and so on. | ||
* Each given index points to a child of the item at the previous index. | ||
* | ||
* Using `Infinity` as an index will point to the last item on the level. | ||
* | ||
* @param indexes {...number} Row indexes to scroll to | ||
*/ | ||
scrollToIndex(...indexes) { | ||
// Synchronous data provider may cause changes to the cache on scroll without | ||
// ending up in a loading state. Try scrolling to the index until the target | ||
// index stabilizes. | ||
let targetIndex; | ||
while (targetIndex !== (targetIndex = this.__getGlobalFlatIndex(indexes))) { | ||
this._scrollToFlatIndex(targetIndex); | ||
} | ||
if (this._cache.isLoading() || !this.clientHeight) { | ||
this.__pendingScrollToIndexes = indexes; | ||
} | ||
} | ||
/** | ||
* Recursively returns the globally flat index of the item the given indexes point to. | ||
* Each index in the array points to a sub-item of the previous index. | ||
* Using `Infinity` as an index will point to the last item on the level. | ||
* | ||
* @param {!Array<number>} indexes | ||
* @param {!ItemCache} cache | ||
* @param {number} flatIndex | ||
* @return {number} | ||
* @private | ||
*/ | ||
__getGlobalFlatIndex([levelIndex, ...subIndexes], cache = this._cache, flatIndex = 0) { | ||
if (levelIndex === Infinity) { | ||
// Treat Infinity as the last index on the level | ||
levelIndex = cache.size - 1; | ||
} | ||
const flatIndexOnLevel = cache.getFlatIndex(levelIndex); | ||
const subCache = cache.itemCaches[levelIndex]; | ||
if (subCache && subCache.effectiveSize && subIndexes.length) { | ||
return this.__getGlobalFlatIndex(subIndexes, subCache, flatIndex + flatIndexOnLevel + 1); | ||
} | ||
return flatIndex + flatIndexOnLevel; | ||
} | ||
/** @private */ | ||
__scrollToPendingIndex() { | ||
if (this.__pendingScrollToIndex && this.$.items.children.length) { | ||
const index = this.__pendingScrollToIndex; | ||
delete this.__pendingScrollToIndex; | ||
this.scrollToIndex(index); | ||
__scrollToPendingIndexes() { | ||
if (this.__pendingScrollToIndexes && this.$.items.children.length) { | ||
const indexes = this.__pendingScrollToIndexes; | ||
delete this.__pendingScrollToIndexes; | ||
this.scrollToIndex(...indexes); | ||
} | ||
@@ -541,0 +609,0 @@ } |
@@ -6,3 +6,3 @@ /** | ||
*/ | ||
import { iterateChildren, updateRowStates } from './vaadin-grid-helpers.js'; | ||
import { iterateChildren, updateBooleanRowStates, updateStringRowStates } from './vaadin-grid-helpers.js'; | ||
@@ -160,3 +160,3 @@ const DropMode = { | ||
updateRowStates(row, { dragstart: rows.length > 1 ? `${rows.length}` : '' }); | ||
updateBooleanRowStates(row, { dragstart: rows.length > 1 ? `${rows.length}` : '' }); | ||
this.style.setProperty('--_grid-drag-start-x', `${e.clientX - rowRect.left + 20}px`); | ||
@@ -166,3 +166,3 @@ this.style.setProperty('--_grid-drag-start-y', `${e.clientY - rowRect.top + 10}px`); | ||
requestAnimationFrame(() => { | ||
updateRowStates(row, { dragstart: null }); | ||
updateBooleanRowStates(row, { dragstart: false }); | ||
this.style.setProperty('--_grid-drag-start-x', ''); | ||
@@ -261,3 +261,3 @@ this.style.setProperty('--_grid-drag-start-y', ''); | ||
if (row.getAttribute('dragover') !== this._dropLocation) { | ||
updateRowStates(row, { dragover: this._dropLocation }, true); | ||
updateStringRowStates(row, { dragover: this._dropLocation }); | ||
} | ||
@@ -317,3 +317,3 @@ } else { | ||
iterateChildren(this.$.items, (row) => { | ||
updateRowStates(row, { dragover: null }, true); | ||
updateStringRowStates(row, { dragover: null }); | ||
}); | ||
@@ -406,3 +406,3 @@ } | ||
updateRowStates(row, { | ||
updateBooleanRowStates(row, { | ||
'drag-disabled': !!dragDisabled, | ||
@@ -409,0 +409,0 @@ 'drop-disabled': !!dropDisabled, |
@@ -89,5 +89,4 @@ /** | ||
* @param {Object} states | ||
* @param {boolean} appendValue | ||
*/ | ||
export function updateRowStates(row, states, appendValue) { | ||
export function updateBooleanRowStates(row, states) { | ||
const cells = getBodyRowCells(row); | ||
@@ -99,3 +98,3 @@ | ||
const rowPart = appendValue ? `${state}-${value}-row` : `${state}-row`; | ||
const rowPart = `${state}-row`; | ||
@@ -111,2 +110,31 @@ // Row part attribute | ||
/** | ||
* @param {!HTMLElement} row | ||
* @param {Object} states | ||
*/ | ||
export function updateStringRowStates(row, states) { | ||
const cells = getBodyRowCells(row); | ||
Object.entries(states).forEach(([state, value]) => { | ||
const prevValue = row.getAttribute(state); | ||
// Row state attribute | ||
updateState(row, state, value); | ||
// remove previous part from row and cells if there was any | ||
if (prevValue) { | ||
const prevRowPart = `${state}-${prevValue}-row`; | ||
updatePart(row, false, prevRowPart); | ||
updateCellsPart(cells, `${prevRowPart}-cell`, false); | ||
} | ||
// set new part to rows and cells if there is a value | ||
if (value) { | ||
const rowPart = `${state}-${value}-row`; | ||
updatePart(row, value, rowPart); | ||
updateCellsPart(cells, `${rowPart}-cell`, value); | ||
} | ||
}); | ||
} | ||
/** | ||
* @param {!HTMLElement} cell | ||
@@ -113,0 +141,0 @@ * @param {string} attribute |
@@ -524,3 +524,8 @@ /** | ||
const columnIndex = this.__getIndexOfChildElement(activeCell); | ||
let columnIndex = this.__getIndexOfChildElement(activeCell); | ||
if (this.$.items.contains(activeCell)) { | ||
// lazy column rendering may be enabled, so we need use the always visible sizer cells to find the column index | ||
columnIndex = [...this.$.sizer.children].findIndex((sizerCell) => sizerCell._column === activeCell._column); | ||
} | ||
const isCurrentCellRowDetails = this.__isDetailsCell(activeCell); | ||
@@ -578,5 +583,23 @@ const activeRowGroup = activeRow.parentNode; | ||
const dstColumnIndex = columnIndexByOrder[dstSortedColumnOrders[dstOrderedColumnIndex]]; | ||
const dstCell = dstRow.children[dstColumnIndex]; | ||
this._scrollHorizontallyToCell(dstCell); | ||
let dstCell; | ||
if (this.$.items.contains(activeCell)) { | ||
const dstSizerCell = this.$.sizer.children[dstColumnIndex]; | ||
if (this._lazyColumns) { | ||
// If the column is not in the viewport, scroll it into view. | ||
if (!this.__isColumnInViewport(dstSizerCell._column)) { | ||
dstSizerCell.scrollIntoView(); | ||
} | ||
this.__updateColumnsBodyContentHidden(); | ||
this.__updateHorizontalScrollPosition(); | ||
} | ||
dstCell = [...dstRow.children].find((cell) => cell._column === dstSizerCell._column); | ||
// Ensure correct horizontal scroll position once the destination cell is available. | ||
this._scrollHorizontallyToCell(dstCell); | ||
} else { | ||
dstCell = dstRow.children[dstColumnIndex]; | ||
this._scrollHorizontallyToCell(dstCell); | ||
} | ||
dstCell.focus(); | ||
@@ -583,0 +606,0 @@ } |
@@ -10,11 +10,46 @@ /** | ||
export type ColumnRendering = 'eager' | 'lazy'; | ||
export declare class ScrollMixinClass { | ||
/** | ||
* Scroll to a specific row index in the virtual list. Note that the row index is | ||
* not always the same for any particular item. For example, sorting/filtering/expanding | ||
* or collapsing hierarchical items can affect the row index related to an item. | ||
* Allows you to choose between modes for rendering columns in the grid: | ||
* | ||
* @param index Row index to scroll to | ||
* "eager" (default): All columns are rendered upfront, regardless of their visibility within the viewport. | ||
* This mode should generally be preferred, as it avoids the limitations imposed by the "lazy" mode. | ||
* Use this mode unless the grid has a large number of columns and performance outweighs the limitations | ||
* in priority. | ||
* | ||
* "lazy": Optimizes the rendering of cells when there are multiple columns in the grid by virtualizing | ||
* horizontal scrolling. In this mode, body cells are rendered only when their corresponding columns are | ||
* inside the visible viewport. | ||
* | ||
* Using "lazy" rendering should be used only if you're dealing with a large number of columns and performance | ||
* is your highest priority. For most use cases, the default "eager" mode is recommended due to the | ||
* limitations imposed by the "lazy" mode. | ||
* | ||
* When using the "lazy" mode, keep the following limitations in mind: | ||
* | ||
* - Row Height: When only a number of columns are visible at once, the height of a row can only be that of | ||
* the highest cell currently visible on that row. Make sure each cell on a single row has the same height | ||
* as all other cells on that row. If row cells have different heights, users may experience jumpiness when | ||
* scrolling the grid horizontally as lazily rendered cells with different heights are scrolled into view. | ||
* | ||
* - Auto-width Columns: For the columns that are initially outside the visible viewport but still use auto-width, | ||
* only the header content is taken into account when calculating the column width because the body cells | ||
* of the columns outside the viewport are not initially rendered. | ||
* | ||
* - Screen Reader Compatibility: Screen readers may not be able to associate the focused cells with the correct | ||
* headers when only a subset of the body cells on a row is rendered. | ||
* | ||
* - Keyboard Navigation: Tabbing through focusable elements inside the grid body may not work as expected because | ||
* some of the columns that would include focusable elements in the body cells may be outside the visible viewport | ||
* and thus not rendered. | ||
*/ | ||
scrollToIndex(index: number): void; | ||
columnRendering: ColumnRendering; | ||
/** | ||
* Scroll to a flat index in the grid. The method doesn't take into account | ||
* the hierarchy of the items. | ||
*/ | ||
protected _scrollToFlatIndex(index: number): void; | ||
} |
@@ -13,2 +13,3 @@ /** | ||
SCROLLING: 500, | ||
UPDATE_CONTENT_VISIBILITY: 100, | ||
}; | ||
@@ -24,2 +25,44 @@ | ||
/** | ||
* Allows you to choose between modes for rendering columns in the grid: | ||
* | ||
* "eager" (default): All columns are rendered upfront, regardless of their visibility within the viewport. | ||
* This mode should generally be preferred, as it avoids the limitations imposed by the "lazy" mode. | ||
* Use this mode unless the grid has a large number of columns and performance outweighs the limitations | ||
* in priority. | ||
* | ||
* "lazy": Optimizes the rendering of cells when there are multiple columns in the grid by virtualizing | ||
* horizontal scrolling. In this mode, body cells are rendered only when their corresponding columns are | ||
* inside the visible viewport. | ||
* | ||
* Using "lazy" rendering should be used only if you're dealing with a large number of columns and performance | ||
* is your highest priority. For most use cases, the default "eager" mode is recommended due to the | ||
* limitations imposed by the "lazy" mode. | ||
* | ||
* When using the "lazy" mode, keep the following limitations in mind: | ||
* | ||
* - Row Height: When only a number of columns are visible at once, the height of a row can only be that of | ||
* the highest cell currently visible on that row. Make sure each cell on a single row has the same height | ||
* as all other cells on that row. If row cells have different heights, users may experience jumpiness when | ||
* scrolling the grid horizontally as lazily rendered cells with different heights are scrolled into view. | ||
* | ||
* - Auto-width Columns: For the columns that are initially outside the visible viewport but still use auto-width, | ||
* only the header content is taken into account when calculating the column width because the body cells | ||
* of the columns outside the viewport are not initially rendered. | ||
* | ||
* - Screen Reader Compatibility: Screen readers may not be able to associate the focused cells with the correct | ||
* headers when only a subset of the body cells on a row is rendered. | ||
* | ||
* - Keyboard Navigation: Tabbing through focusable elements inside the grid body may not work as expected because | ||
* some of the columns that would include focusable elements in the body cells may be outside the visible viewport | ||
* and thus not rendered. | ||
* | ||
* @attr {eager|lazy} column-rendering | ||
* @type {!ColumnRendering} | ||
*/ | ||
columnRendering: { | ||
type: String, | ||
value: 'eager', | ||
}, | ||
/** | ||
* Cached array of frozen cells | ||
@@ -47,2 +90,6 @@ * @private | ||
static get observers() { | ||
return ['__columnRenderingChanged(_columnTree, columnRendering)']; | ||
} | ||
/** @private */ | ||
@@ -67,2 +114,7 @@ get _scrollLeft() { | ||
/** @protected */ | ||
get _lazyColumns() { | ||
return this.columnRendering === 'lazy'; | ||
} | ||
/** @protected */ | ||
ready() { | ||
@@ -94,9 +146,9 @@ super.ready(); | ||
/** | ||
* Scroll to a specific row index in the virtual list. Note that the row index is | ||
* not always the same for any particular item. For example, sorting/filtering/expanding | ||
* or collapsing hierarchical items can affect the row index related to an item. | ||
* Scroll to a flat index in the grid. The method doesn't take into account | ||
* the hierarchy of the items. | ||
* | ||
* @param {number} index Row index to scroll to | ||
* @protected | ||
*/ | ||
scrollToIndex(index) { | ||
_scrollToFlatIndex(index) { | ||
index = Math.min(this._effectiveSize - 1, Math.max(0, index)); | ||
@@ -151,5 +203,123 @@ this.__virtualizer.scrollToIndex(index); | ||
this._updateOverflow(); | ||
this._debounceColumnContentVisibility = Debouncer.debounce( | ||
this._debounceColumnContentVisibility, | ||
timeOut.after(timeouts.UPDATE_CONTENT_VISIBILITY), | ||
() => { | ||
// If horizontal scroll position changed and lazy column rendering is enabled, | ||
// update the visible columns. | ||
if (this._lazyColumns && this.__cachedScrollLeft !== this._scrollLeft) { | ||
this.__cachedScrollLeft = this._scrollLeft; | ||
this.__updateColumnsBodyContentHidden(); | ||
} | ||
}, | ||
); | ||
} | ||
/** @private */ | ||
__updateColumnsBodyContentHidden() { | ||
if (!this._columnTree) { | ||
return; | ||
} | ||
const columnsInOrder = this._getColumnsInOrder(); | ||
// Return if sizer cells are not yet assigned to columns | ||
if (!columnsInOrder[0] || !columnsInOrder[0]._sizerCell) { | ||
return; | ||
} | ||
let bodyContentHiddenChanged = false; | ||
// Remove the column cells from the DOM if the column is outside the viewport. | ||
// Add the column cells to the DOM if the column is inside the viewport. | ||
// | ||
// Update the _bodyContentHidden property of the column to reflect the current | ||
// visibility state and make it run renderers for the cells if necessary. | ||
columnsInOrder.forEach((column) => { | ||
const bodyContentHidden = this._lazyColumns && !this.__isColumnInViewport(column); | ||
if (column._bodyContentHidden !== bodyContentHidden) { | ||
bodyContentHiddenChanged = true; | ||
column._cells.forEach((cell) => { | ||
if (cell !== column._sizerCell) { | ||
if (bodyContentHidden) { | ||
cell.remove(); | ||
} else if (cell.__parentRow) { | ||
// Add the cell to the correct DOM position in the row | ||
const followingColumnCell = [...cell.__parentRow.children].find( | ||
(child) => columnsInOrder.indexOf(child._column) > columnsInOrder.indexOf(column), | ||
); | ||
cell.__parentRow.insertBefore(cell, followingColumnCell); | ||
} | ||
} | ||
}); | ||
} | ||
column._bodyContentHidden = bodyContentHidden; | ||
}); | ||
if (bodyContentHiddenChanged) { | ||
// Frozen columns may have changed their visibility | ||
this._frozenCellsChanged(); | ||
} | ||
if (this._lazyColumns) { | ||
// Calculate the offset to apply to the body cells | ||
const lastFrozenColumn = [...columnsInOrder].reverse().find((column) => column.frozen); | ||
const lastFrozenColumnEnd = this.__getColumnEnd(lastFrozenColumn); | ||
const firstVisibleColumn = columnsInOrder.find((column) => !column.frozen && !column._bodyContentHidden); | ||
this.__lazyColumnsStart = this.__getColumnStart(firstVisibleColumn) - lastFrozenColumnEnd; | ||
this.$.items.style.setProperty('--_grid-lazy-columns-start', `${this.__lazyColumnsStart}px`); | ||
// Make sure the body has a focusable element in lazy columns mode | ||
this._resetKeyboardNavigation(); | ||
} | ||
} | ||
/** @private */ | ||
__getColumnEnd(column) { | ||
if (!column) { | ||
return this.__isRTL ? this.$.table.clientWidth : 0; | ||
} | ||
return column._sizerCell.offsetLeft + (this.__isRTL ? 0 : column._sizerCell.offsetWidth); | ||
} | ||
/** @private */ | ||
__getColumnStart(column) { | ||
if (!column) { | ||
return this.__isRTL ? this.$.table.clientWidth : 0; | ||
} | ||
return column._sizerCell.offsetLeft + (this.__isRTL ? column._sizerCell.offsetWidth : 0); | ||
} | ||
/** | ||
* Returns true if the given column is horizontally inside the viewport. | ||
* @private | ||
*/ | ||
__isColumnInViewport(column) { | ||
if (column.frozen || column.frozenToEnd) { | ||
// Assume frozen columns to always be inside the viewport | ||
return true; | ||
} | ||
// Check if the column's sizer cell is inside the viewport | ||
return ( | ||
column._sizerCell.offsetLeft + column._sizerCell.offsetWidth >= this._scrollLeft && | ||
column._sizerCell.offsetLeft <= this._scrollLeft + this.clientWidth | ||
); | ||
} | ||
/** @private */ | ||
__columnRenderingChanged(_columnTree, columnRendering) { | ||
if (columnRendering === 'eager') { | ||
this.$.scroller.removeAttribute('column-rendering'); | ||
} else { | ||
this.$.scroller.setAttribute('column-rendering', columnRendering); | ||
} | ||
this.__updateColumnsBodyContentHidden(); | ||
} | ||
/** @private */ | ||
_updateOverflow() { | ||
@@ -263,2 +433,4 @@ this._debounceOverflow = Debouncer.debounce(this._debounceOverflow, animationFrame, () => { | ||
} | ||
this.__updateColumnsBodyContentHidden(); | ||
} | ||
@@ -268,2 +440,5 @@ | ||
__updateHorizontalScrollPosition() { | ||
if (!this._columnTree) { | ||
return; | ||
} | ||
const scrollWidth = this.$.table.scrollWidth; | ||
@@ -290,4 +465,27 @@ const clientWidth = this.$.table.clientWidth; | ||
const transformFrozenToEnd = `translate(${remaining}px, 0)`; | ||
let transformFrozenToEndBody = transformFrozenToEnd; | ||
if (this._lazyColumns) { | ||
// Lazy column rendering is used, calculate the offset to apply to the frozen to end cells | ||
const columnsInOrder = this._getColumnsInOrder(); | ||
const lastVisibleColumn = [...columnsInOrder] | ||
.reverse() | ||
.find((column) => !column.frozenToEnd && !column._bodyContentHidden); | ||
const lastVisibleColumnEnd = this.__getColumnEnd(lastVisibleColumn); | ||
const firstFrozenToEndColumn = columnsInOrder.find((column) => column.frozenToEnd); | ||
const firstFrozenToEndColumnStart = this.__getColumnStart(firstFrozenToEndColumn); | ||
const translateX = remaining + (firstFrozenToEndColumnStart - lastVisibleColumnEnd) + this.__lazyColumnsStart; | ||
transformFrozenToEndBody = `translate(${translateX}px, 0)`; | ||
} | ||
this._frozenToEndCells.forEach((cell) => { | ||
cell.style.transform = transformFrozenToEnd; | ||
if (this.$.items.contains(cell)) { | ||
cell.style.transform = transformFrozenToEndBody; | ||
} else { | ||
cell.style.transform = transformFrozenToEnd; | ||
} | ||
}); | ||
@@ -294,0 +492,0 @@ |
@@ -125,5 +125,9 @@ /** | ||
[part~='row'][loading] [part~='body-cell'] ::slotted(vaadin-grid-cell-content) { | ||
opacity: 0; | ||
visibility: hidden; | ||
} | ||
[column-rendering='lazy'] [part~='body-cell']:not([frozen]):not([frozen-to-end]) { | ||
transform: translateX(var(--_grid-lazy-columns-start)); | ||
} | ||
#items [part~='row'] { | ||
@@ -342,4 +346,20 @@ position: absolute; | ||
} | ||
@media (forced-colors: active) { | ||
[part~='selected-row'] [part~='first-column-cell']::after { | ||
content: ''; | ||
position: absolute; | ||
top: 0; | ||
left: 0; | ||
bottom: 0; | ||
border: 2px solid; | ||
} | ||
[part~='focused-cell']::before { | ||
outline: 2px solid !important; | ||
outline-offset: -1px; | ||
} | ||
} | ||
`, | ||
{ moduleId: 'vaadin-grid-styles' }, | ||
); |
@@ -37,3 +37,3 @@ /** | ||
static get observers() { | ||
return ['_onRendererOrBindingChanged(_renderer, _cells, _cells.*, path)']; | ||
return ['_onRendererOrBindingChanged(_renderer, _cells, _bodyContentHidden, _cells.*, path)']; | ||
} | ||
@@ -40,0 +40,0 @@ |
@@ -8,6 +8,5 @@ /** | ||
import './vaadin-grid-styles.js'; | ||
import { beforeNextRender } from '@polymer/polymer/lib/utils/render-status.js'; | ||
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js'; | ||
import { TabindexMixin } from '@vaadin/a11y-base/src/tabindex-mixin.js'; | ||
import { microTask } from '@vaadin/component-base/src/async.js'; | ||
import { animationFrame, microTask } from '@vaadin/component-base/src/async.js'; | ||
import { isAndroid, isChrome, isFirefox, isIOS, isSafari, isTouch } from '@vaadin/component-base/src/browser-utils.js'; | ||
@@ -31,3 +30,3 @@ import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js'; | ||
import { FilterMixin } from './vaadin-grid-filter-mixin.js'; | ||
import { getBodyRowCells, iterateChildren, updateCellsPart, updateRowStates } from './vaadin-grid-helpers.js'; | ||
import { getBodyRowCells, iterateChildren, updateBooleanRowStates, updateCellsPart } from './vaadin-grid-helpers.js'; | ||
import { KeyboardNavigationMixin } from './vaadin-grid-keyboard-navigation-mixin.js'; | ||
@@ -508,3 +507,8 @@ import { RowDetailsMixin } from './vaadin-grid-row-details-mixin.js'; | ||
new ResizeObserver(() => setTimeout(() => this.__updateFooterPositioning())).observe(this.$.footer); | ||
new ResizeObserver(() => | ||
setTimeout(() => { | ||
this.__updateFooterPositioning(); | ||
this.__updateColumnsBodyContentHidden(); | ||
}), | ||
).observe(this.$.table); | ||
@@ -747,8 +751,10 @@ processTemplates(this); | ||
beforeNextRender(this, () => { | ||
this._updateFirstAndLastColumn(); | ||
this._resetKeyboardNavigation(); | ||
this._afterScroll(); | ||
this.__itemsReceived(); | ||
}); | ||
this.__afterCreateScrollerRowsDebouncer = Debouncer.debounce( | ||
this.__afterCreateScrollerRowsDebouncer, | ||
animationFrame, | ||
() => { | ||
this._afterScroll(); | ||
this.__itemsReceived(); | ||
}, | ||
); | ||
return rows; | ||
@@ -872,4 +878,11 @@ } | ||
cell.setAttribute('part', 'cell body-cell'); | ||
row.appendChild(cell); | ||
cell.__parentRow = row; | ||
if (!column._bodyContentHidden) { | ||
row.appendChild(cell); | ||
} | ||
if (row === this.$.sizer) { | ||
column._sizerCell = cell; | ||
} | ||
if (index === cols.length - 1 && this.rowDetailsRenderer) { | ||
@@ -1012,2 +1025,3 @@ // Add details cell as last cell to body rows | ||
this.recalculateColumnWidths(); | ||
this.__updateColumnsBodyContentHidden(); | ||
} | ||
@@ -1017,3 +1031,3 @@ | ||
_updateRowOrderParts(row, index = row.index) { | ||
updateRowStates(row, { | ||
updateBooleanRowStates(row, { | ||
first: index === 0, | ||
@@ -1028,3 +1042,3 @@ last: index === this._effectiveSize - 1, | ||
_updateRowStateParts(row, { expanded, selected, detailsOpened }) { | ||
updateRowStates(row, { | ||
updateBooleanRowStates(row, { | ||
expanded, | ||
@@ -1158,3 +1172,3 @@ selected, | ||
requestAnimationFrame(() => { | ||
this.__scrollToPendingIndex(); | ||
this.__scrollToPendingIndexes(); | ||
}); | ||
@@ -1161,0 +1175,0 @@ } |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
588190
14086
+ Added@vaadin/a11y-base@24.1.0-alpha10(transitive)
+ Added@vaadin/checkbox@24.1.0-alpha10(transitive)
+ Added@vaadin/component-base@24.1.0-alpha10(transitive)
+ Added@vaadin/field-base@24.1.0-alpha10(transitive)
+ Added@vaadin/icon@24.1.0-alpha10(transitive)
+ Added@vaadin/input-container@24.1.0-alpha10(transitive)
+ Added@vaadin/lit-renderer@24.1.0-alpha10(transitive)
+ Added@vaadin/text-field@24.1.0-alpha10(transitive)
+ Added@vaadin/vaadin-lumo-styles@24.1.0-alpha10(transitive)
+ Added@vaadin/vaadin-material-styles@24.1.0-alpha10(transitive)
+ Added@vaadin/vaadin-themable-mixin@24.1.0-alpha10(transitive)
- Removed@vaadin/a11y-base@24.1.0-alpha1(transitive)
- Removed@vaadin/checkbox@24.1.0-alpha1(transitive)
- Removed@vaadin/component-base@24.1.0-alpha1(transitive)
- Removed@vaadin/field-base@24.1.0-alpha1(transitive)
- Removed@vaadin/icon@24.1.0-alpha1(transitive)
- Removed@vaadin/input-container@24.1.0-alpha1(transitive)
- Removed@vaadin/lit-renderer@24.1.0-alpha1(transitive)
- Removed@vaadin/text-field@24.1.0-alpha1(transitive)
- Removed@vaadin/vaadin-lumo-styles@24.1.0-alpha1(transitive)
- Removed@vaadin/vaadin-material-styles@24.1.0-alpha1(transitive)
- Removed@vaadin/vaadin-themable-mixin@24.1.0-alpha1(transitive)