diff options
| author | kimdahey <claire_kim1@brown.edu> | 2019-11-03 17:07:18 -0500 |
|---|---|---|
| committer | kimdahey <claire_kim1@brown.edu> | 2019-11-03 17:07:18 -0500 |
| commit | b1e9619a4e04a9784d927c766361d734f549d6c5 (patch) | |
| tree | 36b875ccc0c45c3706d2533c4c28cc1492b38ce5 /src/client/views/collections/CollectionStackingView.tsx | |
| parent | b2777f92b50f926357ad9b595c7907368b996fbd (diff) | |
| parent | 2786a2e4b33ff874f320697b89fecec3105df201 (diff) | |
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web into webcam_mohammad
Diffstat (limited to 'src/client/views/collections/CollectionStackingView.tsx')
| -rw-r--r-- | src/client/views/collections/CollectionStackingView.tsx | 199 |
1 files changed, 106 insertions, 93 deletions
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 4b81db3a6..7f0165ad4 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -23,6 +23,7 @@ import { CollectionSubView } from "./CollectionSubView"; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from "../ContextMenuItem"; import { ScriptBox } from "../ScriptBox"; +import { CollectionMasonryViewFieldRow } from "./CollectionMasonryViewFieldRow"; @observer export class CollectionStackingView extends CollectionSubView(doc => doc) { @@ -32,7 +33,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { _sectionFilterDisposer?: IReactionDisposer; _docXfs: any[] = []; _columnStart: number = 0; - @observable private cursor: CursorProperty = "grab"; + @observable _heightMap = new Map<string, number>(); + @observable _cursor: CursorProperty = "grab"; @observable _scroll = 0; // used to force the document decoration to update when scrolling @computed get sectionHeaders() { return Cast(this.props.Document.sectionHeaders, listSpec(SchemaHeaderField)); } @computed get sectionFilter() { return StrCast(this.props.Document.sectionFilter); } @@ -42,18 +44,35 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { @computed get gridGap() { return NumCast(this.props.Document.gridGap, 10); } @computed get isStackingView() { return BoolCast(this.props.Document.singleColumn, true); } @computed get numGroupColumns() { return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1; } - @computed get showAddAGroup() { return (this.sectionFilter && this.props.ContainingCollectionDoc && (this.props.ContainingCollectionDoc.chromeStatus !== 'view-mode' && this.props.ContainingCollectionDoc.chromeStatus !== 'disabled')); } + @computed get showAddAGroup() { return (this.sectionFilter && (this.props.Document.chromeStatus !== 'view-mode' && this.props.Document.chromeStatus !== 'disabled')); } @computed get columnWidth() { return Math.min(this.props.PanelWidth() / (this.props as any).ContentScaling() - 2 * this.xMargin, this.isStackingView ? Number.MAX_VALUE : NumCast(this.props.Document.columnWidth, 250)); } + @computed get NodeWidth() { return this.props.PanelWidth() - this.gridGap; } childDocHeight(child: Doc) { return this.getDocHeight(Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, child).layout); } - get layoutDoc() { - // if this document's layout field contains a document (ie, a rendering template), then we will use that - // to determine the render JSX string, otherwise the layout field should directly contain a JSX layout string. - return this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document; + children(docs: Doc[]) { + this._docXfs.length = 0; + return docs.map((d, i) => { + let pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, d); + let layoutDoc = pair.layout ? Doc.Layout(pair.layout) : d; + let width = () => Math.min(layoutDoc.nativeWidth && !layoutDoc.ignoreAspect && !this.props.Document.fillColumn ? layoutDoc[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns); + let height = () => this.getDocHeight(layoutDoc); + let dref = React.createRef<HTMLDivElement>(); + let dxf = () => this.getDocTransform(layoutDoc, dref.current!); + this._docXfs.push({ dxf: dxf, width: width, height: height }); + let rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap); + let style = this.isStackingView ? { width: width(), margin: "auto", marginTop: i === 0 ? 0 : this.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` }; + return <div className={`collectionStackingView-${this.isStackingView ? "columnDoc" : "masonryDoc"}`} key={d[Id]} ref={dref} style={style} > + {this.getDisplayDoc(pair.layout || d, pair.data, dxf, width)} + </div>; + }); + } + @action + setDocHeight = (key: string, sectionHeight: number) => { + this._heightMap.set(key, sectionHeight); } get Sections() { @@ -86,19 +105,30 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } componentDidMount() { - // is there any reason this needs to exist? -syip. yes, it handles autoHeight for stacking views (masonry isn't yet supported). + super.componentDidMount(); this._heightDisposer = reaction(() => { - if (this.isStackingView && BoolCast(this.props.Document.autoHeight)) { + if (this.props.Document.autoHeight) { let sectionsList = Array.from(this.Sections.size ? this.Sections.values() : [this.filteredChildren]); - return this.props.ContentScaling() * sectionsList.reduce((maxHght, s) => Math.max(maxHght, - (this.Sections.size ? 50 : 0) + s.reduce((height, d, i) => height + this.childDocHeight(d) + (i === s.length - 1 ? this.yMargin : this.gridGap), this.yMargin) - ), 0); + if (this.isStackingView) { + let res = this.props.ContentScaling() * sectionsList.reduce((maxHght, s) => { + let r1 = Math.max(maxHght, + (this.Sections.size ? 50 : 0) + s.reduce((height, d, i) => { + let val = height + this.childDocHeight(d) + (i === s.length - 1 ? this.yMargin : this.gridGap); + return val; + }, this.yMargin)); + return r1; + }, 0); + return res; + } else { + let sum = Array.from(this._heightMap.values()).reduce((acc: number, curr: number) => acc += curr, 0); + return this.props.ContentScaling() * (sum + (this.Sections.size ? (this.props.Document.miniHeaders ? 20 : 85) : -15)); + } } return -1; }, (hgt: number) => { let doc = hgt === -1 ? undefined : this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc; - doc && (doc.height = hgt); + doc && hgt > 0 && (Doc.Layout(doc).height = hgt); }, { fireImmediately: true } ); @@ -110,6 +140,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { ); } componentWillUnmount() { + super.componentWillUnmount(); this._heightDisposer && this._heightDisposer(); this._sectionFilterDisposer && this._sectionFilterDisposer(); } @@ -120,7 +151,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } createRef = (ele: HTMLDivElement | null) => { this._masonryGridRef = ele; - this.createDropTarget(ele!); + this.createDropTarget(ele!); //so the whole grid is the drop target? } overlays = (doc: Doc) => { @@ -130,17 +161,19 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); } @computed get onClickHandler() { return ScriptCast(this.Document.onChildClick); } - getDisplayDoc(layoutDoc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) { - let height = () => this.getDocHeight(layoutDoc); + getDisplayDoc(doc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) { + let layoutDoc = Doc.Layout(doc); + let height = () => this.getDocHeight(doc); let finalDxf = () => dxf().scale(this.columnWidth / layoutDoc[WidthSym]()); return <CollectionSchemaPreview - Document={layoutDoc} + Document={doc} DataDocument={dataDoc} + fieldKey={this.props.fieldKey} showOverlays={this.overlays} renderDepth={this.props.renderDepth} ruleProvider={this.props.Document.isRuleProvider && layoutDoc.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider} fitToBox={this.props.fitToBox} - onClick={layoutDoc.isTemplate ? this.onClickHandler : this.onChildClickHandler} + onClick={layoutDoc.isTemplateDoc ? this.onClickHandler : this.onChildClickHandler} PanelWidth={width} PanelHeight={height} getTransform={finalDxf} @@ -160,21 +193,23 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } getDocHeight(d?: Doc) { if (!d) return 0; - let nw = NumCast(d.nativeWidth); - let nh = NumCast(d.nativeHeight); - if (!d.ignoreAspect && !d.fitWidth && nw && nh) { + let layoutDoc = Doc.Layout(d); + let nw = NumCast(layoutDoc.nativeWidth); + let nh = NumCast(layoutDoc.nativeHeight); + let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1); + if (!layoutDoc.ignoreAspect && !layoutDoc.fitWidth && nw && nh) { let aspect = nw && nh ? nh / nw : 1; - let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1); - if (!(d.nativeWidth && !d.ignoreAspect && this.props.Document.fillColumn)) wid = Math.min(d[WidthSym](), wid); + if (!(d.nativeWidth && !layoutDoc.ignoreAspect && this.props.Document.fillColumn)) wid = Math.min(layoutDoc[WidthSym](), wid); return wid * aspect; } - return d.fitWidth ? this.props.PanelHeight() - 2 * this.yMargin : d[HeightSym](); + return layoutDoc.fitWidth ? !layoutDoc.nativeHeight ? this.props.PanelHeight() - 2 * this.yMargin : + Math.min(wid * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc.nativeHeight)) / NumCast(layoutDoc.nativeWidth, 1), this.props.PanelHeight() - 2 * this.yMargin) : layoutDoc[HeightSym](); } columnDividerDown = (e: React.PointerEvent) => { e.stopPropagation(); e.preventDefault(); - runInAction(() => this.cursor = "grabbing"); + runInAction(() => this._cursor = "grabbing"); document.addEventListener("pointermove", this.onDividerMove); document.addEventListener('pointerup', this.onDividerUp); this._columnStart = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0]; @@ -184,18 +219,18 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { let dragPos = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0]; let delta = dragPos - this._columnStart; this._columnStart = dragPos; - this.layoutDoc.columnWidth = this.columnWidth + delta; + this.layoutDoc.columnWidth = Math.max(10, this.columnWidth + delta); } @action onDividerUp = (e: PointerEvent): void => { - runInAction(() => this.cursor = "grab"); + runInAction(() => this._cursor = "grab"); document.removeEventListener("pointermove", this.onDividerMove); document.removeEventListener('pointerup', this.onDividerUp); } @computed get columnDragger() { - return <div className="collectionStackingView-columnDragger" onPointerDown={this.columnDividerDown} ref={this._draggerRef} style={{ cursor: this.cursor, left: `${this.columnWidth + this.xMargin}px` }} > + return <div className="collectionStackingView-columnDragger" onPointerDown={this.columnDividerDown} ref={this._draggerRef} style={{ cursor: this._cursor, left: `${this.columnWidth + this.xMargin}px` }} > <FontAwesomeIcon icon={"arrows-alt-h"} /> </div>; } @@ -286,52 +321,29 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { translate(offset[0], offset[1] + (this.props.ChromeHeight ? this.props.ChromeHeight() : 0)). scale(NumCast(doc.width, 1) / this.columnWidth); } - masonryChildren(docs: Doc[]) { - this._docXfs.length = 0; - return docs.map((d, i) => { - const pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, d); - if (!pair.layout || pair.data instanceof Promise) { - return (null); - } - let dref = React.createRef<HTMLDivElement>(); - let width = () => (d.nativeWidth && !d.ignoreAspect && !this.props.Document.fillColumn ? Math.min(d[WidthSym](), this.columnWidth) : this.columnWidth);/// (uniqueHeadings.length + 1); - let height = () => this.getDocHeight(pair.layout); - let dxf = () => this.getDocTransform(pair.layout!, dref.current!); - let rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap); - this._docXfs.push({ dxf: dxf, width: width, height: height }); - return !pair.layout ? (null) : <div className="collectionStackingView-masonryDoc" key={d[Id]} ref={dref} style={{ gridRowEnd: `span ${rowSpan}` }} > - {this.getDisplayDoc(pair.layout, pair.data, dxf, width)} - </div>; - }); - } - @observable _headingsHack: number = 1; - sectionMasonry(heading: SchemaHeaderField | undefined, docList: Doc[]) { - let cols = Math.max(1, Math.min(docList.length, - Math.floor((this.props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap)))); - if (isNaN(cols)) { - console.log("naN"); - cols = 1; + sectionMasonry = (heading: SchemaHeaderField | undefined, docList: Doc[]) => { + let key = this.sectionFilter; + let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined; + let types = docList.length ? docList.map(d => typeof d[key]) : this.childDocs.map(d => typeof d[key]); + if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) { + type = types[0]; } - return <div key={heading ? heading.heading : "empty"} className="collectionStackingView-masonrySection"> - {!heading ? (null) : - <div key={`${heading.heading}`} className="collectionStackingView-sectionHeader" style={{ background: heading.color }} - onClick={action(() => this._headingsHack++ && heading.setCollapsed(!heading.collapsed))} > - {heading.heading} - </div>} - {this._headingsHack && heading && heading.collapsed ? (null) : - <div key={`${heading}-stack`} className={`collectionStackingView-masonryGrid`} - style={{ - padding: `${this.yMargin}px ${this.xMargin}px`, - width: `${cols * (this.columnWidth + this.gridGap) + 2 * this.xMargin - this.gridGap}px`, - gridGap: this.gridGap, - gridTemplateColumns: numberRange(cols).reduce((list, i) => list + ` ${this.columnWidth}px`, ""), - }}> - {this.masonryChildren(docList)} - {this.columnDragger} - </div> - } - </div>; + let rows = () => !this.isStackingView ? 1 : Math.max(1, Math.min(docList.length, + Math.floor((this.props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap)))); + return <CollectionMasonryViewFieldRow + key={heading ? heading.heading : ""} + rows={rows} + headings={this.headings} + heading={heading ? heading.heading : ""} + headingObject={heading} + docList={docList} + parent={this} + type={type} + createDropTarget={this.createDropTarget} + screenToLocalTransform={this.props.ScreenToLocalTransform} + setDocHeight={this.setDocHeight} + />; } @action @@ -351,7 +363,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } onToggle = (checked: Boolean) => { - this.props.ContainingCollectionDoc && (this.props.ContainingCollectionDoc.chromeStatus = checked ? "collapsed" : "view-mode"); + this.props.Document.chromeStatus = checked ? "collapsed" : "view-mode"; } onContextMenu = (e: React.MouseEvent): void => { @@ -376,33 +388,34 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { SetValue: this.addGroup, contents: "+ ADD A GROUP" }; - Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey); let sections = [[undefined, this.filteredChildren] as [SchemaHeaderField | undefined, Doc[]]]; if (this.sectionFilter) { let entries = Array.from(this.Sections.entries()); sections = entries.sort(this.sortFunc); } + console.log("NUM = " + this.numGroupColumns); return ( - <div className={this.isStackingView ? "collectionStackingView" : "collectionMasonryView"} - ref={this.createRef} - onScroll={action((e: React.UIEvent<HTMLDivElement>) => this._scroll = e.currentTarget.scrollTop)} - onDrop={this.onDrop.bind(this)} - onContextMenu={this.onContextMenu} - onWheel={(e: React.WheelEvent) => e.stopPropagation()} > - {sections.map(section => this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1]))} - {!this.showAddAGroup ? (null) : - <div key={`${this.props.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton" - style={{ width: this.columnWidth / this.numGroupColumns - 10, marginTop: 10 }}> - <EditableView {...editableViewProps} /> - </div>} - {this.props.ContainingCollectionDoc && this.props.ContainingCollectionDoc.chromeStatus !== 'disabled' ? <Switch - onChange={this.onToggle} - onClick={this.onToggle} - defaultChecked={this.props.ContainingCollectionDoc.chromeStatus !== 'view-mode'} - checkedChildren="edit" - unCheckedChildren="view" - /> : null} - </div> + <div className="collectionStackingMasonry-cont" > + <div className={this.isStackingView ? "collectionStackingView" : "collectionMasonryView"} + ref={this.createRef} + onScroll={action((e: React.UIEvent<HTMLDivElement>) => this._scroll = e.currentTarget.scrollTop)} + onDrop={this.onDrop.bind(this)} + onContextMenu={this.onContextMenu} + onWheel={(e: React.WheelEvent) => e.stopPropagation()} > + {sections.map(section => this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1]))} + {!this.showAddAGroup ? (null) : + <div key={`${this.props.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton" + style={{ width: !this.isStackingView ? "100%" : this.columnWidth / this.numGroupColumns - 10, marginTop: 10 }}> + <EditableView {...editableViewProps} /> + </div>} + {this.props.Document.chromeStatus !== 'disabled' ? <Switch + onChange={this.onToggle} + onClick={this.onToggle} + defaultChecked={this.props.Document.chromeStatus !== 'view-mode'} + checkedChildren="edit" + unCheckedChildren="view" + /> : null} + </div> </div> ); } }
\ No newline at end of file |
