diff options
-rw-r--r-- | src/client/documents/Documents.ts | 2 | ||||
-rw-r--r-- | src/client/views/collections/CollectionStackingView.tsx | 31 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentContentsView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/ImageBox.tsx | 69 | ||||
-rw-r--r-- | src/new_fields/Doc.ts | 18 |
5 files changed, 60 insertions, 62 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index e149963b9..be678d765 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -355,7 +355,7 @@ export namespace Docs { AudioBox.ActiveRecordings.map(d => DocUtils.MakeLink({ doc: viewDoc }, { doc: d }, "audio link", "link to audio: " + d.title)); - return Doc.assign(viewDoc, delegateProps); + return Doc.assign(viewDoc, delegateProps, true); } /** diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 03c757a51..3ab0326e2 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -39,7 +39,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { @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); } - @computed get filteredChildren() { return this.childDocs.filter(d => !d.isMinimized); } + @computed get filteredChildren() { return this.childDocs.filter(d => !d.isMinimized).map(d => (Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, d).layout as Doc) || d); } @computed get xMargin() { return NumCast(this.props.Document.xMargin, 2 * this.gridGap); } @computed get yMargin() { return Math.max(this.props.Document.showTitle ? 30 : 0, NumCast(this.props.Document.yMargin, 2 * this.gridGap)); } @computed get gridGap() { return NumCast(this.props.Document.gridGap, 10); } @@ -52,22 +52,18 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } @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); } - children(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); - const layoutDoc = pair.layout ? Doc.Layout(pair.layout) : d; - const width = () => Math.min(layoutDoc.nativeWidth && !layoutDoc.ignoreAspect && !this.props.Document.fillColumn ? layoutDoc[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns); - const height = () => this.getDocHeight(layoutDoc); + const width = () => Math.min(d.nativeWidth && !d.ignoreAspect && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns); + const height = () => this.getDocHeight(d); const dref = React.createRef<HTMLDivElement>(); - const dxf = () => this.getDocTransform(layoutDoc, dref.current!); + const dxf = () => this.getDocTransform(d, dref.current!); this._docXfs.push({ dxf: dxf, width: width, height: height }); const rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap); const style = this.isStackingView ? { width: width(), 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)} + {this.getDisplayDoc(d, (d.resolvedDataDoc as Doc) || d, dxf, width)} </div>; }); } @@ -114,7 +110,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { const res = this.props.ContentScaling() * sectionsList.reduce((maxHght, s) => { const r1 = Math.max(maxHght, (this.Sections.size ? 50 : 0) + s.reduce((height, d, i) => { - const val = height + this.childDocHeight(d) + (i === s.length - 1 ? this.yMargin : this.gridGap); + const val = height + this.getDocHeight(d) + (i === s.length - 1 ? this.yMargin : this.gridGap); return val; }, this.yMargin)); return r1; @@ -291,7 +287,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { sectionStacking = (heading: SchemaHeaderField | undefined, docList: Doc[]) => { const key = this.sectionFilter; let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined; - const types = docList.length ? docList.map(d => typeof d[key]) : this.childDocs.map(d => typeof d[key]); + const types = docList.length ? docList.map(d => typeof d[key]) : this.filteredChildren.map(d => typeof d[key]); if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) { type = types[0]; } @@ -316,15 +312,17 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { const y = this._scroll; // required for document decorations to update when the text box container is scrolled const { scale, translateX, translateY } = Utils.GetScreenTransform(dref); const outerXf = Utils.GetScreenTransform(this._masonryGridRef!); + const scaling = 1 / Math.min(1, this.props.PanelHeight() / this.layoutDoc[HeightSym]()); const offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY); - return this.props.ScreenToLocalTransform(). - translate(offset[0], offset[1] + (this.props.ChromeHeight && this.props.ChromeHeight() < 0 ? this.props.ChromeHeight() : 0)); + const offsetx = (doc[WidthSym]() - doc[WidthSym]() / scaling) / 2; + const offsety = (this.props.ChromeHeight && this.props.ChromeHeight() < 0 ? this.props.ChromeHeight() : 0); + return this.props.ScreenToLocalTransform().translate(offset[0] - offsetx, offset[1] + offsety).scale(scaling); } sectionMasonry = (heading: SchemaHeaderField | undefined, docList: Doc[]) => { const key = this.sectionFilter; let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined; - const types = docList.length ? docList.map(d => typeof d[key]) : this.childDocs.map(d => typeof d[key]); + const types = docList.length ? docList.map(d => typeof d[key]) : this.filteredChildren.map(d => typeof d[key]); if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) { type = types[0]; } @@ -396,6 +394,11 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { <div className="collectionStackingMasonry-cont" > <div className={this.isStackingView ? "collectionStackingView" : "collectionMasonryView"} ref={this.createRef} + style={{ + transform: `scale(${Math.min(1, this.props.PanelHeight() / this.layoutDoc[HeightSym]())})`, + height: `${100 * 1 / Math.min(this.props.PanelWidth() / this.layoutDoc[WidthSym](), this.props.PanelHeight() / this.layoutDoc[HeightSym]())}%`, + transformOrigin: "top" + }} onScroll={action((e: React.UIEvent<HTMLDivElement>) => this._scroll = e.currentTarget.scrollTop)} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu} diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 8f6bfc8e1..66886165e 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -82,7 +82,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & { return this.props.DataDoc; } get layoutDoc() { - return this.props.DataDoc === undefined ? Doc.expandTemplateLayout(Doc.Layout(this.props.Document), this.props.Document) : Doc.Layout(this.props.Document); + return Doc.expandTemplateLayout(Doc.Layout(this.props.Document), this.props.Document); } CreateBindings(): JsxBindings { diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 09e627078..634555012 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -214,37 +214,23 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum } _curSuffix = "_m"; - _resized = false; - resize = (srcpath: string) => { - requestImageSize(srcpath) + _resized = ""; + resize = (imgPath: string) => { + requestImageSize(imgPath) .then((size: any) => { const rotation = NumCast(this.dataDoc.rotation) % 180; const realsize = rotation === 90 || rotation === 270 ? { height: size.width, width: size.height } : size; const aspect = realsize.height / realsize.width; if (this.Document.width && (Math.abs(1 - NumCast(this.Document.height) / NumCast(this.Document.width) / (realsize.height / realsize.width)) > 0.1)) { setTimeout(action(() => { - this._resized = true; - this.Document.height = this.Document[WidthSym]() * aspect; - this.Document.nativeHeight = realsize.height; - this.Document.nativeWidth = realsize.width; + if (this.pathInfos.srcpath === imgPath && (!this.layoutDoc.isTemplateDoc || this.dataDoc !== this.layoutDoc)) { + this._resized = imgPath; + this.Document.height = this.Document[WidthSym]() * aspect; + this.Document.nativeHeight = realsize.height; + this.Document.nativeWidth = realsize.width; + } }), 0); - } else this._resized = true; - }) - .catch((err: any) => console.log(err)); - } - fadesize = (srcpath: string) => { - requestImageSize(srcpath) - .then((size: any) => { - const rotation = NumCast(this.dataDoc.rotation) % 180; - const realsize = rotation === 90 || rotation === 270 ? { height: size.width, width: size.height } : size; - const aspect = realsize.height / realsize.width; - if (this.Document.width && (Math.abs(1 - NumCast(this.Document.height) / NumCast(this.Document.width) / (realsize.height / realsize.width)) > 0.1)) { - setTimeout(action(() => { - this.Document.height = this.Document[WidthSym]() * aspect; - this.Document.nativeHeight = realsize.height; - this.Document.nativeWidth = realsize.width; - }), 0); - } + } else this._resized = imgPath; }) .catch((err: any) => console.log(err)); } @@ -285,18 +271,16 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum return !tags ? (null) : (<img id={"google-tags"} src={"/assets/google_tags.png"} />); } - @computed get content() { - TraceMobx(); - const extensionDoc = this.extensionDoc; - if (!extensionDoc) return (null); - // let transform = this.props.ScreenToLocalTransform().inverse(); + @computed get nativeSize() { const pw = typeof this.props.PanelWidth === "function" ? this.props.PanelWidth() : typeof this.props.PanelWidth === "number" ? (this.props.PanelWidth as any) as number : 50; - // var [sptX, sptY] = transform.transformPoint(0, 0); - // let [bptX, bptY] = transform.transformPoint(pw, this.props.PanelHeight()); - // let w = bptX - sptX; - const nativeWidth = (this.Document.nativeWidth || pw); const nativeHeight = (this.Document.nativeHeight || 1); + return { nativeWidth, nativeHeight }; + } + + @computed get pathInfos() { + const extensionDoc = this.extensionDoc!; + const { nativeWidth, nativeHeight } = this.nativeSize; let paths = [[Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png"), nativeWidth / nativeHeight]]; // this._curSuffix = ""; // if (w > 20) { @@ -308,15 +292,24 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum // else if (this._largeRetryCount < 10) this._curSuffix = "_l"; if (field instanceof ImageField) paths = [[this.choosePath(field.url), nativeWidth / nativeHeight]]; paths.push(...altpaths); - // } - const rotation = NumCast(this.Document.rotation, 0); - const aspect = (rotation % 180) ? this.Document[HeightSym]() / this.Document[WidthSym]() : 1; - const shift = (rotation % 180) ? (nativeHeight - nativeWidth / aspect) / 2 : 0; const srcpath = paths[Math.min(paths.length - 1, (this.Document.curPage || 0))][0] as string; const srcaspect = paths[Math.min(paths.length - 1, (this.Document.curPage || 0))][1] as number; const fadepath = paths[Math.min(paths.length - 1, 1)][0] as string; + return { srcpath, srcaspect, fadepath }; + } + + @computed get content() { + TraceMobx(); + const extensionDoc = this.extensionDoc; + if (!extensionDoc) return (null); + + const { srcpath, srcaspect, fadepath } = this.pathInfos; + const { nativeWidth, nativeHeight } = this.nativeSize; + const rotation = NumCast(this.Document.rotation, 0); + const aspect = (rotation % 180) ? this.Document[HeightSym]() / this.Document[WidthSym]() : 1; + const shift = (rotation % 180) ? (nativeHeight - nativeWidth / aspect) / 2 : 0; - !this.Document.ignoreAspect && !this._resized && this.resize(srcpath); + !this.Document.ignoreAspect && this._resized !== srcpath && this.resize(srcpath); return <div className="imageBox-cont" key={this.props.Document[Id]} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}> <div className="imageBox-fader" > diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index c809ad17a..d4634ba4b 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -290,14 +290,13 @@ export namespace Doc { * @param fields the fields to project onto the target. Its type signature defines a mapping from some string key * to a potentially undefined field, where each entry in this mapping is optional. */ - export function assign<K extends string>(doc: Doc, fields: Partial<Record<K, Opt<Field>>>) { + export function assign<K extends string>(doc: Doc, fields: Partial<Record<K, Opt<Field>>>, skipUndefineds: boolean = false) { for (const key in fields) { if (fields.hasOwnProperty(key)) { const value = fields[key]; - // Do we want to filter out undefineds? - // if (value !== undefined) { - doc[key] = value; - // } + if (!skipUndefineds || value !== undefined) { // Do we want to filter out undefineds? + doc[key] = value; + } } } return doc; @@ -406,8 +405,9 @@ export namespace Doc { } export function MakeAlias(doc: Doc) { const alias = !GetT(doc, "isPrototype", "boolean", true) ? Doc.MakeCopy(doc) : Doc.MakeDelegate(doc); - if (alias.layout instanceof Doc) { - alias.layout = Doc.MakeAlias(alias.layout); + let layout = Doc.Layout(alias); + if (layout instanceof Doc && layout !== alias) { + Doc.SetLayout(alias, Doc.MakeAlias(layout)); } const aliasNumber = Doc.GetProto(doc).aliasNumber = NumCast(Doc.GetProto(doc).aliasNumber) + 1; alias.title = ComputedField.MakeFunction(`renameAlias(this, ${aliasNumber})`); @@ -455,6 +455,7 @@ export namespace Doc { if (resolvedDataDoc && Doc.WillExpandTemplateLayout(childDocLayout, resolvedDataDoc)) { const extensionDoc = fieldExtensionDoc(resolvedDataDoc, StrCast(childDocLayout.templateField, StrCast(childDocLayout.title))); layoutDoc = Doc.expandTemplateLayout(childDocLayout, extensionDoc !== resolvedDataDoc ? extensionDoc : undefined); + layoutDoc && (layoutDoc.resolvedDataDoc = resolvedDataDoc); } else layoutDoc = childDocLayout; return { layout: layoutDoc, data: resolvedDataDoc }; } @@ -468,7 +469,7 @@ export namespace Doc { // export function fieldExtensionDoc(doc: Doc, fieldKey: string) { const extension = doc[fieldKey + "_ext"]; - if (extension === undefined) { + if (doc instanceof Doc && extension === undefined) { setTimeout(() => CreateDocumentExtensionForField(doc, fieldKey), 0); } return extension ? extension as Doc : undefined; @@ -657,6 +658,7 @@ export namespace Doc { // the document containing the view layout information - will be the Document itself unless the Document has // a layout field. In that case, all layout information comes from there unless overriden by Document export function Layout(doc: Doc) { return Doc.LayoutField(doc) instanceof Doc ? doc[StrCast(doc.layoutKey, "layout")] as Doc : doc; } + export function SetLayout(doc: Doc, layout: Doc | string) { doc[StrCast(doc.layoutKey, "layout")] = layout; } export function LayoutField(doc: Doc) { return doc[StrCast(doc.layoutKey, "layout")]; } const manager = new DocData(); export function SearchQuery(): string { return manager._searchQuery; } |