diff options
| author | ab <abdullah_ahmed@brown.edu> | 2019-07-14 23:39:56 -0400 |
|---|---|---|
| committer | ab <abdullah_ahmed@brown.edu> | 2019-07-14 23:39:56 -0400 |
| commit | b7dd805549c5cdb6b583312e5e9637f9f3e0ee93 (patch) | |
| tree | 24217f91f213e57c503d226bae1475acb84c6a1d /src/client/views/collections/CollectionStackingView.tsx | |
| parent | baf446a5d65f8e0203eb3c7fb2f43d62a6997daa (diff) | |
| parent | d7c6f0da00d4ed56d28f679d6f7de1002684864a (diff) | |
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web
Diffstat (limited to 'src/client/views/collections/CollectionStackingView.tsx')
| -rw-r--r-- | src/client/views/collections/CollectionStackingView.tsx | 229 |
1 files changed, 139 insertions, 90 deletions
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 088a9bae8..fe01103d6 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -1,6 +1,6 @@ import React = require("react"); import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, computed, IReactionDisposer, reaction } from "mobx"; +import { action, computed, IReactionDisposer, reaction, untracked } from "mobx"; import { observer } from "mobx-react"; import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; @@ -10,7 +10,10 @@ import { ContextMenu } from "../ContextMenu"; import { CollectionSchemaPreview } from "./CollectionSchemaView"; import "./CollectionStackingView.scss"; import { CollectionSubView } from "./CollectionSubView"; -import { resolve } from "bluebird"; +import { undoBatch } from "../../util/UndoManager"; +import { DragManager } from "../../util/DragManager"; +import { DocumentType } from "../../documents/Documents"; +import { Transform } from "../../util/Transform"; @observer export class CollectionStackingView extends CollectionSubView(doc => doc) { @@ -18,29 +21,20 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { _draggerRef = React.createRef<HTMLDivElement>(); _heightDisposer?: IReactionDisposer; _gridSize = 1; + _docXfs: any[] = []; @computed get xMargin() { return NumCast(this.props.Document.xMargin, 2 * this.gridGap); } @computed get yMargin() { return NumCast(this.props.Document.yMargin, 2 * this.gridGap); } @computed get gridGap() { return NumCast(this.props.Document.gridGap, 10); } @computed get singleColumn() { return BoolCast(this.props.Document.singleColumn, true); } @computed get columnWidth() { return this.singleColumn ? (this.props.PanelWidth() / (this.props as any).ContentScaling() - 2 * this.xMargin) : Math.min(this.props.PanelWidth() - 2 * this.xMargin, NumCast(this.props.Document.columnWidth, 250)); } + @computed get filteredChildren() { return this.childDocs.filter(d => !d.isMinimized); } - singleColDocHeight(d: Doc) { - let nw = NumCast(d.nativeWidth); - let nh = NumCast(d.nativeHeight); - let aspect = nw && nh ? nh / nw : 1; - let wid = Math.min(d[WidthSym](), this.columnWidth); - return (nw && nh) ? wid * aspect : d[HeightSym](); - } componentDidMount() { this._heightDisposer = reaction(() => [this.yMargin, this.gridGap, this.columnWidth, this.childDocs.map(d => [d.height, d.width, d.zoomBasis, d.nativeHeight, d.nativeWidth, d.isMinimized])], - () => { - if (this.singleColumn) { - let children = this.childDocs.filter(d => !d.isMinimized); - this.props.Document.height = children.reduce((height, d, i) => - height + this.singleColDocHeight(d) + (i === children.length - 1 ? this.yMargin : this.gridGap) - , this.yMargin); - } - }, { fireImmediately: true }); + () => this.singleColumn && + (this.props.Document.height = this.filteredChildren.reduce((height, d, i) => + height + this.getDocHeight(d) + (i === this.filteredChildren.length - 1 ? this.yMargin : this.gridGap), this.yMargin)) + , { fireImmediately: true }); } componentWillUnmount() { if (this._heightDisposer) this._heightDisposer(); @@ -48,85 +42,89 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { @action moveDocument = (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean): boolean => { - this.props.removeDocument(doc); - addDocument(doc); - return true; - } - getDocTransform(doc: Doc, dref: HTMLDivElement) { - let { scale, translateX, translateY } = Utils.GetScreenTransform(dref); - let outerXf = Utils.GetScreenTransform(this._masonryGridRef!); - let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY); - return this.props.ScreenToLocalTransform().translate(offset[0], offset[1]).scale(NumCast(doc.width, 1) / this.columnWidth); + return this.props.removeDocument(doc) && addDocument(doc); } createRef = (ele: HTMLDivElement | null) => { this._masonryGridRef = ele; this.createDropTarget(ele!); } - @computed - get singleColumnChildren() { - let children = this.childDocs.filter(d => !d.isMinimized); - return children.map((d, i) => { - let layoutDoc = Doc.expandTemplateLayout(d, this.props.DataDoc); - let dref = React.createRef<HTMLDivElement>(); - let dxf = () => this.getDocTransform(layoutDoc, dref.current!).scale(this.columnWidth / d[WidthSym]()); - let width = () => d.nativeWidth ? Math.min(d[WidthSym](), this.columnWidth) : this.columnWidth; - let height = () => this.singleColDocHeight(layoutDoc); - return <div className="collectionStackingView-columnDoc" - key={d[Id]} - ref={dref} - style={{ width: width(), height: height() }} > - <CollectionSchemaPreview - Document={layoutDoc} - DataDocument={d != this.props.DataDoc ? this.props.DataDoc : undefined} - renderDepth={this.props.renderDepth} - width={width} - height={height} - getTransform={dxf} - CollectionView={this.props.CollectionView} - addDocument={this.props.addDocument} - moveDocument={this.props.moveDocument} - removeDocument={this.props.removeDocument} - active={this.props.active} - whenActiveChanged={this.props.whenActiveChanged} - addDocTab={this.props.addDocTab} - setPreviewScript={emptyFunction} - previewScript={undefined}> - </CollectionSchemaPreview> - </div>; - }); + overlays = (doc: Doc) => { + return doc.type === DocumentType.IMG || doc.type === DocumentType.VID ? { title: "title", caption: "caption" } : {}; + } + + getDisplayDoc(layoutDoc: Doc, d: Doc, dxf: () => Transform) { + let resolvedDataDoc = !this.props.Document.isTemplate && this.props.DataDoc !== this.props.Document ? this.props.DataDoc : undefined; + let width = () => d.nativeWidth ? Math.min(layoutDoc[WidthSym](), this.columnWidth) : this.columnWidth; + let height = () => this.getDocHeight(layoutDoc); + let finalDxf = () => dxf().scale(this.columnWidth / layoutDoc[WidthSym]()); + return <CollectionSchemaPreview + Document={layoutDoc} + DataDocument={resolvedDataDoc} + showOverlays={this.overlays} + renderDepth={this.props.renderDepth} + width={width} + height={height} + getTransform={finalDxf} + CollectionView={this.props.CollectionView} + addDocument={this.props.addDocument} + moveDocument={this.props.moveDocument} + removeDocument={this.props.removeDocument} + active={this.props.active} + whenActiveChanged={this.props.whenActiveChanged} + addDocTab={this.props.addDocTab} + setPreviewScript={emptyFunction} + previewScript={undefined}> + </CollectionSchemaPreview>; + } + getDocHeight(d: Doc) { + let nw = NumCast(d.nativeWidth); + let nh = NumCast(d.nativeHeight); + let aspect = nw && nh ? nh / nw : 1; + let wid = Math.min(d[WidthSym](), this.columnWidth); + return (nw && nh) ? wid * aspect : d[HeightSym](); } + + + offsetTransform(doc: Doc, translateX: number, translateY: number) { + let outerXf = Utils.GetScreenTransform(this._masonryGridRef!); + let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY); + return this.props.ScreenToLocalTransform().translate(offset[0], offset[1]).scale(NumCast(doc.width, 1) / this.columnWidth); + } + getDocTransform(doc: Doc, dref: HTMLDivElement) { + let { scale, translateX, translateY } = Utils.GetScreenTransform(dref); + return this.offsetTransform(doc, translateX, translateY); + } + getSingleDocTransform(doc: Doc, ind: number, width: number) { + let localY = this.filteredChildren.reduce((height, d, i) => + height + (i < ind ? this.getDocHeight(Doc.expandTemplateLayout(d, this.props.DataDoc)) + this.gridGap : 0), this.yMargin); + let translate = this.props.ScreenToLocalTransform().inverse().transformPoint((this.props.PanelWidth() - width) / 2, localY); + return this.offsetTransform(doc, translate[0], translate[1]); + } + @computed get children() { - return this.childDocs.filter(d => !d.isMinimized).map((d, i) => { - let aspect = d.nativeHeight ? NumCast(d.nativeWidth) / NumCast(d.nativeHeight) : undefined; - let dref = React.createRef<HTMLDivElement>(); - let dxf = () => this.getDocTransform(d, dref.current!).scale(this.columnWidth / d[WidthSym]()); - let width = () => d.nativeWidth ? Math.min(d[WidthSym](), this.columnWidth) : this.columnWidth; - let height = () => aspect ? width() / aspect : d[HeightSym](); - let rowSpan = Math.ceil((height() + this.gridGap) / (this._gridSize + this.gridGap)); - return (<div className="collectionStackingView-masonryDoc" - key={d[Id]} - ref={dref} - style={{ gridRowEnd: `span ${rowSpan}` }} > - <CollectionSchemaPreview - Document={d} - DataDocument={this.props.Document.layout instanceof Doc ? this.props.Document : this.props.DataDoc} - renderDepth={this.props.renderDepth} - CollectionView={this.props.CollectionView} - addDocument={this.props.addDocument} - moveDocument={this.props.moveDocument} - removeDocument={this.props.removeDocument} - getTransform={dxf} - width={width} - height={height} - active={this.props.active} - addDocTab={this.props.addDocTab} - whenActiveChanged={this.props.whenActiveChanged} - setPreviewScript={emptyFunction} - previewScript={undefined}> - </CollectionSchemaPreview> - </div>); + this._docXfs.length = 0; + return this.filteredChildren.map((d, i) => { + let layoutDoc = Doc.expandTemplateLayout(d, this.props.DataDoc); + let width = () => d.nativeWidth ? Math.min(layoutDoc[WidthSym](), this.columnWidth) : this.columnWidth; + let height = () => this.getDocHeight(layoutDoc); + if (this.singleColumn) { + let dxf = () => this.getSingleDocTransform(layoutDoc, i, width()); + let rowHgtPcnt = height() / (this.props.Document[HeightSym]() - 2 * this.yMargin) * 100; + this._docXfs.push({ dxf: dxf, width: width, height: height }); + return <div className="collectionStackingView-columnDoc" key={d[Id]} style={{ width: width(), marginTop: i === 0 ? 0 : this.gridGap, height: `${rowHgtPcnt}%` }} > + {this.getDisplayDoc(layoutDoc, d, dxf)} + </div>; + } else { + let dref = React.createRef<HTMLDivElement>(); + let dxf = () => this.getDocTransform(layoutDoc, dref.current!); + let rowSpan = Math.ceil((height() + this.gridGap) / (this._gridSize + this.gridGap)); + this._docXfs.push({ dxf: dxf, width: width, height: height }); + return <div className="collectionStackingView-masonryDoc" key={d[Id]} ref={dref} style={{ gridRowEnd: `span ${rowSpan}` }} > + {this.getDisplayDoc(layoutDoc, d, dxf)} + </div>; + } }); } @@ -166,13 +164,64 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { }); } } + + @undoBatch + @action + drop = (e: Event, de: DragManager.DropEvent) => { + let targInd = -1; + let where = [de.x, de.y]; + if (de.data instanceof DragManager.DocumentDragData) { + this._docXfs.map((cd, i) => { + let pos = cd.dxf().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap); + let pos1 = cd.dxf().inverse().transformPoint(cd.width(), cd.height()); + if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && where[1] < pos1[1]) { + targInd = i; + } + }); + } + if (super.drop(e, de)) { + let newDoc = de.data.droppedDocuments[0]; + let docs = this.childDocList; + if (docs) { + if (targInd === -1) targInd = docs.length; + else targInd = docs.indexOf(this.filteredChildren[targInd]); + let srcInd = docs.indexOf(newDoc); + docs.splice(srcInd, 1); + docs.splice(targInd > srcInd ? targInd - 1 : targInd, 0, newDoc); + } + } + return false; + } + @undoBatch + @action + onDrop = (e: React.DragEvent): void => { + let where = [e.clientX, e.clientY]; + let targInd = -1; + this._docXfs.map((cd, i) => { + let pos = cd.dxf().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap); + let pos1 = cd.dxf().inverse().transformPoint(cd.width(), cd.height()); + if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && where[1] < pos1[1]) { + targInd = i; + } + }); + super.onDrop(e, {}, () => { + if (targInd !== -1) { + let newDoc = this.childDocs[this.childDocs.length - 1]; + let docs = this.childDocList; + if (docs) { + docs.splice(docs.length - 1, 1); + docs.splice(targInd, 0, newDoc); + } + } + }); + } render() { - let cols = this.singleColumn ? 1 : Math.max(1, Math.min(this.childDocs.filter(d => !d.isMinimized).length, + let cols = this.singleColumn ? 1 : Math.max(1, Math.min(this.filteredChildren.length, Math.floor((this.props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap)))); let templatecols = ""; for (let i = 0; i < cols; i++) templatecols += `${this.columnWidth}px `; return ( - <div className="collectionStackingView" ref={this.createRef} onContextMenu={this.onContextMenu} onWheel={(e: React.WheelEvent) => e.stopPropagation()} > + <div className="collectionStackingView" ref={this.createRef} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu} onWheel={(e: React.WheelEvent) => e.stopPropagation()} > <div className={`collectionStackingView-masonry${this.singleColumn ? "Single" : "Grid"}`} style={{ padding: this.singleColumn ? `${this.yMargin}px ${this.xMargin}px ${this.yMargin}px ${this.xMargin}px` : `${this.yMargin}px ${this.xMargin}px`, @@ -185,7 +234,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { gridAutoRows: this.singleColumn ? undefined : `${this._gridSize}px` }} > - {this.singleColumn ? this.singleColumnChildren : this.children} + {this.children} {this.singleColumn ? (null) : this.columnDragger} </div> </div> |
