From 8d311fcaceded4f6fc93bec1c352ba4268bf29ff Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Tue, 26 May 2020 02:53:06 -0700 Subject: added reaction for layout doc tracking, cleanup, fixed layout on reload, imported scss file --- .../collectionGrid/CollectionGridView.tsx | 181 +++++++++------------ 1 file changed, 74 insertions(+), 107 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx index d06bb3192..e127ab95c 100644 --- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx +++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx @@ -1,10 +1,9 @@ -import { computed, observable, action } from 'mobx'; +import { computed, observable, Lambda } from 'mobx'; import * as React from "react"; -import { Doc, DocListCast } from '../../../../fields/Doc'; +import { Doc, DocListCast, Opt } from '../../../../fields/Doc'; import { documentSchema } from '../../../../fields/documentSchemas'; -import { makeInterface, createSchema } from '../../../../fields/Schema'; -import { BoolCast, NumCast, ScriptCast, StrCast, Cast } from '../../../../fields/Types'; -import { DragManager } from '../../../util/DragManager'; +import { makeInterface } from '../../../../fields/Schema'; +import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; import { Transform } from '../../../util/Transform'; import { undoBatch } from '../../../util/UndoManager'; import { ContentFittingDocumentView } from '../../nodes/ContentFittingDocumentView'; @@ -15,44 +14,60 @@ import { returnZero } from '../../../../Utils'; import Grid, { Layout } from "./Grid"; import { Id } from '../../../../fields/FieldSymbols'; import { observer } from 'mobx-react'; - +import "./CollectionGridView.scss"; type GridSchema = makeInterface<[typeof documentSchema]>; const GridSchema = makeInterface(documentSchema); @observer export class CollectionGridView extends CollectionSubView(GridSchema) { + private changeListenerDisposer: Opt; constructor(props: Readonly) { super(props); - - this.props.Document.numCols = this.props.Document.numCols ? this.props.Document.numCols : 10; - this.props.Document.rowHeight = this.props.Document.rowHeight ? this.props.Document.rowHeight : 100; + this.props.Document.numCols = NumCast(this.props.Document.numCols, 10); + this.props.Document.rowHeight = NumCast(this.props.Document.rowHeight, 100); } componentDidMount() { - if (!(this.props.Document.gridLayouts as List)?.length) { - - console.log("no layouts stored on doc"); - + if (!this.props.Document.gridLayouts) { this.props.Document.gridLayouts = new List(); - - for (let i = 0; i < this.childLayoutPairs.length; i++) { - - const layoutDoc: Doc = new Doc(); - layoutDoc.i = layoutDoc[Id]; - layoutDoc.x = 2 * (i % 5); - layoutDoc.y = 2 * Math.floor(i / 5); - layoutDoc.w = 2; - layoutDoc.h = 2; - - (this.props.Document.gridLayouts as List).push(layoutDoc); - - // use childlayoutpairs length instead - } - } + this.changeListenerDisposer = computed(() => this.childLayoutPairs).observe(({ oldValue, newValue }) => { + if (!oldValue) { + return; + } + console.log(`Reaction fired! ${oldValue.length} => ${newValue.length}`); + const gridLayouts = DocListCast(this.props.Document.gridLayouts); + const previousLength = oldValue.length; + const currentLength = newValue.length; + + if (currentLength > previousLength) { + newValue.forEach(({ layout }) => { + const targetId = layout[Id]; + if (!gridLayouts.find((gridLayout: Doc) => StrCast(gridLayout.i) === targetId)) { + const layoutDoc: Doc = new Doc(); + layoutDoc.i = targetId; + layoutDoc.x = 2 * (previousLength % 5); + layoutDoc.y = 2 * Math.floor(previousLength / 5); + layoutDoc.w = 2; + layoutDoc.h = 2; + Doc.AddDocToList(this.props.Document, "gridLayouts", layoutDoc); + } + }); + } else { + oldValue.forEach(({ layout }) => { + if (!newValue.find(({ layout: preserved }) => preserved[Id] === layout[Id])) { + const gridLayoutDoc = gridLayouts.find((gridLayout: Doc) => StrCast(gridLayout.i) === layout[Id]); + gridLayoutDoc && Doc.RemoveDocFromList(this.props.Document, "gridLayouts", gridLayoutDoc); + } + }); + } + }, true); + } + componentWillUnmount() { + this.changeListenerDisposer && this.changeListenerDisposer(); } /** @@ -62,7 +77,6 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { * documents before the target. */ private lookupIndividualTransform = (doc: Doc) => { - const yTranslation = (this.props.Document.rowHeight as number) * (doc.y as number) + 10 * (doc.y as number); return this.props.ScreenToLocalTransform().translate(-this.props.PanelWidth() / (this.props.Document.numCols as number) * (doc.x as number), -yTranslation); } @@ -119,30 +133,21 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { */ @undoBatch setLayout(layouts: Layout[]) { - - console.log("setting layout in CollectionGridView"); - console.log(layouts?.[0].w); - //this.props.Document.gridLayouts = new List(); - - const docList: Doc[] = []; - - for (const layout of layouts) { - const layoutDoc = new Doc(); - layoutDoc.i = layout.i; - layoutDoc.x = layout.x; - layoutDoc.y = layout.y; - layoutDoc.w = layout.w; - layoutDoc.h = layout.h; - - docList.push(layoutDoc); - } - - this.props.Document.gridLayouts = new List(docList); + this.childLayoutPairs.forEach(({ layout: doc }) => { + let update: Opt; + const targetId = doc[Id]; + const gridLayout = DocListCast(this.props.Document.gridLayouts).find(gridLayout => StrCast(gridLayout.i) === targetId); + if (gridLayout && (update = layouts.find(layout => layout.i === targetId))) { + gridLayout.x = update.x; + gridLayout.y = update.y; + gridLayout.w = update.w; + gridLayout.h = update.h; + } + }); } // _.reject() on item removal? - /** * @returns a list of `ContentFittingDocumentView`s inside wrapper divs. * The key of the wrapper div must be the same as the `i` value of the corresponding layout. @@ -151,26 +156,20 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { private get contents(): JSX.Element[] { const { childLayoutPairs } = this; const collector: JSX.Element[] = []; - //const layoutArray: Layout[] = []; - const docList: Doc[] = DocListCast(this.props.Document.gridLayouts); - - const previousLength = docList.length; - // layoutArray.push(...this.layout); - - if (!previousLength) { - // console.log("early return"); + if (!docList.length) { return []; } for (let i = 0; i < childLayoutPairs.length; i++) { const { layout } = childLayoutPairs[i]; - const dxf = () => this.lookupIndividualTransform(docList?.[i]); - const width = () => this.width(docList?.[i]); - const height = () => this.height(docList?.[i]); + const gridLayout = docList[i]; + const dxf = () => this.lookupIndividualTransform(gridLayout); + const width = () => this.width(gridLayout); + const height = () => this.height(gridLayout); collector.push(
{this.getDisplayDoc(layout, dxf, width, height)}
@@ -185,52 +184,21 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { * @param docLayoutList `Doc[]` */ toLayoutList(docLayoutList: Doc[]): Layout[] { - - const layouts: Layout[] = []; - for (const layout of docLayoutList) { - layouts.push( - { i: layout.i as string, x: layout.x as number, y: layout.y as number, w: layout.w as number, h: layout.h as number } - ); - } - return layouts; + return docLayoutList.map(({ i, x, y, w, h }) => ({ + i: i as string, + x: x as number, + y: y as number, + w: w as number, + h: h as number + })); } - /** - * Checks whether a new node has been added to the grid and updates the Document accordingly. - */ - @undoBatch - checkUpdate() { - const previousLength = (this.props.Document.gridLayouts as List)?.length; - if (this.childLayoutPairs.length > previousLength) { - console.log("adding doc"); - const layoutDoc: Doc = new Doc(); - layoutDoc.i = layoutDoc[Id]; - layoutDoc.x = 2 * (previousLength % 5); - layoutDoc.y = 2 * Math.floor(previousLength / 5); - layoutDoc.w = 2; - layoutDoc.h = 2; - - (this.props.Document.gridLayouts as List).push(layoutDoc); + render() { + const layoutDocList: Doc[] = DocListCast(this.props.Document.gridLayouts); + const childDocumentViews: JSX.Element[] = this.contents; + if (!(childDocumentViews.length && layoutDocList.length)) { + return null; } - } - - render(): JSX.Element { - - this.checkUpdate(); - - const docList: Doc[] = DocListCast(this.props.Document.gridLayouts); - - const contents: JSX.Element[] = this.contents; - const layout: Layout[] = this.toLayoutList(docList); - - // if (layout.length === 0) { - // console.log("layouts not loaded"); - // } - // else { - // console.log("rendering with this"); - // console.log(layout[0].w); - // } - return (
e.stopPropagation()} > this.setLayout(layout)} -- cgit v1.2.3-70-g09d2 From 5e600dcd6df1f5fedbcffc846433fc1894403722 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Tue, 26 May 2020 03:05:36 -0700 Subject: comments --- .../collectionGrid/CollectionGridView.tsx | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx index e127ab95c..465ff15f1 100644 --- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx +++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx @@ -35,14 +35,15 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { } this.changeListenerDisposer = computed(() => this.childLayoutPairs).observe(({ oldValue, newValue }) => { if (!oldValue) { + // on page's initial load return; } - console.log(`Reaction fired! ${oldValue.length} => ${newValue.length}`); const gridLayouts = DocListCast(this.props.Document.gridLayouts); const previousLength = oldValue.length; const currentLength = newValue.length; if (currentLength > previousLength) { + // for each document that was added, add a corresponding grid layout document newValue.forEach(({ layout }) => { const targetId = layout[Id]; if (!gridLayouts.find((gridLayout: Doc) => StrCast(gridLayout.i) === targetId)) { @@ -56,6 +57,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { } }); } else { + // for each document that was removed, remove its corresponding grid layout document oldValue.forEach(({ layout }) => { if (!newValue.find(({ layout: preserved }) => preserved[Id] === layout[Id])) { const gridLayoutDoc = gridLayouts.find((gridLayout: Doc) => StrCast(gridLayout.i) === layout[Id]); @@ -133,6 +135,8 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { */ @undoBatch setLayout(layouts: Layout[]) { + // for every child in the collection, check to see if there's a corresponding grid layout document and + // updated layout object. If both exist, which they should, update the grid layout document from the updated object this.childLayoutPairs.forEach(({ layout: doc }) => { let update: Opt; const targetId = doc[Id]; @@ -193,14 +197,18 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { })); } + /** + * DocListCast only includes *resolved* documents, i.e. filters out promises. So even if we have a nonzero + * number of documents in either of these Dash lists on the document, the DocListCast version may evaluate to empty + * if the corresponding documents are all promises, waiting to be fetched from the server. If we don't return early + * in the event that promises are encountered, we might feed inaccurate data to the grid since the corresponding gridLayout + * documents are unresolved (or the grid may misinterpret an empty array) which has the unfortunate byproduct of triggering + * the setLayout event, which makes these unintended changes permanent by writing them to the likely now resolved documents. + */ render() { const layoutDocList: Doc[] = DocListCast(this.props.Document.gridLayouts); const childDocumentViews: JSX.Element[] = this.contents; - if (!(childDocumentViews.length && layoutDocList.length)) { - return null; - } - - return ( + return !(childDocumentViews.length && layoutDocList.length) ? null : (
Date: Tue, 26 May 2020 11:03:08 -0700 Subject: cleanup and comments --- .../collectionGrid/CollectionGridView.tsx | 25 ++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx index 465ff15f1..26be4bee8 100644 --- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx +++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx @@ -49,18 +49,18 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { if (!gridLayouts.find((gridLayout: Doc) => StrCast(gridLayout.i) === targetId)) { const layoutDoc: Doc = new Doc(); layoutDoc.i = targetId; - layoutDoc.x = 2 * (previousLength % 5); - layoutDoc.y = 2 * Math.floor(previousLength / 5); layoutDoc.w = 2; layoutDoc.h = 2; + this.findNextLayout(layoutDoc, previousLength); Doc.AddDocToList(this.props.Document, "gridLayouts", layoutDoc); } }); } else { // for each document that was removed, remove its corresponding grid layout document oldValue.forEach(({ layout }) => { - if (!newValue.find(({ layout: preserved }) => preserved[Id] === layout[Id])) { - const gridLayoutDoc = gridLayouts.find((gridLayout: Doc) => StrCast(gridLayout.i) === layout[Id]); + const targetId = layout[Id]; + if (!newValue.find(({ layout: preserved }) => preserved[Id] === targetId)) { + const gridLayoutDoc = gridLayouts.find((gridLayout: Doc) => StrCast(gridLayout.i) === targetId); gridLayoutDoc && Doc.RemoveDocFromList(this.props.Document, "gridLayouts", gridLayoutDoc); } }); @@ -72,6 +72,23 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { this.changeListenerDisposer && this.changeListenerDisposer(); } + /** + * Establishes the x and y properties of the @param layoutDoc, currently + * using the @param previousLength for the computations. + * + * However, this could also be more of a first fit algorithm, iterating through + * this.toLayoutList(DocListCast(this.props.Document.gridLayouts)) and finding the + * first gap in the layout structure that suits the width and height. It would be + * easiest to see that a column is free (for a given row, if two documents' x are separated + * by a value greater than the ratio width of the document you're trying to insert), + * but you would then have to ensure that the next row at that column has a y at least + * as big as the ratio height of the document you're trying to insert. + */ + private findNextLayout(layoutDoc: Doc, previousLength: number) { + layoutDoc.x = 2 * (previousLength % 5); + layoutDoc.y = 2 * Math.floor(previousLength / 5); + } + /** * @returns the transform that will correctly place * the document decorations box, shifted to the right by -- cgit v1.2.3-70-g09d2 From 8e25ea949c63e80fecd53c9d86b0c2ea568f3468 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Tue, 26 May 2020 11:04:40 -0700 Subject: tweak --- src/client/views/collections/collectionGrid/CollectionGridView.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx index 26be4bee8..4a3596190 100644 --- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx +++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx @@ -49,8 +49,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { if (!gridLayouts.find((gridLayout: Doc) => StrCast(gridLayout.i) === targetId)) { const layoutDoc: Doc = new Doc(); layoutDoc.i = targetId; - layoutDoc.w = 2; - layoutDoc.h = 2; + layoutDoc.w = layoutDoc.h = 2; this.findNextLayout(layoutDoc, previousLength); Doc.AddDocToList(this.props.Document, "gridLayouts", layoutDoc); } @@ -85,7 +84,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { * as big as the ratio height of the document you're trying to insert. */ private findNextLayout(layoutDoc: Doc, previousLength: number) { - layoutDoc.x = 2 * (previousLength % 5); + layoutDoc.x = 2 * (previousLength % 5); // does this assume that there are 5 columns? layoutDoc.y = 2 * Math.floor(previousLength / 5); } -- cgit v1.2.3-70-g09d2 From 271cdc02354e21824bfa3387ee8c7d08d2bff4f6 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Sat, 30 May 2020 18:37:32 -0700 Subject: rendering fixes --- .../collections/collectionGrid/CollectionGridView.tsx | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx index 4a3596190..1775bcb7d 100644 --- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx +++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx @@ -34,23 +34,17 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { this.props.Document.gridLayouts = new List(); } this.changeListenerDisposer = computed(() => this.childLayoutPairs).observe(({ oldValue, newValue }) => { - if (!oldValue) { - // on page's initial load - return; - } const gridLayouts = DocListCast(this.props.Document.gridLayouts); - const previousLength = oldValue.length; - const currentLength = newValue.length; - if (currentLength > previousLength) { + if (!oldValue || newValue.length > oldValue.length) { // for each document that was added, add a corresponding grid layout document - newValue.forEach(({ layout }) => { + newValue.forEach(({ layout }, i) => { const targetId = layout[Id]; if (!gridLayouts.find((gridLayout: Doc) => StrCast(gridLayout.i) === targetId)) { const layoutDoc: Doc = new Doc(); layoutDoc.i = targetId; layoutDoc.w = layoutDoc.h = 2; - this.findNextLayout(layoutDoc, previousLength); + this.findNextLayout(layoutDoc, i); Doc.AddDocToList(this.props.Document, "gridLayouts", layoutDoc); } }); @@ -177,7 +171,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { const { childLayoutPairs } = this; const collector: JSX.Element[] = []; const docList: Doc[] = DocListCast(this.props.Document.gridLayouts); - if (!docList.length) { + if (!docList.length || docList.length !== childLayoutPairs.length) { return []; } @@ -224,7 +218,10 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { render() { const layoutDocList: Doc[] = DocListCast(this.props.Document.gridLayouts); const childDocumentViews: JSX.Element[] = this.contents; - return !(childDocumentViews.length && layoutDocList.length) ? null : ( + if (!(childDocumentViews.length && layoutDocList.length)) { + return null; + } + return (