import * as React from "react"; import {ReactElement} from "react"; const cssStyle = require("./Table.scss"); export interface TableColumn { name: string; header: () => ReactElement | ReactElement[]; width?: number; fixedWidth?: string; className?: string; } export interface TableRow { columns: {[key: string]: () => ReactElement | ReactElement[]}; className?: string; userData?: T; } export interface TableProperties { columns: TableColumn[]; rows: TableRow[]; className?: string; headerClassName?: string; bodyClassName?: string; bodyOverlayOnly?: boolean; bodyOverlay?: () => ReactElement; hiddenColumns?: string[]; onHeaderContextMenu?: (event: React.MouseEvent) => void; onBodyContextMenu?: (event: React.MouseEvent) => void; onDrop?: (event: React.DragEvent) => void; onDragOver?: (event: React.DragEvent) => void; renderRow?: (row: TableRow, columns: TableColumn[], uniqueId: string) => React.ReactElement; } export interface TableState { hiddenColumns: string[]; } export interface TableRowProperties { columns: TableColumn[]; rowData: TableRow; } export interface TableRowState { hidden?: boolean; } export class TableRowElement extends React.Component, TableRowState> { constructor(props) { super(props); this.state = {}; } render() { if(this.state.hidden) return null; let totalWidth = this.props.columns.map(e => e.width | 0).reduce((a, b) => a + b, 0); if(totalWidth === 0) totalWidth = 1; const properties = Object.assign({}, this.props) as any; delete properties.rowData; delete properties.columns; properties.className = (properties.className || "") + " " + cssStyle.row; const children = Array.isArray(this.props.children) ? this.props.children : typeof this.props.children !== "undefined" ? [this.props.children] : []; return React.createElement("div", properties, ...this.props.columns.map(column => { const supplier = this.props.rowData.columns[column.name]; if(column.width) { return (
{supplier ? supplier() : undefined}
); } else if(column.fixedWidth) { return (
{supplier ? supplier() : undefined}
); } }), ...children); } } export class Table extends React.Component { private rowIndex = 0; private refHeader = React.createRef(); private refHiddenHeader = React.createRef(); private refBody = React.createRef(); private lastHeaderHeight = 20; private lastScrollbarWidth = 20; constructor(props) { super(props); this.state = { hiddenColumns: this.props.hiddenColumns || [] }; } render() { const columns = this.props.columns.filter(e => this.state.hiddenColumns.findIndex(b => e.name === b) === -1); let totalWidth = columns.map(e => e.width | 0).reduce((a, b) => a + b, 0); if(totalWidth === 0) totalWidth = 1; const rowRenderer = this.props.renderRow || ((row, columns, uniqueId) => { return ; }); let body; if(this.props.bodyOverlayOnly) { body = this.props.bodyOverlay ? this.props.bodyOverlay() : undefined; } else { body = this.props.rows.map((row: TableRow & { __rowIndex: number }) => { if(typeof row.__rowIndex !== "number") row.__rowIndex = ++this.rowIndex; return rowRenderer(row, columns, "tr-" + row.__rowIndex); }); if(this.props.bodyOverlay) { body.push(this.props.bodyOverlay()); } } return (
this.props.onDrop && this.props.onDrop(e)} onDragOver={e => this.props.onDragOver && this.props.onDragOver(e)} >
this.props.onHeaderContextMenu && this.props.onHeaderContextMenu(event)} > {columns.map(column => { if(column.width) { return (
{column.header()}
); } else if(column.fixedWidth) { return (
{column.header()}
); } })}
this.props.onBodyContextMenu && this.props.onBodyContextMenu(e)} >
{body}
); } componentDidUpdate(prevProps: Readonly, prevState: Readonly, snapshot?: any): void { if(!this.refHiddenHeader.current || !this.refHeader.current || !this.refBody.current) return; setTimeout(() => { this.lastHeaderHeight = this.refHeader.current.clientHeight; this.lastScrollbarWidth = this.refBody.current.parentElement.clientWidth - this.refBody.current.clientWidth; this.refHiddenHeader.current.style.height = this.lastHeaderHeight + "px"; this.refHeader.current.style.right = this.lastScrollbarWidth + "px"; }, 10); } }