diff options
-rw-r--r-- | src/client/views/nodes/DataVizBox/DataVizBox.scss | 11 | ||||
-rw-r--r-- | src/client/views/nodes/DataVizBox/DataVizBox.tsx | 183 |
2 files changed, 148 insertions, 46 deletions
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.scss b/src/client/views/nodes/DataVizBox/DataVizBox.scss index 84d4b21a6..a3132dc6e 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.scss +++ b/src/client/views/nodes/DataVizBox/DataVizBox.scss @@ -1,4 +1,4 @@ -.dataviz { +.dataViz-box { overflow: auto; height: 100%; width: 100%; @@ -38,6 +38,15 @@ .button-container { pointer-events: unset; } + + .dataVizBox-annotationLayer{ + position: absolute; + transform-origin: left top; + top: 0; + width: 100%; + pointer-events: none; + mix-blend-mode: multiply; + } } .start-message { margin: 10px; diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 9692345a6..f3670622a 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -6,7 +6,7 @@ import { Doc, Field, Opt, StrListCast } from '../../../../fields/Doc'; import { List } from '../../../../fields/List'; import { Cast, CsvCast, DocCast, NumCast, StrCast } from '../../../../fields/Types'; import { CsvField } from '../../../../fields/URLField'; -import { DocUtils, Docs } from '../../../documents/Documents'; +import { Docs } from '../../../documents/Documents'; import { ViewBoxAnnotatableComponent } from '../../DocComponent'; import { FieldView, FieldViewProps } from '../FieldView'; import { PinProps } from '../trails'; @@ -17,12 +17,16 @@ import { TableBox } from './components/TableBox'; import './DataVizBox.scss'; import { UndoManager, undoable } from '../../../util/UndoManager'; import { SidebarAnnos } from '../../SidebarAnnos'; -import { PDFBox } from '../PDFBox'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { emptyFunction, setupMoveUpEvents } from '../../../../Utils'; -import { LinkManager } from '../../../util/LinkManager'; +import { emptyFunction, returnEmptyString, returnFalse, setupMoveUpEvents } from '../../../../Utils'; import { DocumentView } from '../DocumentView'; import { DocumentManager } from '../../../util/DocumentManager'; +import { TraceMobx } from '../../../../fields/util'; +import { MarqueeAnnotator } from '../../MarqueeAnnotator'; +import { InkTool } from '../../../../fields/InkField'; +import { AnchorMenu } from '../../pdf/AnchorMenu'; +import { GPTPopup } from '../../pdf/GPTPopup/GPTPopup'; +import { CollectionFreeFormView } from '../../collections/collectionFreeForm'; export enum DataVizView { TABLE = 'table', @@ -33,6 +37,41 @@ export enum DataVizView { @observer export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { + private _getAnchor: (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => Opt<Doc> = () => undefined; + private _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); + private _ffref = React.createRef<CollectionFreeFormView>(); + private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef(); + @observable _marqueeing: number[] | undefined; + @observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>(); + @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.rootDoc._freeform_scale, 1) <= NumCast(this.rootDoc.freeform_scaleMin, 1) && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) { + setupMoveUpEvents( + this, + e, + action(e => { + MarqueeAnnotator.clearAnnotations(this._savedAnnotations); + //this._marqueeing = [e.pageX, e.pageY]; + this._marqueeing = [e.clientX, e.clientY]; + return true; + }), + returnFalse, + () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), + false + ); + } + }; + @action + finishMarquee = () => { + this._getAnchor = AnchorMenu.Instance?.GetAnchor; + this._marqueeing = undefined; + this.props.select(false); + }; + savedAnnotations = () => this._savedAnnotations; + public static LayoutString(fieldStr: string) { return FieldView.LayoutString(DataVizBox, fieldStr); } @@ -83,10 +122,17 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { return func() ?? false; }; getAnchor = (addAsAnnotation?: boolean, pinProps?: PinProps) => { + const visibleAnchor = this._getAnchor?.(this._savedAnnotations, false); // use marquee anchor, otherwise, save zoom/pan as anchor const anchor = !pinProps ? this.rootDoc : this._vizRenderer?.getAnchor(pinProps) ?? + visibleAnchor ?? Docs.Create.ConfigDocument({ + title: 'ImgAnchor:' + this.rootDoc.title, + config_panX: NumCast(this.layoutDoc._freeform_panX), + config_panY: NumCast(this.layoutDoc._freeform_panY), + config_viewScale: Cast(this.layoutDoc._freeform_scale, 'number', null), + annotationOn: this.rootDoc, // 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*/ @@ -102,7 +148,9 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { anchor[key] = this.layoutDoc[key]; } }); + this.addDocument(anchor); + //addAsAnnotation && this.addDocument(anchor); return anchor; }; @@ -110,11 +158,6 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { const createFunc = undoable( action(() => { const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false), ['latitude', 'longitude', '-linkedTo']); - // if (note && this.selectedPin) { - // note.latitude = this.selectedPin.latitude; - // note.longitude = this.selectedPin.longitude; - // note.map = this.selectedPin.map; - // } }), 'create note annotation' ); @@ -195,32 +238,8 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { } sidebarWidth = () => (Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth(); sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => { - console.log('sideBarAddDoc') if (!this.SidebarShown) this.toggleSidebar(); return this.addDocument(doc, sidebarKey); - // if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar(); - // const docs = doc instanceof Doc ? [doc] : doc; - // docs.forEach(doc => { - // let existingPin = Docs.Create.TextDocument("test"); - // //let existingPin = this.allPushpins.find(pin => pin.latitude === doc.latitude && pin.longitude === doc.longitude) ?? this.selectedPin; - // // if (doc.latitude !== undefined && doc.longitude !== undefined && !existingPin) { - // // existingPin = this.createPushpin(NumCast(doc.latitude), NumCast(doc.longitude), StrCast(doc.map)); - // // } - // if (existingPin) { - // setTimeout(() => { - // // we use a timeout in case this is called from the sidebar which may have just added a link that hasn't made its way into th elink manager yet - // if (!LinkManager.Instance.getAllRelatedLinks(doc).some(link => DocCast(link.link_anchor_1)?.mapPin === existingPin || DocCast(link.link_anchor_2)?.mapPin === existingPin)) { - // // const anchor = this.getAnchor(true, undefined, existingPin); - // const anchor = this.getAnchor(true, undefined); - // anchor && DocUtils.MakeLink(anchor, doc, { link_relationship: 'link to datapoint' }); - // // doc.latitude = existingPin?.latitude; - // // doc.longitude = existingPin?.longitude; - // } - // }); - // } - // }); //add to annotation list - - // return this.addDocument(doc, sidebarKey); // add to sidebar list }; sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => this.removeDocument(doc, sidebarKey); @@ -259,35 +278,91 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { } }; + @action + onPointerDown = (e: React.PointerEvent): void => { + if ((this.props.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)) { + this.props.select(false); + MarqueeAnnotator.clearAnnotations(this._savedAnnotations); + this._marqueeing = [e.clientX, e.clientY]; + const target = e.target as any; + if (e.target && (target.className.includes('endOfContent') || (target.parentElement.className !== 'textLayer' && target.parentElement.parentElement?.className !== 'textLayer'))) { + } else { + // if textLayer is hit, then we select text instead of using a marquee so clear out the marquee. + setTimeout( + action(() => (this._marqueeing = undefined)), + 100 + ); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it. + + document.addEventListener('pointerup', this.onSelectEnd); + } + } + }; + + @action + onSelectEnd = (e: PointerEvent): void => { + this.props.select(false); + document.removeEventListener('pointerup', this.onSelectEnd); + + const sel = window.getSelection(); + if (sel) { + AnchorMenu.Instance.setSelectedText(sel.toString()); + } + + if (sel?.type === 'Range') { + AnchorMenu.Instance.jumpTo(e.clientX, e.clientY); + } + + // Changing which document to add the annotation to (the currently selected PDF) + GPTPopup.Instance.setSidebarId('data_sidebar'); + GPTPopup.Instance.addDoc = this.sidebarAddDocument; + }; + render() { return !this.records.length ? ( // displays how to get data into the DataVizBox if its empty <div className="start-message">To create a DataViz box, either import / drag a CSV file into your canvas or copy a data table and use the command 'ctrl + p' to bring the data table to your canvas.</div> ) : ( <div - className="dataViz" + className="dataViz-box" + onPointerDown={this.marqueeDown} style={{ pointerEvents: this.props.isContentActive() === true ? 'all' : 'none', }} onWheel={e => 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 } - ) - }> + ref={this._mainCont} + > <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} /> <Toggle text={'PIE CHART'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.PIECHART)} toggleStatus={this.layoutDoc._dataViz == DataVizView.PIECHART} /> </div> + + <CollectionFreeFormView + ref={this._ffref} + {...this.props} + setContentView={emptyFunction} + renderDepth={this.props.renderDepth + 1} + fieldKey={this.annotationKey} + styleProvider={this.props.styleProvider} + isAnnotationOverlay={true} + annotationLayerHostsContent={true} + PanelWidth={this.props.PanelWidth} + PanelHeight={this.props.PanelHeight} + select={emptyFunction} + isAnyChildContentActive={returnFalse} + whenChildContentsActiveChanged={this.whenChildContentsActiveChanged} + removeDocument={this.removeDocument} + moveDocument={this.moveDocument} + addDocument={this.addDocument}> + </CollectionFreeFormView> + {this.renderVizView()} - <div className="dataviz-sidebar" style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}> + <div className="dataviz-sidebar" + style={{ width: `${this.sidebarWidthPercent}`, + backgroundColor: `${this.sidebarColor}` }} + onPointerDown={this.onPointerDown}> <SidebarAnnos ref={this._sidebarRef} {...this.props} @@ -306,6 +381,24 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { /> </div> {this.sidebarHandle} + {this.annotationLayer} + {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : ( + <MarqueeAnnotator + rootDoc={this.rootDoc} + scrollTop={0} + down={this._marqueeing} + scaling={this.props.NativeDimScaling} + docView={this.props.docViewPath().slice(-1)[0]} + addDocument={this.sidebarAddDocument} + finishMarquee={this.finishMarquee} + savedAnnotations={this.savedAnnotations} + selectionText={returnEmptyString} + annotationLayer={this._annotationLayer.current} + mainCont={this._mainCont.current} + highlightDragSrcColor={''} + /> + + )} </div> ); } |