From 3b40f044b468256caf4ffd36a91fc7e68a2d13bb Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 29 Mar 2021 20:21:03 +0300 Subject: [PATCH] refactoring & fixes Signed-off-by: Roman --- src/renderer/components/dock/log-list.scss | 48 +++++++++---------- .../item-object-list/item-list-layout.tsx | 6 ++- src/renderer/components/table/table-cell.scss | 29 +++++++++++ src/renderer/components/table/table-cell.tsx | 32 +++++++++---- src/renderer/components/table/table-head.tsx | 1 + src/renderer/components/table/table.tsx | 43 ++++++++++++++--- .../components/virtual-list/virtual-list.scss | 7 +-- .../components/virtual-list/virtual-list.tsx | 35 +++++++------- 8 files changed, 135 insertions(+), 66 deletions(-) diff --git a/src/renderer/components/dock/log-list.scss b/src/renderer/components/dock/log-list.scss index 8a39dcf925..5a448eb10a 100644 --- a/src/renderer/components/dock/log-list.scss +++ b/src/renderer/components/dock/log-list.scss @@ -14,39 +14,35 @@ .VirtualList { height: 100%; - .list { - overflow-x: scroll!important; + .LogRow { + padding: 2px 16px; + height: 18px; // Must be equal to lineHeight variable in pod-log-list.tsx + font-family: var(--font-monospace); + font-size: smaller; + white-space: nowrap; - .LogRow { - padding: 2px 16px; - height: 18px; // Must be equal to lineHeight variable in pod-log-list.tsx - font-family: var(--font-monospace); - font-size: smaller; - white-space: nowrap; + &:hover { + background: var(--logRowHoverBackground); + } - &:hover { - background: var(--logRowHoverBackground); - } + span { + -webkit-font-smoothing: auto; // Better readability on non-retina screens + } + + span.overlay { + border-radius: 2px; + -webkit-font-smoothing: auto; + background-color: var(--overlay-bg); span { - -webkit-font-smoothing: auto; // Better readability on non-retina screens + background-color: var(--overlay-bg) !important; // Rewriting inline styles from AnsiUp library } - span.overlay { - border-radius: 2px; - -webkit-font-smoothing: auto; - background-color: var(--overlay-bg); + &.active { + background-color: var(--overlay-active-bg); span { - background-color: var(--overlay-bg)!important; // Rewriting inline styles from AnsiUp library - } - - &.active { - background-color: var(--overlay-active-bg); - - span { - background-color: var(--overlay-active-bg)!important; // Rewriting inline styles from AnsiUp library - } + background-color: var(--overlay-active-bg) !important; // Rewriting inline styles from AnsiUp library } } } @@ -59,7 +55,7 @@ &.isScrollHidden { .VirtualList .list { - overflow-x: hidden!important; // fixing scroll to bottom issues in PodLogs + overflow-x: hidden !important; // fixing scroll to bottom issues in PodLogs } } 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 a6ed57b7c1..d2ddc6c6d2 100644 --- a/src/renderer/components/item-object-list/item-list-layout.tsx +++ b/src/renderer/components/item-object-list/item-list-layout.tsx @@ -407,10 +407,12 @@ export class ItemListLayout extends React.Component { )} {renderTableHeader.map((cellProps, index) => { if (!this.isHiddenColumn(cellProps)) { + const storageId = cellProps.id ?? cellProps.storageId; + return ( ); diff --git a/src/renderer/components/table/table-cell.scss b/src/renderer/components/table/table-cell.scss index deef61b3bb..1ee56a6ce7 100644 --- a/src/renderer/components/table/table-cell.scss +++ b/src/renderer/components/table/table-cell.scss @@ -19,8 +19,37 @@ } } + &.warning { + flex: 0 0 40px !important; + } + &.resizable { position: relative; // required for resizing-anchor with `position:absolute` + min-width: 50px; + + > .ResizingAnchor { + width: 18px; + } + + .TableHead:hover & { + > .ResizingAnchor { + opacity: .25; + + &:hover { + opacity: .75; + } + + &:after { + font: 20px "Material Icons"; + content: "compress"; + position: absolute; + left: -8px; + top: 10px; + transform: rotate(90deg); + transform-origin: center; + } + } + } &.resizing { filter: invert(50%); diff --git a/src/renderer/components/table/table-cell.tsx b/src/renderer/components/table/table-cell.tsx index 19a8ab0ea2..c57b954ffc 100644 --- a/src/renderer/components/table/table-cell.tsx +++ b/src/renderer/components/table/table-cell.tsx @@ -27,9 +27,9 @@ export interface TableCellProps extends React.DOMAttributes { title?: ReactNode; /** * Allow to resize width and save to local storage, default: true - * (applicable only with props.id) */ isResizable?: boolean; + onResizeEnd?: () => void; checkbox?: boolean; // render cell with a checkbox isChecked?: boolean; // mark checkbox as checked or not renderBoolean?: boolean; // show "true" or "false" for all of the children elements are "typeof boolean" @@ -43,7 +43,7 @@ export interface TableCellProps extends React.DOMAttributes { @observer export class TableCell extends React.Component { - private elem: HTMLElement; + @observable.ref elem?: HTMLElement; static defaultProps: TableCellProps = { isResizable: true, @@ -97,7 +97,9 @@ export class TableCell extends React.Component { } @computed get columnSize(): number { - return getColumnSize(this.props.tableId, this.columnId) ?? 0; + const savedSize = getColumnSize(this.props.tableId, this.columnId); + + return savedSize ?? this.elem?.offsetWidth; } @computed get isResizable(): boolean { @@ -113,6 +115,7 @@ export class TableCell extends React.Component { if (this.isResizable && this.columnSize) { styles.flexGrow = 0; + styles.flexShrink = 0; styles.flexBasis = this.columnSize; } @@ -123,10 +126,20 @@ export class TableCell extends React.Component { onResize(extent: number) { const { tableId } = this.props; const { columnId } = this; - const size = extent || this.elem?.offsetWidth; // persist state in storage - setColumnSize({ tableId, columnId, size }); + setColumnSize({ tableId, columnId, size: extent }); + } + + @autobind() + onResizeStart() { + this.isResizing = true; + } + + @autobind() + onResizeEnd() { + this.isResizing = false; + this.props.onResizeEnd?.(); } @autobind() @@ -136,7 +149,7 @@ export class TableCell extends React.Component { render() { const { - className, checkbox, isChecked, isResizable, sortBy, + className, checkbox, isChecked, isResizable, sortBy, onResizeEnd, _sort, _sorting, _nowrap, children, title, tableId, storageId, renderBoolean: displayBoolean, showWithColumn, ...cellProps @@ -147,7 +160,7 @@ export class TableCell extends React.Component { nowrap: _nowrap, sorting: this.isSortable, resizing: this.isResizing, - resizable: isResizable, + resizable: this.isResizable, }); const content = displayBooleans(displayBoolean, title || children); @@ -158,12 +171,13 @@ export class TableCell extends React.Component { {this.renderSortIcon()} {this.isResizable && ( this.columnSize} - onStart={() => this.isResizing = true} - onEnd={() => this.isResizing = false} + onStart={this.onResizeStart} + onEnd={this.onResizeEnd} onDrag={this.onResize} /> )} diff --git a/src/renderer/components/table/table-head.tsx b/src/renderer/components/table/table-head.tsx index 32b4d0b797..af55104271 100644 --- a/src/renderer/components/table/table-head.tsx +++ b/src/renderer/components/table/table-head.tsx @@ -10,6 +10,7 @@ export interface TableHeadProps extends React.DOMAttributes { showTopLine?: boolean; // show border line at the top sticky?: boolean; // keep header on top when scrolling nowrap?: boolean; // white-space: nowrap, align inner in one line + style?: React.CSSProperties; } export class TableHead extends React.Component { diff --git a/src/renderer/components/table/table.tsx b/src/renderer/components/table/table.tsx index 60094e930e..d5b4799012 100644 --- a/src/renderer/components/table/table.tsx +++ b/src/renderer/components/table/table.tsx @@ -1,7 +1,7 @@ import "./table.scss"; import React from "react"; -import { orderBy } from "lodash"; +import { orderBy, throttle } from "lodash"; import { observer } from "mobx-react"; import { observable } from "mobx"; import { autobind, cssNames, noop } from "../../utils"; @@ -57,6 +57,8 @@ export const orderByUrlParam = createPageParam({ @observer export class Table extends React.Component { + @observable.ref elemRef = React.createRef(); + static defaultProps: TableProps = { scrollable: true, autoSize: true, @@ -73,6 +75,14 @@ export class Table extends React.Component { this.props.sortByDefault, ); + componentDidMount() { + window.addEventListener("resize", this.refreshDimensions); + } + + componentWillUnmount() { + window.removeEventListener("resize", this.refreshDimensions); + } + renderHead() { const { sortable, children } = this.props; const content = React.Children.toArray(children) as (TableRowElem | TableHeadElem)[]; @@ -83,6 +93,10 @@ export class Table extends React.Component { const columns = React.Children.toArray(headElem.props.children) as TableCellElem[]; return React.cloneElement(headElem, { + style: { + ...(headElem.props.style ?? {}), + width: this.contentWidth, + }, children: columns.map(elem => { if (elem.props.checkbox) { return elem; @@ -98,6 +112,10 @@ export class Table extends React.Component { _sort: this.sort, _sorting: this.sortParams, _nowrap: headElem.props.nowrap, + onResizeEnd: () => { + elem.props.onResizeEnd?.(); + this.refreshDimensions(); + } }); }) }); @@ -180,6 +198,10 @@ export class Table extends React.Component { getRow={getTableRow} selectedItemId={selectedItemId} className={className} + // must match to table's content width for proper scrolling header and virtual-list items. + // required if some column(s) are resized and overall content-area more than 100%. + // why: table & virtual-list has own scrolling areas and table-head is sticky.. + width={this.contentWidth} /> ); } @@ -187,16 +209,25 @@ export class Table extends React.Component { return rows; } - render() { - const { selectable, scrollable, sortable, autoSize, virtual } = this.props; - let { className } = this.props; + @observable refreshKey: number; - className = cssNames("Table flex column", className, { + get contentWidth() { + return this.elemRef.current?.scrollWidth; + } + + refreshDimensions = throttle(() => { + // using "full refresh" with changing "key" as this.forceUpdate() don't update some internals + this.refreshKey = Math.random(); + }, 250); + + render() { + const { selectable, scrollable, sortable, autoSize, virtual, className } = this.props; + const classNames = cssNames("Table flex column", className, { selectable, scrollable, sortable, autoSize, virtual, }); return ( -
+
{this.renderHead()} {this.renderRows()}
diff --git a/src/renderer/components/virtual-list/virtual-list.scss b/src/renderer/components/virtual-list/virtual-list.scss index a7e1deda36..858eddacbb 100644 --- a/src/renderer/components/virtual-list/virtual-list.scss +++ b/src/renderer/components/virtual-list/virtual-list.scss @@ -1,7 +1,4 @@ .VirtualList { - overflow: hidden; - - > .list { - overflow-y: overlay!important; - } + overflow-x: hidden !important; + overflow-y: overlay !important; } \ No newline at end of file diff --git a/src/renderer/components/virtual-list/virtual-list.tsx b/src/renderer/components/virtual-list/virtual-list.tsx index 025fe59d93..9ae39f22f2 100644 --- a/src/renderer/components/virtual-list/virtual-list.tsx +++ b/src/renderer/components/virtual-list/virtual-list.tsx @@ -32,7 +32,7 @@ interface State { } const defaultProps: Partial = { - width: "100%", + width: "auto", initialOffset: 1, readyOffset: 10, onScroll: noop @@ -52,7 +52,7 @@ export class VirtualList extends Component { componentDidMount() { this.setListHeight(); this.scrollToSelectedItem(); - new ResizeSensor(this.parentRef.current as any as Element, this.setListHeight); + new ResizeSensor(this.parentRef.current, this.setListHeight); this.setState({ overscanCount: this.props.readyOffset }); } @@ -100,22 +100,21 @@ export class VirtualList extends Component { }; return ( -
- - {Row} - -
+ + {Row} + ); } }