From 42f68b5db3ca983fa817f8dd7ac6f678ce9200a1 Mon Sep 17 00:00:00 2001 From: Alex Culliere Date: Fri, 12 Feb 2021 16:34:25 +0200 Subject: [PATCH] Make column resize sorta-functioning Signed-off-by: Alex Culliere --- .../item-object-list/item-list-layout.tsx | 33 +++++++++--- .../kube-object/kube-object-list-layout.tsx | 1 + src/renderer/components/table/table-cell.tsx | 54 +++++++++++++++++-- 3 files changed, 77 insertions(+), 11 deletions(-) diff --git a/src/renderer/components/item-object-list/item-list-layout.tsx b/src/renderer/components/item-object-list/item-list-layout.tsx index 667e7e2524..127d86c322 100644 --- a/src/renderer/components/item-object-list/item-list-layout.tsx +++ b/src/renderer/components/item-object-list/item-list-layout.tsx @@ -80,6 +80,10 @@ export interface ItemListLayoutProps { renderFooter?: (parent: ItemListLayout) => React.ReactNode; } +interface ItemListLayoutState { + cellSizes: number[]; +} + const defaultProps: Partial = { showHeader: true, isSearchable: true, @@ -96,9 +100,8 @@ const defaultProps: Partial = { }; @observer -export class ItemListLayout extends React.Component { +export class ItemListLayout extends React.Component { static defaultProps = defaultProps as object; - private cellSizes: Array; private storage = createStorage("item_list_layout", { showFilters: false, // setup defaults @@ -114,7 +117,10 @@ export class ItemListLayout extends React.Component { constructor(props: ItemListLayoutProps) { super(props); - this.cellSizes = new Array(props.renderTableHeader.length); + + this.state = { + cellSizes: new Array(props.renderTableHeader.length) + }; } async componentDidMount() { @@ -215,6 +221,13 @@ export class ItemListLayout extends React.Component { return this.applyFilters(filterItems.concat(this.props.filterItems), items); } + handleCellResize(index: number, width: number) { + const cellSizes = [...this.state.cellSizes]; + + cellSizes[index] = width; + this.setState({ cellSizes }); + } + @autobind() getRow(uid: string) { const { @@ -249,7 +262,7 @@ export class ItemListLayout extends React.Component { renderTableContents(item).map((content, index) => { const cellProps: TableCellProps = isReactNode(content) ? { children: content } : content; const headCell = renderTableHeader?.[index]; - const cellSize = this.cellSizes[index]; + const cellSize = this.state.cellSizes[index]; if (copyClassNameFromHeadCells && headCell) { cellProps.className = cssNames(cellProps.className, headCell.className); @@ -259,8 +272,12 @@ export class ItemListLayout extends React.Component { cellProps.size = cellSize; } + if (resizable) { + cellProps._onResize = width => this.handleCellResize(index, width); + } + if (!headCell || !this.isHiddenColumn(headCell)) { - return ; + return ; } }) } @@ -411,9 +428,9 @@ export class ItemListLayout extends React.Component { /> )} {renderTableHeader.map((cellProps, index) => { - const cellSize = this.cellSizes[index]; + const cellSize = this.state.cellSizes[index]; const _cellProps = resizable ? - { ...cellProps, _onResize: (width: number) => this.cellSizes[index] = width } : + { ...cellProps, _onResize: (width: number) => this.handleCellResize(index, width), resizable } : cellProps; if (cellSize !== undefined) { @@ -421,7 +438,7 @@ export class ItemListLayout extends React.Component { } if (!this.isHiddenColumn(cellProps)) { - return ; + return ; } })} diff --git a/src/renderer/components/kube-object/kube-object-list-layout.tsx b/src/renderer/components/kube-object/kube-object-list-layout.tsx index 0ecb602c16..0fa83f0336 100644 --- a/src/renderer/components/kube-object/kube-object-list-layout.tsx +++ b/src/renderer/components/kube-object/kube-object-list-layout.tsx @@ -46,6 +46,7 @@ export class KubeObjectListLayout extends React.Component { _onResize?(width: number): void; // resize callbalck, don't use this prop outside (!) } +type ResizeHandlerState = { + mousePosX: number, + currentColumn?: HTMLDivElement; + nextColumn?: HTMLDivElement; +}; + export class TableCell extends React.Component { + private resizeHandlerState?: ResizeHandlerState; + private cellContainer: React.RefObject = React.createRef(); + @autobind() onClick(evt: React.MouseEvent) { if (this.props.onClick) { @@ -37,6 +47,10 @@ export class TableCell extends React.Component { } } + componentWillUnmount() { + this.removeMouseEventListeners(); + } + get isSortable() { const { _sorting, sortBy } = this.props; @@ -67,8 +81,34 @@ export class TableCell extends React.Component { } } + addMouseEventListeners() { + document.addEventListener("mousemove", this.mouseMoveHandler.bind(this)); + document.addEventListener("mouseup", this.mouseUpHandler.bind(this)); + } + + removeMouseEventListeners() { + document.removeEventListener("mousemove", this.mouseMoveHandler.bind(this)); + document.removeEventListener("mouseup", this.mouseUpHandler.bind(this)); + } + + mouseMoveHandler(event: MouseEvent) { + if (!this.resizeHandlerState) { + return; + } + + const diffPosX = event.pageX - this.resizeHandlerState.mousePosX; + const currentWidth = this.cellContainer.current.offsetWidth; + + this.props?._onResize(currentWidth + diffPosX); + } + + mouseUpHandler() { + this.removeMouseEventListeners(); + this.resizeHandlerState = undefined; + } + render() { - const { className, checkbox, isChecked, resizable, size, sortBy, _sort, _sorting, _nowrap, children, title, renderBoolean: displayBoolean, showWithColumn, ...cellProps } = this.props; + const { className, checkbox, isChecked, resizable, size, sortBy, _sort, _sorting, _nowrap, children, title, renderBoolean: displayBoolean, showWithColumn, _onResize, ...cellProps } = this.props; const classNames = cssNames("TableCell", className, { checkbox, nowrap: _nowrap, @@ -78,13 +118,21 @@ export class TableCell extends React.Component { const cellStyle: React.CSSProperties = size !== undefined ? { flexBasis: `${size}px` } : {}; + const resizeHandlers: React.DetailedHTMLProps, HTMLSpanElement> = { + onMouseDown: event => { + this.addMouseEventListeners(); + this.resizeHandlerState = { + mousePosX: event.pageX + }; + } + }; return ( -
+
{this.renderCheckbox()} {_nowrap ?
{content}
: content} {this.renderSortIcon()} - {resizable && } + {resizable && }
); }