diff options
author | bobzel <zzzman@gmail.com> | 2024-05-03 10:23:46 -0400 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2024-05-03 10:23:46 -0400 |
commit | 723c8b33ade753764d1d02b130c189fb65e20425 (patch) | |
tree | e278304fdace45a2c38562e72a3ccd2e8e91b759 /src/client/views/nodes/DataVizBox/components/TableBox.tsx | |
parent | f410a7b314dd78244e18c9c52140b67b37ab0c87 (diff) | |
parent | 2caf7b7bb80b663b6ba585f88cdbd2d725f8505e (diff) |
Merge branch 'master' into nathan-starter
Diffstat (limited to 'src/client/views/nodes/DataVizBox/components/TableBox.tsx')
-rw-r--r-- | src/client/views/nodes/DataVizBox/components/TableBox.tsx | 224 |
1 files changed, 199 insertions, 25 deletions
diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index 1b239b5e5..67e1c67bd 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -12,7 +12,8 @@ import { ObservableReactComponent } from '../../../ObservableReactComponent'; import { DocumentView } from '../../DocumentView'; import { DataVizView } from '../DataVizBox'; import './Chart.scss'; -const { default: { DATA_VIZ_TABLE_ROW_HEIGHT } } = require('../../../global/globalCssVariables.module.scss'); // prettier-ignore +import { undoable } from '../../../../util/UndoManager'; +const { DATA_VIZ_TABLE_ROW_HEIGHT } = require('../../../global/globalCssVariables.module.scss'); // prettier-ignore interface TableBoxProps { Document: Doc; layoutDoc: Doc; @@ -37,6 +38,13 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> { _inputChangedDisposer?: IReactionDisposer; _containerRef: HTMLDivElement | null = null; + @observable settingTitle: boolean = false; // true when setting a title column + @observable hasRowsToFilter: boolean = false; // true when any rows are selected + @observable filtering: boolean = false; // true when the filtering menu is open + @observable filteringColumn: any = ''; // column to filter + @observable filteringType: string = 'Value'; // "Value" or "Range" + filteringVal: any[] = ['', '']; // value or range to filter the column with + @observable _scrollTop = -1; @observable _tableHeight = 0; @observable _tableContainerHeight = 0; @@ -49,6 +57,8 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> { // if the tableData changes (ie., when records are selected by the parent (input) visulization), // then we need to remove any selected rows that are no longer part of the visualized dataset. this._inputChangedDisposer = reaction(() => this._tableData.slice(), this.filterSelectedRowsDown, { fireImmediately: true }); + const selected = NumListCast(this._props.layoutDoc.dataViz_selectedRows); + if (selected.length > 0) this.hasRowsToFilter = true; this.handleScroll(); } componentWillUnmount() { @@ -64,9 +74,6 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> { @computed get parentViz() { return DocCast(this._props.Document.dataViz_parentViz); - // return LinkManager.Instance.getAllRelatedLinks(this._props.Document) // out of all links - // .filter(link => link.link_anchor_1 == this._props.Document.dataViz_parentViz) // get links where this chart doc is the target of the link - // .map(link => DocCast(link.link_anchor_1)); // then return the source of the link } @computed get columns() { @@ -115,6 +122,7 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> { } else selected?.push(rowId); } e.stopPropagation(); + this.hasRowsToFilter = selected.length > 0 ? true : false; }; columnPointerDown = (e: React.PointerEvent, col: string) => { @@ -155,15 +163,15 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> { }, emptyFunction, action(e => { - if (e.shiftKey){ - if (this._props.titleCol == col) this._props.titleCol = ""; + if (e.shiftKey || this.settingTitle) { + if (this.settingTitle) this.settingTitle = false; + if (this._props.titleCol == col) this._props.titleCol = ''; else this._props.titleCol = col; this._props.selectTitleCol(this._props.titleCol); - } - else{ + } else { const newAxes = this._props.axes; if (newAxes.includes(col)) newAxes.splice(newAxes.indexOf(col), 1); - else if (newAxes.length > 2) newAxes[newAxes.length-1] = col; + else if (newAxes.length > 2) newAxes[newAxes.length - 1] = col; else newAxes.push(col); this._props.selectAxes(newAxes); } @@ -171,6 +179,134 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> { ); }; + /** + * These functions handle the filtering popup for when the "filter" button is pressed to select rows + */ + filter = undoable((e: any) => { + var start: any; + var end: any; + if (this.filteringType == 'Range') { + start = (this.filteringVal[0] as Number) ? Number(this.filteringVal[0]) : this.filteringVal[0]; + end = (this.filteringVal[1] as Number) ? Number(this.filteringVal[1]) : this.filteringVal[0]; + } + + this._tableDataIds.forEach(rowID => { + if (this.filteringType == 'Value') { + if (this._props.records[rowID][this.filteringColumn] == this.filteringVal[0]) { + if (!NumListCast(this._props.layoutDoc.dataViz_selectedRows).includes(rowID)) { + this.tableRowClick(e, rowID); + } + } + } else { + let compare = this._props.records[rowID][this.filteringColumn]; + if (compare as Number) compare = Number(compare); + if (start <= compare && compare <= end) { + if (!NumListCast(this._props.layoutDoc.dataViz_selectedRows).includes(rowID)) { + this.tableRowClick(e, rowID); + } + } + } + }); + this.filtering = false; + this.filteringColumn = ''; + this.filteringVal = ['', '']; + }, 'filter table'); + @action + setFilterColumn = (e: any) => { + this.filteringColumn = e.currentTarget.value; + }; + @action + setFilterType = (e: any) => { + this.filteringType = e.currentTarget.value; + }; + changeFilterValue = action((e: React.ChangeEvent<HTMLInputElement>) => { + this.filteringVal[0] = e.target.value; + }); + changeFilterRange0 = action((e: React.ChangeEvent<HTMLInputElement>) => { + this.filteringVal[0] = e.target.value; + }); + changeFilterRange1 = action((e: React.ChangeEvent<HTMLInputElement>) => { + this.filteringVal[1] = e.target.value; + }); + @computed get renderFiltering() { + if (this.filteringColumn === '') this.filteringColumn = this.columns[0]; + return ( + <div className="tableBox-filterPopup" style={{ right: this._props.width * 0.05 }}> + <div className="tableBox-filterPopup-selectColumn"> + Column: + <select className="tableBox-filterPopup-selectColumn-each" value={this.filteringColumn != '' ? this.filteringColumn : this.columns[0]} onChange={e => this.setFilterColumn(e)}> + {this.columns.map(column => ( + <option className="" key={column} value={column}> + {' '} + {column}{' '} + </option> + ))} + </select> + </div> + <div className="tableBox-filterPopup-setValue"> + <select className="tableBox-filterPopup-setValue-each" value={this.filteringType} onChange={e => this.setFilterType(e)}> + <option className="" key={'Value'} value={'Value'}> + {' '} + {'Value'}{' '} + </option> + <option className="" key={'Range'} value={'Range'}> + {' '} + {'Range'}{' '} + </option> + </select> + : + {this.filteringType == 'Value' ? ( + <input + className="tableBox-filterPopup-setValue-input" + defaultValue="" + autoComplete="off" + onChange={this.changeFilterValue} + onKeyDown={e => { + e.stopPropagation(); + }} + type="text" + placeholder="" + id="search-input" + /> + ) : ( + <div> + <input + className="tableBox-filterPopup-setValue-input" + defaultValue="" + autoComplete="off" + onChange={this.changeFilterRange0} + onKeyDown={e => { + e.stopPropagation(); + }} + type="text" + placeholder="" + id="search-input" + style={{ width: this._props.width * 0.15 }} + /> + to + <input + className="tableBox-filterPopup-setValue-input" + defaultValue="" + autoComplete="off" + onChange={this.changeFilterRange1} + onKeyDown={e => { + e.stopPropagation(); + }} + type="text" + placeholder="" + id="search-input" + style={{ width: this._props.width * 0.15 }} + /> + </div> + )} + </div> + <div className="tableBox-filterPopup-setFilter"> + <Button onClick={action(e => this.filter(e))} text="Set Filter" type={Type.SEC} color={'black'} /> + </div> + </div> + ); + } + render() { if (this._tableData.length > 0) { return ( @@ -184,9 +320,39 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> { this._props.layoutDoc.dataViz_selectedRows = new List<number>(this._tableDataIds); } }}> - <div className="selectAll-buttons"> - <Button onClick={action(() => (this._props.layoutDoc.dataViz_selectedRows = new List<number>(this._tableDataIds)))} text="Select All" type={Type.SEC} color={'black'} /> - <Button onClick={action(() => (this._props.layoutDoc.dataViz_selectedRows = new List<number>()))} text="Deselect All" type={Type.SEC} color={'black'} /> + <div className="tableBox-selectButtons"> + <div className="tableBox-selectTitle"> + <Button onClick={action(() => (this.settingTitle = !this.settingTitle))} text="Select Title Column" type={Type.SEC} color={'black'} /> + </div> + <div className="tableBox-filtering"> + {this.filtering ? this.renderFiltering : null} + <Button onClick={action(() => (this.filtering = !this.filtering))} text="Filter" type={Type.SEC} color={'black'} /> + <div className="tableBox-filterAll"> + {this.hasRowsToFilter ? ( + <Button + onClick={action(() => { + this._props.layoutDoc.dataViz_selectedRows = new List<number>(); + this.hasRowsToFilter = false; + })} + text="Deselect All" + type={Type.SEC} + color={'black'} + tooltip="Select rows to be displayed in any DataViz boxes dragged off of this one." + /> + ) : ( + <Button + onClick={action(() => { + this._props.layoutDoc.dataViz_selectedRows = new List<number>(this._tableDataIds); + this.hasRowsToFilter = true; + })} + text="Select All" + type={Type.SEC} + color={'black'} + tooltip="Select rows to be displayed in any DataViz boxes dragged off of this one." + /> + )} + </div> + </div> </div> <div className={`tableBox-container ${this.columns[0]}`} @@ -220,15 +386,23 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> { <th key={this.columns.indexOf(col)} style={{ - color: this._props.axes.slice().reverse().lastElement() === col ? 'darkgreen' - : (this._props.axes.length>2 && this._props.axes.lastElement() === col) ? 'darkred' - : (this._props.axes.lastElement()===col || (this._props.axes.length>2 && this._props.axes[1]==col))? 'darkblue' : undefined, - background: this._props.axes.slice().reverse().lastElement() === col ? '#E3fbdb' - : (this._props.axes.length>2 && this._props.axes.lastElement() === col) ? '#Fbdbdb' - : (this._props.axes.lastElement()===col || (this._props.axes.length>2 && this._props.axes[1]==col))? '#c6ebf7' : undefined, - // blue: #ADD8E6 - // green: #E3fbdb - // red: #Fbdbdb + color: + this._props.axes.slice().reverse().lastElement() === col + ? 'darkgreen' + : this._props.axes.length > 2 && this._props.axes.lastElement() === col + ? 'darkred' + : this._props.axes.lastElement() === col || (this._props.axes.length > 2 && this._props.axes[1] == col) + ? 'darkblue' + : undefined, + background: this.settingTitle + ? 'lightgrey' + : this._props.axes.slice().reverse().lastElement() === col + ? '#E3fbdb' + : this._props.axes.length > 2 && this._props.axes.lastElement() === col + ? '#Fbdbdb' + : this._props.axes.lastElement() === col || (this._props.axes.length > 2 && this._props.axes[1] == col) + ? '#c6ebf7' + : undefined, fontWeight: 'bolder', border: '3px solid black', }} @@ -251,10 +425,10 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> { }}> {this.columns.map(col => { var colSelected = false; - if (this._props.axes.length>2) colSelected = this._props.axes[0]==col || this._props.axes[1]==col || this._props.axes[2]==col; - else if (this._props.axes.length>1) colSelected = this._props.axes[0]==col || this._props.axes[1]==col; - else if (this._props.axes.length>0) colSelected = this._props.axes[0]==col; - if (this._props.titleCol==col) colSelected = true; + if (this._props.axes.length > 2) colSelected = this._props.axes[0] == col || this._props.axes[1] == col || this._props.axes[2] == col; + else if (this._props.axes.length > 1) colSelected = this._props.axes[0] == col || this._props.axes[1] == col; + else if (this._props.axes.length > 0) colSelected = this._props.axes[0] == col; + if (this._props.titleCol == col) colSelected = true; return ( <td key={this.columns.indexOf(col)} style={{ border: colSelected ? '3px solid black' : '1px solid black', fontWeight: colSelected ? 'bolder' : 'normal' }}> <div className="tableBox-cell">{this._props.records[rowId][col]}</div> |