Skip to content

Commit

Permalink
feat: add columns resize (#41)
Browse files Browse the repository at this point in the history
  • Loading branch information
artemmufazalov authored Apr 12, 2024
1 parent be75da5 commit 2d3f79f
Show file tree
Hide file tree
Showing 12 changed files with 471 additions and 44 deletions.
45 changes: 25 additions & 20 deletions README.md

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion src/.eslintrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
{
"extends": ["@gravity-ui/eslint-config", "@gravity-ui/eslint-config/prettier"]
"extends": ["@gravity-ui/eslint-config", "@gravity-ui/eslint-config/prettier"],
"rules": {
"no-implicit-globals": "off"
}
}
20 changes: 20 additions & 0 deletions src/lib/DataTable.scss
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ $cell-border: 1px solid var(--data-table-border-color);
}

&__th {
box-sizing: border-box;
font-weight: 500;
cursor: default;
padding: var(--data-table-header-vertical-padding) var(--data-table-cell-horizontal-padding);
Expand Down Expand Up @@ -98,6 +99,7 @@ $cell-border: 1px solid var(--data-table-border-color);
}

&__td {
box-sizing: border-box;
padding: var(--data-table-cell-vertical-padding) var(--data-table-cell-horizontal-padding);
border: $cell-border;
vertical-align: var(--data-table-cell-align);
Expand Down Expand Up @@ -267,4 +269,22 @@ $cell-border: 1px solid var(--data-table-border-color);
--data-table-color-hover-area: #ffeba0;
--data-table-color-footer-area: var(--data-table-color-base);
}

&__resize-handler {
visibility: hidden;
position: absolute;
right: 0;
top: 0;
cursor: col-resize;
width: 6px;
height: 100%;
background-color: var(--g-color-base-generic);

&_resizing {
visibility: visible;
}
}
&__th:hover > &__resize-handler {
visibility: visible;
}
}
81 changes: 65 additions & 16 deletions src/lib/DataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,20 @@ import * as React from 'react';
import ReactList from 'react-list';

import './DataTable.scss';
import {ResizeHandler} from './ResizeHandler';
import {ASCENDING, CENTER, DESCENDING, FIXED, INDEX_COLUMN, LEFT, MOVING, RIGHT} from './constants';
import {positionStickySupported} from './featureSupport';
import {HeightObserver} from './height-observer';
import {Column, DataTableProps, HeadPosition, OrderType, Settings, SortedDataItem} from './types';
import {
SlimColumn,
cn,
b,
externalToInternalSortOrder,
getSortOrder,
getSortedData,
internalToExternalSortOrder,
} from './util';

const b = cn('data-table');

const ICON_ASC = (
<svg className={b('icon')} viewBox="0 0 10 6" width="10" height="6">
<path fill="currentColor" d="M0 5h10l-5 -5z" />
Expand Down Expand Up @@ -122,19 +121,27 @@ class TableRow<T> extends React.PureComponent<TableRowProps<T>> {
}

const value = column._getValue(row);

let style = column.customStyle({
row,
index,
name: column.name,
header: false,
footer,
headerData,
});

// Fixed cell width for resizeable columns for proper content wrap
if (column.resizeable) {
style = {...style, width: column.width, maxWidth: column.width};
}

return (
<td
key={columnIndex}
className={column._className}
title={column._getTitle(row)}
style={column.customStyle({
row,
index,
name: column.name,
header: false,
footer,
headerData,
})}
style={style}
colSpan={colSpans ? colSpans[column.name] : undefined}
rowSpan={rowSpan}
onClick={column._getOnClick({row, index, footer, headerData})}
Expand All @@ -160,6 +167,7 @@ interface TableHeadProps<T> {
displayIndices?: boolean;

onSort?: TableProps<T>['onSort'];
onResize?: TableProps<T>['onResize'];
onColumnsUpdated?: (widths: number[]) => void;
renderedDataRows?: React.ReactNode;

Expand All @@ -169,7 +177,7 @@ interface TableHeadProps<T> {
class TableHead<T> extends React.Component<TableHeadProps<T>> {
_dataRowsRef: HTMLTableSectionElement | null = null;
dataRowsHeightObserver?: HeightObserver;
renderedColumns: HTMLTableHeaderCellElement[] = [];
renderedColumns: HTMLTableCellElement[] = [];

componentDidMount() {
this._calculateColumnsWidth();
Expand Down Expand Up @@ -238,6 +246,7 @@ class TableHead<T> extends React.Component<TableHeadProps<T>> {
: undefined;
}
renderHeadCell = (headCell: HeadCellsType<T>) => {
const {onResize} = this.props;
const {column, rowSpan, colSpan} = headCell;
const {
sortable = false,
Expand All @@ -246,26 +255,48 @@ class TableHead<T> extends React.Component<TableHeadProps<T>> {
index,
columnIndex,
align,
name,
width,
resizeable,
resizeMinWidth,
resizeMaxWidth,
} = column;

const {headerTitle = (typeof header === 'string' && header) || undefined} = column;

let style = column.customStyle?.({header: true, name});

// Fixed cell width for resizeable columns for proper content wrap
if (resizeable) {
style = {...style, width, maxWidth: width};
}

return (
<th
ref={column.dataColumn ? this._getColumnRef(columnIndex!) : null}

Check warning on line 276 in src/lib/DataTable.tsx

View workflow job for this annotation

GitHub Actions / Verify Files

Forbidden non-null assertion
className={b('th', {sortable, align}, className)}
key={column.name}
key={name}
title={headerTitle}
data-index={index}
colSpan={colSpan}
rowSpan={rowSpan}
style={column.customStyle && column.customStyle({header: true, name: column.name})}
style={style}
onClick={this._getOnSortClick(column)}
>
<div className={b('head-cell')}>
{header}
{<ColumnSortIcon {...column} />}
</div>
{resizeable && (
<ResizeHandler
getColumn={this._getRenderedColumn}
columnIndex={columnIndex}
onResize={onResize}
columnId={name}
minWidth={resizeMinWidth}
maxWidth={resizeMaxWidth}
/>
)}
</th>
);
};
Expand All @@ -289,6 +320,12 @@ class TableHead<T> extends React.Component<TableHeadProps<T>> {
this.renderedColumns[index] = node;
};
};
_getRenderedColumn = (index?: number) => {
if (index) {
return this.renderedColumns[index];
}
return undefined;
};
}

interface StickyHeadProps<T> {
Expand All @@ -298,6 +335,7 @@ interface StickyHeadProps<T> {
mode: HeadPositionInner;
displayIndices?: Settings['displayIndices'];
onSort?: TableProps<T>['onSort'];
onResize?: TableProps<T>['onResize'];
top: number;

renderedDataRows: React.ReactNode;
Expand Down Expand Up @@ -534,6 +572,7 @@ interface TableProps<T> {
rowKey: (row: T, index: number) => string | number;
startIndex: DataTableProps<T>['startIndex'];
onSort: DataTableView<T>['onSort'];
onResize: DataTableProps<T>['onResize'];
renderEmptyRow: unknown;
nullBeforeNumbers?: boolean;
getColSpansOfRow?: (
Expand Down Expand Up @@ -673,7 +712,7 @@ class Table<T> extends React.PureComponent<TableProps<T>, TableState> {
}

renderHead() {
const {columns, onSort} = this.props;
const {columns, onSort, onResize} = this.props;
const {displayIndices} = this.props.settings;
const rows = this.renderHeaderRows();
return (
Expand All @@ -682,13 +721,14 @@ class Table<T> extends React.PureComponent<TableProps<T>, TableState> {
{...columns}
displayIndices={Boolean(displayIndices)}
onSort={onSort}
onResize={onResize}
onColumnsUpdated={this._onColumnsUpdated}
renderedDataRows={rows}
/>
);
}
renderStickyHead() {
const {columns, onSort} = this.props;
const {columns, onSort, onResize} = this.props;
const {displayIndices, stickyTop, stickyHead} = this.props.settings;
const top =
stickyTop === 'auto' && this._body && this._body.parentNode
Expand All @@ -703,6 +743,7 @@ class Table<T> extends React.PureComponent<TableProps<T>, TableState> {
{...columns}
displayIndices={displayIndices}
onSort={onSort}
onResize={onResize}
renderedDataRows={rows}
onDataRowsHeightChange={this.onMovingHeaderDataRowsHeightChange}
/>
Expand Down Expand Up @@ -916,6 +957,7 @@ class DataTableView<T> extends React.Component<DataTableProps<T>, DataTableViewS
sortable: true,
externalSort: false,
defaultOrder: ASCENDING as OrderType,
defaultResizeable: false,
},
rowKey: (row: any, index: number) =>

Check warning on line 962 in src/lib/DataTable.tsx

View workflow job for this annotation

GitHub Actions / Verify Files

Unexpected any. Specify a different type
Object.prototype.hasOwnProperty.call(row, 'id') ? row.id : index,
Expand Down Expand Up @@ -971,6 +1013,7 @@ class DataTableView<T> extends React.Component<DataTableProps<T>, DataTableViewS
return startIndex + index;
},
sortable: false,
resizeable: false,
width: 20 + Math.ceil(Math.log10(lastIndex)) * 10,
};
}
Expand Down Expand Up @@ -1006,6 +1049,7 @@ class DataTableView<T> extends React.Component<DataTableProps<T>, DataTableViewS
rowClassName,
rowKey,
onRowClick,
onResize,
theme,
renderEmptyRow,
nullBeforeNumbers,
Expand Down Expand Up @@ -1055,6 +1099,7 @@ class DataTableView<T> extends React.Component<DataTableProps<T>, DataTableViewS
)}
footerData={footerData}
onSort={this.onSort}
onResize={onResize}
/>
);
}
Expand All @@ -1079,6 +1124,7 @@ class DataTableView<T> extends React.Component<DataTableProps<T>, DataTableViewS
};

getColumn = (column: Column<T>, columnIndex: number) => {
const {onResize} = this.props;
const {settings} = this.state;
const {defaultOrder} = settings;
const {sortOrder = {}, sortColumns, indexColumn} = this.state;
Expand All @@ -1098,6 +1144,8 @@ class DataTableView<T> extends React.Component<DataTableProps<T>, DataTableViewS
const {sortAccessor, onClick} = column;
const _className = b('td', {align}, column.className);

const resizeable = (column.resizeable ?? settings.defaultResizeable) && Boolean(onResize);

const _getValue =
typeof accessor === 'function'
? (row: T) => accessor(row)
Expand Down Expand Up @@ -1137,6 +1185,7 @@ class DataTableView<T> extends React.Component<DataTableProps<T>, DataTableViewS
dataColumn: true,
defaultOrder,
...column,
resizeable,
sortable: sortable && isSortEnabled,
_className,
_getValue,
Expand Down
Loading

0 comments on commit 2d3f79f

Please sign in to comment.