Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add option to freeze columns to end #3566

Merged
merged 15 commits into from
Mar 22, 2022
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions packages/grid/src/vaadin-grid-column-group.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ class GridColumnGroup extends ColumnBaseMixin(PolymerElement) {
'_updateVisibleChildColumns(_childColumns)',
'_childColumnsChanged(_childColumns)',
'_groupFrozenChanged(frozen, _rootColumns)',
'_groupFrozenToEndChanged(frozenToEnd, _rootColumns)',
'_groupHiddenChanged(hidden, _rootColumns)',
'_visibleChildColumnsChanged(_visibleChildColumns)',
'_colSpanChanged(_colSpan, _headerCell, _footerCell)',
Expand Down Expand Up @@ -135,6 +136,16 @@ class GridColumnGroup extends ColumnBaseMixin(PolymerElement) {
// Don’t unfreeze the frozen group because of a non-frozen child
this._lastFrozen = this._lastFrozen || value;
}

if (path === 'frozenToEnd') {
// Don’t unfreeze the frozen group because of a non-frozen child
this.frozenToEnd = this.frozenToEnd || value;
}

if (path === 'firstFrozenToEnd') {
// Don’t unfreeze the frozen group because of a non-frozen child
this._firstFrozenToEnd = this._firstFrozenToEnd || value;
}
}

/** @private */
Expand Down Expand Up @@ -239,6 +250,17 @@ class GridColumnGroup extends ColumnBaseMixin(PolymerElement) {
}
}

_groupFrozenToEndChanged(frozenToEnd, rootColumns) {
if (rootColumns === undefined || frozenToEnd === undefined) {
return;
}

// Don’t propagate the default `false` value.
if (frozenToEnd !== false) {
Array.from(rootColumns).forEach((col) => (col.frozenToEnd = frozenToEnd));
}
DiegoCardoso marked this conversation as resolved.
Show resolved Hide resolved
}

/** @private */
_groupHiddenChanged(hidden, rootColumns) {
if (rootColumns && !this._preventHiddenCascade) {
Expand Down
7 changes: 5 additions & 2 deletions packages/grid/src/vaadin-grid-column-reordering-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,10 @@ export const ColumnReorderingMixin = (superClass) =>
if (column1 && column2) {
const differentColumns = column1 !== column2;
const sameParent = column1.parentElement === column2.parentElement;
const sameFrozen = column1.frozen === column2.frozen;
const sameFrozen =
(column1.frozen && column2.frozen) || // both columns are frozen
(column1.frozenToEnd && column2.frozenToEnd) || // both columns are frozen to end
(!column1.frozen && !column1.frozenToEnd && !column2.frozen && !column2.frozenToEnd);
return differentColumns && sameParent && sameFrozen;
}
}
Expand Down Expand Up @@ -351,7 +354,7 @@ export const ColumnReorderingMixin = (superClass) =>
const _order = column1._order;
column1._order = column2._order;
column2._order = _order;
this._updateLastFrozen();
this._updateFrozenColumn();
this._updateFirstAndLastColumn();
}

Expand Down
19 changes: 14 additions & 5 deletions packages/grid/src/vaadin-grid-column-resizing-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,20 @@ export const ColumnResizingMixin = (superClass) =>
parseInt(style.borderRightWidth) +
parseInt(style.marginLeft) +
parseInt(style.marginRight);
const maxWidth =
targetCell.offsetWidth +
(this.__isRTL
? targetCell.getBoundingClientRect().left - e.detail.x
: e.detail.x - targetCell.getBoundingClientRect().right);

let maxWidth;

const eventX = e.detail.x;
const cellWidth = targetCell.offsetWidth;
const cellRect = targetCell.getBoundingClientRect();

// For cells frozen to end, resize handle is flipped horizontally.
if (targetCell.hasAttribute('frozen-to-end')) {
maxWidth = cellWidth + (this.__isRTL ? eventX - cellRect.right : cellRect.left - eventX);
} else {
maxWidth = cellWidth + (this.__isRTL ? cellRect.left - eventX : eventX - cellRect.right);
}

column.width = Math.max(minWidth, maxWidth) + 'px';
column.flexGrow = 0;
}
Expand Down
11 changes: 11 additions & 0 deletions packages/grid/src/vaadin-grid-column.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@ export declare class ColumnBaseMixinClass<TItem> {
*/
frozen: boolean;

/**
* When true, the column is frozen to end of grid.
*
* When a column inside of a column group is frozen to end, all of the sibling columns
* inside the group will get frozen to end also.
*
* Column can not be set as `frozen` and `frozenToEnd` at the same time.
* @attr {boolean} frozen-to-end
*/
frozenToEnd: boolean;

/**
* When set to true, the cells for this column are hidden.
*/
Expand Down
61 changes: 60 additions & 1 deletion packages/grid/src/vaadin-grid-column.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,21 @@ export const ColumnBaseMixin = (superClass) =>
value: false
},

/**
* When true, the column is frozen to end of grid.
*
* When a column inside of a column group is frozen to end, all of the sibling columns
* inside the group will get frozen to end also.
*
* Column can not be set as `frozen` and `frozenToEnd` at the same time.
* @attr {boolean} frozen-to-end
* @type {boolean}
*/
frozenToEnd: {
type: Boolean,
value: false
},

/**
* When set to true, the cells for this column are hidden.
*/
Expand Down Expand Up @@ -79,6 +94,15 @@ export const ColumnBaseMixin = (superClass) =>
value: false
},

/**
* @type {boolean}
* @protected
*/
_firstFrozenToEnd: {
type: Boolean,
value: false
},

/** @protected */
_order: Number,

Expand Down Expand Up @@ -176,10 +200,12 @@ export const ColumnBaseMixin = (superClass) =>
return [
'_widthChanged(width, _headerCell, _footerCell, _cells.*)',
'_frozenChanged(frozen, _headerCell, _footerCell, _cells.*)',
'_frozenToEndChanged(frozenToEnd, _headerCell, _footerCell, _cells.*)',
'_flexGrowChanged(flexGrow, _headerCell, _footerCell, _cells.*)',
'_textAlignChanged(textAlign, _cells.*, _headerCell, _footerCell)',
'_orderChanged(_order, _headerCell, _footerCell, _cells.*)',
'_lastFrozenChanged(_lastFrozen)',
'_firstFrozenToEndChanged(_firstFrozenToEnd)',
'_onRendererOrBindingChanged(_renderer, _cells, _cells.*, path)',
'_onHeaderRendererOrBindingChanged(_headerRenderer, _headerCell, path, header)',
'_onFooterRendererOrBindingChanged(_footerRenderer, _footerCell)',
Expand Down Expand Up @@ -314,6 +340,23 @@ export const ColumnBaseMixin = (superClass) =>
this._grid && this._grid._frozenCellsChanged && this._grid._frozenCellsChanged();
}

/** @private */
_frozenToEndChanged(frozenToEnd) {
if (this.parentElement && this.parentElement._columnPropChanged) {
this.parentElement._columnPropChanged('frozenToEnd', frozenToEnd);
}

this._allCells.forEach((cell) => {
// Skip sizer cells to keep correct scrollWidth.
if (this._grid && cell.parentElement === this._grid.$.sizer) {
return;
}
cell.toggleAttribute('frozen-to-end', frozenToEnd);
});

this._grid && this._grid._frozenCellsChanged && this._grid._frozenCellsChanged();
}

/** @private */
_lastFrozenChanged(lastFrozen) {
this._allCells.forEach((cell) => cell.toggleAttribute('last-frozen', lastFrozen));
Expand All @@ -323,6 +366,22 @@ export const ColumnBaseMixin = (superClass) =>
}
}

/** @private */
_firstFrozenToEndChanged(firstFrozenToEnd) {
this._allCells.forEach((cell) => {
// Skip sizer cells to keep correct scrollWidth.
if (this._grid && cell.parentElement === this._grid.$.sizer) {
return;
}

cell.toggleAttribute('first-frozen-to-end', firstFrozenToEnd);
});

if (this.parentElement && this.parentElement._columnPropChanged) {
this.parentElement._firstFrozenToEnd = firstFrozenToEnd;
}
}

/**
* @param {string} path
* @return {string}
Expand Down Expand Up @@ -421,7 +480,7 @@ export const ColumnBaseMixin = (superClass) =>
}
);

this._grid._updateLastFrozen && this._grid._updateLastFrozen();
this._grid._updateFrozenColumn && this._grid._updateFrozenColumn();
this._grid._resetKeyboardNavigation && this._grid._resetKeyboardNavigation();
}
this._previousHidden = hidden;
Expand Down
6 changes: 3 additions & 3 deletions packages/grid/src/vaadin-grid-keyboard-navigation-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -854,7 +854,7 @@ export const KeyboardNavigationMixin = (superClass) =>
* @protected
*/
_scrollHorizontallyToCell(dstCell) {
if (dstCell.hasAttribute('frozen') || this.__isDetailsCell(dstCell)) {
if (dstCell.hasAttribute('frozen') || dstCell.hasAttribute('frozen-to-end') || this.__isDetailsCell(dstCell)) {
// These cells are, by design, always visible, no need to scroll.
return;
}
Expand All @@ -870,7 +870,7 @@ export const KeyboardNavigationMixin = (superClass) =>
if (cell.hasAttribute('hidden') || this.__isDetailsCell(cell)) {
continue;
}
if (cell.hasAttribute('frozen')) {
if (cell.hasAttribute('frozen') || cell.hasAttribute('frozen-to-end')) {
leftBoundary = cell.getBoundingClientRect().right;
break;
}
Expand All @@ -880,7 +880,7 @@ export const KeyboardNavigationMixin = (superClass) =>
if (cell.hasAttribute('hidden') || this.__isDetailsCell(cell)) {
continue;
}
if (cell.hasAttribute('frozen')) {
if (cell.hasAttribute('frozen') || cell.hasAttribute('frozen-to-end')) {
rightBoundary = cell.getBoundingClientRect().left;
break;
}
Expand Down
58 changes: 50 additions & 8 deletions packages/grid/src/vaadin-grid-scroll-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ export const ScrollMixin = (superClass) =>
value: () => []
},

/**
* Cached array of cells frozen to end
* @private
*/
_frozenToEndCells: {
type: Array,
value: () => []
},

/** @private */
_rowWithFocusedElement: Element
};
Expand Down Expand Up @@ -177,13 +186,14 @@ export const ScrollMixin = (superClass) =>
cell.style.transform = '';
});
this._frozenCells = Array.prototype.slice.call(this.$.table.querySelectorAll('[frozen]'));
this._frozenToEndCells = Array.prototype.slice.call(this.$.table.querySelectorAll('[frozen-to-end]'));
this.__updateHorizontalScrollPosition();
});
this._updateLastFrozen();
this._updateFrozenColumn();
}

/** @protected */
_updateLastFrozen() {
_updateFrozenColumn() {
if (!this._columnTree) {
return;
}
Expand All @@ -192,26 +202,58 @@ export const ScrollMixin = (superClass) =>
columnsRow.sort((a, b) => {
return a._order - b._order;
});
const lastFrozen = columnsRow.reduce((prev, col, index) => {

let lastFrozen;
let firstFrozenToEnd;

// Use for loop to only iterate columns once
for (let i = 0; i < columnsRow.length; i++) {
const col = columnsRow[i];

col._lastFrozen = false;
return col.frozen && !col.hidden ? index : prev;
}, undefined);
col._firstFrozenToEnd = false;

if (firstFrozenToEnd === undefined && col.frozenToEnd && !col.hidden) {
firstFrozenToEnd = i;
}

if (col.frozen && !col.hidden) {
lastFrozen = i;
}
}

if (lastFrozen !== undefined) {
columnsRow[lastFrozen]._lastFrozen = true;
}

if (firstFrozenToEnd !== undefined) {
columnsRow[firstFrozenToEnd]._firstFrozenToEnd = true;
}
}

/** @private */
__updateHorizontalScrollPosition() {
const scrollWidth = this.$.table.scrollWidth;
const clientWidth = this.$.table.clientWidth;
const scrollLeft = this.__getNormalizedScrollLeft(this.$.table);

// Position cells frozen to end
const remaining = scrollLeft + clientWidth - scrollWidth;

this.$.table.style.setProperty('--_grid-horizontal-scroll-remaining', remaining + 'px');
tomivirkki marked this conversation as resolved.
Show resolved Hide resolved
this.$.table.style.setProperty('--_grid-horizontal-scroll-position', -this._scrollLeft + 'px');

if (this.__isRTL) {
// Translating the sticky sections using a CSS variable works nicely on LTR.
// On RTL, it causes jumpy behavior (on Desktop Safari) so we need to translate manually.
const x = this.__getNormalizedScrollLeft(this.$.table) + this.$.table.clientWidth - this.$.table.scrollWidth;
const transform = `translate(${x}px, 0)`;
const transformFrozen = `translate(${remaining}px, 0)`;
for (let i = 0; i < this._frozenCells.length; i++) {
this._frozenCells[i].style.transform = transform;
this._frozenCells[i].style.transform = transformFrozen;
}

const transformFrozenToEnd = `translate(${scrollLeft}px, 0)`;
for (let i = 0; i < this._frozenToEndCells.length; i++) {
this._frozenToEndCells[i].style.transform = transformFrozenToEnd;
}
}
}
Expand Down
Loading