From b4ef60552f226e4022fac5092ea66f1b685bb08c Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Mon, 22 Jul 2019 18:50:26 -0400 Subject: Started adding schema functionality --- .../views/collections/CollectionSchemaView.tsx | 92 ++++++++++++++++++++-- src/client/views/collections/CollectionView.tsx | 9 ++- .../collectionFreeForm/CollectionFreeFormView.tsx | 63 +++++++-------- 3 files changed, 124 insertions(+), 40 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index f72b1aa07..479fe2b0a 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -15,7 +15,7 @@ import { Cast, FieldValue, NumCast, StrCast, BoolCast } from "../../../new_field import { Docs } from "../../documents/Documents"; import { Gateway } from "../../northstar/manager/Gateway"; import { SetupDrag, DragManager } from "../../util/DragManager"; -import { CompileScript } from "../../util/Scripting"; +import { CompileScript, ts, Transformer } from "../../util/Scripting"; import { Transform } from "../../util/Transform"; import { COLLECTION_BORDER_WIDTH, MAX_ROW_HEIGHT } from '../../views/globalCssVariables.scss'; import { ContextMenu } from "../ContextMenu"; @@ -31,6 +31,7 @@ import { CollectionVideoView } from "./CollectionVideoView"; import { CollectionView } from "./CollectionView"; import { undoBatch } from "../../util/UndoManager"; import { timesSeries } from "async"; +import { ComputedField } from "../../../new_fields/ScriptField"; library.add(faCog); @@ -97,6 +98,78 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { return this.props.Document; } + getField(row: number, col?: number) { + const docs = DocListCast(this.props.Document[this.props.fieldKey]); + row = row % docs.length; + while (row < 0) row += docs.length; + const columns = this.columns; + const doc = docs[row]; + if (col === undefined) { + return doc; + } + if (col >= 0 && col < columns.length) { + const column = this.columns[col]; + return doc[column]; + } + return undefined; + } + + createTransformer = (row: number, col: number): Transformer => { + const self = this; + const captures: { [name: string]: Field } = {}; + + const transformer: ts.TransformerFactory = context => { + return root => { + function visit(node: ts.Node) { + node = ts.visitEachChild(node, visit, context); + if (ts.isIdentifier(node)) { + const isntPropAccess = !ts.isPropertyAccessExpression(node.parent) || node.parent.expression === node; + const isntPropAssign = !ts.isPropertyAssignment(node.parent) || node.parent.name !== node; + if (isntPropAccess && isntPropAssign) { + if (node.text === "$r") { + return ts.createNumericLiteral(row.toString()); + } else if (node.text === "$c") { + return ts.createNumericLiteral(col.toString()); + } else if (node.text === "$") { + if (ts.isCallExpression(node.parent)) { + captures.doc = self.props.Document; + captures.key = self.props.fieldKey; + } + } + } + } + + return node; + } + return ts.visitNode(root, visit); + }; + }; + + const getVars = () => { + return { capturedVariables: captures }; + }; + + return { transformer, getVars }; + } + + setComputed(script: string, doc: Doc, field: string, row: number, col: number): boolean { + script = + `const $ = (row:number, col?:number) => { + if(col === undefined) { + return (doc as any)[key][row + ${row}]; + } + return (doc as any)[key][row + ${row}][(doc as any).schemaColumns[col + ${col}]]; + } + return ${script}`; + const compiled = CompileScript(script, { params: { this: Doc.name }, typecheck: true, transformer: this.createTransformer(row, col) }); + if (compiled.compiled) { + doc[field] = new ComputedField(compiled); + return true; + } + + return false; + } + renderCell = (rowProps: CellInfo) => { let props: FieldViewProps = { Document: rowProps.original, @@ -122,12 +195,13 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { (!this.props.CollectionView.props.isSelected() ? undefined : SetupDrag(reference, () => props.Document, this.props.moveDocument, this.props.Document.schemaDoc ? "copy" : undefined)(e)); }; - let applyToDoc = (doc: Doc, run: (args?: { [name: string]: any }) => any) => { - const res = run({ this: doc }); + let applyToDoc = (doc: Doc, row: number, column: number, run: (args?: { [name: string]: any }) => any) => { + const res = run({ this: doc, $r: row, $c: column, $: (r: number = 0, c: number = 0) => this.getField(r + row, c + column) }); if (!res.success) return false; doc[props.fieldKey] = res.result; return true; }; + const colIndex = this.columns.indexOf(rowProps.column.id!); return (
doc) { return ""; }} SetValue={(value: string) => { - let script = CompileScript(value, { addReturn: true, params: { this: Doc.name } }); + if (value.startsWith(":=")) { + return this.setComputed(value.substring(2), props.Document, rowProps.column.id!, rowProps.index, colIndex); + } + let script = CompileScript(value, { addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } }); if (!script.compiled) { return false; } - return applyToDoc(props.Document, script.run); + return applyToDoc(props.Document, rowProps.index, colIndex, script.run); }} OnFillDown={async (value: string) => { - let script = CompileScript(value, { addReturn: true, params: { this: Doc.name } }); + let script = CompileScript(value, { addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } }); if (!script.compiled) { return; } const run = script.run; - //TODO This should be able to be refactored to compile the script once const val = await DocListCastAsync(this.props.Document[this.props.fieldKey]); - val && val.forEach(doc => applyToDoc(doc, run)); + val && val.forEach((doc, i) => applyToDoc(doc, i, colIndex, run)); }}>
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 56750668d..ae99e930b 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -1,5 +1,5 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree } from '@fortawesome/free-solid-svg-icons'; +import { faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree, faFingerprint, faLaptopCode } from '@fortawesome/free-solid-svg-icons'; import { observer } from "mobx-react"; import * as React from 'react'; import { Doc } from '../../../new_fields/Doc'; @@ -24,6 +24,7 @@ library.add(faSquare); library.add(faProjectDiagram); library.add(faSignature); library.add(faThList); +library.add(faFingerprint); @observer export class CollectionView extends React.Component { @@ -55,6 +56,12 @@ export class CollectionView extends React.Component { subItems.push({ description: "Schema", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Schema), icon: "th-list" }); subItems.push({ description: "Treeview", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Tree), icon: "tree" }); subItems.push({ description: "Stacking", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Stacking), icon: "th-list" }); + switch (this.props.Document.viewType) { + case CollectionViewType.Freeform: { + subItems.push({ description: "Custom", icon: "fingerprint", event: CollectionFreeFormView.AddCustomLayout(this.props.Document, this.props.fieldKey) }); + break; + } + } ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems }); ContextMenu.Instance.addItem({ description: "Apply Template", event: undoBatch(() => { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 703873681..dd577a467 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -477,43 +477,44 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { }, "arrange contents"); } }); - ContextMenu.Instance.addItem({ - description: "Add freeform arrangement", - event: () => { - let addOverlay = (key: "arrangeScript" | "arrangeInit", options: OverlayElementOptions, params?: Record, requiredType?: string) => { - let overlayDisposer: () => void = emptyFunction; - const script = this.Document[key]; - let originalText: string | undefined = undefined; - if (script) originalText = script.script.originalScript; - // tslint:disable-next-line: no-unnecessary-callback-wrapper - let scriptingBox = overlayDisposer()} onSave={(text, onError) => { - const script = CompileScript(text, { - params, - requiredType, - typecheck: false - }); - if (!script.compiled) { - onError(script.errors.map(error => error.messageText).join("\n")); - return; - } - const docs = DocListCast(this.Document[this.props.fieldKey]); - docs.map(d => d.transition = "transform 1s"); - this.Document[key] = new ScriptField(script); - overlayDisposer(); - setTimeout(() => docs.map(d => d.transition = undefined), 1200); - }} />; - overlayDisposer = OverlayView.Instance.addWindow(scriptingBox, options); - }; - addOverlay("arrangeInit", { x: 400, y: 100, width: 400, height: 300, title: "Layout Initialization" }, { collection: "Doc", docs: "Doc[]" }, undefined); - addOverlay("arrangeScript", { x: 400, y: 500, width: 400, height: 300, title: "Layout Script" }, { doc: "Doc", index: "number", collection: "Doc", state: "any", docs: "Doc[]" }, "{x: number, y: number, width?: number, height?: number}"); - } - }); } private childViews = () => [ , ...this.views ] + + public static AddCustomLayout(doc: Doc, dataKey: string): () => void { + return () => { + let addOverlay = (key: "arrangeScript" | "arrangeInit", options: OverlayElementOptions, params?: Record, requiredType?: string) => { + let overlayDisposer: () => void = emptyFunction; + const script = Cast(doc[key], ScriptField); + let originalText: string | undefined = undefined; + if (script) originalText = script.script.originalScript; + // tslint:disable-next-line: no-unnecessary-callback-wrapper + let scriptingBox = overlayDisposer()} onSave={(text, onError) => { + const script = CompileScript(text, { + params, + requiredType, + typecheck: false + }); + if (!script.compiled) { + onError(script.errors.map(error => error.messageText).join("\n")); + return; + } + const docs = DocListCast(doc[dataKey]); + docs.map(d => d.transition = "transform 1s"); + doc[key] = new ScriptField(script); + overlayDisposer(); + setTimeout(() => docs.map(d => d.transition = undefined), 1200); + }} />; + overlayDisposer = OverlayView.Instance.addWindow(scriptingBox, options); + }; + addOverlay("arrangeInit", { x: 400, y: 100, width: 400, height: 300, title: "Layout Initialization" }, { collection: "Doc", docs: "Doc[]" }, undefined); + addOverlay("arrangeScript", { x: 400, y: 500, width: 400, height: 300, title: "Layout Script" }, { doc: "Doc", index: "number", collection: "Doc", state: "any", docs: "Doc[]" }, "{x: number, y: number, width?: number, height?: number}"); + }; + } + render() { const easing = () => this.props.Document.panTransformType === "Ease"; -- cgit v1.2.3-70-g09d2 From 86971952237b8bd01a23b52db662740126bd8477 Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 23 Jul 2019 18:00:52 -0400 Subject: some fixes for fitToBox, templates, and showTitle --- src/client/views/DocumentDecorations.tsx | 4 +- src/client/views/EditableView.tsx | 2 +- .../views/collections/CollectionStackingView.tsx | 3 +- .../views/collections/CollectionTreeView.tsx | 76 +++++++++++++--------- src/client/views/collections/CollectionView.tsx | 15 +---- .../collectionFreeForm/CollectionFreeFormView.tsx | 9 +-- src/client/views/nodes/DocumentContentsView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 17 ++--- src/new_fields/Doc.ts | 26 ++++++-- 9 files changed, 87 insertions(+), 67 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 2f7bea365..989b35581 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -525,8 +525,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> let actualdH = Math.max(height + (dH * scale), 20); doc.x = (doc.x || 0) + dX * (actualdW - width); doc.y = (doc.y || 0) + dY * (actualdH - height); - let proto = Doc.GetProto(element.props.Document); - let fixedAspect = e.ctrlKey || (!BoolCast(proto.ignoreAspect, false) && nwidth && nheight); + let proto = doc.isTemplate ? doc : Doc.GetProto(element.props.Document); // bcz: 'doc' didn't work here... + let fixedAspect = e.ctrlKey || (!BoolCast(proto.ignoreAspect) && nwidth && nheight); if (fixedAspect && (!nwidth || !nheight)) { proto.nativeWidth = nwidth = doc.width || 0; proto.nativeHeight = nheight = doc.height || 0; diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index f2cdffd38..c66a92f48 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -93,7 +93,7 @@ export class EditableView extends React.Component {
- {this.props.contents} + {this.props.contents}
); } diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 0e5f9a321..54b0e37b5 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -5,7 +5,7 @@ import { observer } from "mobx-react"; import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; import { BoolCast, NumCast, Cast, StrCast } from "../../../new_fields/Types"; -import { emptyFunction, Utils } from "../../../Utils"; +import { emptyFunction, Utils, returnTrue } from "../../../Utils"; import { CollectionSchemaPreview } from "./CollectionSchemaView"; import "./CollectionStackingView.scss"; import { CollectionSubView } from "./CollectionSubView"; @@ -74,6 +74,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { DataDocument={resolvedDataDoc} showOverlays={this.overlays} renderDepth={this.props.renderDepth} + fitToBox={true} width={width} height={height} getTransform={finalDxf} diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index d05cc375e..f98629c5b 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -74,15 +74,15 @@ class TreeView extends React.Component { @observable _collapsed: boolean = true; @computed get fieldKey() { - let keys = Array.from(Object.keys(this.resolvedDataDoc)); // bcz: Argh -- make untracked to avoid this rerunning whenever 'libraryBrush' is set - if (this.resolvedDataDoc.proto instanceof Doc) { - let arr = Array.from(Object.keys(this.resolvedDataDoc.proto));// bcz: Argh -- make untracked to avoid this rerunning whenever 'libraryBrush' is set + let keys = Array.from(Object.keys(this.dataDoc)); // bcz: Argh -- make untracked to avoid this rerunning whenever 'libraryBrush' is set + if (this.dataDoc.proto instanceof Doc) { + let arr = Array.from(Object.keys(this.dataDoc.proto));// bcz: Argh -- make untracked to avoid this rerunning whenever 'libraryBrush' is set keys.push(...arr); while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1); } let keyList: string[] = []; keys.map(key => { - let docList = Cast(this.resolvedDataDoc[key], listSpec(Doc)); + let docList = Cast(this.dataDoc[key], listSpec(Doc)); if (docList && docList.length > 0) { keyList.push(key); } @@ -94,7 +94,16 @@ class TreeView extends React.Component { return keyList.length ? keyList[0] : "data"; } - @computed get resolvedDataDoc() { return BoolCast(this.props.document.isTemplate) && this.props.dataDoc ? this.props.dataDoc : this.props.document; } + @computed get dataDoc() { return this.resolvedDataDoc ? this.resolvedDataDoc : this.props.document; } + @computed get resolvedDataDoc() { + if (this.props.dataDoc === undefined && this.props.document.layout instanceof Doc) { + // if there is no dataDoc (ie, we're not rendering a template layout), but this document + // has a template layout document, then we will render the template layout but use + // this document as the data document for the layout. + return this.props.document; + } + return this.props.dataDoc ? this.props.dataDoc : undefined; + } protected createTreeDropTarget = (ele: HTMLDivElement) => { this._treedropDisposer && this._treedropDisposer(); @@ -103,7 +112,7 @@ class TreeView extends React.Component { } } - @undoBatch delete = () => this.props.deleteDoc(this.resolvedDataDoc); + @undoBatch delete = () => this.props.deleteDoc(this.dataDoc); @undoBatch openRight = async () => this.props.addDocTab(this.props.document, undefined, "onRight"); onPointerDown = (e: React.PointerEvent) => e.stopPropagation(); @@ -135,7 +144,7 @@ class TreeView extends React.Component { @action remove = (document: Document, key: string): boolean => { - let children = Cast(this.resolvedDataDoc[key], listSpec(Doc), []); + let children = Cast(this.dataDoc[key], listSpec(Doc), []); if (children.indexOf(document) !== -1) { children.splice(children.indexOf(document), 1); return true; @@ -151,8 +160,8 @@ class TreeView extends React.Component { indent = () => this.props.addDocument(this.props.document) && this.delete() renderBullet() { - let docList = Cast(this.resolvedDataDoc[this.fieldKey], listSpec(Doc)); - let doc = Cast(this.resolvedDataDoc[this.fieldKey], Doc); + let docList = Cast(this.dataDoc[this.fieldKey], listSpec(Doc)); + let doc = Cast(this.dataDoc[this.fieldKey], Doc); let isDoc = doc instanceof Doc || docList; let c; return
this._collapsed = !this._collapsed)} style={{ color: StrCast(this.props.document.color, "black"), opacity: 0.4 }}> @@ -164,16 +173,17 @@ class TreeView extends React.Component { editableView = (key: string, style?: string) => ( StrCast(this.props.document[key])} - SetValue={(value: string) => (Doc.GetProto(this.resolvedDataDoc)[key] = value) ? true : true} + SetValue={(value: string) => (Doc.GetProto(this.dataDoc)[key] = value) ? true : true} OnFillDown={(value: string) => { - Doc.GetProto(this.resolvedDataDoc)[key] = value; - let doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List([Templates.Title.Layout]) }); + Doc.GetProto(this.dataDoc)[key] = value; + let doc = this.props.document.detailedLayout instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.document.detailedLayout)) : undefined; + if (!doc) doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List([Templates.Title.Layout]) }); TreeView.loadId = doc[Id]; return this.props.addDocument(doc); }} @@ -181,16 +191,16 @@ class TreeView extends React.Component { />) @computed get keyList() { - let keys = Array.from(Object.keys(this.resolvedDataDoc)); - if (this.resolvedDataDoc.proto instanceof Doc) { - keys.push(...Array.from(Object.keys(this.resolvedDataDoc.proto))); + let keys = Array.from(Object.keys(this.dataDoc)); + if (this.dataDoc.proto instanceof Doc) { + keys.push(...Array.from(Object.keys(this.dataDoc.proto))); } let keyList: string[] = keys.reduce((l, key) => { - let listspec = DocListCast(this.resolvedDataDoc[key]); + let listspec = DocListCast(this.dataDoc[key]); if (listspec && listspec.length) return [...l, key]; return l; }, [] as string[]); - keys.map(key => Cast(this.resolvedDataDoc[key], Doc) instanceof Doc && keyList.push(key)); + keys.map(key => Cast(this.dataDoc[key], Doc) instanceof Doc && (Cast(this.dataDoc[key], Doc) as Doc).type !== undefined && keyList.push(key)); if (LinkManager.Instance.getAllRelatedLinks(this.props.document).length > 0) keyList.push("links"); if (keyList.indexOf(this.fieldKey) !== -1) { keyList.splice(keyList.indexOf(this.fieldKey), 1); @@ -203,7 +213,7 @@ class TreeView extends React.Component { */ renderTitle() { let reference = React.createRef(); - let onItemDown = SetupDrag(reference, () => this.resolvedDataDoc, this.move, this.props.dropAction, this.props.treeViewId, true); + let onItemDown = SetupDrag(reference, () => this.dataDoc, this.move, this.props.dropAction, this.props.treeViewId, true); let headerElements = ( { {this._chosenKey} ); let dataDocs = CollectionDockingView.Instance ? Cast(CollectionDockingView.Instance.props.Document[this.fieldKey], listSpec(Doc), []) : []; - let openRight = dataDocs && dataDocs.indexOf(this.resolvedDataDoc) !== -1 ? (null) : ( + let openRight = dataDocs && dataDocs.indexOf(this.dataDoc) !== -1 ? (null) : (
); @@ -241,12 +251,12 @@ class TreeView extends React.Component { if (NumCast(this.props.document.viewType) !== CollectionViewType.Docking) { ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.resolvedDataDoc, "inTab"), icon: "folder" }); ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.resolvedDataDoc, "onRight"), icon: "caret-square-right" }); - if (DocumentManager.Instance.getDocumentViews(this.resolvedDataDoc).length) { - ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.resolvedDataDoc).map(view => view.props.focus(this.props.document, true)), icon: "camera" }); + if (DocumentManager.Instance.getDocumentViews(this.dataDoc).length) { + ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.dataDoc).map(view => view.props.focus(this.props.document, true)), icon: "camera" }); } ContextMenu.Instance.addItem({ description: "Delete Item", event: undoBatch(() => this.props.deleteDoc(this.props.document)), icon: "trash-alt" }); } else { - ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.resolvedDataDoc)), icon: "caret-square-right" }); + ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.dataDoc)), icon: "caret-square-right" }); ContextMenu.Instance.addItem({ description: "Delete Workspace", event: undoBatch(() => this.props.deleteDoc(this.props.document)), icon: "trash-alt" }); } ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.Create.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, this.props.dataDoc ? this.props.dataDoc : kvp, "onRight"); }, icon: "layer-group" }); @@ -274,7 +284,7 @@ class TreeView extends React.Component { if (de.data.draggedDocuments[0] === this.props.document) return true; let addDoc = (doc: Doc) => this.props.addDocument(doc, this.resolvedDataDoc, before); if (inside) { - let docList = Cast(this.resolvedDataDoc.data, listSpec(Doc)); + let docList = Cast(this.dataDoc.data, listSpec(Doc)); if (docList !== undefined) { addDoc = (doc: Doc) => { docList && docList.push(doc); return true; }; } @@ -326,7 +336,7 @@ class TreeView extends React.Component { @computed get boundsOfCollectionDocument() { if (StrCast(this.props.document.type).indexOf(DocumentType.COL) === -1) return undefined; - let layoutDoc = Doc.expandTemplateLayout(this.props.document, this.props.dataDoc); + let layoutDoc = this.props.document; return Doc.ComputeContentBounds(DocListCast(layoutDoc.data)); } docWidth = () => { @@ -344,27 +354,30 @@ class TreeView extends React.Component { })()); } + noOverlays = (doc: Doc) => { return { title: "", caption: "" } }; + render() { let contentElement: (JSX.Element | null) = null; - let docList = Cast(this.resolvedDataDoc[this._chosenKey], listSpec(Doc)); + let docList = Cast(this.dataDoc[this._chosenKey], listSpec(Doc)); let remDoc = (doc: Doc) => this.remove(doc, this._chosenKey); - let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.resolvedDataDoc, this._chosenKey, doc, addBefore, before); - let doc = Cast(this.resolvedDataDoc[this._chosenKey], Doc); + let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, this._chosenKey, doc, addBefore, before); + let doc = Cast(this.dataDoc[this._chosenKey], Doc); if (!this._collapsed) { if (!this.props.document.embed) { contentElement =
    {this._chosenKey === "links" ? this.renderLinks() : - TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, this.props.document, this.props.dataDoc, this._chosenKey, addDoc, remDoc, this.move, + TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, this.props.document, this.resolvedDataDoc, this._chosenKey, addDoc, remDoc, this.move, this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth, this.props.renderDepth)}
; } else { - let layoutDoc = Doc.expandTemplateLayout(this.props.document, this.props.dataDoc); + let layoutDoc = this.props.document; contentElement =
(Doc.GetProto(this.resolvedDataDoc).title = value) ? true : true} OnFillDown={(value: string) => { Doc.GetProto(this.props.Document).title = value; - let doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List([Templates.Title.Layout]) }); + let doc = this.props.Document.detailedLayout instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.Document.detailedLayout)) : undefined; + if (!doc) doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List([Templates.Title.Layout]) }); TreeView.loadId = doc[Id]; Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, this.childDocs.length ? this.childDocs[0] : undefined, true); }} /> diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 045c8531e..4a51a1f58 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -50,7 +50,6 @@ export class CollectionView extends React.Component { get isAnnotationOverlay() { return this.props.fieldExt ? true : false; } - static _applyCount: number = 0; onContextMenu = (e: React.MouseEvent): void => { if (!this.isAnnotationOverlay && !e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 let subItems: ContextMenuProps[] = []; @@ -63,19 +62,7 @@ export class CollectionView extends React.Component { subItems.push({ description: "Stacking", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Stacking), icon: "ellipsis-v" }); subItems.push({ description: "Masonry", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Masonry), icon: "columns" }); ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems }); - ContextMenu.Instance.addItem({ - description: "Apply Template", event: undoBatch(() => { - let otherdoc = new Doc(); - otherdoc.width = this.props.Document[WidthSym](); - otherdoc.height = this.props.Document[HeightSym](); - otherdoc.title = this.props.Document.title + "(..." + CollectionView._applyCount++ + ")"; // previously "applied" - otherdoc.layout = Doc.MakeDelegate(this.props.Document); - otherdoc.miniLayout = StrCast(this.props.Document.miniLayout); - otherdoc.detailedLayout = otherdoc.layout; - otherdoc.type = DocumentType.TEMPLATE; - this.props.addDocTab && this.props.addDocTab(otherdoc, undefined, "onRight"); - }), icon: "project-diagram" - }); + ContextMenu.Instance.addItem({ description: "Apply Template", event: undoBatch(() => this.props.addDocTab && this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight")), icon: "project-diagram" }); } } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 703873681..6bb082b66 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -195,10 +195,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { this._pheight / this.zoomScaling()); let panelwidth = panelDim[0]; let panelheight = panelDim[1]; - if (ranges[0][0] - dx > (this.panX() + panelwidth / 2)) x = ranges[0][1] + panelwidth / 2; - if (ranges[0][1] - dx < (this.panX() - panelwidth / 2)) x = ranges[0][0] - panelwidth / 2; - if (ranges[1][0] - dy > (this.panY() + panelheight / 2)) y = ranges[1][1] + panelheight / 2; - if (ranges[1][1] - dy < (this.panY() - panelheight / 2)) y = ranges[1][0] - panelheight / 2; + // if (ranges[0][0] - dx > (this.panX() + panelwidth / 2)) x = ranges[0][1] + panelwidth / 2; + // if (ranges[0][1] - dx < (this.panX() - panelwidth / 2)) x = ranges[0][0] - panelwidth / 2; + // if (ranges[1][0] - dy > (this.panY() + panelheight / 2)) y = ranges[1][1] + panelheight / 2; + // if (ranges[1][1] - dy < (this.panY() - panelheight / 2)) y = ranges[1][0] - panelheight / 2; } this.setPan(x - dx, y - dy); this._lastX = e.pageX; @@ -358,6 +358,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { getChildDocumentViewProps(childDocLayout: Doc): DocumentViewProps { let self = this; let resolvedDataDoc = !this.props.Document.isTemplate && this.props.DataDoc !== this.props.Document ? this.props.DataDoc : undefined; + resolvedDataDoc && Doc.UpdateDocumentExtensionForField(resolvedDataDoc, this.props.fieldKey); let layoutDoc = Doc.expandTemplateLayout(childDocLayout, resolvedDataDoc); return { DataDoc: resolvedDataDoc !== layoutDoc && resolvedDataDoc ? resolvedDataDoc : undefined, diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index ed6b224a7..ef65c12cf 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -64,7 +64,7 @@ export class DocumentContentsView extends React.Component(Docu @undoBatch @action freezeNativeDimensions = (): void => { - let proto = Doc.GetProto(this.props.Document); + let proto = this.props.Document.isTemplate ? this.props.Document : Doc.GetProto(this.props.Document); if (proto.ignoreAspect === undefined && !proto.nativeWidth) { proto.nativeWidth = this.props.PanelWidth(); proto.nativeHeight = this.props.PanelHeight(); @@ -629,10 +629,10 @@ export class DocumentView extends DocComponent(Docu var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; let showOverlays = this.props.showOverlays ? this.props.showOverlays(this.props.Document) : undefined; - let showTitle = showOverlays && showOverlays.title ? showOverlays.title : StrCast(this.props.Document.showTitle); - let showCaption = showOverlays && showOverlays.caption ? showOverlays.caption : StrCast(this.props.Document.showCaption); + let showTitle = showOverlays && showOverlays.title !== "undefined" ? showOverlays.title : StrCast(this.props.Document.showTitle); + let showCaption = showOverlays && showOverlays.caption !== "undefined" ? showOverlays.caption : StrCast(this.props.Document.showCaption); let templates = Cast(this.props.Document.templates, listSpec("string")); - if (templates instanceof List) { + if (!showOverlays && templates instanceof List) { templates.map(str => { if (str.indexOf("{props.Document.title}") !== -1) showTitle = "title"; if (str.indexOf("fieldKey={\"caption\"}") !== -1) showCaption = "caption"; @@ -663,12 +663,12 @@ export class DocumentView extends DocComponent(Docu {!showTitle && !showCaption ? this.contents :
-
+
{this.contents}
{!showTitle ? (null) :
(Docu contents={this.props.Document[showTitle]} display={"block"} height={72} - GetValue={() => StrCast(this.props.Document[showTitle])} - SetValue={(value: string) => (Doc.GetProto(this.props.Document)[showTitle] = value) ? true : true} + fontSize={12} + GetValue={() => StrCast(this.props.Document[showTitle!])} + SetValue={(value: string) => (Doc.GetProto(this.props.Document)[showTitle!] = value) ? true : true} />
} diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 2ad6ae5f0..5ae0753d8 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -244,7 +244,7 @@ export namespace Doc { let r = (doc === other); let r2 = (doc.proto === other); let r3 = (other.proto === doc); - let r4 = (doc.proto === other.proto); + let r4 = (doc.proto === other.proto && other.proto !== undefined); return r || r2 || r3 || r4; } @@ -298,7 +298,7 @@ export namespace Doc { x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y), r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b) }; - }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE }); + }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: -Number.MAX_VALUE, b: -Number.MAX_VALUE }); return bounds; } @@ -350,19 +350,21 @@ export namespace Doc { if (expandedTemplateLayout instanceof Doc) { return expandedTemplateLayout; } + expandedTemplateLayout = dataDoc[templateLayoutDoc.title + templateLayoutDoc[Id]]; + if (expandedTemplateLayout instanceof Doc) { + return expandedTemplateLayout; + } if (expandedTemplateLayout === undefined && BoolCast(templateLayoutDoc.isTemplate)) { setTimeout(() => { let expandedDoc = Doc.MakeDelegate(templateLayoutDoc); expandedDoc.title = templateLayoutDoc.title + "[" + StrCast(dataDoc.title).match(/\.\.\.[0-9]*/) + "]"; expandedDoc.isExpandedTemplate = templateLayoutDoc; - dataDoc[templateLayoutDoc[Id]] = expandedDoc; + dataDoc[templateLayoutDoc.title + templateLayoutDoc[Id]] = expandedDoc; }, 0); } return templateLayoutDoc; // use the templateLayout when it's not a template or the expandedTemplate is pending. } - let _pendingExpansions: Map = new Map(); - export function MakeCopy(doc: Doc, copyProto: boolean = false): Doc { const copy = new Doc; Object.keys(doc).forEach(key => { @@ -398,6 +400,20 @@ export namespace Doc { return undefined; } + let _applyCount: number = 0; + export function ApplyTemplate(templateDoc: Doc) { + if (!templateDoc) return undefined; + let otherdoc = new Doc(); + otherdoc.width = templateDoc[WidthSym](); + otherdoc.height = templateDoc[HeightSym](); + otherdoc.title = templateDoc.title + "(..." + _applyCount++ + ")"; + otherdoc.layout = Doc.MakeDelegate(templateDoc); + otherdoc.miniLayout = StrCast(templateDoc.miniLayout); + otherdoc.detailedLayout = otherdoc.layout; + otherdoc.type = DocumentType.TEMPLATE; + return otherdoc; + } + export function MakeTemplate(fieldTemplate: Doc, metaKey: string, proto: Doc) { // move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??) let backgroundLayout = StrCast(fieldTemplate.backgroundLayout); -- cgit v1.2.3-70-g09d2 From 6d25848a9e8614c1debc8550e42fc706f77883db Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 23 Jul 2019 19:07:08 -0400 Subject: more template stuff --- src/client/views/collections/CollectionTreeView.tsx | 20 +++++++++++--------- .../collectionFreeForm/CollectionFreeFormView.tsx | 9 +++++++-- src/new_fields/Doc.ts | 9 +++++++-- 3 files changed, 25 insertions(+), 13 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index f98629c5b..ca5812eba 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -74,9 +74,10 @@ class TreeView extends React.Component { @observable _collapsed: boolean = true; @computed get fieldKey() { - let keys = Array.from(Object.keys(this.dataDoc)); // bcz: Argh -- make untracked to avoid this rerunning whenever 'libraryBrush' is set - if (this.dataDoc.proto instanceof Doc) { - let arr = Array.from(Object.keys(this.dataDoc.proto));// bcz: Argh -- make untracked to avoid this rerunning whenever 'libraryBrush' is set + let target = this.props.document.layout && this.dataDoc ? this.dataDoc : this.props.document; + let keys = Array.from(Object.keys(target)); // bcz: Argh -- make untracked to avoid this rerunning whenever 'libraryBrush' is set + if (target.proto instanceof Doc) { + let arr = Array.from(Object.keys(target.proto));// bcz: Argh -- make untracked to avoid this rerunning whenever 'libraryBrush' is set keys.push(...arr); while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1); } @@ -191,16 +192,17 @@ class TreeView extends React.Component { />) @computed get keyList() { - let keys = Array.from(Object.keys(this.dataDoc)); - if (this.dataDoc.proto instanceof Doc) { - keys.push(...Array.from(Object.keys(this.dataDoc.proto))); + let target = this.props.document.layout && this.dataDoc ? this.dataDoc : this.props.document; + let keys = Array.from(Object.keys(target)); + if (target.proto instanceof Doc) { + keys.push(...Array.from(Object.keys(target.proto))); } let keyList: string[] = keys.reduce((l, key) => { - let listspec = DocListCast(this.dataDoc[key]); + let listspec = DocListCast(target[key]); if (listspec && listspec.length) return [...l, key]; return l; }, [] as string[]); - keys.map(key => Cast(this.dataDoc[key], Doc) instanceof Doc && (Cast(this.dataDoc[key], Doc) as Doc).type !== undefined && keyList.push(key)); + keys.map(key => Cast(target[key], Doc) instanceof Doc && keyList.push(key)); if (LinkManager.Instance.getAllRelatedLinks(this.props.document).length > 0) keyList.push("links"); if (keyList.indexOf(this.fieldKey) !== -1) { keyList.splice(keyList.indexOf(this.fieldKey), 1); @@ -361,7 +363,7 @@ class TreeView extends React.Component { let docList = Cast(this.dataDoc[this._chosenKey], listSpec(Doc)); let remDoc = (doc: Doc) => this.remove(doc, this._chosenKey); let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, this._chosenKey, doc, addBefore, before); - let doc = Cast(this.dataDoc[this._chosenKey], Doc); + let doc = Cast((this.props.document.layout ? this.dataDoc : this.props.document)[this._chosenKey], Doc); if (!this._collapsed) { if (!this.props.document.embed) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 6bb082b66..e7d2a66e5 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -31,6 +31,7 @@ import { ScriptField } from "../../../../new_fields/ScriptField"; import { OverlayView, OverlayElementOptions } from "../../OverlayView"; import { ScriptBox } from "../../ScriptBox"; import { CompileScript } from "../../../util/Scripting"; +import { resolve } from "bluebird"; export const panZoomSchema = createSchema({ @@ -358,8 +359,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { getChildDocumentViewProps(childDocLayout: Doc): DocumentViewProps { let self = this; let resolvedDataDoc = !this.props.Document.isTemplate && this.props.DataDoc !== this.props.Document ? this.props.DataDoc : undefined; - resolvedDataDoc && Doc.UpdateDocumentExtensionForField(resolvedDataDoc, this.props.fieldKey); - let layoutDoc = Doc.expandTemplateLayout(childDocLayout, resolvedDataDoc); + let layoutDoc = childDocLayout; + if (resolvedDataDoc && Doc.WillExpandTemplateLayout(childDocLayout, resolvedDataDoc)) { + Doc.UpdateDocumentExtensionForField(resolvedDataDoc, this.props.fieldKey); + let fieldExtensionDoc = Doc.resolvedFieldDataDoc(resolvedDataDoc, StrCast(childDocLayout.templateField, StrCast(childDocLayout.title)), "dummy"); + layoutDoc = Doc.expandTemplateLayout(childDocLayout, fieldExtensionDoc !== resolvedDataDoc ? fieldExtensionDoc : undefined); + } else layoutDoc = Doc.expandTemplateLayout(childDocLayout, resolvedDataDoc); return { DataDoc: resolvedDataDoc !== layoutDoc && resolvedDataDoc ? resolvedDataDoc : undefined, Document: layoutDoc, diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 5ae0753d8..e4bf4a6c1 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -337,11 +337,15 @@ export namespace Doc { return Doc.MakeDelegate(doc); // bcz? } - export function expandTemplateLayout(templateLayoutDoc: Doc, dataDoc?: Doc) { + export function WillExpandTemplateLayout(templateLayoutDoc: Doc, dataDoc?: Doc) { let resolvedDataDoc = (templateLayoutDoc !== dataDoc) ? dataDoc : undefined; if (!dataDoc || !(templateLayoutDoc && !(Cast(templateLayoutDoc.layout, Doc) instanceof Doc) && resolvedDataDoc && resolvedDataDoc !== templateLayoutDoc)) { - return templateLayoutDoc; + return false; } + return true; + } + export function expandTemplateLayout(templateLayoutDoc: Doc, dataDoc?: Doc) { + if (!WillExpandTemplateLayout(templateLayoutDoc, dataDoc) || !dataDoc) return templateLayoutDoc; // if we have a data doc that doesn't match the layout, then we're rendering a template. // ... which means we change the layout to be an expanded view of the template layout. // This allows the view override the template's properties and be referenceable as its own document. @@ -433,6 +437,7 @@ export namespace Doc { layoutDelegate.layout = layout; fieldTemplate.title = metaKey; + fieldTemplate.templateField = metaKey; fieldTemplate.layout = layoutDelegate !== fieldTemplate ? layoutDelegate : layout; fieldTemplate.backgroundLayout = backgroundLayout; fieldTemplate.nativeWidth = nw; -- cgit v1.2.3-70-g09d2 From cb95d4b43238538f59babcaa0b0d9dabb9b393b7 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Tue, 23 Jul 2019 22:07:13 -0400 Subject: Ink recognition and presentation view toggle --- src/client/cognitive_services/CognitiveServices.ts | 86 +++++++++++++++++++++- src/client/views/InkingCanvas.tsx | 4 +- src/client/views/MainView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 11 +++ .../views/presentationview/PresentationView.tsx | 12 ++- src/new_fields/FieldSymbols.ts | 2 +- src/new_fields/InkField.ts | 6 +- src/server/index.ts | 19 ++--- 8 files changed, 124 insertions(+), 18 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts index d4085cf76..71e4276dc 100644 --- a/src/client/cognitive_services/CognitiveServices.ts +++ b/src/client/cognitive_services/CognitiveServices.ts @@ -8,10 +8,12 @@ import { RouteStore } from "../../server/RouteStore"; import { Utils } from "../../Utils"; import { CompileScript } from "../util/Scripting"; import { ComputedField } from "../../new_fields/ScriptField"; +import { InkData } from "../../new_fields/InkField"; export enum Services { ComputerVision = "vision", - Face = "face" + Face = "face", + Handwriting = "handwriting" } export enum Confidence { @@ -132,4 +134,86 @@ export namespace CognitiveServices { } + export namespace Inking { + + export interface AzureStrokeData { + id: number; + points: string; + language?: string; + } + + export interface HandwritingUnit { + version: number; + language: string; + unit: string; + strokes: AzureStrokeData[]; + } + + export const analyze = async (inkData: InkData, target: Doc) => { + return fetch(Utils.prepend(`${RouteStore.cognitiveServices}/${Services.Handwriting}`)).then(async response => { + let apiKey = await response.text(); + if (!apiKey) { + return undefined; + } + + let xhttp = new XMLHttpRequest(); + let serverAddress = "https://api.cognitive.microsoft.com"; + let endpoint = serverAddress + "/inkrecognizer/v1.0-preview/recognize"; + + let results = await new Promise((resolve, reject) => { + let result: any; + xhttp.onreadystatechange = function () { + if (this.readyState === 4) { + try { + result = JSON.parse(xhttp.responseText); + } catch (e) { + return reject(e); + } + switch (this.status) { + case 200: + return resolve(result); + case 400: + default: + return reject(result); + } + } + }; + + xhttp.open("PUT", endpoint, true); + xhttp.setRequestHeader('Ocp-Apim-Subscription-Key', apiKey); + xhttp.setRequestHeader('Content-Type', 'application/json'); + xhttp.send(JSON.stringify(toHandwritingUnit(inkData))); + }); + + let recognizedText = results.recognitionUnits.map((unit: any) => unit.recognizedText); + let individualWords = recognizedText.filter((text: string) => text && text.split(" ").length === 1); + target.inkAnalysis = Docs.Get.DocumentHierarchyFromJson(results.recognitionUnits, "Ink Analysis"); + target.handwriting = individualWords.join(" "); + }); + }; + + const toHandwritingUnit = (inkData: InkData): HandwritingUnit => { + let entries = inkData.entries(), next = entries.next(); + let strokes: AzureStrokeData[] = []; + let id = 0; + while (!next.done) { + let entry = next.value; + let data = { + id: id++, + points: entry[1].pathData.map(point => `${point.x},${point.y}`).join(","), + language: "en-US" + }; + strokes.push(data); + next = entries.next(); + } + return { + version: 1, + language: "en-US", + unit: "mm", + strokes: strokes + }; + }; + + } + } \ No newline at end of file diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx index 3e0d7b476..e48b45b56 100644 --- a/src/client/views/InkingCanvas.tsx +++ b/src/client/views/InkingCanvas.tsx @@ -10,6 +10,8 @@ import { undoBatch, UndoManager } from "../util/UndoManager"; import { StrokeData, InkField, InkTool } from "../../new_fields/InkField"; import { Doc } from "../../new_fields/Doc"; import { Cast, PromiseValue, NumCast } from "../../new_fields/Types"; +import { CognitiveServices } from "../cognitive_services/CognitiveServices"; +import { ContextMenu } from "./ContextMenu"; interface InkCanvasProps { getScreenTransform: () => Transform; @@ -178,7 +180,7 @@ export class InkingCanvas extends React.Component { render() { let svgCanvasStyle = InkingControl.Instance.selectedTool !== InkTool.None ? "canSelect" : "noSelect"; return ( -
+
{this.props.children()} {this.drawnPaths} diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 94a4835a1..53be0278e 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -394,6 +394,7 @@ export class MainView extends React.Component {
  • +
  • {btns.map(btn => @@ -444,7 +445,6 @@ export class MainView extends React.Component { this.isSearchVisible = !this.isSearchVisible; } - render() { return (
    diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 703873681..3fd86e950 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -31,6 +31,7 @@ import { ScriptField } from "../../../../new_fields/ScriptField"; import { OverlayView, OverlayElementOptions } from "../../OverlayView"; import { ScriptBox } from "../../ScriptBox"; import { CompileScript } from "../../../util/Scripting"; +import { CognitiveServices } from "../../../cognitive_services/CognitiveServices"; export const panZoomSchema = createSchema({ @@ -51,6 +52,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { private _lastY: number = 0; private get _pwidth() { return this.props.PanelWidth(); } private get _pheight() { return this.props.PanelHeight(); } + private inkKey = "ink"; @computed get contentBounds() { let bounds = this.props.fitToBox && !NumCast(this.nativeWidth) ? Doc.ComputeContentBounds(DocListCast(this.props.Document.data)) : undefined; @@ -477,6 +479,15 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { }, "arrange contents"); } }); + ContextMenu.Instance.addItem({ + description: "Analyze Strokes", event: async () => { + let data = Cast(this.fieldExtensionDoc[this.inkKey], InkField); + if (!data) { + return; + } + CognitiveServices.Inking.analyze(data.inkData, Doc.GetProto(this.props.Document)); + } + }); ContextMenu.Instance.addItem({ description: "Add freeform arrangement", event: () => { diff --git a/src/client/views/presentationview/PresentationView.tsx b/src/client/views/presentationview/PresentationView.tsx index f80840f96..966ade3de 100644 --- a/src/client/views/presentationview/PresentationView.tsx +++ b/src/client/views/presentationview/PresentationView.tsx @@ -31,6 +31,8 @@ export interface PresViewProps { Documents: List; } +const expandedWidth = 400; + @observer export class PresentationView extends React.Component { public static Instance: PresentationView; @@ -67,6 +69,14 @@ export class PresentationView extends React.Component { PresentationView.Instance = this; } + toggle = (forcedValue: boolean | undefined) => { + if (forcedValue !== undefined) { + this.curPresentation.width = forcedValue ? expandedWidth : 0; + } else { + this.curPresentation.width = this.curPresentation.width === expandedWidth ? 0 : expandedWidth; + } + } + //The first lifecycle function that gets called to set up the current presentation. async componentWillMount() { this.props.Documents.forEach(async (doc, index: number) => { @@ -543,7 +553,7 @@ export class PresentationView extends React.Component { this.curPresentation.data = new List([doc]); } - this.curPresentation.width = 400; + this.toggle(true); } //Function that sets the store of the children docs. diff --git a/src/new_fields/FieldSymbols.ts b/src/new_fields/FieldSymbols.ts index a436dcf2b..b5b3aa588 100644 --- a/src/new_fields/FieldSymbols.ts +++ b/src/new_fields/FieldSymbols.ts @@ -7,4 +7,4 @@ export const Id = Symbol("Id"); export const OnUpdate = Symbol("OnUpdate"); export const Parent = Symbol("Parent"); export const Copy = Symbol("Copy"); -export const ToScriptString = Symbol("Copy"); \ No newline at end of file +export const ToScriptString = Symbol("ToScriptString"); \ No newline at end of file diff --git a/src/new_fields/InkField.ts b/src/new_fields/InkField.ts index 39c6c8ce3..8f64c1c2e 100644 --- a/src/new_fields/InkField.ts +++ b/src/new_fields/InkField.ts @@ -19,6 +19,8 @@ export interface StrokeData { page: number; } +export type InkData = Map; + const pointSchema = createSimpleSchema({ x: true, y: true }); @@ -31,9 +33,9 @@ const strokeDataSchema = createSimpleSchema({ @Deserializable("ink") export class InkField extends ObjectField { @serializable(map(object(strokeDataSchema))) - readonly inkData: Map; + readonly inkData: InkData; - constructor(data?: Map) { + constructor(data?: InkData) { super(); this.inkData = data || new Map; } diff --git a/src/server/index.ts b/src/server/index.ts index 5b086a2cf..66c982adc 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -284,18 +284,15 @@ addSecureRoute( RouteStore.getCurrUser ); +const ServicesApiKeyMap = new Map([ + ["face", process.env.FACE], + ["vision", process.env.VISION], + ["handwriting", process.env.HANDWRITING] +]); + addSecureRoute(Method.GET, (user, res, req) => { - let requested = req.params.requestedservice; - switch (requested) { - case "face": - res.send(process.env.FACE); - break; - case "vision": - res.send(process.env.VISION); - break; - default: - res.send(undefined); - } + let service = req.params.requestedservice; + res.send(ServicesApiKeyMap.get(service)); }, undefined, `${RouteStore.cognitiveServices}/:requestedservice`); class NodeCanvasFactory { -- cgit v1.2.3-70-g09d2 From 2bba7deec5e16671b4a1bbed6eac6008ca01ff7c Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 23 Jul 2019 22:28:58 -0400 Subject: cleanup of templates for treeview --- src/client/views/collections/CollectionTreeView.tsx | 8 ++++---- src/client/views/nodes/FieldView.tsx | 3 ++- src/new_fields/Doc.ts | 21 +++++++++++++++------ 3 files changed, 21 insertions(+), 11 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index ca5812eba..2ff9e43fe 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -74,7 +74,7 @@ class TreeView extends React.Component { @observable _collapsed: boolean = true; @computed get fieldKey() { - let target = this.props.document.layout && this.dataDoc ? this.dataDoc : this.props.document; + let target = this.props.document; let keys = Array.from(Object.keys(target)); // bcz: Argh -- make untracked to avoid this rerunning whenever 'libraryBrush' is set if (target.proto instanceof Doc) { let arr = Array.from(Object.keys(target.proto));// bcz: Argh -- make untracked to avoid this rerunning whenever 'libraryBrush' is set @@ -89,7 +89,7 @@ class TreeView extends React.Component { } }); let layout = StrCast(this.props.document.layout); - if (layout.indexOf("fieldKey={\"") !== -1) { + if (layout.indexOf("fieldKey={\"") !== -1 && layout.indexOf("fieldExt=") === -1) { return layout.split("fieldKey={\"")[1].split("\"")[0]; } return keyList.length ? keyList[0] : "data"; @@ -192,7 +192,7 @@ class TreeView extends React.Component { />) @computed get keyList() { - let target = this.props.document.layout && this.dataDoc ? this.dataDoc : this.props.document; + let target = this.props.document; let keys = Array.from(Object.keys(target)); if (target.proto instanceof Doc) { keys.push(...Array.from(Object.keys(target.proto))); @@ -363,10 +363,10 @@ class TreeView extends React.Component { let docList = Cast(this.dataDoc[this._chosenKey], listSpec(Doc)); let remDoc = (doc: Doc) => this.remove(doc, this._chosenKey); let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, this._chosenKey, doc, addBefore, before); - let doc = Cast((this.props.document.layout ? this.dataDoc : this.props.document)[this._chosenKey], Doc); if (!this._collapsed) { if (!this.props.document.embed) { + let doc = Cast(this.props.document[this._chosenKey], Doc); contentElement =
      {this._chosenKey === "links" ? this.renderLinks() : TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, this.props.document, this.resolvedDataDoc, this._chosenKey, addDoc, remDoc, this.move, diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index ea6730cd0..ffaee8042 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -87,7 +87,8 @@ export class FieldView extends React.Component { return

      {field.date.toLocaleString()}

      ; } else if (field instanceof Doc) { - return

      {field.title + " : id= " + field[Id]}

      ; + return

      {field.title}

      ; + //return

      {field.title + " : id= " + field[Id]}

      ; // let returnHundred = () => 100; // return ( // { let docExtensionForField = new Doc(doc[Id] + fieldKey, true); - docExtensionForField.title = doc.title + ":" + fieldKey + ".ext"; + docExtensionForField.title = fieldKey + "[" + StrCast(doc.title).match(/\.\.\.[0-9]*/) + "].ext"; docExtensionForField.extendsDoc = doc; let proto: Doc | undefined = doc; while (proto && !Doc.IsPrototype(proto)) { @@ -337,9 +337,16 @@ export namespace Doc { return Doc.MakeDelegate(doc); // bcz? } - export function WillExpandTemplateLayout(templateLayoutDoc: Doc, dataDoc?: Doc) { - let resolvedDataDoc = (templateLayoutDoc !== dataDoc) ? dataDoc : undefined; - if (!dataDoc || !(templateLayoutDoc && !(Cast(templateLayoutDoc.layout, Doc) instanceof Doc) && resolvedDataDoc && resolvedDataDoc !== templateLayoutDoc)) { + // + // Determines whether the combination of the layoutDoc and dataDoc represents + // a template relationship. If so, the layoutDoc will be expanded into a new + // document that inherits the properties of the original layout while allowing + // for individual layout properties to be overridden in the expanded layout. + // + export function WillExpandTemplateLayout(layoutDoc: Doc, dataDoc?: Doc) { + let resolvedDataDoc = (layoutDoc !== dataDoc) ? dataDoc : undefined; + if (!dataDoc || !(layoutDoc && !(Cast(layoutDoc.layout, Doc) instanceof Doc) && + resolvedDataDoc && resolvedDataDoc !== layoutDoc)) { return false; } return true; @@ -354,16 +361,18 @@ export namespace Doc { if (expandedTemplateLayout instanceof Doc) { return expandedTemplateLayout; } - expandedTemplateLayout = dataDoc[templateLayoutDoc.title + templateLayoutDoc[Id]]; + // expandedTemplateLayout = dataDoc[templateLayoutDoc.title + templateLayoutDoc[Id]]; + expandedTemplateLayout = dataDoc["Layout[" + templateLayoutDoc[Id] + "]"]; if (expandedTemplateLayout instanceof Doc) { return expandedTemplateLayout; } if (expandedTemplateLayout === undefined && BoolCast(templateLayoutDoc.isTemplate)) { setTimeout(() => { let expandedDoc = Doc.MakeDelegate(templateLayoutDoc); + //expandedDoc.title = templateLayoutDoc.title + "[" + StrCast(dataDoc.title).match(/\.\.\.[0-9]*/) + "]"; expandedDoc.title = templateLayoutDoc.title + "[" + StrCast(dataDoc.title).match(/\.\.\.[0-9]*/) + "]"; expandedDoc.isExpandedTemplate = templateLayoutDoc; - dataDoc[templateLayoutDoc.title + templateLayoutDoc[Id]] = expandedDoc; + dataDoc["Layout[" + templateLayoutDoc[Id] + "]"] = expandedDoc; }, 0); } return templateLayoutDoc; // use the templateLayout when it's not a template or the expandedTemplate is pending. -- cgit v1.2.3-70-g09d2 From 5bc848bde4f37ec3dc812b22d81ded16f2a86f93 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 24 Jul 2019 00:35:29 -0400 Subject: fixed reordering tree view items. restored double-click to view detail view. --- src/client/views/collections/CollectionTreeView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 1 + src/new_fields/Doc.ts | 8 ++++++++ 3 files changed, 10 insertions(+), 1 deletion(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 2ff9e43fe..fe636942c 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -448,7 +448,7 @@ class TreeView extends React.Component { dataDoc={dataDoc} containingCollection={containingCollection} treeViewId={treeViewId} - key={child[Id] + "child " + i} + key={child[Id]} indentDocument={indent} renderDepth={renderDepth} deleteDoc={remove} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 09a1b49fe..8d7b3d835 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -295,6 +295,7 @@ export class DocumentView extends DocComponent(Docu if (this._doubleTap && this.props.renderDepth) { let fullScreenAlias = Doc.MakeAlias(this.props.Document); fullScreenAlias.templates = new List(); + Doc.UseDetailLayout(fullScreenAlias); this.props.addDocTab(fullScreenAlias, this.dataDoc, "inTab"); SelectionManager.DeselectAll(); this.props.Document.libraryBrush = undefined; diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 2a697e621..a1241aac3 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -463,4 +463,12 @@ export namespace Doc { d.layout !== miniLayout ? miniLayout && (d.layout = d.miniLayout) : detailLayout && (d.layout = detailLayout); if (d.layout === detailLayout) Doc.GetProto(d).nativeWidth = Doc.GetProto(d).nativeHeight = undefined; } + export async function UseDetailLayout(d: Doc) { + let miniLayout = await PromiseValue(d.miniLayout); + let detailLayout = await PromiseValue(d.detailedLayout); + if (miniLayout && d.layout === miniLayout && detailLayout) { + d.layout = detailLayout; + Doc.GetProto(d).nativeWidth = Doc.GetProto(d).nativeHeight = undefined; + } + } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 52dc823c6a4c4b3e8deb994358a10acca4b6bd6c Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Wed, 24 Jul 2019 12:31:07 -0400 Subject: Added transitions --- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 3 --- src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 3 ++- src/client/views/nodes/DocumentView.tsx | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 70c43bbec..7a9b94bfd 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -503,11 +503,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { onError(script.errors.map(error => error.messageText).join("\n")); return; } - const docs = DocListCast(doc[dataKey]); - docs.map(d => d.transition = "transform 1s"); doc[key] = new ScriptField(script); overlayDisposer(); - setTimeout(() => docs.map(d => d.transition = undefined), 1200); }} />; overlayDisposer = OverlayView.Instance.addWindow(scriptingBox, options); }; diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index b09538d1a..389b9f68b 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -82,6 +82,7 @@ export class CollectionFreeFormDocumentView extends DocComponent(Docu cm.addItem({ description: "Toggle detail", event: () => Doc.ToggleDetailLayout(this.props.Document), icon: "image" }); } cm.addItem({ description: "Add Repl", icon: "laptop-code", event: () => OverlayView.Instance.addWindow(, { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" }) }); - cm.addItem({ description: "Add Repl", event: () => OverlayView.Instance.addWindow(, { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" }) }); cm.addItem({ description: "Center View", event: () => this.props.focus(this.props.Document, false), icon: "crosshairs" }); cm.addItem({ description: "Zoom to Document", event: () => this.props.focus(this.props.Document, true), icon: "search" }); if (!ClientUtils.RELEASE) { -- cgit v1.2.3-70-g09d2 From 4c9c8375dc5f36f29cc38ba79b379b7a75e4a1a5 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Wed, 24 Jul 2019 17:24:28 -0400 Subject: Added custom views to layout scripts --- .vscode/launch.json | 8 ++++++ src/client/views/ScriptingRepl.tsx | 12 +++----- .../collectionFreeForm/CollectionFreeFormView.scss | 5 ++++ .../collectionFreeForm/CollectionFreeFormView.tsx | 33 ++++++++++++++++++++-- 4 files changed, 48 insertions(+), 10 deletions(-) (limited to 'src/client/views/collections') diff --git a/.vscode/launch.json b/.vscode/launch.json index e4196600e..fd67a7647 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,6 +7,10 @@ { "type": "chrome", "request": "launch", + "runtimeArgs": [ + "--enable-logging", + "--v=1" + ], "name": "Launch Chrome against localhost", "sourceMaps": true, "breakOnLoad": true, @@ -25,6 +29,10 @@ { "type": "chrome", "request": "launch", + "runtimeArgs": [ + "--enable-logging", + "--v=1" + ], "name": "Launch Chrome against Dash server", "sourceMaps": true, "breakOnLoad": true, diff --git a/src/client/views/ScriptingRepl.tsx b/src/client/views/ScriptingRepl.tsx index 52273c357..9e538cf1b 100644 --- a/src/client/views/ScriptingRepl.tsx +++ b/src/client/views/ScriptingRepl.tsx @@ -16,18 +16,14 @@ library.add(faCaretRight); @observer export class DocumentIcon extends React.Component<{ view: DocumentView, index: number }> { render() { - this.props.view.props.ScreenToLocalTransform(); - const doc = this.props.view.props.Document; - doc.width; - doc.height; - doc.x; - doc.y; - const screenCoords = this.props.view.screenRect(); + const view = this.props.view; + const transform = view.props.ScreenToLocalTransform().scale(view.props.ContentScaling()).inverse(); + const { x, y, width, height } = transform.transformBounds(0, 0, view.props.PanelWidth(), view.props.PanelHeight()); return (

      ${this.props.index}

      diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss index 00407d39a..cca199afa 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss @@ -19,6 +19,11 @@ transform-origin: left top; } +.collectionFreeform-customText { + position: absolute; + text-align: center; +} + .collectionfreeformview-container { .collectionfreeformview>.jsx-parser { position: inherit; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 840f253ff..bd5c6f84b 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -420,6 +420,25 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { return result.result === undefined ? {} : result.result; } + private viewDefToJSX(viewDef: any): JSX.Element | undefined { + if (viewDef.type === "text") { + const text = Cast(viewDef.text, "string"); + const x = Cast(viewDef.x, "number"); + const y = Cast(viewDef.y, "number"); + const width = Cast(viewDef.width, "number"); + const height = Cast(viewDef.height, "number"); + const fontSize = Cast(viewDef.fontSize, "number"); + if ([text, x, y].some(val => val === undefined)) { + return undefined; + } + + return
      {text}
      ; + } + } + @computed.struct get views() { let curPage = FieldValue(this.Document.curPage, -1); @@ -427,10 +446,20 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { const script = this.Document.arrangeScript; let state: any = undefined; const docs = this.childDocs; + let elements: JSX.Element[] = []; if (initScript) { const initResult = initScript.script.run({ docs, collection: this.Document }); if (initResult.success) { - state = initResult.result; + const result = initResult.result; + const { state: scriptState, views } = result; + state = scriptState; + if (Array.isArray(views)) { + elements = views.reduce((prev, ele) => { + const jsx = this.viewDefToJSX(ele); + jsx && prev.push(jsx); + return prev; + }, elements); + } } } let docviews = docs.reduce((prev, doc) => { @@ -445,7 +474,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } } return prev; - }, [] as JSX.Element[]); + }, elements); setTimeout(() => this._selectOnLoaded = "", 600);// bcz: surely there must be a better way .... -- cgit v1.2.3-70-g09d2 From d6cef0815815c2587b9cc791e6a37c742aba1b45 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Thu, 25 Jul 2019 03:31:34 -0400 Subject: cognitive services refactor, buxton python script fixes, covered up imagebox context menu bug --- src/client/cognitive_services/CognitiveServices.ts | 151 +++++++++++---------- src/client/views/InkingCanvas.tsx | 4 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/ImageBox.tsx | 6 +- src/scraping/buxton/scraper.py | 17 +-- 5 files changed, 93 insertions(+), 87 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts index dcd27f858..d689b424d 100644 --- a/src/client/cognitive_services/CognitiveServices.ts +++ b/src/client/cognitive_services/CognitiveServices.ts @@ -1,5 +1,5 @@ import * as request from "request-promise"; -import { Doc, Field } from "../../new_fields/Doc"; +import { Doc, Field, Opt } from "../../new_fields/Doc"; import { Cast } from "../../new_fields/Types"; import { ImageField } from "../../new_fields/URLField"; import { List } from "../../new_fields/List"; @@ -10,7 +10,15 @@ import { CompileScript } from "../util/Scripting"; import { ComputedField } from "../../new_fields/ScriptField"; import { InkData } from "../../new_fields/InkField"; -export enum Services { +type APIManager = { requester: RequestExecutor, applier: AnalysisApplier }; +type RequestExecutor = (apiKey: string, data: D, service: Service) => Promise; +type AnalysisApplier = (target: Doc, ...args: any) => any; +type Converter = (results: any) => Field; + +export type Tag = { name: string, confidence: number }; +export type Rectangle = { top: number, left: number, width: number, height: number }; + +export enum Service { ComputerVision = "vision", Face = "face", Handwriting = "handwriting" @@ -25,11 +33,6 @@ export enum Confidence { Excellent = 0.95 } -export type Tag = { name: string, confidence: number }; -export type Rectangle = { top: number, left: number, width: number, height: number }; -export type Face = { faceAttributes: any, faceId: string, faceRectangle: Rectangle }; -export type Converter = (results: any) => Field; - /** * A file that handles all interactions with Microsoft Azure's Cognitive * Services APIs. These machine learning endpoints allow basic data analytics for @@ -37,19 +40,33 @@ export type Converter = (results: any) => Field; */ export namespace CognitiveServices { + const executeQuery = async (service: Service, executor: RequestExecutor, data: D): Promise> => { + return fetch(Utils.prepend(`${RouteStore.cognitiveServices}/${service}`)).then(async response => { + let apiKey = await response.text(); + if (!apiKey) { + return undefined; + } + + let results: Opt; + try { + results = await executor(apiKey, data, service).then(json => JSON.parse(json)); + } catch { + results = undefined; + } + return results; + }); + }; + export namespace Image { - export const analyze = async (imageUrl: string, service: Services) => { - return fetch(Utils.prepend(`${RouteStore.cognitiveServices}/${service}`)).then(async response => { - let apiKey = await response.text(); - if (!apiKey) { - return undefined; - } + export const Manager: APIManager = { + + requester: (async (apiKey: string, imageUrl: string, service: Service) => { let uriBase; let parameters; switch (service) { - case Services.Face: + case Service.Face: uriBase = 'face/v1.0/detect'; parameters = { 'returnFaceId': 'true', @@ -58,7 +75,7 @@ export namespace CognitiveServices { 'emotion,hair,makeup,occlusion,accessories,blur,exposure,noise' }; break; - case Services.ComputerVision: + case Service.ComputerVision: uriBase = 'vision/v2.0/analyze'; parameters = { 'visualFeatures': 'Categories,Description,Color,Objects,Tags,Adult', @@ -78,35 +95,32 @@ export namespace CognitiveServices { } }; - let results: any; - try { - results = await request.post(options).then(response => JSON.parse(response)); - } catch (e) { - results = undefined; - } - return results; - }); - }; + return request.post(options); + }) as RequestExecutor, - const analyzeDocument = async (target: Doc, service: Services, converter: Converter, storageKey: string) => { - let imageData = Cast(target.data, ImageField); - if (!imageData || await Cast(target[storageKey], Doc)) { - return; - } - let toStore: any; - let results = await analyze(imageData.url.href, service); - if (!results) { - toStore = "Cognitive Services could not process the given image URL."; - } else { - if (!results.length) { - toStore = converter(results); + applier: (async (target: Doc, service: Service, converter: Converter, storageKey: string) => { + let imageData = Cast(target.data, ImageField); + if (!imageData || await Cast(target[storageKey], Doc)) { + return; + } + let toStore: any; + let results = await executeQuery(service, Manager.requester, imageData.url.href); + if (!results) { + toStore = "Cognitive Services could not process the given image URL."; } else { - toStore = results.length > 0 ? converter(results) : "Empty list returned."; + if (!results.length) { + toStore = converter(results); + } else { + toStore = results.length > 0 ? converter(results) : "Empty list returned."; + } } - } - target[storageKey] = toStore; + target[storageKey] = toStore; + }) as AnalysisApplier + }; + export type Face = { faceAttributes: any, faceId: string, faceRectangle: Rectangle }; + export const generateMetadata = async (target: Doc, threshold: Confidence = Confidence.Excellent) => { let converter = (results: any) => { let tagDoc = new Doc; @@ -120,7 +134,7 @@ export namespace CognitiveServices { tagDoc.confidence = threshold; return tagDoc; }; - analyzeDocument(target, Services.ComputerVision, converter, "generatedTags"); + Manager.applier(target, Service.ComputerVision, converter, "generatedTags"); }; export const extractFaces = async (target: Doc) => { @@ -129,48 +143,24 @@ export namespace CognitiveServices { results.map((face: Face) => faceDocs.push(Docs.Get.DocumentHierarchyFromJson(face, `Face: ${face.faceId}`)!)); return faceDocs; }; - analyzeDocument(target, Services.Face, converter, "faces"); + Manager.applier(target, Service.Face, converter, "faces"); }; } export namespace Inking { - export interface AzureStrokeData { - id: number; - points: string; - language?: string; - } - - export interface HandwritingUnit { - version: number; - language: string; - unit: string; - strokes: AzureStrokeData[]; - } - - export const analyze = async (inkData: InkData, target: Doc) => { - return fetch(Utils.prepend(`${RouteStore.cognitiveServices}/${Services.Handwriting}`)).then(async response => { - let apiKey = await response.text(); - if (!apiKey) { - return undefined; - } + export const Manager: APIManager = { + requester: (async (apiKey: string, inkData: InkData) => { let xhttp = new XMLHttpRequest(); let serverAddress = "https://api.cognitive.microsoft.com"; let endpoint = serverAddress + "/inkrecognizer/v1.0-preview/recognize"; - let requestExecutor = (resolve: any, reject: any) => { - let result: any; - let body = format(inkData); - + let promisified = (resolve: any, reject: any) => { xhttp.onreadystatechange = function () { if (this.readyState === 4) { - try { - result = JSON.parse(xhttp.responseText); - } catch (e) { - return reject(e); - } + let result = xhttp.responseText; switch (this.status) { case 200: return resolve(result); @@ -184,11 +174,14 @@ export namespace CognitiveServices { xhttp.open("PUT", endpoint, true); xhttp.setRequestHeader('Ocp-Apim-Subscription-Key', apiKey); xhttp.setRequestHeader('Content-Type', 'application/json'); - xhttp.send(body); + xhttp.send(format(inkData)); }; - let results = await new Promise(requestExecutor); + return new Promise(promisified); + }) as RequestExecutor, + applier: (async (target: Doc, inkData: InkData) => { + let results = await executeQuery(Service.Handwriting, Manager.requester, inkData); if (results) { results.recognitionUnits && (results = results.recognitionUnits); target.inkAnalysis = Docs.Get.DocumentHierarchyFromJson(results, "Ink Analysis"); @@ -196,9 +189,23 @@ export namespace CognitiveServices { let individualWords = recognizedText.filter((text: string) => text && text.split(" ").length === 1); target.handwriting = individualWords.join(" "); } - }); + }) as AnalysisApplier + }; + export interface AzureStrokeData { + id: number; + points: string; + language?: string; + } + + export interface HandwritingUnit { + version: number; + language: string; + unit: string; + strokes: AzureStrokeData[]; + } + const format = (inkData: InkData): string => { let entries = inkData.entries(), next = entries.next(); let strokes: AzureStrokeData[] = [], id = 0; diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx index e48b45b56..c4cd863d1 100644 --- a/src/client/views/InkingCanvas.tsx +++ b/src/client/views/InkingCanvas.tsx @@ -6,12 +6,10 @@ import "./InkingCanvas.scss"; import { InkingControl } from "./InkingControl"; import { InkingStroke } from "./InkingStroke"; import React = require("react"); -import { undoBatch, UndoManager } from "../util/UndoManager"; +import { UndoManager } from "../util/UndoManager"; import { StrokeData, InkField, InkTool } from "../../new_fields/InkField"; import { Doc } from "../../new_fields/Doc"; import { Cast, PromiseValue, NumCast } from "../../new_fields/Types"; -import { CognitiveServices } from "../cognitive_services/CognitiveServices"; -import { ContextMenu } from "./ContextMenu"; interface InkCanvasProps { getScreenTransform: () => Transform; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index bd5c6f84b..4b7ddd49d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -519,7 +519,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { if (!data) { return; } - CognitiveServices.Inking.analyze(data.inkData, Doc.GetProto(this.props.Document)); + CognitiveServices.Inking.Manager.applier(Doc.GetProto(this.props.Document), data.inkData); } }); } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 89ad18dd6..3d77696fe 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -216,9 +216,9 @@ export class ImageBox extends DocComponent(ImageD }); let modes: ContextMenuProps[] = []; - let dataDoc = Doc.GetProto(this.Document); - modes.push({ description: "Generate Tags", event: () => CognitiveServices.Image.generateMetadata(dataDoc), icon: "tag" }); - modes.push({ description: "Find Faces", event: () => CognitiveServices.Image.extractFaces(dataDoc), icon: "camera" }); + // let dataDoc = Doc.GetProto(this.Document); + modes.push({ description: "Generate Tags", event: () => CognitiveServices.Image.generateMetadata(this.Document), icon: "tag" }); + modes.push({ description: "Find Faces", event: () => CognitiveServices.Image.extractFaces(this.Document), icon: "camera" }); ContextMenu.Instance.addItem({ description: "Image Funcs...", subitems: funcs }); ContextMenu.Instance.addItem({ description: "Analyze...", subitems: modes }); diff --git a/src/scraping/buxton/scraper.py b/src/scraping/buxton/scraper.py index 700269727..14490cfe4 100644 --- a/src/scraping/buxton/scraper.py +++ b/src/scraping/buxton/scraper.py @@ -15,6 +15,7 @@ source = "./source" dist = "../../server/public/files" db = MongoClient("localhost", 27017)["Dash"] +target_collection = db.newDocuments schema_guids = [] @@ -84,7 +85,7 @@ def write_schema(parse_results, display_fields, storage_key): "height": 600, "panX": 0, "panY": 0, - "zoomBasis": 0.5, + "zoomBasis": 1, "zIndex": 2, "libraryBrush": False, "viewType": 2 @@ -106,8 +107,8 @@ def write_schema(parse_results, display_fields, storage_key): fields["isPrototype"] = True fields["page"] = -1 - db.newDocuments.insert_one(data_doc) - db.newDocuments.insert_one(view_doc) + target_collection.insert_one(data_doc) + target_collection.insert_one(view_doc) data_doc_guid = data_doc["_id"] print(f"inserted view document ({view_doc_guid})") @@ -158,8 +159,8 @@ def write_text_doc(content): "__type": "Doc" } - db.newDocuments.insert_one(view_doc) - db.newDocuments.insert_one(data_doc) + target_collection.insert_one(view_doc) + target_collection.insert_one(data_doc) return view_doc_guid @@ -209,8 +210,8 @@ def write_image(folder, name): "__type": "Doc" } - db.newDocuments.insert_one(view_doc) - db.newDocuments.insert_one(data_doc) + target_collection.insert_one(view_doc) + target_collection.insert_one(data_doc) return view_doc_guid @@ -372,7 +373,7 @@ parent_guid = write_schema({ }, ["title", "short_description", "original_price"], "data") print("appending parent schema to main workspace...\n") -db.newDocuments.update_one( +target_collection.update_one( {"fields.title": "WS collection 1"}, {"$push": {"fields.data.fields": {"fieldId": parent_guid, "__type": "proxy"}}} ) -- cgit v1.2.3-70-g09d2 From 1846c217adb814a2b0297b8ab808c2f587741936 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Thu, 25 Jul 2019 09:26:33 -0400 Subject: final cognitive services refactor --- src/client/cognitive_services/CognitiveServices.ts | 84 +++++++++++----------- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 +- 2 files changed, 47 insertions(+), 41 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts index d689b424d..0cda17325 100644 --- a/src/client/cognitive_services/CognitiveServices.ts +++ b/src/client/cognitive_services/CognitiveServices.ts @@ -10,9 +10,10 @@ import { CompileScript } from "../util/Scripting"; import { ComputedField } from "../../new_fields/ScriptField"; import { InkData } from "../../new_fields/InkField"; -type APIManager = { requester: RequestExecutor, applier: AnalysisApplier }; -type RequestExecutor = (apiKey: string, data: D, service: Service) => Promise; -type AnalysisApplier = (target: Doc, ...args: any) => any; +type APIManager = { converter: BodyConverter, requester: RequestExecutor, analyzer: AnalysisApplier }; +type RequestExecutor = (apiKey: string, body: string, service: Service) => Promise; +type AnalysisApplier = (target: Doc, relevantKeys: string[], ...args: any) => any; +type BodyConverter = (data: D) => string; type Converter = (results: any) => Field; export type Tag = { name: string, confidence: number }; @@ -40,7 +41,7 @@ export enum Confidence { */ export namespace CognitiveServices { - const executeQuery = async (service: Service, executor: RequestExecutor, data: D): Promise> => { + const executeQuery = async (service: Service, manager: APIManager, data: D): Promise> => { return fetch(Utils.prepend(`${RouteStore.cognitiveServices}/${service}`)).then(async response => { let apiKey = await response.text(); if (!apiKey) { @@ -49,7 +50,7 @@ export namespace CognitiveServices { let results: Opt; try { - results = await executor(apiKey, data, service).then(json => JSON.parse(json)); + results = await manager.requester(apiKey, manager.converter(data), service).then(json => JSON.parse(json)); } catch { results = undefined; } @@ -61,7 +62,9 @@ export namespace CognitiveServices { export const Manager: APIManager = { - requester: (async (apiKey: string, imageUrl: string, service: Service) => { + converter: (imageUrl: string) => JSON.stringify({ url: imageUrl }), + + requester: async (apiKey: string, body: string, service: Service) => { let uriBase; let parameters; @@ -88,7 +91,7 @@ export namespace CognitiveServices { const options = { uri: 'https://eastus.api.cognitive.microsoft.com/' + uriBase, qs: parameters, - body: `{"url": "${imageUrl}"}`, + body: body, headers: { 'Content-Type': 'application/json', 'Ocp-Apim-Subscription-Key': apiKey @@ -96,15 +99,16 @@ export namespace CognitiveServices { }; return request.post(options); - }) as RequestExecutor, + }, - applier: (async (target: Doc, service: Service, converter: Converter, storageKey: string) => { + analyzer: async (target: Doc, keys: string[], service: Service, converter: Converter) => { let imageData = Cast(target.data, ImageField); + let storageKey = keys[0]; if (!imageData || await Cast(target[storageKey], Doc)) { return; } let toStore: any; - let results = await executeQuery(service, Manager.requester, imageData.url.href); + let results = await executeQuery(service, Manager, imageData.url.href); if (!results) { toStore = "Cognitive Services could not process the given image URL."; } else { @@ -115,7 +119,7 @@ export namespace CognitiveServices { } } target[storageKey] = toStore; - }) as AnalysisApplier + } }; @@ -134,7 +138,7 @@ export namespace CognitiveServices { tagDoc.confidence = threshold; return tagDoc; }; - Manager.applier(target, Service.ComputerVision, converter, "generatedTags"); + Manager.analyzer(target, ["generatedTags"], Service.ComputerVision, converter); }; export const extractFaces = async (target: Doc) => { @@ -143,7 +147,7 @@ export namespace CognitiveServices { results.map((face: Face) => faceDocs.push(Docs.Get.DocumentHierarchyFromJson(face, `Face: ${face.faceId}`)!)); return faceDocs; }; - Manager.applier(target, Service.Face, converter, "faces"); + Manager.analyzer(target, ["faces"], Service.Face, converter); }; } @@ -152,7 +156,26 @@ export namespace CognitiveServices { export const Manager: APIManager = { - requester: (async (apiKey: string, inkData: InkData) => { + converter: (inkData: InkData): string => { + let entries = inkData.entries(), next = entries.next(); + let strokes: AzureStrokeData[] = [], id = 0; + while (!next.done) { + strokes.push({ + id: id++, + points: next.value[1].pathData.map(point => `${point.x},${point.y}`).join(","), + language: "en-US" + }); + next = entries.next(); + } + return JSON.stringify({ + version: 1, + language: "en-US", + unit: "mm", + strokes: strokes + }); + }, + + requester: async (apiKey: string, body: string) => { let xhttp = new XMLHttpRequest(); let serverAddress = "https://api.cognitive.microsoft.com"; let endpoint = serverAddress + "/inkrecognizer/v1.0-preview/recognize"; @@ -174,22 +197,22 @@ export namespace CognitiveServices { xhttp.open("PUT", endpoint, true); xhttp.setRequestHeader('Ocp-Apim-Subscription-Key', apiKey); xhttp.setRequestHeader('Content-Type', 'application/json'); - xhttp.send(format(inkData)); + xhttp.send(body); }; return new Promise(promisified); - }) as RequestExecutor, + }, - applier: (async (target: Doc, inkData: InkData) => { - let results = await executeQuery(Service.Handwriting, Manager.requester, inkData); + analyzer: async (target: Doc, keys: string[], inkData: InkData) => { + let results = await executeQuery(Service.Handwriting, Manager, inkData); if (results) { results.recognitionUnits && (results = results.recognitionUnits); - target.inkAnalysis = Docs.Get.DocumentHierarchyFromJson(results, "Ink Analysis"); + target[keys[0]] = Docs.Get.DocumentHierarchyFromJson(results, "Ink Analysis"); let recognizedText = results.map((item: any) => item.recognizedText); let individualWords = recognizedText.filter((text: string) => text && text.split(" ").length === 1); - target.handwriting = individualWords.join(" "); + target[keys[1]] = individualWords.join(" "); } - }) as AnalysisApplier + } }; @@ -206,25 +229,6 @@ export namespace CognitiveServices { strokes: AzureStrokeData[]; } - const format = (inkData: InkData): string => { - let entries = inkData.entries(), next = entries.next(); - let strokes: AzureStrokeData[] = [], id = 0; - while (!next.done) { - strokes.push({ - id: id++, - points: next.value[1].pathData.map(point => `${point.x},${point.y}`).join(","), - language: "en-US" - }); - next = entries.next(); - } - return JSON.stringify({ - version: 1, - language: "en-US", - unit: "mm", - strokes: strokes - }); - }; - } } \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 4b7ddd49d..d39d6f255 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -519,7 +519,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { if (!data) { return; } - CognitiveServices.Inking.Manager.applier(Doc.GetProto(this.props.Document), data.inkData); + let target = Doc.GetProto(this.props.Document); + let relevantKeys = ["inkAnalysis", "handwriting"]; + CognitiveServices.Inking.Manager.analyzer(target, relevantKeys, data.inkData); } }); } -- cgit v1.2.3-70-g09d2 From b4888c53bcc4eb9a37ed4b3dac740bfffc64d3c5 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Thu, 25 Jul 2019 11:46:28 -0400 Subject: postponed dealing with circular import bug imagebox --- src/client/DocServer.ts | 1 - .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 5 ++++- src/client/views/nodes/ImageBox.tsx | 6 +++--- 3 files changed, 7 insertions(+), 5 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 077c8e5ba..df9828029 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -5,7 +5,6 @@ import { Utils, emptyFunction } from '../Utils'; import { SerializationHelper } from './util/SerializationHelper'; import { RefField } from '../new_fields/RefField'; import { Id, HandleUpdate } from '../new_fields/FieldSymbols'; -import { CurrentUserUtils } from '../server/authentication/models/current_user_utils'; /** * This class encapsulates the transfer and cross-client synchronization of diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index d39d6f255..701574cfc 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -32,7 +32,10 @@ import { OverlayView, OverlayElementOptions } from "../../OverlayView"; import { ScriptBox } from "../../ScriptBox"; import { CompileScript } from "../../../util/Scripting"; import { CognitiveServices } from "../../../cognitive_services/CognitiveServices"; +import { library } from "@fortawesome/fontawesome-svg-core"; +import { faEye } from "@fortawesome/free-regular-svg-icons"; +library.add(faEye); export const panZoomSchema = createSchema({ panX: "number", @@ -522,7 +525,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { let target = Doc.GetProto(this.props.Document); let relevantKeys = ["inkAnalysis", "handwriting"]; CognitiveServices.Inking.Manager.analyzer(target, relevantKeys, data.inkData); - } + }, icon: "eye" }); } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 3d77696fe..c0c64b6d5 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -216,9 +216,9 @@ export class ImageBox extends DocComponent(ImageD }); let modes: ContextMenuProps[] = []; - // let dataDoc = Doc.GetProto(this.Document); - modes.push({ description: "Generate Tags", event: () => CognitiveServices.Image.generateMetadata(this.Document), icon: "tag" }); - modes.push({ description: "Find Faces", event: () => CognitiveServices.Image.extractFaces(this.Document), icon: "camera" }); + let dataDoc = Doc.GetProto(this.props.Document); + modes.push({ description: "Generate Tags", event: () => CognitiveServices.Image.generateMetadata(dataDoc), icon: "tag" }); + modes.push({ description: "Find Faces", event: () => CognitiveServices.Image.extractFaces(dataDoc), icon: "camera" }); ContextMenu.Instance.addItem({ description: "Image Funcs...", subitems: funcs }); ContextMenu.Instance.addItem({ description: "Analyze...", subitems: modes }); -- cgit v1.2.3-70-g09d2 From 6c111fdc538775f9f7b04f91d0b5701fd2a1aaca Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Thu, 25 Jul 2019 12:59:41 -0400 Subject: icon and stop prevention of undo redo propagation --- src/client/views/GlobalKeyHandler.ts | 4 +++- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index e8a588e58..7477c5b4f 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -1,4 +1,4 @@ -import { UndoManager, undoBatch } from "../util/UndoManager"; +import { UndoManager } from "../util/UndoManager"; import { SelectionManager } from "../util/SelectionManager"; import { CollectionDockingView } from "./collections/CollectionDockingView"; import { MainView } from "./MainView"; @@ -144,9 +144,11 @@ export default class KeyManager { break; case "y": UndoManager.Redo(); + stopPropagation = false; break; case "z": UndoManager.Undo(); + stopPropagation = false; break; case "a": case "c": diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 701574cfc..15734ce0d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -34,8 +34,9 @@ import { CompileScript } from "../../../util/Scripting"; import { CognitiveServices } from "../../../cognitive_services/CognitiveServices"; import { library } from "@fortawesome/fontawesome-svg-core"; import { faEye } from "@fortawesome/free-regular-svg-icons"; +import { faTable } from "@fortawesome/free-solid-svg-icons"; -library.add(faEye); +library.add(faEye, faTable); export const panZoomSchema = createSchema({ panX: "number", @@ -492,6 +493,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { onContextMenu = () => { ContextMenu.Instance.addItem({ description: "Arrange contents in grid", + icon: "table", event: async () => { const docs = await DocListCastAsync(this.Document[this.props.fieldKey]); UndoManager.RunInBatch(() => { -- cgit v1.2.3-70-g09d2 From eceff76609deaa3e7a60c686b62cb4fd15e9699b Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 25 Jul 2019 15:00:16 -0400 Subject: several fixes and added basic fields to treeview display --- .vscode/launch.json | 4 - .../views/collections/CollectionTreeView.tsx | 106 +++++++++++++-------- src/client/views/nodes/DocumentView.tsx | 4 +- src/client/views/nodes/ImageBox.tsx | 2 +- src/client/views/search/SearchItem.tsx | 6 +- src/new_fields/Doc.ts | 2 +- 6 files changed, 71 insertions(+), 53 deletions(-) (limited to 'src/client/views/collections') diff --git a/.vscode/launch.json b/.vscode/launch.json index fd67a7647..822a06024 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,10 +7,6 @@ { "type": "chrome", "request": "launch", - "runtimeArgs": [ - "--enable-logging", - "--v=1" - ], "name": "Launch Chrome against localhost", "sourceMaps": true, "breakOnLoad": true, diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 006de0c70..ff3221ada 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -3,7 +3,7 @@ import { faAngleRight, faCamera, faExpand, faTrash, faBell, faCaretDown, faCaret import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable, trace, untracked } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast, HeightSym, WidthSym, Opt } from '../../../new_fields/Doc'; +import { Doc, DocListCast, HeightSym, WidthSym, Opt, Field } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; import { List } from '../../../new_fields/List'; import { Document, listSpec } from '../../../new_fields/Schema'; @@ -26,6 +26,8 @@ import { CollectionSubView } from "./CollectionSubView"; import "./CollectionTreeView.scss"; import React = require("react"); import { LinkManager } from '../../util/LinkManager'; +import { ComputedField } from '../../../new_fields/ScriptField'; +import { KeyValueBox } from '../nodes/KeyValueBox'; export interface TreeViewProps { @@ -68,8 +70,7 @@ class TreeView extends React.Component { private _header?: React.RefObject = React.createRef(); private _treedropDisposer?: DragManager.DragDropDisposer; private _dref = React.createRef(); - @observable __chosenKey: string = ""; - @computed get _chosenKey() { return this.__chosenKey ? this.__chosenKey : this.fieldKey; } + @computed get treeViewExpandedView() { return StrCast(this.props.document.treeViewExpandedView, "data"); } @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.document.maxEmbedHeight, 300); } @observable _collapsed: boolean = true; @@ -125,12 +126,12 @@ class TreeView extends React.Component { } } onPointerLeave = (e: React.PointerEvent): void => { - this.props.document.libraryBrush = undefined; + this.props.document.libraryBrush = false; this._header!.current!.className = "treeViewItem-header"; document.removeEventListener("pointermove", this.onDragMove, true); } onDragMove = (e: PointerEvent): void => { - this.props.document.libraryBrush = undefined; + this.props.document.libraryBrush = false; let x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY); let rect = this._header!.current!.getBoundingClientRect(); let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2); @@ -191,25 +192,6 @@ class TreeView extends React.Component { OnTab={() => this.props.indentDocument && this.props.indentDocument()} />) - @computed get keyList() { - let target = this.props.document; - let keys = Array.from(Object.keys(target)); - if (target.proto instanceof Doc) { - keys.push(...Array.from(Object.keys(target.proto))); - } - let keyList: string[] = keys.reduce((l, key) => { - let listspec = DocListCast(target[key]); - if (listspec && listspec.length) return [...l, key]; - return l; - }, [] as string[]); - keys.map(key => Cast(target[key], Doc) instanceof Doc && keyList.push(key)); - if (LinkManager.Instance.getAllRelatedLinks(this.props.document).length > 0) keyList.push("links"); - if (keyList.indexOf(this.fieldKey) !== -1) { - keyList.splice(keyList.indexOf(this.fieldKey), 1); - } - keyList.splice(0, 0, this.fieldKey); - return keyList.filter((item, index) => keyList.indexOf(item) >= index); - } /** * Renders the EditableView title element for placement into the tree. */ @@ -218,13 +200,9 @@ class TreeView extends React.Component { let onItemDown = SetupDrag(reference, () => this.dataDoc, this.move, this.props.dropAction, this.props.treeViewId, true); let headerElements = ( - { - let ind = this.keyList.indexOf(this._chosenKey); - ind = (ind + 1) % this.keyList.length; - this.__chosenKey = this.keyList[ind]; - })} > - {this._chosenKey} + this.props.document.treeViewExpandedView = this.treeViewExpandedView === "data" ? "fields" : this.treeViewExpandedView === "fields" && this.props.document.layout ? "layout" : "data")}> + {this.treeViewExpandedView} ); let dataDocs = CollectionDockingView.Instance ? Cast(CollectionDockingView.Instance.props.Document[this.fieldKey], listSpec(Doc), []) : []; let openRight = dataDocs && dataDocs.indexOf(this.dataDoc) !== -1 ? (null) : ( @@ -249,7 +227,6 @@ class TreeView extends React.Component { onWorkspaceContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped()) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 - ContextMenu.Instance.addItem({ description: (BoolCast(this.props.document.embed) ? "Collapse" : "Expand") + " inline", event: () => this.props.document.embed = !BoolCast(this.props.document.embed), icon: "expand" }); if (NumCast(this.props.document.viewType) !== CollectionViewType.Docking) { ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.resolvedDataDoc, "inTab"), icon: "folder" }); ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.resolvedDataDoc, "onRight"), icon: "caret-square-right" }); @@ -311,8 +288,8 @@ class TreeView extends React.Component { renderLinks = () => { let ele: JSX.Element[] = []; - let remDoc = (doc: Doc) => this.remove(doc, this._chosenKey); - let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.props.document, this._chosenKey, doc, addBefore, before); + let remDoc = (doc: Doc) => this.remove(doc, this.fieldKey); + let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.props.document, this.fieldKey, doc, addBefore, before); let groups = LinkManager.Instance.getRelatedGroupedLinks(this.props.document); groups.forEach((groupLinkDocs, groupType) => { // let destLinks = groupLinkDocs.map(d => LinkManager.Instance.getOppositeAnchor(d, this.props.document)); @@ -358,20 +335,65 @@ class TreeView extends React.Component { noOverlays = (doc: Doc) => ({ title: "", caption: "" }); + expandedField = (doc?: Doc) => { + if (!doc) return
      ; + let realDoc = doc; + + let ids: { [key: string]: string } = {}; + Object.keys(doc).forEach(key => { + if (!(key in ids) && realDoc[key] !== ComputedField.undefined) { + ids[key] = key; + } + }); + + let rows: JSX.Element[] = []; + for (let key of Object.keys(ids).sort()) { + let contents = realDoc[key] ? realDoc[key] : undefined; + let contentElement: JSX.Element[] | JSX.Element = []; + + if (contents instanceof Doc || Cast(contents, listSpec(Doc))) { + let docList = contents; + let remDoc = (doc: Doc) => this.remove(doc, key); + let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before); + contentElement = key === "links" ? this.renderLinks() : + TreeView.GetChildElements(docList instanceof Doc ? [docList as Doc] : DocListCast(docList), this.props.treeViewId, realDoc, undefined, key, addDoc, remDoc, this.move, + this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth, this.props.renderDepth); + } else { + contentElement = Field.toKeyValueString(realDoc, key)} + SetValue={(value: string) => KeyValueBox.SetField(realDoc, key, value)} />; + } + rows.push(
      + {key + ":"} +   + {contentElement} +
      ); + } + return rows; + } + render() { let contentElement: (JSX.Element | null) = null; - let docList = Cast(this.dataDoc[this._chosenKey], listSpec(Doc)); - let remDoc = (doc: Doc) => this.remove(doc, this._chosenKey); - let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, this._chosenKey, doc, addBefore, before); + let docList = Cast(this.dataDoc[this.fieldKey], listSpec(Doc)); + let remDoc = (doc: Doc) => this.remove(doc, this.fieldKey); + let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, this.fieldKey, doc, addBefore, before); if (!this._collapsed) { - if (!this.props.document.embed) { - let doc = Cast(this.props.document[this._chosenKey], Doc); - contentElement =
        - {this._chosenKey === "links" ? this.renderLinks() : - TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, this.props.document, this.resolvedDataDoc, this._chosenKey, addDoc, remDoc, this.move, + if (this.treeViewExpandedView === "data") { + let doc = Cast(this.props.document[this.fieldKey], Doc); + contentElement =
          + {this.fieldKey === "links" ? this.renderLinks() : + TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, this.props.document, this.resolvedDataDoc, this.fieldKey, addDoc, remDoc, this.move, this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth, this.props.renderDepth)}
        ; + } else if (this.treeViewExpandedView === "fields") { + contentElement =
          + {this.expandedField(this.dataDoc)} +
        ; } else { let layoutDoc = this.props.document; contentElement =
        diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index b39c77adb..b5e64ed19 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -300,7 +300,7 @@ export class DocumentView extends DocComponent(Docu Doc.UseDetailLayout(fullScreenAlias); this.props.addDocTab(fullScreenAlias, this.dataDoc, "inTab"); SelectionManager.DeselectAll(); - this.props.Document.libraryBrush = undefined; + this.props.Document.libraryBrush = false; } else if (CurrentUserUtils.MainDocId !== this.props.Document[Id] && (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && @@ -612,7 +612,7 @@ export class DocumentView extends DocComponent(Docu } onPointerEnter = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = true; }; - onPointerLeave = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = undefined; }; + onPointerLeave = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = false; }; isSelected = () => SelectionManager.IsSelected(this); @action select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); }; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 3d77696fe..14208ce17 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -175,7 +175,7 @@ export class ImageBox extends DocComponent(ImageD const url = Utils.prepend(files[0]); // upload to server with known URL let audioDoc = Docs.Create.AudioDocument(url, { title: "audio test", x: NumCast(self.props.Document.x), y: NumCast(self.props.Document.y), width: 200, height: 32 }); - audioDoc.embed = true; + audioDoc.treeViewExpandedView = "layout"; let audioAnnos = Cast(self.extensionDoc.audioAnnotations, listSpec(Doc)); if (audioAnnos === undefined) { self.extensionDoc.audioAnnotations = new List([audioDoc]); diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx index a995140e2..5c2ced2eb 100644 --- a/src/client/views/search/SearchItem.tsx +++ b/src/client/views/search/SearchItem.tsx @@ -205,13 +205,13 @@ export class SearchItem extends React.Component { let doc1 = Cast(this.props.doc.anchor1, Doc, null); let doc2 = Cast(this.props.doc.anchor2, Doc, null); - doc1 && (doc1.libraryBrush = undefined); - doc2 && (doc2.libraryBrush = undefined); + doc1 && (doc1.libraryBrush = false); + doc2 && (doc2.libraryBrush = false); } } else { let docViews: DocumentView[] = DocumentManager.Instance.getAllDocumentViews(this.props.doc); docViews.forEach(element => { - element.props.Document.libraryBrush = undefined; + element.props.Document.libraryBrush = false; }); } } diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 64b4acb7b..c37c0fd22 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -380,7 +380,7 @@ export namespace Doc { } if (expandedTemplateLayout === undefined) { setTimeout(() => - dataDoc[expandedLayoutFieldKey] = Doc.MakeDelegate(templateLayoutDoc, undefined, templateLayoutDoc.title + ".layout"), 0); + dataDoc[expandedLayoutFieldKey] = Doc.MakeDelegate(templateLayoutDoc, undefined, "["+templateLayoutDoc.title + "]"), 0); } return templateLayoutDoc; // use the templateLayout when it's not a template or the expandedTemplate is pending. } -- cgit v1.2.3-70-g09d2 From 8e567d94618fcac3345a1f495cdea71a543f7804 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Thu, 25 Jul 2019 15:09:24 -0400 Subject: added misc icons and updated metadata storange onto field extension doc --- src/client/views/InkingControl.tsx | 2 +- .../collections/collectionFreeForm/CollectionFreeFormView.tsx | 9 ++++----- src/client/views/nodes/ImageBox.tsx | 11 ++++++----- 3 files changed, 11 insertions(+), 11 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index 1910e409b..da388e532 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -20,7 +20,7 @@ export class InkingControl extends React.Component { static Instance: InkingControl = new InkingControl({}); @observable private _selectedTool: InkTool = InkTool.None; @observable private _selectedColor: string = "rgb(244, 67, 54)"; - @observable private _selectedWidth: string = "25"; + @observable private _selectedWidth: string = "5"; @observable public _open: boolean = false; constructor(props: Readonly<{}>) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 15734ce0d..6500b3273 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -34,9 +34,9 @@ import { CompileScript } from "../../../util/Scripting"; import { CognitiveServices } from "../../../cognitive_services/CognitiveServices"; import { library } from "@fortawesome/fontawesome-svg-core"; import { faEye } from "@fortawesome/free-regular-svg-icons"; -import { faTable } from "@fortawesome/free-solid-svg-icons"; +import { faTable, faPaintBrush, faAsterisk } from "@fortawesome/free-solid-svg-icons"; -library.add(faEye, faTable); +library.add(faEye, faTable, faPaintBrush); export const panZoomSchema = createSchema({ panX: "number", @@ -524,10 +524,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { if (!data) { return; } - let target = Doc.GetProto(this.props.Document); let relevantKeys = ["inkAnalysis", "handwriting"]; - CognitiveServices.Inking.Manager.analyzer(target, relevantKeys, data.inkData); - }, icon: "eye" + CognitiveServices.Inking.Manager.analyzer(this.fieldExtensionDoc, relevantKeys, data.inkData); + }, icon: "paint-brush" }); } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index c0c64b6d5..5e93251a3 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -1,5 +1,5 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faImage, faFileAudio } from '@fortawesome/free-solid-svg-icons'; +import { faImage, faFileAudio, faPaintBrush, faAsterisk } from '@fortawesome/free-solid-svg-icons'; import { action, observable, computed, runInAction } from 'mobx'; import { observer } from "mobx-react"; import Lightbox from 'react-image-lightbox'; @@ -27,13 +27,14 @@ import { Font } from '@react-pdf/renderer'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { CognitiveServices } from '../../cognitive_services/CognitiveServices'; import FaceRectangles from './FaceRectangles'; +import { faEye } from '@fortawesome/free-regular-svg-icons'; var requestImageSize = require('../../util/request-image-size'); var path = require('path'); const { Howl, Howler } = require('howler'); -library.add(faImage); -library.add(faFileAudio); +library.add(faImage, faEye, faPaintBrush); +library.add(faFileAudio, faAsterisk); export const pageSchema = createSchema({ @@ -220,8 +221,8 @@ export class ImageBox extends DocComponent(ImageD modes.push({ description: "Generate Tags", event: () => CognitiveServices.Image.generateMetadata(dataDoc), icon: "tag" }); modes.push({ description: "Find Faces", event: () => CognitiveServices.Image.extractFaces(dataDoc), icon: "camera" }); - ContextMenu.Instance.addItem({ description: "Image Funcs...", subitems: funcs }); - ContextMenu.Instance.addItem({ description: "Analyze...", subitems: modes }); + ContextMenu.Instance.addItem({ description: "Image Funcs...", subitems: funcs, icon: "asterisk" }); + ContextMenu.Instance.addItem({ description: "Analyze...", subitems: modes, icon: "eye" }); } } -- cgit v1.2.3-70-g09d2 From 834ea7588594df80524e9775a5b2637b90e41293 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 25 Jul 2019 20:41:06 -0400 Subject: expand treeview when switching modes --- src/client/views/collections/CollectionTreeView.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index ff3221ada..eba78e772 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -201,7 +201,11 @@ class TreeView extends React.Component { let headerElements = ( this.props.document.treeViewExpandedView = this.treeViewExpandedView === "data" ? "fields" : this.treeViewExpandedView === "fields" && this.props.document.layout ? "layout" : "data")}> + onPointerDown={action(() => { + this.props.document.treeViewExpandedView = this.treeViewExpandedView === "data" ? "fields" : + this.treeViewExpandedView === "fields" && this.props.document.layout ? "layout" : "data"; + this._collapsed = false; + })}> {this.treeViewExpandedView} ); let dataDocs = CollectionDockingView.Instance ? Cast(CollectionDockingView.Instance.props.Document[this.fieldKey], listSpec(Doc), []) : []; -- cgit v1.2.3-70-g09d2 From 3ee8a9ebc760e3049b692efad0d6edfb877342cf Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 25 Jul 2019 22:27:26 -0400 Subject: fixed dragging annotaitons on images. fixed border rounding of buttons. fixed dropping to ignore document titles. --- src/client/util/DragManager.ts | 3 ++- src/client/views/collections/CollectionBaseView.tsx | 2 +- src/client/views/nodes/ButtonBox.scss | 2 ++ src/client/views/nodes/DocumentView.tsx | 3 ++- src/client/views/nodes/ImageBox.tsx | 2 -- 5 files changed, 7 insertions(+), 5 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 323908302..5271f2f5d 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -412,7 +412,6 @@ export namespace DragManager { }; let hideDragElements = () => { - SelectionManager.SetIsDragging(false); dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement)); eles.map(ele => (ele.hidden = false)); }; @@ -426,11 +425,13 @@ export namespace DragManager { AbortDrag = () => { hideDragElements(); + SelectionManager.SetIsDragging(false); endDrag(); }; const upHandler = (e: PointerEvent) => { hideDragElements(); dispatchDrag(eles, e, dragData, options, finishDrag); + SelectionManager.SetIsDragging(false); endDrag(); }; document.addEventListener("pointermove", moveHandler, true); diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index 72faf52c4..c39caec24 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -124,7 +124,7 @@ export class CollectionBaseView extends React.Component { @action.bound moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean { let self = this; - let targetDataDoc = this.props.fieldExt || this.props.Document.isTemplate ? this.extensionDoc : this.props.Document; + let targetDataDoc = this.props.Document; if (Doc.AreProtosEqual(targetDataDoc, targetCollection)) { //if (Doc.AreProtosEqual(this.extensionDoc, targetCollection)) { return true; diff --git a/src/client/views/nodes/ButtonBox.scss b/src/client/views/nodes/ButtonBox.scss index 97cc91128..92beafa15 100644 --- a/src/client/views/nodes/ButtonBox.scss +++ b/src/client/views/nodes/ButtonBox.scss @@ -2,9 +2,11 @@ width: 100%; height: 100%; pointer-events: all; + border-radius: inherit; } .buttonBox-mainButton { width: 100%; height: 100%; + border-radius: inherit; } \ No newline at end of file diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index b5e64ed19..4a5e20533 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -298,6 +298,7 @@ export class DocumentView extends DocComponent(Docu let fullScreenAlias = Doc.MakeAlias(this.props.Document); fullScreenAlias.templates = new List(); Doc.UseDetailLayout(fullScreenAlias); + fullScreenAlias.showCaption = true; this.props.addDocTab(fullScreenAlias, this.dataDoc, "inTab"); SelectionManager.DeselectAll(); this.props.Document.libraryBrush = false; @@ -673,7 +674,7 @@ export class DocumentView extends DocComponent(Docu {!showTitle ? (null) :
        diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 6541007d0..ea167286b 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -104,8 +104,6 @@ export class ImageBox extends DocComponent(ImageD e.stopPropagation(); } } - } else if (!this.props.isSelected()) { - e.stopPropagation(); } })); // de.data.removeDocument() bcz: need to implement -- cgit v1.2.3-70-g09d2 From 7d468c19c28a48314ea993d84e5995364e88fa86 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 26 Jul 2019 02:21:09 -0400 Subject: added a MakeBackground and FitToBox menu items. fixed problems with fitToBox and frozen dimensions. fixed issue with drop() on free forms. --- .../views/collections/CollectionBaseView.scss | 2 +- .../views/collections/CollectionBaseView.tsx | 6 ++-- src/client/views/collections/CollectionSubView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 32 +++++++++++++++------- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/nodes/DocumentView.scss | 1 - src/client/views/nodes/DocumentView.tsx | 7 +++++ src/client/views/nodes/ImageBox.tsx | 2 +- 8 files changed, 37 insertions(+), 17 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionBaseView.scss b/src/client/views/collections/CollectionBaseView.scss index 34bcb705e..583e6f6ca 100644 --- a/src/client/views/collections/CollectionBaseView.scss +++ b/src/client/views/collections/CollectionBaseView.scss @@ -6,7 +6,7 @@ border-radius: 0 0 $border-radius $border-radius; box-sizing: border-box; border-radius: inherit; - pointer-events: all; width:100%; height:100%; + overflow: auto; } \ No newline at end of file diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index c39caec24..67112ae7c 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -126,7 +126,6 @@ export class CollectionBaseView extends React.Component { let self = this; let targetDataDoc = this.props.Document; if (Doc.AreProtosEqual(targetDataDoc, targetCollection)) { - //if (Doc.AreProtosEqual(this.extensionDoc, targetCollection)) { return true; } if (this.removeDocument(doc)) { @@ -146,7 +145,10 @@ export class CollectionBaseView extends React.Component { const viewtype = this.collectionViewType; return (
        {viewtype !== undefined ? this.props.children(viewtype, props) : (null)} diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 2ddefb3c0..cbc9e61eb 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -104,7 +104,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { } else if (de.data.moveDocument) { let movedDocs = de.data.options === this.props.Document[Id] ? de.data.draggedDocuments : de.data.droppedDocuments; added = movedDocs.reduce((added: boolean, d) => - de.data.moveDocument(d, /*this.props.DataDoc ? this.props.DataDoc :*/ this.props.Document, this.props.addDocument) || added, false); + de.data.moveDocument(d, this.props.Document, this.props.addDocument) || added, false); } else { added = de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d) || added, false); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 6500b3273..9c4275388 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -34,9 +34,11 @@ import { CompileScript } from "../../../util/Scripting"; import { CognitiveServices } from "../../../cognitive_services/CognitiveServices"; import { library } from "@fortawesome/fontawesome-svg-core"; import { faEye } from "@fortawesome/free-regular-svg-icons"; -import { faTable, faPaintBrush, faAsterisk } from "@fortawesome/free-solid-svg-icons"; +import { faTable, faPaintBrush, faAsterisk, faExpandArrowsAlt, faCompressArrowsAlt } from "@fortawesome/free-solid-svg-icons"; +import { undo } from "prosemirror-history"; +import { number } from "prop-types"; -library.add(faEye, faTable, faPaintBrush); +library.add(faEye, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt); export const panZoomSchema = createSchema({ panX: "number", @@ -58,24 +60,29 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { private get _pheight() { return this.props.PanelHeight(); } private inkKey = "ink"; + get prevScaling() { + return (this.props as any).ContentScaling && this.Document.nativeWidth && this.fitToBox ? (this.props as any).ContentScaling() : 1; + } + @computed get contentBounds() { - let bounds = this.props.fitToBox && !NumCast(this.nativeWidth) ? Doc.ComputeContentBounds(DocListCast(this.props.Document.data)) : undefined; + let bounds = (this.fitToBox) && !this.nativeWidth ? Doc.ComputeContentBounds(DocListCast(this.props.Document.data)) : undefined; return { panX: bounds ? (bounds.x + bounds.r) / 2 : this.Document.panX || 0, panY: bounds ? (bounds.y + bounds.b) / 2 : this.Document.panY || 0, - scale: bounds ? Math.min(this.props.PanelHeight() / (bounds.b - bounds.y), this.props.PanelWidth() / (bounds.r - bounds.x)) : this.Document.scale || 1 + scale: (bounds ? Math.min(this.props.PanelHeight() / (bounds.b - bounds.y), this.props.PanelWidth() / (bounds.r - bounds.x)) : this.Document.scale || 1) / this.prevScaling }; } - @computed get nativeWidth() { return this.Document.nativeWidth || 0; } - @computed get nativeHeight() { return this.Document.nativeHeight || 0; } + @computed get fitToBox() { return this.props.fitToBox || this.props.Document.fitToBox; } + @computed get nativeWidth() { return this.fitToBox ? 0 : this.Document.nativeWidth || 0; } + @computed get nativeHeight() { return this.fitToBox ? 0 : this.Document.nativeHeight || 0; } public get isAnnotationOverlay() { return this.props.fieldExt ? true : false; } // fieldExt will be "" or "annotation". should maybe generalize this, or make it more specific (ie, 'annotation' instead of 'fieldExt') private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; } private panX = () => this.contentBounds.panX; private panY = () => this.contentBounds.panY; private zoomScaling = () => this.contentBounds.scale; - private centeringShiftX = () => !this.nativeWidth ? this._pwidth / 2 : 0; // shift so pan position is at center of window for non-overlay collections - private centeringShiftY = () => !this.nativeHeight ? this._pheight / 2 : 0;// shift so pan position is at center of window for non-overlay collections + private centeringShiftX = () => !this.nativeWidth ? this._pwidth / 2 / this.prevScaling : 0; // shift so pan position is at center of window for non-overlay collections + private centeringShiftY = () => !this.nativeHeight ? this._pheight / 2 / this.prevScaling : 0;// shift so pan position is at center of window for non-overlay collections private getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth + 1, -this.borderWidth + 1).translate(-this.centeringShiftX(), -this.centeringShiftY()).transform(this.getLocalTransform()); private getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth); private getLocalTransform = (): Transform => Transform.Identity().scale(1 / this.zoomScaling()).translate(this.panX(), this.panY()); @@ -109,11 +116,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @undoBatch @action drop = (e: Event, de: DragManager.DropEvent) => { + let xf = this.getTransform(); if (super.drop(e, de)) { if (de.data instanceof DragManager.DocumentDragData) { if (de.data.droppedDocuments.length) { - let dragDoc = de.data.droppedDocuments[0]; - let [xp, yp] = this.getTransform().transformPoint(de.x, de.y); + let [xp, yp] = xf.transformPoint(de.x, de.y); let x = xp - de.data.xOffset; let y = yp - de.data.yOffset; let dropX = NumCast(de.data.droppedDocuments[0].x); @@ -491,6 +498,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } onContextMenu = () => { + ContextMenu.Instance.addItem({ + description: `${this.fitToBox ? "Unset" : "Set"} Fit To Container`, + event: undoBatch(async () => this.props.Document.fitToBox = !this.fitToBox), + icon: !this.fitToBox ? "expand-arrows-alt" : "compress-arrows-alt" + }); ContextMenu.Instance.addItem({ description: "Arrange contents in grid", icon: "table", diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index b765517a2..d96e93aeb 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -365,7 +365,7 @@ export class MarqueeView extends React.Component marqueeSelect() { let selRect = this.Bounds; let selection: Doc[] = []; - this.props.activeDocuments().map(doc => { + this.props.activeDocuments().filter(doc => !doc.isBackground).map(doc => { var z = NumCast(doc.zoomBasis, 1); var x = NumCast(doc.x); var y = NumCast(doc.y); diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index 3a4b46b7e..7c72fb6e6 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -4,7 +4,6 @@ position: inherit; top: 0; left:0; - pointer-events: all; // background: $light-color; //overflow: hidden; transform-origin: left top; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 4a5e20533..9d2dd3121 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -514,6 +514,11 @@ export class DocumentView extends DocComponent(Docu } proto.ignoreAspect = !BoolCast(proto.ignoreAspect, false); } + @undoBatch + @action + makeBackground = (): void => { + this.props.Document.isBackground = true; + } @undoBatch @action @@ -544,6 +549,7 @@ export class DocumentView extends DocComponent(Docu cm.addItem({ description: BoolCast(this.props.Document.ignoreAspect, false) || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "edit" }); cm.addItem({ description: "Pin to Pres", event: () => PresentationView.Instance.PinDoc(this.props.Document), icon: "map-pin" }); cm.addItem({ description: BoolCast(this.props.Document.lockedPosition) ? "Unlock Pos" : "Lock Pos", event: this.toggleLockPosition, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); + cm.addItem({ description: "Make Background", event: this.makeBackground, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); cm.addItem({ description: this.props.Document.isButton ? "Remove Button" : "Make Button", event: this.makeBtnClicked, icon: "concierge-bell" }); cm.addItem({ description: "Make Portal", event: () => { @@ -648,6 +654,7 @@ export class DocumentView extends DocComponent(Docu
        (ImageD if (field instanceof ImageField) paths = [this.choosePath(field.url)]; paths.push(...altpaths); // } - let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive"; + let interactive = InkingControl.Instance.selectedTool || this.props.Document.isBackground ? "" : "-interactive"; let rotation = NumCast(this.dataDoc.rotation, 0); let aspect = (rotation % 180) ? this.dataDoc[HeightSym]() / this.dataDoc[WidthSym]() : 1; let shift = (rotation % 180) ? (nativeHeight - nativeWidth / aspect) / 2 : 0; -- cgit v1.2.3-70-g09d2 From 04030194e690764a2d1a58b25568511a3c6dcb98 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 26 Jul 2019 09:14:06 -0400 Subject: fixed fitToBox layout issue --- .../collectionFreeForm/CollectionFreeFormView.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 9c4275388..5d61aefc7 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -60,16 +60,16 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { private get _pheight() { return this.props.PanelHeight(); } private inkKey = "ink"; - get prevScaling() { - return (this.props as any).ContentScaling && this.Document.nativeWidth && this.fitToBox ? (this.props as any).ContentScaling() : 1; + get parentScaling() { + return (this.props as any).ContentScaling && this.Document.nativeWidth && this.fitToBox && !this.isAnnotationOverlay ? (this.props as any).ContentScaling() : 1; } @computed get contentBounds() { - let bounds = (this.fitToBox) && !this.nativeWidth ? Doc.ComputeContentBounds(DocListCast(this.props.Document.data)) : undefined; + let bounds = this.fitToBox && !this.nativeWidth && !this.isAnnotationOverlay ? Doc.ComputeContentBounds(DocListCast(this.props.Document.data)) : undefined; return { panX: bounds ? (bounds.x + bounds.r) / 2 : this.Document.panX || 0, panY: bounds ? (bounds.y + bounds.b) / 2 : this.Document.panY || 0, - scale: (bounds ? Math.min(this.props.PanelHeight() / (bounds.b - bounds.y), this.props.PanelWidth() / (bounds.r - bounds.x)) : this.Document.scale || 1) / this.prevScaling + scale: (bounds ? Math.min(this.props.PanelHeight() / (bounds.b - bounds.y), this.props.PanelWidth() / (bounds.r - bounds.x)) : this.Document.scale || 1) / this.parentScaling }; } @@ -81,8 +81,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { private panX = () => this.contentBounds.panX; private panY = () => this.contentBounds.panY; private zoomScaling = () => this.contentBounds.scale; - private centeringShiftX = () => !this.nativeWidth ? this._pwidth / 2 / this.prevScaling : 0; // shift so pan position is at center of window for non-overlay collections - private centeringShiftY = () => !this.nativeHeight ? this._pheight / 2 / this.prevScaling : 0;// shift so pan position is at center of window for non-overlay collections + private centeringShiftX = () => !this.nativeWidth && !this.isAnnotationOverlay ? this._pwidth / 2 / this.parentScaling : 0; // shift so pan position is at center of window for non-overlay collections + private centeringShiftY = () => !this.nativeHeight && !this.isAnnotationOverlay ? this._pheight / 2 / this.parentScaling : 0;// shift so pan position is at center of window for non-overlay collections private getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth + 1, -this.borderWidth + 1).translate(-this.centeringShiftX(), -this.centeringShiftY()).transform(this.getLocalTransform()); private getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth); private getLocalTransform = (): Transform => Transform.Identity().scale(1 / this.zoomScaling()).translate(this.panX(), this.panY()); -- cgit v1.2.3-70-g09d2 From e189378e5ce01eedd1373172fbd8d8dabf2ad197 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 26 Jul 2019 16:07:26 -0400 Subject: fixes for templates fo stacking things. --- src/client/views/DocumentDecorations.tsx | 3 +- src/client/views/InkingControl.tsx | 2 +- .../views/collections/CollectionStackingView.tsx | 36 ++++++++++++++-------- .../views/collections/CollectionTreeView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 31 +++++++++++-------- 5 files changed, 45 insertions(+), 29 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 6fecbd3a7..255855b45 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -346,7 +346,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> onRadiusMove = (e: PointerEvent): void => { let dist = Math.sqrt((e.clientX - this._radiusDown[0]) * (e.clientX - this._radiusDown[0]) + (e.clientY - this._radiusDown[1]) * (e.clientY - this._radiusDown[1])); - SelectionManager.SelectedDocuments().map(dv => dv.props.Document.borderRounding = Doc.GetProto(dv.props.Document).borderRounding = `${Math.min(100, dist)}%`); + SelectionManager.SelectedDocuments().map(dv => dv.props.Document.layout instanceof Doc ? dv.props.Document.layout : dv.props.Document.isTemplate ? dv.props.Document : Doc.GetProto(dv.props.Document)). + map(d => d.borderRounding = `${Math.min(100, dist)}%`); e.stopPropagation(); e.preventDefault(); } diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index da388e532..58c83915b 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -47,7 +47,7 @@ export class InkingControl extends React.Component { if (MainOverlayTextBox.Instance.SetColor(color.hex)) return; let selected = SelectionManager.SelectedDocuments(); let oldColors = selected.map(view => { - let targetDoc = view.props.Document.isTemplate ? view.props.Document : Doc.GetProto(view.props.Document); + let targetDoc = view.props.Document.layout instanceof Doc ? view.props.Document.layout : view.props.Document.isTemplate ? view.props.Document : Doc.GetProto(view.props.Document); let oldColor = StrCast(targetDoc.backgroundColor); targetDoc.backgroundColor = this._selectedColor; return { diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 213aa981d..5a123bf65 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -23,15 +23,20 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { _docXfs: any[] = []; _columnStart: number = 0; @observable private cursor: CursorProperty = "grab"; - @computed get xMargin() { return NumCast(this.props.Document.xMargin, 2 * this.gridGap); } - @computed get yMargin() { return NumCast(this.props.Document.yMargin, 2 * this.gridGap); } - @computed get gridGap() { return NumCast(this.props.Document.gridGap, 10); } - @computed get singleColumn() { return BoolCast(this.props.Document.singleColumn, true); } - @computed get columnWidth() { return this.singleColumn ? (this.props.PanelWidth() / (this.props as any).ContentScaling() - 2 * this.xMargin) : Math.min(this.props.PanelWidth() - 2 * this.xMargin, NumCast(this.props.Document.columnWidth, 250)); } + @computed get xMargin() { return NumCast(this.layoutDoc.xMargin, 2 * this.gridGap); } + @computed get yMargin() { return NumCast(this.layoutDoc.yMargin, 2 * this.gridGap); } + @computed get gridGap() { return NumCast(this.layoutDoc.gridGap, 10); } + @computed get singleColumn() { return BoolCast(this.layoutDoc.singleColumn, true); } + @computed get columnWidth() { return this.singleColumn ? (this.props.PanelWidth() / (this.props as any).ContentScaling() - 2 * this.xMargin) : Math.min(this.props.PanelWidth() - 2 * this.xMargin, NumCast(this.layoutDoc.columnWidth, 250)); } @computed get filteredChildren() { return this.childDocs.filter(d => !d.isMinimized); } + 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; + } @computed get Sections() { - let sectionFilter = StrCast(this.props.Document.sectionFilter); + let sectionFilter = StrCast(this.layoutDoc.sectionFilter); let fields = new Map(); sectionFilter && this.filteredChildren.map(d => { let sectionValue = (d[sectionFilter] ? d[sectionFilter] : "-undefined-") as object; @@ -42,10 +47,15 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } componentDidMount() { this._heightDisposer = reaction(() => [this.yMargin, this.gridGap, this.columnWidth, this.childDocs.map(d => [d.height, d.width, d.zoomBasis, d.nativeHeight, d.nativeWidth, d.isMinimized])], - () => this.singleColumn && - (this.props.Document.height = this.Sections.size * 50 + this.filteredChildren.reduce((height, d, i) => - height + this.getDocHeight(d) + (i === this.filteredChildren.length - 1 ? this.yMargin : this.gridGap), this.yMargin)) - , { fireImmediately: true }); + () => { + if (this.singleColumn) { + let hgt = this.Sections.size * 50 + this.filteredChildren.reduce((height, d, i) => { + let xhgt = height + this.getDocHeight(d) + (i === this.filteredChildren.length - 1 ? this.yMargin : this.gridGap); + return xhgt; + }, this.yMargin); + this.layoutDoc.height = hgt; + } + }, { fireImmediately: true }); } componentWillUnmount() { this._heightDisposer && this._heightDisposer(); @@ -65,7 +75,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } getDisplayDoc(layoutDoc: Doc, d: Doc, dxf: () => Transform) { - let resolvedDataDoc = !this.props.Document.isTemplate && this.props.DataDoc !== this.props.Document ? this.props.DataDoc : undefined; + let resolvedDataDoc = !this.layoutDoc.isTemplate && this.props.DataDoc !== this.layoutDoc ? this.props.DataDoc : undefined; let width = () => d.nativeWidth ? Math.min(layoutDoc[WidthSym](), this.columnWidth) : this.columnWidth; let height = () => this.getDocHeight(layoutDoc); let finalDxf = () => dxf().scale(this.columnWidth / layoutDoc[WidthSym]()); @@ -153,7 +163,7 @@ 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.props.Document.columnWidth = this.columnWidth + delta; + this.layoutDoc.columnWidth = this.columnWidth + delta; } @action @@ -250,7 +260,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { ["width > height", this.filteredChildren.filter(f => f[WidthSym]() >= 1 + f[HeightSym]())], ["width = height", this.filteredChildren.filter(f => Math.abs(f[WidthSym]() - f[HeightSym]()) < 1)], ["height > width", this.filteredChildren.filter(f => f[WidthSym]() + 1 <= f[HeightSym]())]]. */} - {this.props.Document.sectionFilter ? Array.from(this.Sections.entries()). + {this.layoutDoc.sectionFilter ? Array.from(this.Sections.entries()). map(section => this.section(section[0].toString(), section[1])) : this.section("", this.filteredChildren)}
        diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index eba78e772..a1697f9b4 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -360,7 +360,7 @@ class TreeView extends React.Component { let remDoc = (doc: Doc) => this.remove(doc, key); let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before); contentElement = key === "links" ? this.renderLinks() : - TreeView.GetChildElements(docList instanceof Doc ? [docList as Doc] : DocListCast(docList), this.props.treeViewId, realDoc, undefined, key, addDoc, remDoc, this.move, + TreeView.GetChildElements(docList instanceof Doc ? [docList] : DocListCast(docList), this.props.treeViewId, realDoc, undefined, key, addDoc, remDoc, this.move, this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth, this.props.renderDepth); } else { contentElement = (Docu return (); } + 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; + } render() { if (this.Document.hidden) { return null; } let self = this; - let backgroundColor = this.props.Document.layout instanceof Doc ? StrCast(this.props.Document.layout.backgroundColor) : this.Document.backgroundColor; - let foregroundColor = StrCast(this.props.Document.layout instanceof Doc ? this.props.Document.layout.color : this.props.Document.color); + let backgroundColor = StrCast(this.layoutDoc.backgroundColor); + let foregroundColor = StrCast(this.layoutDoc.color); var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; - let showOverlays = this.props.showOverlays ? this.props.showOverlays(this.props.Document) : undefined; - let showTitle = showOverlays && showOverlays.title !== "undefined" ? showOverlays.title : StrCast(this.props.Document.showTitle); - let showCaption = showOverlays && showOverlays.caption !== "undefined" ? showOverlays.caption : StrCast(this.props.Document.showCaption); - let templates = Cast(this.props.Document.templates, listSpec("string")); + let showOverlays = this.props.showOverlays ? this.props.showOverlays(this.layoutDoc) : undefined; + let showTitle = showOverlays && showOverlays.title !== "undefined" ? showOverlays.title : StrCast(this.layoutDoc.showTitle); + let showCaption = showOverlays && showOverlays.caption !== "undefined" ? showOverlays.caption : StrCast(this.layoutDoc.showCaption); + let templates = Cast(this.layoutDoc.templates, listSpec("string")); if (!showOverlays && templates instanceof List) { templates.map(str => { if (str.indexOf("{props.Document.title}") !== -1) showTitle = "title"; if (str.indexOf("fieldKey={\"caption\"}") !== -1) showCaption = "caption"; }); } - let showTextTitle = showTitle && StrCast(this.props.Document.layout).startsWith("(Docu transformOrigin: "top left", transform: `scale(${1 / this.props.ContentScaling()})` }}> StrCast(this.props.Document[showTitle!])} - SetValue={(value: string) => (Doc.GetProto(this.props.Document)[showTitle!] = value) ? true : true} + GetValue={() => StrCast(this.layoutDoc[showTitle!])} + SetValue={(value: string) => (Doc.GetProto(this.layoutDoc)[showTitle!] = value) ? true : true} />
        } -- cgit v1.2.3-70-g09d2