diff options
Diffstat (limited to 'src/client/views/nodes/DataVizBox/DataVizBox.tsx')
-rw-r--r-- | src/client/views/nodes/DataVizBox/DataVizBox.tsx | 181 |
1 files changed, 87 insertions, 94 deletions
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index ea053b09d..742dc60d1 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -1,6 +1,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Colors, Toggle, ToggleType, Type } from 'browndash-components'; -import { ObservableMap, action, computed, observable, runInAction } from 'mobx'; +import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { emptyFunction, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents } from '../../../../Utils'; @@ -14,13 +14,13 @@ import { TraceMobx } from '../../../../fields/util'; import { DocUtils, Docs } from '../../../documents/Documents'; import { DocumentManager } from '../../../util/DocumentManager'; import { UndoManager, undoable } from '../../../util/UndoManager'; -import { ViewBoxAnnotatableComponent } from '../../DocComponent'; +import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../../DocComponent'; import { MarqueeAnnotator } from '../../MarqueeAnnotator'; import { SidebarAnnos } from '../../SidebarAnnos'; import { AnchorMenu } from '../../pdf/AnchorMenu'; import { GPTPopup } from '../../pdf/GPTPopup/GPTPopup'; -import { DocFocusOptions, DocumentView } from '../DocumentView'; -import { FieldView, FieldViewProps } from '../FieldView'; +import { DocumentView } from '../DocumentView'; +import { FocusViewOptions, FieldView, FieldViewProps } from '../FieldView'; import { PinProps } from '../trails'; import './DataVizBox.scss'; import { Histogram } from './components/Histogram'; @@ -37,22 +37,28 @@ export enum DataVizView { } @observer -export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { +export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implements ViewBoxInterface { private _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); private _marqueeref = React.createRef<MarqueeAnnotator>(); private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef(); + private _disposers: { [name: string]: IReactionDisposer } = {}; anchorMenuClick?: () => undefined | ((anchor: Doc) => void); crop: ((region: Doc | undefined, addCrop?: boolean) => Doc | undefined) | undefined; @observable _marqueeing: number[] | undefined = undefined; @observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>(); - @observable schemaBoxLength: any = 0; + + constructor(props: FieldViewProps) { + super(props); + makeObservable(this); + this._props.setContentViewBox?.(this); + } @computed get annotationLayer() { TraceMobx(); return <div className="dataVizBox-annotationLayer" style={{ height: this._props.PanelHeight(), width: this._props.PanelWidth() }} ref={this._annotationLayer} />; } marqueeDown = (e: React.PointerEvent) => { - if (!e.altKey && e.button === 0 && NumCast(this.Document._freeform_scale, 1) <= NumCast(this.Document.freeform_scaleMin, 1) && this._props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) { + if (!e.altKey && e.button === 0 && NumCast(this.Document._freeform_scale, 1) <= NumCast(this.Document.freeform_scaleMin, 1) && this._props.isContentActive() && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) { setupMoveUpEvents( this, e, @@ -218,12 +224,12 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { .ScreenToLocalTransform() .scale(this._props.NativeDimScaling?.() || 1) .transformDirection(delta[0], delta[1]); - const fullWidth = this._props.width; - const mapWidth = fullWidth! - this.sidebarWidth(); + const fullWidth = NumCast(this.layoutDoc._width); + const mapWidth = fullWidth - this.sidebarWidth(); if (this.sidebarWidth() + localDelta[0] > 0) { this.layoutDoc._layout_showSidebar = true; - this.layoutDoc._width = fullWidth! + localDelta[0]; - this.layoutDoc._layout_sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth! + localDelta[0])).toString() + '%'; + this.layoutDoc._layout_sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%'; + this.layoutDoc._width = fullWidth + localDelta[0]; } else { this.layoutDoc._layout_showSidebar = false; this.layoutDoc._width = mapWidth; @@ -235,7 +241,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { () => UndoManager.RunInBatch(this.toggleSidebar, 'toggle sidebar') ); }; - getView = async (doc: Doc, options: DocFocusOptions) => { + getView = async (doc: Doc, options: FocusViewOptions) => { if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) { options.didMove = true; this.toggleSidebar(); @@ -256,14 +262,73 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => this.removeDocument(doc, sidebarKey); componentDidMount() { - this._props.setContentView?.(this); + this._props.setContentViewBox?.(this); if (!DataVizBox.dataset.has(CsvCast(this.dataDoc[this.fieldKey]).url.href)) this.fetchData(); + this._disposers.datavis = reaction( + () => { + if (this.layoutDoc.dataViz_schemaLive==undefined) this.layoutDoc.dataViz_schemaLive = true; + const getFrom = DocCast(this.layoutDoc.dataViz_asSchema); + const keys = Cast(getFrom?.schema_columnKeys, listSpec('string'))?.filter(key => key != 'text'); + if (!keys) return; + const children = DocListCast(getFrom[Doc.LayoutFieldKey(getFrom)]); + var current: { [key: string]: string }[] = []; + children + .filter(child => child) + .forEach(child => { + const row: { [key: string]: string } = {}; + keys.forEach(key => { + var cell = child[key]; + if (cell && (cell as string)) cell = cell.toString().replace(/\,/g, ''); + row[key] = StrCast(cell); + }); + current.push(row); + }); + if (!this.layoutDoc._dataViz_schemaOG){ // makes a copy of the original table for the "live" toggle + let csvRows = []; + csvRows.push(keys.join(',')); + for (let i = 0; i < children.length; i++) { + let eachRow = []; + for (let j = 0; j < keys.length; j++) { + var cell = children[i][keys[j]]; + if (cell && (cell as string)) cell = cell.toString().replace(/\,/g, ''); + eachRow.push(cell); + } + csvRows.push(eachRow); + } + const blob = new Blob([csvRows.join('\n')], { type: 'text/csv' }); + const options = { x: 0, y: 0, title: 'schemaTable for static dataviz', _width: 300, _height: 100, type: 'text/csv' }; + const file = new File([blob], 'schemaTable for static dataviz', options); + const loading = Docs.Create.LoadingDocument(file, options); + DocUtils.uploadFileToDoc(file, {}, loading); + this.layoutDoc._dataViz_schemaOG = loading; + } + const ogDoc = this.layoutDoc._dataViz_schemaOG as Doc + const ogHref = CsvCast(ogDoc[this.fieldKey])? CsvCast(ogDoc[this.fieldKey]).url.href : undefined; + const href = CsvCast(this.Document[this.fieldKey]).url.href + if (ogHref && !DataVizBox.datasetSchemaOG.has(href)){ // sets original dataset to the var + DataVizBox.datasetSchemaOG.set(href, []); + fetch('/csvData?uri=' + ogHref) + .then(res => res.json().then(action(res => !res.errno && DataVizBox.datasetSchemaOG.set(href, res)))); + } + return current; + }, + current => { + if (current) { + const href = CsvCast(this.Document[this.fieldKey]).url.href; + if (this.layoutDoc.dataViz_schemaLive) DataVizBox.dataset.set(href, current); + else DataVizBox.dataset.set(href, DataVizBox.datasetSchemaOG.get(href)!); + } + }, + { fireImmediately: true } + ); } fetchData = () => { - DataVizBox.dataset.set(CsvCast(this.dataDoc[this.fieldKey]).url.href, []); // assign temporary dataset as a lock to prevent duplicate server requests - fetch('/csvData?uri=' + this.dataUrl?.url.href) // - .then(res => res.json().then(action(res => !res.errno && DataVizBox.dataset.set(CsvCast(this.dataDoc[this.fieldKey]).url.href, res)))); + if (!this.Document.dataViz_asSchema) { + DataVizBox.dataset.set(CsvCast(this.dataDoc[this.fieldKey]).url.href, []); // assign temporary dataset as a lock to prevent duplicate server requests + fetch('/csvData?uri=' + this.dataUrl?.url.href) // + .then(res => res.json().then(action(res => !res.errno && DataVizBox.dataset.set(CsvCast(this.dataDoc[this.fieldKey]).url.href, res)))); + } }; // toggles for user to decide which chart type to view the data in @@ -277,12 +342,12 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { titleCol: this.titleCol, //width: this.SidebarShown? this._props.PanelWidth()*.9/1.2: this._props.PanelWidth() * 0.9, height: (this._props.PanelHeight() / scale - 32) /* height of 'change view' button */ * 0.9, - width: (this._props.PanelWidth() / scale) * 0.9, + width: ((this._props.PanelWidth() - this.sidebarWidth()) / scale) * 0.9, margin: { top: 10, right: 25, bottom: 75, left: 45 }, }; if (!this.records.length) return 'no data/visualization'; switch (this.dataVizView) { - case DataVizView.TABLE: return <TableBox {...sharedProps} docView={this._props.DocumentView} selectAxes={this.selectAxes} selectTitleCol={this.selectTitleCol}/>; + case DataVizView.TABLE: return <TableBox {...sharedProps} docView={this.DocumentView} selectAxes={this.selectAxes} selectTitleCol={this.selectTitleCol}/>; case DataVizView.LINECHART: return <LineChart {...sharedProps} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => (this._vizRenderer = r ?? undefined)} vizBox={this} />; case DataVizView.HISTOGRAM: return <Histogram {...sharedProps} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => (this._vizRenderer = r ?? undefined)} />; case DataVizView.PIECHART: return <PieChart {...sharedProps} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => (this._vizRenderer = r ?? undefined)} @@ -293,7 +358,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { @action onPointerDown = (e: React.PointerEvent): void => { if ((this.Document._freeform_scale || 1) !== 1) return; - if (!e.altKey && e.button === 0 && this._props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) { + if (!e.altKey && e.button === 0 && this._props.isContentActive() && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) { this._props.select(false); MarqueeAnnotator.clearAnnotations(this._savedAnnotations); this._marqueeing = [e.clientX, e.clientY]; @@ -333,81 +398,9 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { @action changeLiveSchemaCheckbox = () => { this.layoutDoc.dataViz_schemaLive = !this.layoutDoc.dataViz_schemaLive - this.updateSchemaViz(); } - @action - setDataset(href: any, data: any) { - DataVizBox.dataset.set(href, data); - } - - @action - // handles when the dataviz doc was made by copying a schema table - updateSchemaViz = () => { - const href = CsvCast(this.Document[this.fieldKey]).url.href; - const getFromDoc = DocCast(this.layoutDoc.dataViz_asSchema); - const children = DocListCast(getFromDoc[Doc.LayoutFieldKey(getFromDoc)]); - - if (children.length != this.schemaBoxLength) { - if (this.layoutDoc.dataViz_schemaLive || !DataVizBox.datasetSchemaOG.has(href)){ - const keys = Cast(getFromDoc.schema_columnKeys, listSpec('string'))?.filter(key => key != 'text'); - if (!keys) return; - - if (!this.layoutDoc._dataViz_schemaOG){ - // makes a copy of the original table for the "live" toggle - let csvRows = []; - csvRows.push(keys.join(',')); - for (let i = 0; i < children.length; i++) { - let eachRow = []; - for (let j = 0; j < keys.length; j++) { - var cell = children[i][keys[j]]; - if (cell && (cell as string)) cell = cell.toString().replace(/\,/g, ''); - eachRow.push(cell); - } - csvRows.push(eachRow); - } - const blob = new Blob([csvRows.join('\n')], { type: 'text/csv' }); - const options = { x: 0, y: 0, title: 'schemaTable for static dataviz', _width: 300, _height: 100, type: 'text/csv' }; - const file = new File([blob], 'schemaTable for static dataviz', options); - const loading = Docs.Create.LoadingDocument(file, options); - DocUtils.uploadFileToDoc(file, {}, loading); - this.layoutDoc._dataViz_schemaOG = loading; - } - - const ogDoc = this.layoutDoc._dataViz_schemaOG as Doc - const ogHref = CsvCast(ogDoc[this.fieldKey])? CsvCast(ogDoc[this.fieldKey]).url.href : undefined; - if (ogHref && !DataVizBox.datasetSchemaOG.has(href)){ - DataVizBox.datasetSchemaOG.set(href, []); - fetch('/csvData?uri=' + ogHref) - .then(res => res.json().then(action(res => !res.errno && DataVizBox.datasetSchemaOG.set(href, res)))); - } - - var current: { [key: string]: string }[] = []; - for (let i = 0; i < children.length; i++) { - var row: { [key: string]: string } = {}; - if (children[i]) { - for (let j = 0; j < keys.length; j++) { - var cell = children[i][keys[j]]; - if (cell && (cell as string)) cell = cell.toString().replace(/\,/g, ''); - row[keys[j]] = StrCast(cell); - } - } - current.push(row); - } - this.setDataset(href, current); - } - else { - this.setDataset(href, DataVizBox.datasetSchemaOG.get(href)); - } - } - }; - render() { - if (this.layoutDoc.dataViz_schemaLive == undefined) this.layoutDoc.dataViz_schemaLive = true; - if (this.layoutDoc && this.layoutDoc.dataViz_asSchema) { - this.updateSchemaViz(); - } - const scale = this._props.NativeDimScaling?.() || 1; return !this.records.length ? ( // displays how to get data into the DataVizBox if its empty @@ -425,7 +418,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { }} onWheel={e => e.stopPropagation()} ref={this._mainCont}> - <div className={'datatype-button'}> + <div className="datatype-button"> <Toggle text={' TABLE '} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.TABLE)} toggleStatus={this.layoutDoc._dataViz === DataVizView.TABLE} /> <Toggle text={'LINECHART'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.LINECHART)} toggleStatus={this.layoutDoc._dataViz === DataVizView.LINECHART} /> <Toggle text={'HISTOGRAM'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.HISTOGRAM)} toggleStatus={this.layoutDoc._dataViz === DataVizView.HISTOGRAM} /> @@ -461,7 +454,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { </div> {this.sidebarHandle} {this.annotationLayer} - {!this._mainCont.current || !this._annotationLayer.current ? null : ( + {!this._mainCont.current || !this.DocumentView || !this._annotationLayer.current ? null : ( <MarqueeAnnotator ref={this._marqueeref} Document={this.Document} @@ -469,7 +462,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { scrollTop={0} annotationLayerScrollTop={NumCast(this.Document._layout_scrollTop)} scaling={returnOne} - docView={this._props.DocumentView!} + docView={this.DocumentView} addDocument={this.sidebarAddDocument} finishMarquee={this.finishMarquee} savedAnnotations={this.savedAnnotations} |