import { action, computed, observable, ObservableMap, ObservableSet } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, StrListCast } from '../../../../fields/Doc'; import { List } from '../../../../fields/List'; import { Cast, CsvCast, NumCast, StrCast } from '../../../../fields/Types'; import { CsvField } from '../../../../fields/URLField'; import { Docs } from '../../../documents/Documents'; import { ViewBoxAnnotatableComponent } from '../../DocComponent'; import { FieldView, FieldViewProps } from '../FieldView'; import { PinProps } from '../trails'; import { LineChart } from './components/LineChart'; import { TableBox } from './components/TableBox'; import './DataVizBox.scss'; export enum DataVizView { TABLE = 'table', LINECHART = 'lineChart', } @observer export class DataVizBox extends ViewBoxAnnotatableComponent() { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DataVizBox, fieldKey); } // says we have an object and any string // 2 ways of doing it // @observable private pairs: { [key: string]: number | string | undefined }[] = []; // @observable private pairs: { [key: string]: FieldResult }[] = []; static pairSet = new ObservableMap(); @computed.struct get pairs() { return DataVizBox.pairSet.get(CsvCast(this.rootDoc[this.fieldKey]).url.href); } private _chartRenderer: LineChart | undefined; // // another way would be store a schema that defines the type of data we are expecting from an imported doc // method1() { // this.pairs[0].x = 3; // } // method() { // // this.pairs[0].x = 3; // // go through the pairs // const x = this.pairs[0].x; // if (typeof x == 'number') { // let x1 = Number(x); // // let x1 = NumCast(x); // } // } // could use field result // [key: string]: FieldResult; // instead of numeric x,y in there, // TODO: nda - use onmousedown and onmouseup when dragging and changing height and width to update the height and width props only when dragging stops @computed get dataVizView(): DataVizView { return StrCast(this.layoutDoc._dataVizView, 'table') as DataVizView; } @action restoreView = (data: Doc) => { const changedView = this.dataVizView !== data.presDataVizView && (this.layoutDoc._dataVizView = data.presDataVizView); const changedAxes = this.axes.join('') !== StrListCast(data.presDataVizAxes).join('') && (this.layoutDoc._data_vizAxes = new List(StrListCast(data.presDataVizAxes))); const func = () => this._chartRenderer?.restoreView(data); if (changedView || changedAxes) { setTimeout(func, 100); return true; } return func() ?? false; }; getAnchor = (addAsAnnotation?: boolean, pinProps?: PinProps) => { const anchor = !pinProps ? this.rootDoc : this._chartRenderer?.getAnchor(pinProps) ?? Docs.Create.ConfigDocument({ // when we clear selection -> we should have it so chartBox getAnchor returns undefined // this is for when we want the whole doc (so when the chartBox getAnchor returns without a marker) /*put in some options*/ }); anchor.presDataVizView = this.dataVizView; anchor.presDataVizAxes = this.axes.length ? new List(this.axes) : undefined; this.addDocument(anchor); return anchor; }; @computed.struct get axes() { return StrListCast(this.layoutDoc.data_vizAxes); } selectAxes = (axes: string[]) => (this.layoutDoc.data_vizAxes = new List(axes)); @computed get selectView() { const width = this.props.PanelWidth() * 0.9; const height = (this.props.PanelHeight() - 32) /* height of 'change view' button */ * 0.9; const margin = { top: 10, right: 25, bottom: 50, left: 25 }; if (!this.pairs) return 'no data'; // prettier-ignore switch (this.dataVizView) { case DataVizView.TABLE: return ; case DataVizView.LINECHART: return (this._chartRenderer = r ?? undefined)} height={height} width={width} fieldKey={this.fieldKey} margin={margin} rootDoc={this.rootDoc} axes={this.axes} pairs={this.pairs} dataDoc={this.dataDoc} />; } } @computed get dataUrl() { return Cast(this.dataDoc[this.fieldKey], CsvField); } componentDidMount() { this.props.setContentView?.(this); this.fetchData(); } fetchData() { if (DataVizBox.pairSet.has(CsvCast(this.rootDoc[this.fieldKey]).url.href)) return; DataVizBox.pairSet.set(CsvCast(this.rootDoc[this.fieldKey]).url.href, []); fetch('/csvData?uri=' + this.dataUrl?.url.href) // .then(res => res.json().then(action(res => !res.errno && DataVizBox.pairSet.set(CsvCast(this.rootDoc[this.fieldKey]).url.href, res)))); } // handle changing the view using a button @action changeViewHandler(e: React.MouseEvent) { e.preventDefault(); e.stopPropagation(); this.layoutDoc._dataVizView = this.dataVizView === DataVizView.TABLE ? DataVizView.LINECHART : DataVizView.TABLE; } render() { return !this.pairs?.length ? (
Loading...
) : (
e.stopPropagation()} ref={r => r?.addEventListener( 'wheel', // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this) (e: WheelEvent) => { if (!r.scrollTop && e.deltaY <= 0) e.preventDefault(); e.stopPropagation(); }, { passive: false } ) }> {this.selectView}
); } }