From 9955ff8c2de58cfe37e02d6a356b5a8a2930bb05 Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 13 Jan 2020 13:29:18 -0500 Subject: fixed some scripting and added some functions for adding docFilters to collections --- src/new_fields/Doc.ts | 45 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) (limited to 'src/new_fields') diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 8117453e7..32ab36c0f 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -742,8 +742,19 @@ export namespace Doc { source.dragFactory instanceof Doc && source.dragFactory.isTemplateDoc ? source.dragFactory : source && source.layout instanceof Doc && source.layout.isTemplateDoc ? source.layout : undefined; } -} + export function MakeDocFilter(docFilters: string[]) { + let docFilterText = ""; + for (let i = 0; i < docFilters.length; i += 3) { + const key = docFilters[i]; + const value = docFilters[i + 1]; + const modifiers = docFilters[i + 2]; + const scriptText = `${modifiers === "x" ? "!" : ""}matchFieldValue(doc, "${key}", "${value}")`; + docFilterText = docFilterText ? docFilterText + " && " + scriptText : scriptText; + }; + return docFilterText ? "(" + docFilterText + ")" : ""; + } +} Scripting.addGlobal(function renameAlias(doc: any, n: any) { return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, "") + `(${n})`; }); Scripting.addGlobal(function getProto(doc: any) { return Doc.GetProto(doc); }); @@ -761,9 +772,31 @@ Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: bo const docs = DocListCast(Doc.UserDoc().SelectedDocs).filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.DOCUMENT && d.type !== DocumentType.KVP && (!excludeCollections || !Cast(d.data, listSpec(Doc), null))); return docs.length ? new List(docs) : prevValue; }); -Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, type: string, contains: boolean = true) { - const scriptText = `${contains ? "" : "!"}(((doc.${key} && (doc.${key} as ${type})${type === "string" ? ".includes" : "<="}(${value}))) || - ((doc.data_ext && doc.data_ext.${key}) && (doc.data_ext.${key} as ${type})${type === "string" ? ".includes" : "<="}(${value}))))`; - container.docFilter = scriptText; - container.viewSpecScript = ScriptField.MakeFunction(scriptText, { doc: Doc.name }); +Scripting.addGlobal(function matchFieldValue(doc: Doc, key: string, value: any) { + const fieldVal = doc[key] ? doc[key] : doc[key + "_ext"]; + if (StrCast(fieldVal, null) !== undefined) return StrCast(fieldVal) === value; + if (NumCast(fieldVal, null) !== undefined) return NumCast(fieldVal) === value; + if (Cast(fieldVal, listSpec("string"), []).length) { + let vals = Cast(fieldVal, listSpec("string"), []); + return vals.some(v => v === value); + } + return false; +}); +Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, modifiers: string) { + const docFilters = Cast(container.docFilter, listSpec("string"), []); + let found = false; + for (let i = 0; i < docFilters.length && !found; i += 3) { + if (docFilters[i] === key && docFilters[i + 1] === value) { + found = true; + docFilters.splice(i, 3); + } + } + if (!found || modifiers !== "none") { + docFilters.push(key); + docFilters.push(value); + docFilters.push(modifiers); + container.docFilter = new List(docFilters); + } + const docFilterText = Doc.MakeDocFilter(docFilters); + container.viewSpecScript = docFilterText ? ScriptField.MakeFunction(docFilterText, { doc: Doc.name }) : undefined; }); \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 9e18edc6c2b178d9d0960aa95d2b2a9f198ab6d1 Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 13 Jan 2020 16:48:45 -0500 Subject: fixed onChildClick handlers to work for freeform views. added padding for fitTocontents views. fixed performance with templates. added openOnRight script func. switched filter to ||, --- src/Utils.ts | 5 +++-- src/client/views/MainView.tsx | 2 +- src/client/views/collections/CollectionDockingView.tsx | 11 ++++------- src/client/views/collections/CollectionSchemaHeaders.tsx | 4 ++-- .../collectionFreeForm/CollectionFreeFormLayoutEngines.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 8 +++++--- src/new_fields/Doc.ts | 14 ++++++++------ 7 files changed, 24 insertions(+), 22 deletions(-) (limited to 'src/new_fields') diff --git a/src/Utils.ts b/src/Utils.ts index 04fe6750b..562d9d83f 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -328,8 +328,8 @@ export function timenow() { return now.toLocaleDateString() + ' ' + h + ':' + m + ' ' + ampm; } -export function aggregateBounds(boundsList: { x: number, y: number, width: number, height: number }[]) { - return boundsList.reduce((bounds, b) => { +export function aggregateBounds(boundsList: { x: number, y: number, width: number, height: number }[], xpad: number, ypad: number) { + let bounds = boundsList.reduce((bounds, b) => { const [sptX, sptY] = [b.x, b.y]; const [bptX, bptY] = [sptX + b.width, sptY + b.height]; return { @@ -337,6 +337,7 @@ export function aggregateBounds(boundsList: { x: number, y: number, width: numbe r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b) }; }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: -Number.MAX_VALUE, b: -Number.MAX_VALUE }); + return { x: bounds.x !== Number.MAX_VALUE ? bounds.x - xpad : bounds.x, y: bounds.y !== Number.MAX_VALUE ? bounds.y - ypad : bounds.y, r: bounds.r !== -Number.MAX_VALUE ? bounds.r + 2 * xpad : bounds.r, b: bounds.b !== -Number.MAX_VALUE ? bounds.b + 2 * ypad : bounds.b } } export function intersectRect(r1: { left: number, top: number, width: number, height: number }, r2: { left: number, top: number, width: number, height: number }) { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 305966160..91c7f909b 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -349,7 +349,7 @@ export class MainView extends React.Component { addDocTabFunc = (doc: Doc, data: Opt, where: string, libraryPath?: Doc[]): boolean => { return where === "close" ? CollectionDockingView.CloseRightSplit(doc) : doc.dockingConfig ? this.openWorkspace(doc) : - CollectionDockingView.AddRightSplit(doc, undefined, undefined, libraryPath); + CollectionDockingView.AddRightSplit(doc, undefined, libraryPath); } mainContainerXf = () => new Transform(0, -this._buttonBarHeight, 1); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 151b84c50..6c50ea0f2 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -34,6 +34,7 @@ import { DocumentType } from '../../documents/DocumentTypes'; import { ComputedField } from '../../../new_fields/ScriptField'; import { InteractionUtils } from '../../util/InteractionUtils'; import { TraceMobx } from '../../../new_fields/util'; +import { Scripting } from '../../util/Scripting'; library.add(faFile); const _global = (window /* browser */ || global /* node */) as any; @@ -177,7 +178,7 @@ export class CollectionDockingView extends React.Component { if (doc.dockingConfig) { return MainView.Instance.openWorkspace(doc); } else if (location === "onRight") { - return CollectionDockingView.AddRightSplit(doc, dataDoc, undefined, libraryPath); + return CollectionDockingView.AddRightSplit(doc, dataDoc, libraryPath); } else if (location === "close") { return CollectionDockingView.CloseRightSplit(doc); } else { @@ -724,3 +720,4 @@ export class DockedFrameRenderer extends React.Component { ); } } +Scripting.addGlobal(function openOnRight(doc: any) { CollectionDockingView.AddRightSplit(doc, undefined); }); diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx index a965b2e9b..92dc8780e 100644 --- a/src/client/views/collections/CollectionSchemaHeaders.tsx +++ b/src/client/views/collections/CollectionSchemaHeaders.tsx @@ -1,5 +1,5 @@ import React = require("react"); -import { action, observable, runInAction } from "mobx"; +import { action, observable } from "mobx"; import { observer } from "mobx-react"; import "./CollectionSchemaView.scss"; import { faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faSortAmountDown, faSortAmountUp, faTimes } from '@fortawesome/free-solid-svg-icons'; @@ -295,7 +295,7 @@ class KeysDropdown extends React.Component { if (!exactFound && this._searchTerm !== "" && this.props.canAddNew) { this.onSelect(this._searchTerm); } else { - runInAction(() => this._searchTerm = this._key); + this.setSearchTerm(this._key); } } } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index a965a6cc9..8c8da63cc 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -78,7 +78,7 @@ export function computePivotLayout(poolData: ObservableMap, pivotDo x, y: pivotAxisWidth + 50, width: pivotAxisWidth * expander * numCols, - height: 100, + height: NumCast(pivotDoc.pivotFontSize, 10), fontSize: NumCast(pivotDoc.pivotFontSize, 10) }); for (const doc of val) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index e3780261d..29f9ccfcd 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -9,7 +9,7 @@ import { Id } from "../../../../new_fields/FieldSymbols"; import { InkTool, InkField, InkData } from "../../../../new_fields/InkField"; import { createSchema, makeInterface } from "../../../../new_fields/Schema"; import { ScriptField } from "../../../../new_fields/ScriptField"; -import { BoolCast, Cast, DateCast, NumCast, StrCast } from "../../../../new_fields/Types"; +import { BoolCast, Cast, DateCast, NumCast, StrCast, ScriptCast } from "../../../../new_fields/Types"; import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils"; import { aggregateBounds, emptyFunction, intersectRect, returnOne, Utils } from "../../../../Utils"; import { DocServer } from "../../../DocServer"; @@ -82,7 +82,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @computed get fitToContent() { return (this.props.fitToBox || this.Document.fitToBox) && !this.isAnnotationOverlay; } @computed get parentScaling() { return this.props.ContentScaling && this.fitToContent && !this.isAnnotationOverlay ? this.props.ContentScaling() : 1; } - @computed get contentBounds() { return aggregateBounds(this._layoutElements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!)); } + @computed get contentBounds() { return aggregateBounds(this._layoutElements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!), NumCast(this.layoutDoc.xPadding, 10), NumCast(this.layoutDoc.yPadding, 10)); } @computed get nativeWidth() { return this.Document.fitToContent ? 0 : this.Document.nativeWidth || 0; } @computed get nativeHeight() { return this.fitToContent ? 0 : this.Document.nativeHeight || 0; } private get isAnnotationOverlay() { return this.props.isAnnotationOverlay; } @@ -710,6 +710,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { getScale = () => this.Document.scale || 1; @computed get libraryPath() { return this.props.LibraryPath ? [...this.props.LibraryPath, this.props.Document] : []; } + @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); } getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps { return { @@ -719,7 +720,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { LibraryPath: this.libraryPath, layoutKey: undefined, ruleProvider: this.Document.isRuleProvider && childLayout.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider, //bcz: hack! - currently ruleProviders apply to documents in nested colleciton, not direct children of themselves - onClick: undefined, // this.props.onClick, // bcz: check this out -- I don't think we want to inherit click handlers, or we at least need a way to ignore them + //onClick: undefined, // this.props.onClick, // bcz: check this out -- I don't think we want to inherit click handlers, or we at least need a way to ignore them + onClick: this.onChildClickHandler, ScreenToLocalTransform: childLayout.z ? this.getTransformOverlay : this.getTransform, renderDepth: this.props.renderDepth + 1, PanelWidth: childLayout[WidthSym], diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 32ab36c0f..c809ad17a 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -572,13 +572,15 @@ export namespace Doc { return; } - const layoutCustomLayout = Doc.MakeDelegate(templateDoc); + if ((target[targetKey] as Doc)?.proto !== templateDoc) { + const layoutCustomLayout = Doc.MakeDelegate(templateDoc); - titleTarget && (Doc.GetProto(target).title = titleTarget); - Doc.GetProto(target).type = DocumentType.TEMPLATE; - target.onClick = templateDoc.onClick instanceof ObjectField && templateDoc.onClick[Copy](); + titleTarget && (Doc.GetProto(target).title = titleTarget); + Doc.GetProto(target).type = DocumentType.TEMPLATE; + target.onClick = templateDoc.onClick instanceof ObjectField && templateDoc.onClick[Copy](); - Doc.GetProto(target)[targetKey] = layoutCustomLayout; + Doc.GetProto(target)[targetKey] = layoutCustomLayout; + } target.layoutKey = targetKey; return target; } @@ -750,7 +752,7 @@ export namespace Doc { const value = docFilters[i + 1]; const modifiers = docFilters[i + 2]; const scriptText = `${modifiers === "x" ? "!" : ""}matchFieldValue(doc, "${key}", "${value}")`; - docFilterText = docFilterText ? docFilterText + " && " + scriptText : scriptText; + docFilterText = docFilterText ? docFilterText + " || " + scriptText : scriptText; }; return docFilterText ? "(" + docFilterText + ")" : ""; } -- cgit v1.2.3-70-g09d2 From 15f2148f40c351589e18c1f9ac3b39a8de366a40 Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 14 Jan 2020 17:15:36 -0500 Subject: fixes for stacking views to honor panelwidth/height -- allows stacking panels to be placed in Content fitting document views. --- src/client/documents/Documents.ts | 2 +- .../views/collections/CollectionStackingView.tsx | 31 +++++----- src/client/views/nodes/DocumentContentsView.tsx | 2 +- src/client/views/nodes/ImageBox.tsx | 69 ++++++++++------------ src/new_fields/Doc.ts | 18 +++--- 5 files changed, 60 insertions(+), 62 deletions(-) (limited to 'src/new_fields') 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(); - 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
- {this.getDisplayDoc(pair.layout || d, pair.data, dxf, width)} + {this.getDisplayDoc(d, (d.resolvedDataDoc as Doc) || d, dxf, width)}
; }); } @@ -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) {
) => 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 { - 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); } - @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
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(doc: Doc, fields: Partial>>) { + export function assign(doc: Doc, fields: Partial>>, 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; } -- cgit v1.2.3-70-g09d2 From 3ab47ee803372686966092751bd6a589b62a17c6 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Wed, 15 Jan 2020 13:26:07 -0500 Subject: linter + baseline functional multicolumn view --- src/Utils.ts | 4 +-- src/client/views/CollectionMulticolumnView.scss | 7 +++++ src/client/views/CollectionMulticolumnView.tsx | 39 ++++++++++++++++++++++--- src/client/views/nodes/DocumentView.tsx | 2 +- src/new_fields/Doc.ts | 6 ++-- 5 files changed, 48 insertions(+), 10 deletions(-) create mode 100644 src/client/views/CollectionMulticolumnView.scss (limited to 'src/new_fields') diff --git a/src/Utils.ts b/src/Utils.ts index 562d9d83f..f50655f6c 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -329,7 +329,7 @@ export function timenow() { } export function aggregateBounds(boundsList: { x: number, y: number, width: number, height: number }[], xpad: number, ypad: number) { - let bounds = boundsList.reduce((bounds, b) => { + const bounds = boundsList.reduce((bounds, b) => { const [sptX, sptY] = [b.x, b.y]; const [bptX, bptY] = [sptX + b.width, sptY + b.height]; return { @@ -337,7 +337,7 @@ export function aggregateBounds(boundsList: { x: number, y: number, width: numbe r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b) }; }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: -Number.MAX_VALUE, b: -Number.MAX_VALUE }); - return { x: bounds.x !== Number.MAX_VALUE ? bounds.x - xpad : bounds.x, y: bounds.y !== Number.MAX_VALUE ? bounds.y - ypad : bounds.y, r: bounds.r !== -Number.MAX_VALUE ? bounds.r + 2 * xpad : bounds.r, b: bounds.b !== -Number.MAX_VALUE ? bounds.b + 2 * ypad : bounds.b } + return { x: bounds.x !== Number.MAX_VALUE ? bounds.x - xpad : bounds.x, y: bounds.y !== Number.MAX_VALUE ? bounds.y - ypad : bounds.y, r: bounds.r !== -Number.MAX_VALUE ? bounds.r + 2 * xpad : bounds.r, b: bounds.b !== -Number.MAX_VALUE ? bounds.b + 2 * ypad : bounds.b }; } export function intersectRect(r1: { left: number, top: number, width: number, height: number }, r2: { left: number, top: number, width: number, height: number }) { diff --git a/src/client/views/CollectionMulticolumnView.scss b/src/client/views/CollectionMulticolumnView.scss new file mode 100644 index 000000000..84e80da4a --- /dev/null +++ b/src/client/views/CollectionMulticolumnView.scss @@ -0,0 +1,7 @@ +.collectionMulticolumnView_outer, +.collectionMulticolumnView_contents { + width: 100%; + height: 100%; + overflow: hidden; +} + diff --git a/src/client/views/CollectionMulticolumnView.tsx b/src/client/views/CollectionMulticolumnView.tsx index ea1f0c189..0aa615ffc 100644 --- a/src/client/views/CollectionMulticolumnView.tsx +++ b/src/client/views/CollectionMulticolumnView.tsx @@ -1,12 +1,16 @@ import { observer } from 'mobx-react'; import { makeInterface, listSpec } from '../../new_fields/Schema'; import { documentSchema } from '../../new_fields/documentSchemas'; -import { CollectionSubView, SubCollectionViewProps } from './collections/CollectionSubView'; +import { CollectionSubView } from './collections/CollectionSubView'; import { DragManager } from '../util/DragManager'; import * as React from "react"; import { Doc, DocListCast } from '../../new_fields/Doc'; -import { NumCast, Cast } from '../../new_fields/Types'; +import { NumCast, Cast, StrCast } from '../../new_fields/Types'; import { List } from '../../new_fields/List'; +import { ContentFittingDocumentView } from './nodes/ContentFittingDocumentView'; +import { Utils } from '../../Utils'; +import { Transform } from '../util/Transform'; +import "./collectionMulticolumnView.scss"; type MulticolumnDocument = makeInterface<[typeof documentSchema]>; const MulticolumnDocument = makeInterface(documentSchema); @@ -29,14 +33,41 @@ export default class CollectionMulticolumnView extends CollectionSubView(Multico } } + getTransform = (ele: React.RefObject) => () => { + if (!ele.current) return Transform.Identity(); + const { scale, translateX, translateY } = Utils.GetScreenTransform(ele.current); + return new Transform(-translateX, -translateY, 1 / scale); + } + public isCurrent(doc: Doc) { return !doc.isMinimized && (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document.currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); } render() { + const { PanelWidth } = this.props; return (
- {this.configuration.map(config => ).filter(pair => this.isCurrent(pair.layout)).map(({ layout, data }) => { - + {this.configuration.map(config => { + const { target } = config; + if (target instanceof Doc) { + let computedWidth: number = 0; + const widthSpecifier = Cast(config.columnWidth, "number"); + let matches: RegExpExecArray | null; + if (widthSpecifier !== undefined) { + computedWidth = widthSpecifier; + } else if ((matches = /([\d.]+)\%/.exec(StrCast(config.columnWidth))) !== null) { + computedWidth = Number(matches[1]) / 100 * PanelWidth(); + } + return (!computedWidth ? (null) : + computedWidth} + getTransform={this.props.ScreenToLocalTransform} + /> + ); + } + return (null); })}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index e45c19533..8f3fa4530 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -196,7 +196,7 @@ export class DocumentView extends DocComponent(Docu ScriptBox.EditButtonScript("On Button Clicked ...", this.props.Document, "onClick", e.clientX, e.clientY); } else if (this.props.Document.isButton === "Selector") { // this should be moved to an OnClick script FormattedTextBoxComment.Hide(); - this.Document.links?.[0] instanceof Doc && (Doc.UserDoc().SelectedDocs = new List([Doc.LinkOtherAnchor(this.Document.links[0]!, this.props.Document)])); + this.Document.links?.[0] instanceof Doc && (Doc.UserDoc().SelectedDocs = new List([Doc.LinkOtherAnchor(this.Document.links[0], this.props.Document)])); } else if (this.Document.isButton) { SelectionManager.SelectDoc(this, e.ctrlKey); // don't think this should happen if a button action is actually triggered. this.buttonClick(e.altKey, e.ctrlKey); diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index d4634ba4b..26acfa9c3 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -405,7 +405,7 @@ export namespace Doc { } export function MakeAlias(doc: Doc) { const alias = !GetT(doc, "isPrototype", "boolean", true) ? Doc.MakeCopy(doc) : Doc.MakeDelegate(doc); - let layout = Doc.Layout(alias); + const layout = Doc.Layout(alias); if (layout instanceof Doc && layout !== alias) { Doc.SetLayout(alias, Doc.MakeAlias(layout)); } @@ -755,7 +755,7 @@ export namespace Doc { const modifiers = docFilters[i + 2]; const scriptText = `${modifiers === "x" ? "!" : ""}matchFieldValue(doc, "${key}", "${value}")`; docFilterText = docFilterText ? docFilterText + " || " + scriptText : scriptText; - }; + } return docFilterText ? "(" + docFilterText + ")" : ""; } } @@ -781,7 +781,7 @@ Scripting.addGlobal(function matchFieldValue(doc: Doc, key: string, value: any) if (StrCast(fieldVal, null) !== undefined) return StrCast(fieldVal) === value; if (NumCast(fieldVal, null) !== undefined) return NumCast(fieldVal) === value; if (Cast(fieldVal, listSpec("string"), []).length) { - let vals = Cast(fieldVal, listSpec("string"), []); + const vals = Cast(fieldVal, listSpec("string"), []); return vals.some(v => v === value); } return false; -- cgit v1.2.3-70-g09d2 From 8212288108c3acfd129e45b9f496f0606a8d2848 Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 15 Jan 2020 17:17:35 -0500 Subject: changed freeformdocument to use contentfittingviews if specified with FitToBox. fixed bugs with contentfittingviews & titles. --- src/Utils.ts | 2 +- src/client/views/Templates.tsx | 11 ++++++++++- .../views/collections/CollectionStackingView.tsx | 4 ++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 8 ++++++-- .../views/nodes/CollectionFreeFormDocumentView.tsx | 19 ++++++++++--------- src/client/views/nodes/DocumentView.scss | 13 ++++++++++++- src/client/views/nodes/DocumentView.tsx | 13 +++++++------ src/client/views/nodes/FormattedTextBox.tsx | 2 +- src/new_fields/Doc.ts | 2 +- src/new_fields/documentSchemas.ts | 6 +++++- 10 files changed, 55 insertions(+), 25 deletions(-) (limited to 'src/new_fields') diff --git a/src/Utils.ts b/src/Utils.ts index f50655f6c..7bf05a6fc 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -337,7 +337,7 @@ export function aggregateBounds(boundsList: { x: number, y: number, width: numbe r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b) }; }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: -Number.MAX_VALUE, b: -Number.MAX_VALUE }); - return { x: bounds.x !== Number.MAX_VALUE ? bounds.x - xpad : bounds.x, y: bounds.y !== Number.MAX_VALUE ? bounds.y - ypad : bounds.y, r: bounds.r !== -Number.MAX_VALUE ? bounds.r + 2 * xpad : bounds.r, b: bounds.b !== -Number.MAX_VALUE ? bounds.b + 2 * ypad : bounds.b }; + return { x: bounds.x !== Number.MAX_VALUE ? bounds.x - xpad : bounds.x, y: bounds.y !== Number.MAX_VALUE ? bounds.y - ypad : bounds.y, r: bounds.r !== -Number.MAX_VALUE ? bounds.r + xpad : bounds.r, b: bounds.b !== -Number.MAX_VALUE ? bounds.b + ypad : bounds.b }; } export function intersectRect(r1: { left: number, top: number, width: number, height: number }, r2: { left: number, top: number, width: number, height: number }) { diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx index ef78b60d4..8af8a6280 100644 --- a/src/client/views/Templates.tsx +++ b/src/client/views/Templates.tsx @@ -56,8 +56,17 @@ export namespace Templates {
{layout}
` ); + export const TitleHover = new Template("TitleHover", TemplatePosition.InnerTop, + `
+
+ {props.Document.title} +
+
+
{layout}
+
+
` ); - export const TemplateList: Template[] = [Title, Caption]; + export const TemplateList: Template[] = [Title, TitleHover, Caption]; export function sortTemplates(a: Template, b: Template) { if (a.Position < b.Position) { return -1; } diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 3ab0326e2..886a9c870 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -41,7 +41,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { @computed get sectionFilter() { return StrCast(this.props.Document.sectionFilter); } @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 yMargin() { return Math.max(this.props.Document.showTitle && !this.props.Document.showTitleHover ? 30 : 0, NumCast(this.props.Document.yMargin, 2 * this.gridGap)); } @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; } @@ -152,7 +152,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } overlays = (doc: Doc) => { - return doc.type === DocumentType.IMG || doc.type === DocumentType.VID ? { title: StrCast(this.props.Document.showTitles), caption: StrCast(this.props.Document.showCaptions) } : {}; + return doc.type === DocumentType.IMG || doc.type === DocumentType.VID ? { title: StrCast(this.props.Document.showTitles), titleHover: StrCast(this.props.Document.showTitleHovers), caption: StrCast(this.props.Document.showCaptions) } : {}; } @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 29f9ccfcd..7985e541f 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -56,6 +56,8 @@ export const panZoomSchema = createSchema({ useClusters: "boolean", isRuleProvider: "boolean", fitToBox: "boolean", + xPadding: "number", // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set + yPadding: "number", // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set panTransformType: "string", scrollHeight: "number", fitX: "number", @@ -802,9 +804,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } this.childLayoutPairs.filter((pair, i) => this.isCurrent(pair.layout)).forEach(pair => computedElementData.elements.push({ - ele: , + jitterRotation={NumCast(this.props.Document.jitterRotation)} + fitToBox={this.props.fitToBox || this.Document.freeformLayoutEngine === "pivot"} />, bounds: this.childDataProvider(pair.layout) })); diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index f79496ab7..614a68e7a 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -11,6 +11,8 @@ import { DocumentView, DocumentViewProps } from "./DocumentView"; import React = require("react"); import { PositionDocument } from "../../../new_fields/documentSchemas"; import { TraceMobx } from "../../../new_fields/util"; +import { returnFalse } from "../../../Utils"; +import { ContentFittingDocumentView } from "./ContentFittingDocumentView"; export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { dataProvider?: (doc: Doc) => { x: number, y: number, width: number, height: number, z: number, transition?: string } | undefined; @@ -20,6 +22,7 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { height?: number; jitterRotation: number; transition?: string; + fitToBox?: boolean; } @observer @@ -83,8 +86,8 @@ export class CollectionFreeFormDocumentView extends DocComponent this.dataProvider ? this.dataProvider.width : this.panelWidth(); - finalPanelHeight = () => this.dataProvider ? this.dataProvider.height : this.panelHeight(); + finalPanelWidth = () => (this.dataProvider ? this.dataProvider.width : this.panelWidth()); + finalPanelHeight = () => (this.dataProvider ? this.dataProvider.height : this.panelHeight()); render() { TraceMobx(); @@ -104,24 +107,22 @@ export class CollectionFreeFormDocumentView extends DocComponent - - {/* : this.props.focus(doc, false)} - // backgroundColor={this.clusterColorFunc} PanelWidth={this.finalPanelWidth} PanelHeight={this.finalPanelHeight} - /> */} + />}
; } } diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index f44c6dd3b..2ce56c73d 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -55,7 +55,7 @@ position: absolute; } - .documentView-titleWrapper { + .documentView-titleWrapper, .documentView-titleWrapper-hover { overflow: hidden; color: white; transform-origin: top left; @@ -68,6 +68,9 @@ text-overflow: ellipsis; white-space: pre; } + .documentView-titleWrapper-hover { + display:none; + } .documentView-searchHighlight { position: absolute; @@ -85,4 +88,12 @@ } } +} + +.documentView-node:hover, .documentView-node-topmost:hover { + > .documentView-styleWrapper { + > .documentView-titleWrapper-hover { + display:inline-block; + } + } } \ No newline at end of file diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 8f3fa4530..b2c2ccff5 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -29,7 +29,6 @@ import { CollectionDockingView } from "../collections/CollectionDockingView"; import { CollectionView } from "../collections/CollectionView"; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from '../ContextMenuItem'; -import { DictationOverlay } from '../DictationOverlay'; import { DocComponent } from "../DocComponent"; import { EditableView } from '../EditableView'; import { OverlayView } from '../OverlayView'; @@ -64,7 +63,7 @@ export interface DocumentViewProps { moveDocument?: (doc: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean; ScreenToLocalTransform: () => Transform; renderDepth: number; - showOverlays?: (doc: Doc) => { title?: string, caption?: string }; + showOverlays?: (doc: Doc) => { title?: string, titleHover?: string, caption?: string }; ContentScaling: () => number; ruleProvider: Doc | undefined; PanelWidth: () => number; @@ -741,7 +740,8 @@ export class DocumentView extends DocComponent(Docu chromeHeight = () => { const showOverlays = this.props.showOverlays ? this.props.showOverlays(this.Document) : undefined; const showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : StrCast(this.layoutDoc.showTitle); - return (showTitle ? 25 : 0) + 1; + const showTitleHover = showOverlays && "titleHover" in showOverlays ? showOverlays.titleHover : StrCast(this.layoutDoc.showTitleHover); + return (showTitle && !showTitleHover ? 0 : 0) + 1; } @computed get finalLayoutKey() { return this.props.layoutKey || "layout"; } @@ -751,6 +751,7 @@ export class DocumentView extends DocComponent(Docu return ((Docu isSelected={this.isSelected} select={this.select} onClick={this.onClickHandler} - layoutKey={this.finalLayoutKey} - DataDoc={this.props.DataDoc} />); + layoutKey={this.finalLayoutKey} />); } linkEndpoint = (linkDoc: Doc) => Doc.LinkEndpoint(linkDoc, this.props.Document); @@ -795,6 +795,7 @@ export class DocumentView extends DocComponent(Docu TraceMobx(); const showOverlays = this.props.showOverlays ? this.props.showOverlays(this.Document) : undefined; const showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : StrCast(this.getLayoutPropStr("showTitle")); + const showTitleHover = showOverlays && "titleHover" in showOverlays ? showOverlays.titleHover : StrCast(this.getLayoutPropStr("showTitleHover")); const showCaption = showOverlays && "caption" in showOverlays ? showOverlays.caption : this.getLayoutPropStr("showCaption"); const showTextTitle = showTitle && StrCast(this.layoutDoc.layout).indexOf("FormattedTextBox") !== -1 ? showTitle : undefined; const searchHighlight = (!this.Document.searchFields ? (null) : @@ -810,7 +811,7 @@ export class DocumentView extends DocComponent(Docu /> ); const titleView = (!showTitle ? (null) : -
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 8b5c24878..60842bcb0 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -940,7 +940,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & } if (!node && this.ProseRef) { const lastNode = this.ProseRef.children[this.ProseRef.children.length - 1].children[this.ProseRef.children[this.ProseRef.children.length - 1].children.length - 1]; // get the last prosemirror div - if (e.clientY > lastNode.getBoundingClientRect().bottom) { // if we clicked below the last prosemirror div, then set the selection to be the end of the document + if (e.clientY > lastNode?.getBoundingClientRect().bottom) { // if we clicked below the last prosemirror div, then set the selection to be the end of the document this._editorView!.dispatch(this._editorView!.state.tr.setSelection(TextSelection.create(this._editorView!.state.doc, this._editorView!.state.doc.content.size))); } } diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 26acfa9c3..e0ab5d97c 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -455,7 +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); + setTimeout(() => layoutDoc && (layoutDoc.resolvedDataDoc = resolvedDataDoc), 0); } else layoutDoc = childDocLayout; return { layout: layoutDoc, data: resolvedDataDoc }; } diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts index 21e69fbed..909fdc6c3 100644 --- a/src/new_fields/documentSchemas.ts +++ b/src/new_fields/documentSchemas.ts @@ -41,7 +41,8 @@ export const documentSchema = createSchema({ searchFields: "string", // the search fields to display when this document matches a search in its metadata heading: "number", // the logical layout 'heading' of this document (used by rule provider to stylize h1 header elements, from h2, etc) showCaption: "string", // whether editable caption text is overlayed at the bottom of the document - showTitle: "string", // whether an editable title banner is displayed at tht top of the document + showTitle: "string", // the fieldkey whose contents should be displayed at the top of the document + showTitleHover: "string", // the showTitle should be shown only on hover isButton: "boolean", // whether document functions as a button (overiding native interactions of its content) ignoreClick: "boolean", // whether documents ignores input clicks (but does not ignore manipulation and other events) isAnimating: "string", // whether the document is in the midst of animating between two layouts (used by icons to de/iconify documents). value is undefined|"min"|"max" @@ -49,6 +50,9 @@ export const documentSchema = createSchema({ scrollToLinkID: "string", // id of link being traversed. allows this doc to scroll/highlight/etc its link anchor. scrollToLinkID should be set to undefined by this doc after it sets up its scroll,etc. strokeWidth: "number", fontSize: "string", + fitToBox: "boolean", // whether freeform view contents should be zoomed/panned to fill the area of the document view + xPadding: "number", // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set + yPadding: "number", // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set LODarea: "number", // area (width*height) where CollectionFreeFormViews switch from a label to rendering contents LODdisable: "boolean", // whether to disbale LOD switching for CollectionFreeFormViews }); -- cgit v1.2.3-70-g09d2