Skip to content

Commit

Permalink
fix: Input Tables cannot paste more rows than number of visible rows (D…
Browse files Browse the repository at this point in the history
…H-18723) (#2371)

- cherry-picked from #2053 and #2152
- Resolves #2089
- I published an alpha package and tested it against dev-jackson2 with
the test steps from DH-18184

---------

Co-authored-by: Akshat Jawne <[email protected]>
  • Loading branch information
mofojed and AkshatJawne authored Feb 20, 2025
1 parent 301af0d commit 1c14588
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 35 deletions.
11 changes: 1 addition & 10 deletions packages/grid/src/Grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1326,6 +1326,7 @@ class Grid extends PureComponent<GridProps, GridState> {
const { columnCount, rowCount } = model;
let ranges = selectedRanges;
// If each cell is a single selection, we need to update the selection to map to the newly pasted data
// Check for
if (
ranges.every(
range =>
Expand All @@ -1347,16 +1348,6 @@ class Grid extends PureComponent<GridProps, GridState> {
this.setSelectedRanges(ranges);
}

if (
!ranges.every(
range =>
GridRange.rowCount([range]) === tableHeight &&
GridRange.columnCount([range]) === tableWidth
)
) {
throw new PasteError('Copy and paste area are not same size.');
}

const edits: EditOperation[] = [];
ranges.forEach(range => {
for (let x = 0; x < tableWidth; x += 1) {
Expand Down
8 changes: 6 additions & 2 deletions packages/grid/src/GridMetricCalculator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -762,9 +762,13 @@ export class GridMetricCalculator {
visibleWidth: number = this.getVisibleWidth(state)
): VisibleIndex {
const { model } = state;
const { columnCount } = model;
const { columnCount, floatingRightColumnCount } = model;

if (columnCount === 0) {
return 0;
}

let lastLeft = columnCount - 1;
let lastLeft = Math.max(0, columnCount - floatingRightColumnCount - 1);
if (right != null) {
lastLeft = right;
}
Expand Down
21 changes: 16 additions & 5 deletions packages/iris-grid/src/IrisGridModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,17 @@ import type {
import { Formatter } from '@deephaven/jsapi-utils';

type RowIndex = ModelIndex;
type ColumnName = string;

type IrisGridModelEventNames = typeof IrisGridModel.EVENT[keyof typeof IrisGridModel.EVENT];

type IrisGridModelEventMap = {
[E in IrisGridModelEventNames]: Event<E>;
};

const EMPTY_ARRAY: never[] = [];
const EMPTY_SET: Set<never> = new Set();

/**
* Abstract class that extends the GridModel to have more functionality, like filtering and sorting.
* For use from IrisGrid.
Expand Down Expand Up @@ -134,12 +138,12 @@ abstract class IrisGridModel<

/** List of column movements defined by the model. Used as initial movements for IrisGrid */
get movedColumns(): MoveOperation[] {
return [];
return EMPTY_ARRAY;
}

/** List of row movements defined by the model. Used as initial movements for IrisGrid */
get movedRows(): MoveOperation[] {
return [];
return EMPTY_ARRAY;
}

/**
Expand Down Expand Up @@ -262,21 +266,28 @@ abstract class IrisGridModel<
* @returns Names of columns which should be locked to the front, but not floating
*/
get frontColumns(): string[] {
return [];
return EMPTY_ARRAY;
}

/**
* @returns Names of columns which should be locked to the back, but not floating
*/
get backColumns(): string[] {
return [];
return EMPTY_ARRAY;
}

/**
* @returns Names of key columns
*/
get keyColumnSet(): Set<ColumnName> {
return EMPTY_SET;
}

/**
* @returns Names of columns which should be frozen to the front and floating
*/
get frozenColumns(): string[] {
return [];
return EMPTY_ARRAY;
}

/**
Expand Down
67 changes: 49 additions & 18 deletions packages/iris-grid/src/IrisGridTableModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const log = Log.module('IrisGridTableModel');

const SET_VIEWPORT_THROTTLE = 150;
const APPLY_VIEWPORT_THROTTLE = 0;
const EMPTY_ARRAY = Object.freeze([]);

/**
* Model for a grid showing an iris data table
Expand Down Expand Up @@ -544,16 +545,24 @@ class IrisGridTableModel extends IrisGridModel {
return this.getMemoizedColumnMap(this.table.columns);
}

getMemoizedKeyColumnSet = memoize(
inputTableKeys => new Set(inputTableKeys ?? EMPTY_ARRAY)
);

get keyColumnSet() {
return this.getMemoizedKeyColumnSet(this.inputTable?.keys);
}

getMemoizedFrontColumns = memoize(
layoutHintsFrontColumns => layoutHintsFrontColumns ?? []
layoutHintsFrontColumns => layoutHintsFrontColumns ?? EMPTY_ARRAY
);

get frontColumns() {
return this.getMemoizedFrontColumns(this.layoutHints?.frontColumns);
}

getMemoizedBackColumns = memoize(
layoutHintsBackColumns => layoutHintsBackColumns ?? []
layoutHintsBackColumns => layoutHintsBackColumns ?? EMPTY_ARRAY
);

get backColumns() {
Expand All @@ -562,7 +571,7 @@ class IrisGridTableModel extends IrisGridModel {

getMemoizedFrozenColumns = memoize(
(layoutHintsFrozenColumns, userFrozenColumns) =>
userFrozenColumns ?? layoutHintsFrozenColumns ?? []
userFrozenColumns ?? layoutHintsFrozenColumns ?? EMPTY_ARRAY
);

get frozenColumns() {
Expand All @@ -581,7 +590,7 @@ class IrisGridTableModel extends IrisGridModel {
}

get groupedColumns() {
return [];
return EMPTY_ARRAY;
}

get description() {
Expand Down Expand Up @@ -654,7 +663,7 @@ class IrisGridTableModel extends IrisGridModel {
*/
pendingRow(y) {
const pendingRow = y - this.floatingTopRowCount - this.table.size;
if (pendingRow >= 0 && pendingRow < this.pendingNewRowCount) {
if (pendingRow >= 0) {
return pendingRow;
}

Expand Down Expand Up @@ -1252,26 +1261,48 @@ class IrisGridTableModel extends IrisGridModel {
}

isKeyColumn(x) {
return x < (this.inputTable?.keyColumns.length ?? 0);
return this.keyColumnSet.has(this.columns[x].name);
}

isRowMovable() {
return false;
}

isEditableRange(range) {
return (
this.inputTable != null &&
GridRange.isBounded(range) &&
((this.isPendingRow(range.startRow) && this.isPendingRow(range.endRow)) ||
(range.startColumn >= this.inputTable.keyColumns.length &&
range.endColumn >= this.inputTable.keyColumns.length)) &&
range.startRow >= this.floatingTopRowCount &&
range.startRow <
this.floatingTopRowCount + this.table.size + this.pendingRowCount &&
range.endRow <
this.floatingTopRowCount + this.table.size + this.pendingRowCount
);
// Make sure we have an input table and a valid range
if (
this.inputTable == null ||
range.startRow == null ||
range.endRow == null
) {
return false;
}

// Check that the edit is in the editable range
// If an input table has keyed columns, the non-key columns are always editable
// If an input table does not have key columns, it is append only and existing rows cannot be editable
// Pending rows are always editable
const isPendingRange =
this.isPendingRow(range.startRow) && this.isPendingRow(range.endRow);

let isKeyColumnInRange = false;

// Check if any of the columns in grid range are key columns
const bound = range.endColumn ?? this.table.size;
for (let column = range.startColumn; column <= bound; column += 1) {
if (this.isKeyColumn(column)) {
isKeyColumnInRange = true;
break;
}
}

if (
!(isPendingRange || (this.keyColumnSet.size !== 0 && !isKeyColumnInRange))
) {
return false;
}

return true;
}

isDeletableRange(range) {
Expand Down

0 comments on commit 1c14588

Please sign in to comment.