From 9a4b4e9367d9d1fd245450db62124f0e530e4175 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 17 Jul 2019 11:23:19 -0400 Subject: fixed following links to regons seriously --- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 4532239d6..b91aaedee 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -267,7 +267,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { // this.props.Document.panX = panX; // this.props.Document.panY = panY; if (this.props.Document.scrollY) { - this.props.Document.scrollY = panY - this.props.Document[HeightSym](); + this.props.Document.scrollY = panY - scale * this.props.Document[HeightSym](); } } -- cgit v1.2.3-70-g09d2 From 33ba82d031266e87b6915de9f84bef2d914c2ae5 Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 17 Jul 2019 16:21:21 -0400 Subject: patch for pdf's panning to wrong location. --- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index b91aaedee..58218e641 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -4,7 +4,7 @@ import { Doc, DocListCastAsync, HeightSym, WidthSym, DocListCast } from "../../. import { Id } from "../../../../new_fields/FieldSymbols"; import { InkField, StrokeData } from "../../../../new_fields/InkField"; import { createSchema, makeInterface } from "../../../../new_fields/Schema"; -import { BoolCast, Cast, FieldValue, NumCast } from "../../../../new_fields/Types"; +import { BoolCast, Cast, FieldValue, NumCast, StrCast } from "../../../../new_fields/Types"; import { emptyFunction, returnOne } from "../../../../Utils"; import { DocumentManager } from "../../../util/DocumentManager"; import { DragManager } from "../../../util/DragManager"; @@ -263,7 +263,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX)); const newPanY = Math.min((1 - 1 / scale) * this.nativeHeight, Math.max(0, panY)); this.props.Document.panX = this.isAnnotationOverlay ? newPanX : panX; - this.props.Document.panY = this.isAnnotationOverlay ? newPanY : panY; + this.props.Document.panY = this.isAnnotationOverlay && StrCast(this.props.Document.backgroundLayout).indexOf("PDFBox") === -1 ? newPanY : panY; // this.props.Document.panX = panX; // this.props.Document.panY = panY; if (this.props.Document.scrollY) { -- cgit v1.2.3-70-g09d2 From 2b98b5484b942361ab5e271fe9e09d09dc83fa60 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 19 Jul 2019 00:51:15 -0400 Subject: more cleanup for templates --- src/client/views/collections/CollectionView.tsx | 6 +++--- .../collectionFreeForm/CollectionFreeFormView.tsx | 3 +-- src/client/views/nodes/WebBox.tsx | 16 ---------------- src/new_fields/Doc.ts | 14 +++++++++----- 4 files changed, 13 insertions(+), 26 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 1b85a0cdb..b6b690e26 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -2,7 +2,7 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faProjectDiagram, faSignature, faSquare, faTh, faImage, faThList, faTree } from '@fortawesome/free-solid-svg-icons'; import { observer } from "mobx-react"; import * as React from 'react'; -import { Doc, DocListCast } from '../../../new_fields/Doc'; +import { Doc, DocListCast, WidthSym, HeightSym } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils'; import { undoBatch } from '../../util/UndoManager'; @@ -61,8 +61,8 @@ export class CollectionView extends React.Component { ContextMenu.Instance.addItem({ description: "Apply Template", event: undoBatch(() => { let otherdoc = new Doc(); - otherdoc.width = 100; - otherdoc.height = 50; + otherdoc.width = this.props.Document[WidthSym](); + otherdoc.height = this.props.Document[HeightSym](); Doc.GetProto(otherdoc).title = this.props.Document.title + "(..." + CollectionView._applyCount++ + ")"; // previously "applied" Doc.GetProto(otherdoc).layout = Doc.MakeDelegate(this.props.Document); Doc.GetProto(otherdoc).miniLayout = StrCast(this.props.Document.miniLayout); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 58218e641..1997a6a4d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,4 +1,4 @@ -import { action, computed } from "mobx"; +import { action, computed, trace } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCastAsync, HeightSym, WidthSym, DocListCast } from "../../../../new_fields/Doc"; import { Id } from "../../../../new_fields/FieldSymbols"; @@ -516,7 +516,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { ] render() { const easing = () => this.props.Document.panTransformType === "Ease"; - Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey); return (
{ diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 9a4b817d3..7bf1dec53 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -10,6 +10,7 @@ import { RefField, FieldId } from "./RefField"; import { ToScriptString, SelfProxy, Parent, OnUpdate, Self, HandleUpdate, Update, Id } from "./FieldSymbols"; import { scriptingGlobal } from "../client/util/Scripting"; import { List } from "./List"; +import { string } from "prop-types"; export namespace Field { export function toKeyValueString(doc: Doc, key: string): string { @@ -344,20 +345,23 @@ export namespace Doc { // ... 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. - let expandedTemplateLayout = templateLayoutDoc["_expanded_" + dataDoc[Id]]; + let expandedTemplateLayout = dataDoc[templateLayoutDoc[Id]]; if (expandedTemplateLayout instanceof Doc) { return expandedTemplateLayout; } if (expandedTemplateLayout === undefined && BoolCast(templateLayoutDoc.isTemplate)) { setTimeout(() => { - templateLayoutDoc["_expanded_" + dataDoc[Id]] = Doc.MakeDelegate(templateLayoutDoc); - (templateLayoutDoc["_expanded_" + dataDoc[Id]] as Doc).title = templateLayoutDoc.title + "[" + StrCast(dataDoc.title).match(/\.\.\.[0-9]*/) + "]"; // previously: "applied to" - (templateLayoutDoc["_expanded_" + dataDoc[Id]] as Doc).isExpandedTemplate = templateLayoutDoc; + let expandedDoc = Doc.MakeDelegate(templateLayoutDoc); + expandedDoc.title = templateLayoutDoc.title + "[" + StrCast(dataDoc.title).match(/\.\.\.[0-9]*/) + "]"; + expandedDoc.isExpandedTemplate = templateLayoutDoc; + dataDoc[templateLayoutDoc[Id]] = expandedDoc; }, 0); } - return templateLayoutDoc; + 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 => { -- cgit v1.2.3-70-g09d2 From fe188e93f2f8d61356bfdc3216273f72ae074e10 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Mon, 22 Jul 2019 00:10:01 -0400 Subject: Added a bunch of REPL functionality --- src/client/util/Scripting.ts | 69 ++++++- src/client/views/OverlayView.scss | 25 ++- src/client/views/OverlayView.tsx | 52 ++++- src/client/views/ScriptingRepl.scss | 39 ++++ src/client/views/ScriptingRepl.tsx | 228 +++++++++++++++++++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 2 +- 7 files changed, 387 insertions(+), 30 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index 62c2cfe85..46dc320b0 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -1,5 +1,7 @@ -// import * as ts from "typescript" -let ts = (window as any).ts; +import * as ts from "typescript"; +export { ts }; +// export const ts = (window as any).ts; + // // @ts-ignore // import * as typescriptlib from '!!raw-loader!../../../node_modules/typescript/lib/lib.d.ts' // // @ts-ignore @@ -55,13 +57,35 @@ export namespace Scripting { } scriptingGlobals[n] = obj; } + + export function makeMutableGlobalsCopy(globals?: { [name: string]: any }) { + return { ..._scriptingGlobals, ...(globals || {}) }; + } + + export function setScriptingGlobals(globals: { [key: string]: any }) { + scriptingGlobals = globals; + } + + export function resetScriptingGlobals() { + scriptingGlobals = _scriptingGlobals; + } + + // const types = Object.keys(ts.SyntaxKind).map(kind => ts.SyntaxKind[kind]); + export function printNodeType(node: any, indentation = "") { + console.log(indentation + ts.SyntaxKind[node.kind]); + } + + export function getGlobals() { + return Object.keys(scriptingGlobals); + } } export function scriptingGlobal(constructor: { new(...args: any[]): any }) { Scripting.addGlobal(constructor); } -const scriptingGlobals: { [name: string]: any } = {}; +const _scriptingGlobals: { [name: string]: any } = {}; +let scriptingGlobals: { [name: string]: any } = _scriptingGlobals; function Run(script: string | undefined, customParams: string[], diagnostics: any[], originalScript: string, options: ScriptOptions): CompileResult { const errors = diagnostics.some(diag => diag.category === ts.DiagnosticCategory.Error); @@ -162,6 +186,8 @@ class ScriptingCompilerHost { } } +export type Traverser = (node: ts.Node, indentation: string) => boolean | void; +export type TraverserParam = Traverser | { onEnter: Traverser, onLeave: Traverser }; export interface ScriptOptions { requiredType?: string; addReturn?: boolean; @@ -169,10 +195,23 @@ export interface ScriptOptions { capturedVariables?: { [name: string]: Field }; typecheck?: boolean; editable?: boolean; + traverser?: TraverserParam; + transformer?: ts.TransformerFactory; + globals?: { [name: string]: any }; +} + +// function forEachNode(node:ts.Node, fn:(node:any) => void); +function forEachNode(node: ts.Node, onEnter: Traverser, onExit?: Traverser, indentation = "") { + return onEnter(node, indentation) || ts.forEachChild(node, (n: any) => { + forEachNode(n, onEnter, onExit, indentation + " "); + }) || (onExit && onExit(node, indentation)); } export function CompileScript(script: string, options: ScriptOptions = {}): CompileResult { const { requiredType = "", addReturn = false, params = {}, capturedVariables = {}, typecheck = true } = options; + if (options.globals) { + Scripting.setScriptingGlobals(options.globals); + } let host = new ScriptingCompilerHost; let paramNames: string[] = []; if ("this" in params || "this" in capturedVariables) { @@ -192,10 +231,27 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp paramList.push(`${key}: ${capturedVariables[key].constructor.name}`); } let paramString = paramList.join(", "); + if (options.traverser) { + const sourceFile = ts.createSourceFile('script.ts', script, ts.ScriptTarget.ES2015, true); + const onEnter = typeof options.traverser === "object" ? options.traverser.onEnter : options.traverser; + const onLeave = typeof options.traverser === "object" ? options.traverser.onLeave : undefined; + forEachNode(sourceFile, onEnter, onLeave); + } + if (options.transformer) { + const sourceFile = ts.createSourceFile('script.ts', script, ts.ScriptTarget.ES2015, true); + const result = ts.transform(sourceFile, [options.transformer]); + const transformed = result.transformed; + const printer = ts.createPrinter({ + newLine: ts.NewLineKind.LineFeed + }); + script = printer.printFile(transformed[0]); + result.dispose(); + } let funcScript = `(function(${paramString})${requiredType ? `: ${requiredType}` : ''} { ${addReturn ? `return ${script};` : script} })`; host.writeFile("file.ts", funcScript); + if (typecheck) host.writeFile('node_modules/typescript/lib/lib.d.ts', typescriptlib); let program = ts.createProgram(["file.ts"], {}, host); let testResult = program.emit(); @@ -203,7 +259,12 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp let diagnostics = ts.getPreEmitDiagnostics(program).concat(testResult.diagnostics); - return Run(outputText, paramNames, diagnostics, script, options); + const result = Run(outputText, paramNames, diagnostics, script, options); + + if (options.globals) { + Scripting.resetScriptingGlobals(); + } + return result; } Scripting.addGlobal(CompileScript); \ No newline at end of file diff --git a/src/client/views/OverlayView.scss b/src/client/views/OverlayView.scss index 9d0abc96d..4d1e8cf0b 100644 --- a/src/client/views/OverlayView.scss +++ b/src/client/views/OverlayView.scss @@ -1,21 +1,42 @@ .overlayWindow-outerDiv { - position: absolute; border-radius: 5px; overflow: hidden; display: flex; flex-direction: column; } +.overlayWindow-outerDiv, +.overlayView-wrapperDiv { + position: absolute; + z-index: 1; +} + .overlayWindow-titleBar { - height: 30px; + flex: 0 1 30px; background: darkslategray; color: whitesmoke; text-align: center; cursor: move; } +.overlayWindow-content { + flex: 1 1 auto; + display: flex; + flex-direction: column; +} + .overlayWindow-closeButton { float: right; height: 30px; width: 30px; +} + +.overlayWindow-resizeDragger { + background-color: red; + position: absolute; + right: 0px; + bottom: 0px; + width: 10px; + height: 10px; + cursor: nwse-resize; } \ No newline at end of file diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index 6b72abebf..2f2579057 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -25,16 +25,16 @@ export interface OverlayWindowProps { export class OverlayWindow extends React.Component { @observable x: number; @observable y: number; - @observable width?: number; - @observable height?: number; + @observable width: number; + @observable height: number; constructor(props: OverlayWindowProps) { super(props); const opts = props.overlayOptions; this.x = opts.x; this.y = opts.y; - this.width = opts.width; - this.height = opts.height; + this.width = opts.width || 200; + this.height = opts.height || 200; } onPointerDown = (_: React.PointerEvent) => { @@ -44,10 +44,27 @@ export class OverlayWindow extends React.Component { document.addEventListener("pointerup", this.onPointerUp); } + onResizerPointerDown = (_: React.PointerEvent) => { + document.removeEventListener("pointermove", this.onResizerPointerMove); + document.removeEventListener("pointerup", this.onResizerPointerUp); + document.addEventListener("pointermove", this.onResizerPointerMove); + document.addEventListener("pointerup", this.onResizerPointerUp); + } + @action onPointerMove = (e: PointerEvent) => { this.x += e.movementX; + this.x = Math.max(Math.min(this.x, window.innerWidth - this.width), 0); this.y += e.movementY; + this.y = Math.max(Math.min(this.y, window.innerHeight - this.height), 0); + } + + @action + onResizerPointerMove = (e: PointerEvent) => { + this.width += e.movementX; + this.width = Math.max(this.width, 30); + this.height += e.movementY; + this.height = Math.max(this.height, 30); } onPointerUp = (e: PointerEvent) => { @@ -55,6 +72,11 @@ export class OverlayWindow extends React.Component { document.removeEventListener("pointerup", this.onPointerUp); } + onResizerPointerUp = (e: PointerEvent) => { + document.removeEventListener("pointermove", this.onResizerPointerMove); + document.removeEventListener("pointerup", this.onResizerPointerUp); + } + render() { return (
@@ -62,7 +84,10 @@ export class OverlayWindow extends React.Component { {this.props.overlayOptions.title || "Untitled"}
- {this.props.children} +
+ {this.props.children} +
+
); } @@ -87,11 +112,26 @@ export class OverlayView extends React.Component { const index = this._elements.indexOf(ele); if (index !== -1) this._elements.splice(index, 1); }); - ele = {ele}; + ele =
{ele}
; this._elements.push(ele); return remove; } + @action + addWindow(contents: JSX.Element, options: OverlayElementOptions): OverlayDisposer { + const remove = action(() => { + const index = this._elements.indexOf(contents); + if (index !== -1) this._elements.splice(index, 1); + }); + contents = {contents}; + this._elements.push(contents); + return remove; + } + render() { return (
diff --git a/src/client/views/ScriptingRepl.scss b/src/client/views/ScriptingRepl.scss index 1eedb52fa..f1ef64193 100644 --- a/src/client/views/ScriptingRepl.scss +++ b/src/client/views/ScriptingRepl.scss @@ -1,5 +1,8 @@ .scriptingRepl-outerContainer { background-color: whitesmoke; + height: 100%; + display: flex; + flex-direction: column; } .scriptingRepl-resultContainer { @@ -8,4 +11,40 @@ .scriptingRepl-commandInput { width: 100%; +} + +.scriptingRepl-commandResult, +.scriptingRepl-commandString { + overflow-wrap: break-word; +} + +.scriptingRepl-commandsContainer { + flex: 1 1 auto; + overflow-y: scroll; +} + +.documentIcon-outerDiv { + background-color: white; + border-width: 1px; + border-style: solid; + border-radius: 25%; + padding: 2px; +} + +.scriptingObject-icon { + padding: 3px; + cursor: pointer; +} + +.scriptingObject-iconCollapsed { + padding-left: 4px; + padding-right: 5px; +} + +.scriptingObject-fields { + padding-left: 10px; +} + +.scriptingObject-leaf { + margin-left: 15px; } \ No newline at end of file diff --git a/src/client/views/ScriptingRepl.tsx b/src/client/views/ScriptingRepl.tsx index bd6fc9dfb..6eabc7b70 100644 --- a/src/client/views/ScriptingRepl.tsx +++ b/src/client/views/ScriptingRepl.tsx @@ -2,32 +2,193 @@ import * as React from 'react'; import { observer } from 'mobx-react'; import { observable, action } from 'mobx'; import './ScriptingRepl.scss'; -import { Scripting, CompileScript } from '../util/Scripting'; +import { Scripting, CompileScript, ts } from '../util/Scripting'; +import { DocumentManager } from '../util/DocumentManager'; +import { DocumentView } from './nodes/DocumentView'; +import { OverlayView } from './OverlayView'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { library } from '@fortawesome/fontawesome-svg-core'; +import { faCaretDown, faCaretRight } from '@fortawesome/free-solid-svg-icons'; + +library.add(faCaretDown); +library.add(faCaretRight); + +@observer +export class DocumentIcon extends React.Component<{ view: DocumentView, index: number }> { + render() { + this.props.view.props.ScreenToLocalTransform(); + this.props.view.props.Document.width; + this.props.view.props.Document.height; + const screenCoords = this.props.view.screenRect(); + + return ( +
+

${this.props.index}

+
+ ); + } +} + +@observer +export class DocumentIconContainer extends React.Component { + render() { + return DocumentManager.Instance.DocumentViews.map((dv, i) => ); + } +} + +@observer +export class ScriptingObjectDisplay extends React.Component<{ scrollToBottom: () => void, value: { [key: string]: any }, name?: string }> { + @observable collapsed = true; + + @action + toggle = () => { + this.collapsed = !this.collapsed; + this.props.scrollToBottom(); + } + + render() { + const val = this.props.value; + const proto = Object.getPrototypeOf(val); + const name = (proto && proto.constructor && proto.constructor.name) || String(val); + const title = this.props.name ? <>{this.props.name} : {name} : name; + if (this.collapsed) { + return ( +
+ {title} (+{Object.keys(val).length}) +
+ ); + } else { + return ( +
+
+ {title} +
+
+ {Object.keys(val).map(key => )} +
+
+ ); + } + } +} + +@observer +export class ScriptingValueDisplay extends React.Component<{ scrollToBottom: () => void, value: any, name?: string }> { + render() { + const val = this.props.name ? this.props.value[this.props.name] : this.props.value; + if (typeof val === "object") { + return ; + } else if (typeof val === "function") { + const name = "[Function]"; + const title = this.props.name ? <>{this.props.name} : {name} : name; + return
{title}
; + } else { + const name = String(val); + const title = this.props.name ? <>{this.props.name} : {name} : name; + return
{title}
; + } + } +} @observer export class ScriptingRepl extends React.Component { @observable private commands: { command: string, result: any }[] = []; @observable private commandString: string = ""; + private commandBuffer: string = ""; + + @observable private historyIndex: number = -1; + + private commandsRef = React.createRef(); private args: any = {}; + getTransformer: ts.TransformerFactory = context => { + const knownVars: { [name: string]: number } = {}; + const usedDocuments: number[] = []; + Scripting.getGlobals().forEach(global => knownVars[global] = 1); + 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 && !(node.text in knownVars) && !(node.text in globalThis)) { + const match = node.text.match(/\$([0-9]+)/); + if (match) { + const m = parseInt(match[1]); + usedDocuments.push(m); + } else { + return ts.createPropertyAccess(ts.createIdentifier("args"), node); + } + } + } + + return node; + } + return ts.visitNode(root, visit); + }; + } + @action onKeyDown = (e: React.KeyboardEvent) => { - if (e.key === "Enter") { - e.stopPropagation(); + let stopProp = true; + switch (e.key) { + case "Enter": { + const docGlobals: { [name: string]: any } = {}; + DocumentManager.Instance.DocumentViews.forEach((dv, i) => docGlobals[`$${i}`] = dv.props.Document); + const globals = Scripting.makeMutableGlobalsCopy(docGlobals); + const script = CompileScript(this.commandString, { typecheck: false, addReturn: true, editable: true, params: { args: "any" }, transformer: this.getTransformer, globals }); + if (!script.compiled) { + return; + } + const result = script.run({ args: this.args }); + if (!result.success) { + return; + } + this.commands.push({ command: this.commandString, result: result.result }); + + this.maybeScrollToBottom(); - const script = CompileScript(this.commandString, { typecheck: false, addReturn: true, editable: true, params: { args: "any" } }); - if (!script.compiled) { - return; + this.commandString = ""; + this.commandBuffer = ""; + this.historyIndex = -1; + break; } - const result = script.run({ args: this.args }); - if (!result.success) { - return; + case "ArrowUp": { + if (this.historyIndex < this.commands.length - 1) { + this.historyIndex++; + if (this.historyIndex === 0) { + this.commandBuffer = this.commandString; + } + this.commandString = this.commands[this.commands.length - 1 - this.historyIndex].command; + } + break; } - this.commands.push({ command: this.commandString, result: result.result }); + case "ArrowDown": { + if (this.historyIndex >= 0) { + this.historyIndex--; + if (this.historyIndex === -1) { + this.commandString = this.commandBuffer; + this.commandBuffer = ""; + } else { + this.commandString = this.commands[this.commands.length - 1 - this.historyIndex].command; + } + } + break; + } + default: + stopProp = false; + break; + } - this.commandString = ""; + if (stopProp) { + e.stopPropagation(); + e.preventDefault(); } } @@ -36,21 +197,56 @@ export class ScriptingRepl extends React.Component { this.commandString = e.target.value; } + private shouldScroll: boolean = false; + private maybeScrollToBottom = () => { + const ele = this.commandsRef.current; + if (ele && ele.scrollTop === (ele.scrollHeight - ele.offsetHeight)) { + this.shouldScroll = true; + this.forceUpdate(); + } + } + + private scrollToBottom() { + const ele = this.commandsRef.current; + ele && ele.scroll({ behavior: "auto", top: ele.scrollHeight }); + } + + componentDidUpdate() { + if (this.shouldScroll) { + this.shouldScroll = false; + this.scrollToBottom(); + } + } + + overlayDisposer?: () => void; + onFocus = () => { + if (this.overlayDisposer) { + this.overlayDisposer(); + } + this.overlayDisposer = OverlayView.Instance.addElement(, { x: 0, y: 0 }); + } + + onBlur = () => { + this.overlayDisposer && this.overlayDisposer(); + } + render() { return (
-
- {this.commands.map(({ command, result }) => { +
+ {this.commands.map(({ command, result }, i) => { return ( -
-
{command}
-
{String(result)}
+
+
{command ||
}
+
{}
); })}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 58218e641..652aca8f3 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -502,7 +502,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { overlayDisposer(); setTimeout(() => docs.map(d => d.transition = undefined), 1200); }} />; - overlayDisposer = OverlayView.Instance.addElement(scriptingBox, options); + overlayDisposer = OverlayView.Instance.addWindow(scriptingBox, options); }; addOverlay("arrangeInit", { x: 400, y: 100, width: 400, height: 300 }, { collection: "Doc", docs: "Doc[]" }, undefined); addOverlay("arrangeScript", { x: 400, y: 500, width: 400, height: 300 }, { doc: "Doc", index: "number", collection: "Doc", state: "any", docs: "Doc[]" }, "{x: number, y: number, width?: number, height?: number}"); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 52ba643e0..7ff095573 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -557,7 +557,7 @@ export class DocumentView extends DocComponent(Docu this.props.addDocTab && this.props.addDocTab(Docs.Create.SchemaDocument(["title"], aliases, {}), undefined, "onRight"); // bcz: dataDoc? }, icon: "search" }); - cm.addItem({ description: "Add Repl", event: () => OverlayView.Instance.addElement(, { x: 100, y: 100 }) }); + cm.addItem({ description: "Add Repl", event: () => OverlayView.Instance.addWindow(, { x: 300, y: 100, width: 200, height: 200 }) }); 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" }); cm.addItem({ description: "Copy URL", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "link" }); -- cgit v1.2.3-70-g09d2 From 33744cb0068d7dc470d0b39ca70344f256a9cad9 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Mon, 22 Jul 2019 00:12:31 -0400 Subject: Added titles to overlay windows --- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 4 ++-- src/client/views/nodes/DocumentView.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 652aca8f3..703873681 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -504,8 +504,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { }} />; overlayDisposer = OverlayView.Instance.addWindow(scriptingBox, options); }; - addOverlay("arrangeInit", { x: 400, y: 100, width: 400, height: 300 }, { collection: "Doc", docs: "Doc[]" }, undefined); - addOverlay("arrangeScript", { x: 400, y: 500, width: 400, height: 300 }, { doc: "Doc", index: "number", collection: "Doc", state: "any", docs: "Doc[]" }, "{x: number, y: number, width?: number, height?: number}"); + 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}"); } }); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 7ff095573..1c03bfc81 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -557,7 +557,7 @@ export class DocumentView extends DocComponent(Docu this.props.addDocTab && this.props.addDocTab(Docs.Create.SchemaDocument(["title"], aliases, {}), undefined, "onRight"); // bcz: dataDoc? }, icon: "search" }); - cm.addItem({ description: "Add Repl", event: () => OverlayView.Instance.addWindow(, { x: 300, y: 100, width: 200, height: 200 }) }); + 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" }); cm.addItem({ description: "Copy URL", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "link" }); -- cgit v1.2.3-70-g09d2 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 --- src/client/util/Scripting.ts | 50 +++++++----- src/client/views/OverlayView.scss | 2 +- src/client/views/ScriptingRepl.tsx | 59 ++++++++------ .../views/collections/CollectionSchemaView.tsx | 92 ++++++++++++++++++++-- src/client/views/collections/CollectionView.tsx | 9 ++- .../collectionFreeForm/CollectionFreeFormView.tsx | 63 +++++++-------- src/client/views/nodes/DocumentView.tsx | 23 +++--- 7 files changed, 201 insertions(+), 97 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index 46dc320b0..1d0916ac0 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -52,10 +52,10 @@ export namespace Scripting { } else { throw new Error("Must either register an object with a name, or give a name and an object"); } - if (scriptingGlobals.hasOwnProperty(n)) { + if (_scriptingGlobals.hasOwnProperty(n)) { throw new Error(`Global with name ${n} is already registered, choose another name`); } - scriptingGlobals[n] = obj; + _scriptingGlobals[n] = obj; } export function makeMutableGlobalsCopy(globals?: { [name: string]: any }) { @@ -188,6 +188,10 @@ class ScriptingCompilerHost { export type Traverser = (node: ts.Node, indentation: string) => boolean | void; export type TraverserParam = Traverser | { onEnter: Traverser, onLeave: Traverser }; +export type Transformer = { + transformer: ts.TransformerFactory, + getVars?: () => { capturedVariables: { [name: string]: Field } } +}; export interface ScriptOptions { requiredType?: string; addReturn?: boolean; @@ -196,7 +200,7 @@ export interface ScriptOptions { typecheck?: boolean; editable?: boolean; traverser?: TraverserParam; - transformer?: ts.TransformerFactory; + transformer?: Transformer; globals?: { [name: string]: any }; } @@ -213,6 +217,27 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp Scripting.setScriptingGlobals(options.globals); } let host = new ScriptingCompilerHost; + if (options.traverser) { + const sourceFile = ts.createSourceFile('script.ts', script, ts.ScriptTarget.ES2015, true); + const onEnter = typeof options.traverser === "object" ? options.traverser.onEnter : options.traverser; + const onLeave = typeof options.traverser === "object" ? options.traverser.onLeave : undefined; + forEachNode(sourceFile, onEnter, onLeave); + } + if (options.transformer) { + const sourceFile = ts.createSourceFile('script.ts', script, ts.ScriptTarget.ES2015, true); + const result = ts.transform(sourceFile, [options.transformer.transformer]); + if (options.transformer.getVars) { + const newCaptures = options.transformer.getVars(); + // tslint:disable-next-line: prefer-object-spread + options.capturedVariables = Object.assign(capturedVariables, newCaptures.capturedVariables) as any; + } + const transformed = result.transformed; + const printer = ts.createPrinter({ + newLine: ts.NewLineKind.LineFeed + }); + script = printer.printFile(transformed[0]); + result.dispose(); + } let paramNames: string[] = []; if ("this" in params || "this" in capturedVariables) { paramNames.push("this"); @@ -227,26 +252,11 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp }); for (const key in capturedVariables) { if (key === "this") continue; + const val = capturedVariables[key]; paramNames.push(key); - paramList.push(`${key}: ${capturedVariables[key].constructor.name}`); + paramList.push(`${key}: ${typeof val === "object" ? Object.getPrototypeOf(val).constructor.name : typeof val}`); } let paramString = paramList.join(", "); - if (options.traverser) { - const sourceFile = ts.createSourceFile('script.ts', script, ts.ScriptTarget.ES2015, true); - const onEnter = typeof options.traverser === "object" ? options.traverser.onEnter : options.traverser; - const onLeave = typeof options.traverser === "object" ? options.traverser.onLeave : undefined; - forEachNode(sourceFile, onEnter, onLeave); - } - if (options.transformer) { - const sourceFile = ts.createSourceFile('script.ts', script, ts.ScriptTarget.ES2015, true); - const result = ts.transform(sourceFile, [options.transformer]); - const transformed = result.transformed; - const printer = ts.createPrinter({ - newLine: ts.NewLineKind.LineFeed - }); - script = printer.printFile(transformed[0]); - result.dispose(); - } let funcScript = `(function(${paramString})${requiredType ? `: ${requiredType}` : ''} { ${addReturn ? `return ${script};` : script} })`; diff --git a/src/client/views/OverlayView.scss b/src/client/views/OverlayView.scss index 4d1e8cf0b..dc122497f 100644 --- a/src/client/views/OverlayView.scss +++ b/src/client/views/OverlayView.scss @@ -32,7 +32,7 @@ } .overlayWindow-resizeDragger { - background-color: red; + background-color: rgb(0, 0, 0); position: absolute; right: 0px; bottom: 0px; diff --git a/src/client/views/ScriptingRepl.tsx b/src/client/views/ScriptingRepl.tsx index 6eabc7b70..52273c357 100644 --- a/src/client/views/ScriptingRepl.tsx +++ b/src/client/views/ScriptingRepl.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { observer } from 'mobx-react'; import { observable, action } from 'mobx'; import './ScriptingRepl.scss'; -import { Scripting, CompileScript, ts } from '../util/Scripting'; +import { Scripting, CompileScript, ts, Transformer } from '../util/Scripting'; import { DocumentManager } from '../util/DocumentManager'; import { DocumentView } from './nodes/DocumentView'; import { OverlayView } from './OverlayView'; @@ -17,8 +17,11 @@ library.add(faCaretRight); export class DocumentIcon extends React.Component<{ view: DocumentView, index: number }> { render() { this.props.view.props.ScreenToLocalTransform(); - this.props.view.props.Document.width; - this.props.view.props.Document.height; + const doc = this.props.view.props.Document; + doc.width; + doc.height; + doc.x; + doc.y; const screenCoords = this.props.view.screenRect(); return ( @@ -26,7 +29,7 @@ export class DocumentIcon extends React.Component<{ view: DocumentView, index: n position: "absolute", transform: `translate(${screenCoords.left + screenCoords.width / 2}px, ${screenCoords.top}px)`, }}> -

${this.props.index}

+

${this.props.index}

); } @@ -106,31 +109,35 @@ export class ScriptingRepl extends React.Component { private args: any = {}; - getTransformer: ts.TransformerFactory = context => { - const knownVars: { [name: string]: number } = {}; - const usedDocuments: number[] = []; - Scripting.getGlobals().forEach(global => knownVars[global] = 1); - return root => { - function visit(node: ts.Node) { - node = ts.visitEachChild(node, visit, context); + getTransformer = (): Transformer => { + return { + transformer: context => { + const knownVars: { [name: string]: number } = {}; + const usedDocuments: number[] = []; + Scripting.getGlobals().forEach(global => knownVars[global] = 1); + 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 && !(node.text in knownVars) && !(node.text in globalThis)) { - const match = node.text.match(/\$([0-9]+)/); - if (match) { - const m = parseInt(match[1]); - usedDocuments.push(m); - } else { - return ts.createPropertyAccess(ts.createIdentifier("args"), node); + 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 && !(node.text in knownVars) && !(node.text in globalThis)) { + const match = node.text.match(/\$([0-9]+)/); + if (match) { + const m = parseInt(match[1]); + usedDocuments.push(m); + } else { + return ts.createPropertyAccess(ts.createIdentifier("args"), node); + } + } } - } - } - return node; + return node; + } + return ts.visitNode(root, visit); + }; } - return ts.visitNode(root, visit); }; } @@ -142,7 +149,7 @@ export class ScriptingRepl extends React.Component { const docGlobals: { [name: string]: any } = {}; DocumentManager.Instance.DocumentViews.forEach((dv, i) => docGlobals[`$${i}`] = dv.props.Document); const globals = Scripting.makeMutableGlobalsCopy(docGlobals); - const script = CompileScript(this.commandString, { typecheck: false, addReturn: true, editable: true, params: { args: "any" }, transformer: this.getTransformer, globals }); + const script = CompileScript(this.commandString, { typecheck: false, addReturn: true, editable: true, params: { args: "any" }, transformer: this.getTransformer(), globals }); if (!script.compiled) { return; } 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"; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 1c03bfc81..7280dbcf6 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -37,6 +37,7 @@ import { RouteStore } from '../../../server/RouteStore'; import { FormattedTextBox } from './FormattedTextBox'; import { OverlayView } from '../OverlayView'; import { ScriptingRepl } from '../ScriptingRepl'; +import { ClientUtils } from '../../util/ClientUtils'; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? library.add(fa.faTrash); @@ -58,6 +59,7 @@ library.add(fa.faCrosshairs); library.add(fa.faDesktop); library.add(fa.faUnlock); library.add(fa.faLock); +library.add(fa.faLaptopCode); // const linkSchema = createSchema({ @@ -551,17 +553,18 @@ export class DocumentView extends DocComponent(Docu this.props.removeDocument && this.props.removeDocument(this.props.Document); }, icon: "window-restore" }); - cm.addItem({ - description: "Find aliases", event: async () => { - const aliases = await SearchUtil.GetAliasesOfDocument(this.props.Document); - this.props.addDocTab && this.props.addDocTab(Docs.Create.SchemaDocument(["title"], aliases, {}), undefined, "onRight"); // bcz: dataDoc? - }, icon: "search" - }); - 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: "Find aliases", event: async () => { + // const aliases = await SearchUtil.GetAliasesOfDocument(this.props.Document); + // this.props.addDocTab && this.props.addDocTab(Docs.Create.SchemaDocument(["title"], aliases, {}), undefined, "onRight"); // bcz: dataDoc? + // }, icon: "search" + // }); + 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: "Zoom to Document", event: () => this.props.focus(this.props.Document, true), icon: "search" }); - cm.addItem({ description: "Copy URL", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "link" }); - cm.addItem({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document[Id]), icon: "fingerprint" }); + if (!ClientUtils.RELEASE) { + cm.addItem({ description: "Copy URL", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "link" }); + cm.addItem({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document[Id]), icon: "fingerprint" }); + } cm.addItem({ description: "Delete", event: this.deleteClicked, icon: "trash" }); type User = { email: string, userDocumentId: string }; let usersMenu: ContextMenuProps[] = []; -- 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/collectionFreeForm/CollectionFreeFormView.tsx') 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/collectionFreeForm/CollectionFreeFormView.tsx') 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/collectionFreeForm/CollectionFreeFormView.tsx') 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 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/collectionFreeForm/CollectionFreeFormView.tsx') 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/collectionFreeForm/CollectionFreeFormView.tsx') 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/collectionFreeForm/CollectionFreeFormView.tsx') 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/collectionFreeForm/CollectionFreeFormView.tsx') 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/collectionFreeForm/CollectionFreeFormView.tsx') 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/collectionFreeForm/CollectionFreeFormView.tsx') 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 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/collectionFreeForm/CollectionFreeFormView.tsx') 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 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/collectionFreeForm/CollectionFreeFormView.tsx') 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/collectionFreeForm/CollectionFreeFormView.tsx') 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 5b70c7f5562821231665c4058d788bbf3b41ff04 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Fri, 26 Jul 2019 17:46:11 -0400 Subject: Changed render depth for overlay and background layouts --- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/ImageBox.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 6500b3273..c711b5204 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -400,7 +400,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { removeDocument: this.props.removeDocument, moveDocument: this.props.moveDocument, ScreenToLocalTransform: this.getTransform, - renderDepth: this.props.renderDepth + 1, + renderDepth: this.props.renderDepth, selectOnLoad: layoutDoc[Id] === this._selectOnLoaded, PanelWidth: layoutDoc[WidthSym], PanelHeight: layoutDoc[HeightSym], diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 6541007d0..c691754ea 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -248,7 +248,7 @@ export class ImageBox extends DocComponent(ImageD return url.href; } let ext = path.extname(url.href); - const suffix = this.props.renderDepth <= 1 ? "_o" : this._curSuffix; + const suffix = this.props.renderDepth < 1 ? "_o" : this._curSuffix; return url.href.replace(ext, suffix + ext); } -- cgit v1.2.3-70-g09d2 From cc806ebbe8d48d1c5a4c3c49231a5d38d6f39943 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 27 Jul 2019 04:12:07 -0400 Subject: fixed a bunch of template and stacking view issues among others. --- src/client/views/DocumentDecorations.tsx | 4 +- src/client/views/GlobalKeyHandler.ts | 1 + src/client/views/MainView.tsx | 2 +- .../views/collections/CollectionDockingView.tsx | 39 +++++-------- .../views/collections/CollectionSchemaView.tsx | 2 +- .../views/collections/CollectionStackingView.tsx | 68 +++++++++------------- src/client/views/collections/CollectionView.tsx | 2 - .../collectionFreeForm/CollectionFreeFormView.tsx | 22 +++---- .../views/nodes/CollectionFreeFormDocumentView.tsx | 16 +---- src/client/views/nodes/DocumentView.tsx | 14 +++-- src/new_fields/Doc.ts | 13 ++++- 11 files changed, 75 insertions(+), 108 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 255855b45..c08a84742 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -533,7 +533,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> proto.nativeHeight = nheight = doc.height || 0; proto.ignoreAspect = true; } - if (nwidth > 0 && nheight > 0) { + if (nwidth > 0 && nheight > 0 && !BoolCast(proto.ignoreAspect)) { if (Math.abs(dW) > Math.abs(dH)) { if (!fixedAspect) { Doc.SetInPlace(element.props.Document, "nativeWidth", actualdW / (doc.width || 1) * (doc.nativeWidth || 0), true); @@ -553,7 +553,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> } else { dW && (doc.width = actualdW); dH && (doc.height = actualdH); - Doc.SetInPlace(element.props.Document, "autoHeight", undefined, true); + dH && Doc.SetInPlace(element.props.Document, "autoHeight", undefined, true); } } }); diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 7477c5b4f..5050f34ab 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -67,6 +67,7 @@ export default class KeyManager { } } MainView.Instance.toggleColorPicker(true); + SelectionManager.DeselectAll(); break; case "delete": case "backspace": diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 61a013963..bfb50bc75 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -407,7 +407,7 @@ export class MainView extends React.Component {
    )}
  • -
  • diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 5d61aefc7..790c312fa 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -13,7 +13,7 @@ import { SelectionManager } from "../../../util/SelectionManager"; import { Transform } from "../../../util/Transform"; import { undoBatch, UndoManager } from "../../../util/UndoManager"; import { COLLECTION_BORDER_WIDTH } from "../../../views/globalCssVariables.scss"; -import { ContextMenu } from "../../ContextMenu"; +import { SubmenuProps, ContextMenuProps } from "../../ContextMenuItem"; import { InkingCanvas } from "../../InkingCanvas"; import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView"; import { DocumentContentsView } from "../../nodes/DocumentContentsView"; @@ -34,11 +34,12 @@ 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, faExpandArrowsAlt, faCompressArrowsAlt } from "@fortawesome/free-solid-svg-icons"; +import { faTable, faPaintBrush, faAsterisk, faExpandArrowsAlt, faCompressArrowsAlt, faCompass } from "@fortawesome/free-solid-svg-icons"; import { undo } from "prosemirror-history"; import { number } from "prop-types"; +import { ContextMenu } from "../../ContextMenu"; -library.add(faEye, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt); +library.add(faEye, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass); export const panZoomSchema = createSchema({ panX: "number", @@ -498,12 +499,13 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } onContextMenu = () => { - ContextMenu.Instance.addItem({ + let layoutItems: ContextMenuProps[] = []; + layoutItems.push({ 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({ + layoutItems.push({ description: "Arrange contents in grid", icon: "table", event: async () => { @@ -530,6 +532,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { }, "arrange contents"); } }); + ContextMenu.Instance.addItem({ description: "Layout...", subitems: layoutItems, icon: "compass" }); ContextMenu.Instance.addItem({ description: "Analyze Strokes", event: async () => { let data = Cast(this.fieldExtensionDoc[this.inkKey], InkField); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index da72f212c..392faf3e9 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -61,8 +61,7 @@ library.add(fa.faCrosshairs); library.add(fa.faDesktop); library.add(fa.faUnlock); library.add(fa.faLock); -library.add(fa.faLaptopCode, fa.faMale, fa.faCopy, faHandPointRight); - +library.add(fa.faLaptopCode, fa.faMale, fa.faCopy, fa.faHandPointRight, fa.faCompass, fa.faSnowflake); // const linkSchema = createSchema({ // title: "string", @@ -547,7 +546,7 @@ export class DocumentView extends DocComponent(Docu subitems.push({ description: "Open Right Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.dataDoc, "onRight"), icon: "caret-square-right" }); subitems.push({ description: "Open Fields", event: this.fieldsClicked, icon: "layer-group" }); cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" }); - 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: BoolCast(this.props.Document.ignoreAspect, false) || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "snowflake" }); cm.addItem({ description: "Pin to Presentation", event: () => PresentationView.Instance.PinDoc(this.props.Document), icon: "map-pin" }); cm.addItem({ description: BoolCast(this.props.Document.lockedPosition) ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); let makes: ContextMenuProps[] = []; @@ -575,8 +574,11 @@ export class DocumentView 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: "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" }); + let existing = ContextMenu.Instance.findByDescription("Layout..."); + let layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : []; + layoutItems.push({ description: "Center View", event: () => this.props.focus(this.props.Document, false), icon: "crosshairs" }); + layoutItems.push({ description: "Zoom to Document", event: () => this.props.focus(this.props.Document, true), icon: "search" }); + !existing && cm.addItem({ description: "Layout...", subitems: layoutItems, icon: "compass" }); if (!ClientUtils.RELEASE) { let copies: ContextMenuProps[] = []; copies.push({ description: "Copy URL", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "link" }); -- cgit v1.2.3-70-g09d2 From c360dc0adb468ae3aaa1c2d943606993d01a5a52 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 27 Jul 2019 11:08:44 -0400 Subject: fixed handling of keyboard events to avoid triggering global key events. made BoolCast default to false. fixed templating to deal with conflict between field fields and template fields --- src/client/util/DocumentManager.ts | 16 +++++++-------- src/client/views/EditableView.tsx | 3 +++ src/client/views/MainView.tsx | 1 - src/client/views/MetadataEntryMenu.tsx | 1 + .../views/collections/CollectionSchemaView.tsx | 2 +- .../views/collections/CollectionStackingView.tsx | 3 ++- .../CollectionFreeFormLinkView.tsx | 8 ++++---- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 8 ++++---- src/client/views/nodes/FormattedTextBox.tsx | 4 ++-- src/client/views/nodes/IconBox.tsx | 2 +- src/client/views/nodes/KeyValueBox.tsx | 3 ++- src/client/views/nodes/LinkMenuItem.tsx | 2 +- src/client/views/pdf/Page.tsx | 22 ++++++++------------ .../views/presentationview/PresentationElement.tsx | 24 ++++++++++------------ .../views/presentationview/PresentationView.tsx | 2 +- src/new_fields/Doc.ts | 19 +++++++++-------- src/new_fields/Types.ts | 2 +- 18 files changed, 62 insertions(+), 62 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 262194a40..32f728c71 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,16 +1,14 @@ -import { computed, observable, action } from 'mobx'; -import { DocumentView } from '../views/nodes/DocumentView'; -import { Doc, DocListCast, Opt } from '../../new_fields/Doc'; -import { FieldValue, Cast, NumCast, BoolCast, StrCast } from '../../new_fields/Types'; -import { listSpec } from '../../new_fields/Schema'; -import { undoBatch, UndoManager } from './UndoManager'; +import { action, computed, observable } from 'mobx'; +import { Doc } from '../../new_fields/Doc'; +import { Id } from '../../new_fields/FieldSymbols'; +import { BoolCast, Cast, NumCast } from '../../new_fields/Types'; import { CollectionDockingView } from '../views/collections/CollectionDockingView'; -import { CollectionView } from '../views/collections/CollectionView'; import { CollectionPDFView } from '../views/collections/CollectionPDFView'; import { CollectionVideoView } from '../views/collections/CollectionVideoView'; -import { Id } from '../../new_fields/FieldSymbols'; +import { CollectionView } from '../views/collections/CollectionView'; +import { DocumentView } from '../views/nodes/DocumentView'; import { LinkManager } from './LinkManager'; -import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; +import { undoBatch, UndoManager } from './UndoManager'; export class DocumentManager { diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index c66a92f48..7cabebddd 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -50,8 +50,10 @@ export class EditableView extends React.Component { @action onKeyDown = (e: React.KeyboardEvent) => { if (e.key === "Tab") { + e.stopPropagation(); this.props.OnTab && this.props.OnTab(); } else if (e.key === "Enter") { + e.stopPropagation(); if (!e.ctrlKey) { if (this.props.SetValue(e.currentTarget.value, e.shiftKey)) { this._editing = false; @@ -61,6 +63,7 @@ export class EditableView extends React.Component { this._editing = false; } } else if (e.key === "Escape") { + e.stopPropagation(); this._editing = false; } } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index bfb50bc75..8b7eb8856 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -7,7 +7,6 @@ import "normalize.css"; import * as React from 'react'; import { SketchPicker } from 'react-color'; import Measure from 'react-measure'; -import * as request from 'request'; import { Doc, DocListCast, Opt, HeightSym } from '../../new_fields/Doc'; import { Id } from '../../new_fields/FieldSymbols'; import { InkTool } from '../../new_fields/InkField'; diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx index bd5a307b3..c50e02f0d 100644 --- a/src/client/views/MetadataEntryMenu.tsx +++ b/src/client/views/MetadataEntryMenu.tsx @@ -76,6 +76,7 @@ export class MetadataEntryMenu extends React.Component{ onValueKeyDown = async (e: React.KeyboardEvent) => { if (e.key === "Enter") { + e.stopPropagation(); const script = KeyValueBox.CompileKVPScript(this._currentValue); if (!script) return; let doc = this.props.docs; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 8ce3b40e5..0f67c47aa 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -11,7 +11,7 @@ import { Doc, DocListCast, DocListCastAsync, Field } from "../../../new_fields/D import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; import { listSpec } from "../../../new_fields/Schema"; -import { Cast, FieldValue, NumCast, StrCast, BoolCast } from "../../../new_fields/Types"; +import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types"; import { Docs } from "../../documents/Documents"; import { Gateway } from "../../northstar/manager/Gateway"; import { SetupDrag, DragManager } from "../../util/DragManager"; diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 3da13bff5..d5a7b90da 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -53,7 +53,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { let xhgt = height + this.getDocHeight(d) + (i === this.filteredChildren.length - 1 ? this.yMargin : this.gridGap); return xhgt; }, this.yMargin); - this.layoutDoc.height = hgt * (this.props as any).ContentScaling(); + (this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc) + .height = hgt * (this.props as any).ContentScaling(); } }, { fireImmediately: true }); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index b546d1b78..6af87b138 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -21,10 +21,10 @@ export class CollectionFreeFormLinkView extends React.Component { // let width = l[WidthSym](); // l.x = (x1 + x2) / 2 - width / 2; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index f83d02271..eaad43c10 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -471,7 +471,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { if (!(doc instanceof Doc)) return prev; var page = NumCast(doc.page, -1); if ((Math.abs(Math.round(page) - Math.round(curPage)) < 3) || page === -1) { - let minim = BoolCast(doc.isMinimized, false); + let minim = BoolCast(doc.isMinimized); if (minim === undefined || !minim) { const pos = script ? this.getCalculatedPositions(script, { doc, index: prev.length, collection: this.Document, docs, state }) : {}; state = pos.state === undefined ? state : pos.state; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 3152d6a2a..bc2af2ae8 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -275,7 +275,7 @@ export class DocumentView extends DocComponent(Docu let iconAnimating = Cast(maximizedDoc.isIconAnimating, List); if (!iconAnimating || (Date.now() - iconAnimating[2] > 1000)) { if (isMinimized === undefined) { - isMinimized = BoolCast(maximizedDoc.isMinimized, false); + isMinimized = BoolCast(maximizedDoc.isMinimized); } maximizedDoc.willMaximize = isMinimized; maximizedDoc.isMinimized = false; @@ -308,7 +308,7 @@ export class DocumentView extends DocComponent(Docu Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) { SelectionManager.SelectDoc(this, e.ctrlKey); let isExpander = (e.target as any).id === "isExpander"; - if (BoolCast(this.props.Document.isButton, false) || isExpander) { + if (BoolCast(this.props.Document.isButton) || isExpander) { SelectionManager.DeselectAll(); let subBulletDocs = await DocListCastAsync(this.props.Document.subBulletDocs); let maximizedDocs = await DocListCastAsync(this.props.Document.maximizedDocs); @@ -326,7 +326,7 @@ export class DocumentView extends DocComponent(Docu maxLocation = this.props.Document.maximizeLocation = (ctrlKey ? maxLocation : (maxLocation === "inPlace" || !maxLocation ? "inTab" : "inPlace")); if (!maxLocation || maxLocation === "inPlace") { let hadView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedDocs[0], this.props.ContainingCollectionView); - let wasMinimized = !hadView && expandedDocs.reduce((min, d) => !min && !BoolCast(d.IsMinimized, false), false); + let wasMinimized = !hadView && expandedDocs.reduce((min, d) => !min && !BoolCast(d.IsMinimized), false); expandedDocs.forEach(maxDoc => Doc.GetProto(maxDoc).isMinimized = false); let hasView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedDocs[0], this.props.ContainingCollectionView); if (!hasView) { @@ -409,7 +409,7 @@ export class DocumentView extends DocComponent(Docu @undoBatch makeBtnClicked = (): void => { let doc = Doc.GetProto(this.props.Document); - doc.isButton = !BoolCast(doc.isButton, false); + doc.isButton = !BoolCast(doc.isButton); if (doc.isButton) { if (!doc.nativeWidth) { doc.nativeWidth = this.props.Document[WidthSym](); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 0a79677e2..bd0fd8301 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -459,8 +459,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe specificContextMenu = (e: React.MouseEvent): void => { let subitems: ContextMenuProps[] = []; subitems.push({ - description: BoolCast(this.props.Document.autoHeight, false) ? "Manual Height" : "Auto Height", - event: action(() => Doc.GetProto(this.props.Document).autoHeight = !BoolCast(this.props.Document.autoHeight, false)), icon: "expand-arrows-alt" + description: BoolCast(this.props.Document.autoHeight) ? "Manual Height" : "Auto Height", + event: action(() => Doc.GetProto(this.props.Document).autoHeight = !BoolCast(this.props.Document.autoHeight)), icon: "expand-arrows-alt" }); ContextMenu.Instance.addItem({ description: "Text Funcs...", subitems: subitems }); } diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx index d6ab2a34a..6f50033a4 100644 --- a/src/client/views/nodes/IconBox.tsx +++ b/src/client/views/nodes/IconBox.tsx @@ -64,7 +64,7 @@ export class IconBox extends React.Component { let hideLabel = BoolCast(this.props.Document.hideLabel); let maxDocs = DocListCast(this.props.Document.maximizedDocs); let firstDoc = maxDocs.length ? maxDocs[0] : undefined; - let label = hideLabel ? "" : (firstDoc && labelField && !BoolCast(this.props.Document.useOwnTitle, false) ? firstDoc[labelField] : this.props.Document.title); + let label = hideLabel ? "" : (firstDoc && labelField && !BoolCast(this.props.Document.useOwnTitle) ? firstDoc[labelField] : this.props.Document.title); return (
    {this.minimizedIcon} diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 9fc0f2080..77824b4ff 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -46,6 +46,7 @@ export class KeyValueBox extends React.Component { @action onEnterKey = (e: React.KeyboardEvent): void => { if (e.key === 'Enter') { + e.stopPropagation(); if (this._keyInput && this._valueInput && this.fieldDocToLayout) { if (KeyValueBox.SetField(this.fieldDocToLayout, this._keyInput, this._valueInput)) { this._keyInput = ""; @@ -153,7 +154,7 @@ export class KeyValueBox extends React.Component { - + ) diff --git a/src/client/views/nodes/LinkMenuItem.tsx b/src/client/views/nodes/LinkMenuItem.tsx index a0c37a719..d4c92c9f2 100644 --- a/src/client/views/nodes/LinkMenuItem.tsx +++ b/src/client/views/nodes/LinkMenuItem.tsx @@ -7,7 +7,7 @@ import { undoBatch } from "../../util/UndoManager"; import './LinkMenu.scss'; import React = require("react"); import { Doc } from '../../../new_fields/Doc'; -import { StrCast, Cast, BoolCast, FieldValue, NumCast } from '../../../new_fields/Types'; +import { StrCast, Cast, FieldValue, NumCast } from '../../../new_fields/Types'; import { observable, action } from 'mobx'; import { LinkManager } from '../../util/LinkManager'; import { DragLinkAsDocument } from '../../util/DragManager'; diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index c9d442fe5..a6864e0f3 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -1,22 +1,18 @@ +import { action, IReactionDisposer, observable } from "mobx"; import { observer } from "mobx-react"; -import React = require("react"); -import { observable, action, runInAction, IReactionDisposer, reaction } from "mobx"; import * as Pdfjs from "pdfjs-dist"; -import { Opt, Doc, FieldResult, Field, DocListCast, WidthSym, HeightSym, DocListCastAsync } from "../../../new_fields/Doc"; -import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; -import { PDFBox } from "../nodes/PDFBox"; -import { DragManager } from "../../util/DragManager"; -import { Docs, DocUtils } from "../../documents/Documents"; +import { Doc, DocListCastAsync, Opt, WidthSym } from "../../../new_fields/Doc"; import { List } from "../../../new_fields/List"; -import { emptyFunction } from "../../../Utils"; -import { Cast, NumCast, StrCast, BoolCast } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; -import { menuBar } from "prosemirror-menu"; -import { AnnotationTypes, PDFViewer, scale } from "./PDFViewer"; +import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; +import { Docs, DocUtils } from "../../documents/Documents"; +import { DragManager } from "../../util/DragManager"; +import { PDFBox } from "../nodes/PDFBox"; import PDFMenu from "./PDFMenu"; -import { UndoManager } from "../../util/UndoManager"; -import { copy } from "typescript-collections/dist/lib/arrays"; +import { scale } from "./PDFViewer"; +import "./PDFViewer.scss"; +import React = require("react"); interface IPageProps { diff --git a/src/client/views/presentationview/PresentationElement.tsx b/src/client/views/presentationview/PresentationElement.tsx index 329630875..36f1178f1 100644 --- a/src/client/views/presentationview/PresentationElement.tsx +++ b/src/client/views/presentationview/PresentationElement.tsx @@ -1,21 +1,19 @@ -import { observer } from "mobx-react"; -import React = require("react"); -import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc"; -import { NumCast, BoolCast, StrCast, Cast } from "../../../new_fields/Types"; -import { Id } from "../../../new_fields/FieldSymbols"; -import { observable, action, computed, runInAction } from "mobx"; -import "./PresentationView.scss"; -import { Utils } from "../../../Utils"; import { library } from '@fortawesome/fontawesome-svg-core'; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faFile as fileSolid, faFileDownload, faLocationArrow, faArrowUp, faSearch } from '@fortawesome/free-solid-svg-icons'; import { faFile as fileRegular } from '@fortawesome/free-regular-svg-icons'; +import { faArrowUp, faFile as fileSolid, faFileDownload, faLocationArrow, faSearch } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { action, computed, observable, runInAction } from "mobx"; +import { observer } from "mobx-react"; +import { Doc } from "../../../new_fields/Doc"; +import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; import { listSpec } from "../../../new_fields/Schema"; -import { DragManager, SetupDrag, dropActionType } from "../../util/DragManager"; +import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types"; +import { Utils } from "../../../Utils"; +import { DragManager, dropActionType, SetupDrag } from "../../util/DragManager"; import { SelectionManager } from "../../util/SelectionManager"; -import { indexOf } from "typescript-collections/dist/lib/arrays"; -import { map } from "bluebird"; +import "./PresentationView.scss"; +import React = require("react"); library.add(faArrowUp); library.add(fileSolid); diff --git a/src/client/views/presentationview/PresentationView.tsx b/src/client/views/presentationview/PresentationView.tsx index f2fef7f16..e25725275 100644 --- a/src/client/views/presentationview/PresentationView.tsx +++ b/src/client/views/presentationview/PresentationView.tsx @@ -171,7 +171,7 @@ export class PresentationView extends React.Component { //storing the presentation status,ie. whether it was stopped or playing - let presStatusBackUp = BoolCast(this.curPresentation.presStatus, null); + let presStatusBackUp = BoolCast(this.curPresentation.presStatus); runInAction(() => this.presStatus = presStatusBackUp); } diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index dbb0dc505..b5708e97b 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -455,7 +455,7 @@ export namespace Doc { return otherdoc; } - export function MakeTemplate(fieldTemplate: Doc, metaKey: string, proto: Doc) { + export function MakeTemplate(fieldTemplate: Doc, metaKey: string, templateDataDoc: Doc) { // move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??) let backgroundLayout = StrCast(fieldTemplate.backgroundLayout); let fieldLayoutDoc = fieldTemplate; @@ -466,21 +466,24 @@ export namespace Doc { if (backgroundLayout) { backgroundLayout = backgroundLayout.replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`); } - let nw = Cast(fieldTemplate.nativeWidth, "number"); - let nh = Cast(fieldTemplate.nativeHeight, "number"); let layoutDelegate = fieldTemplate.layout instanceof Doc ? fieldLayoutDoc : fieldTemplate; layoutDelegate.layout = layout; - fieldTemplate.title = metaKey; fieldTemplate.templateField = metaKey; + fieldTemplate.title = metaKey; + fieldTemplate.isTemplate = true; fieldTemplate.layout = layoutDelegate !== fieldTemplate ? layoutDelegate : layout; fieldTemplate.backgroundLayout = backgroundLayout; - fieldTemplate.nativeWidth = nw; - fieldTemplate.nativeHeight = nh; - fieldTemplate.isTemplate = true; + /* move certain layout properties from the original data doc to the template layout to avoid + inheriting them from the template's data doc which may also define these fields for its own use. + */ + fieldTemplate.ignoreAspect = BoolCast(fieldTemplate.ignoreAspect); + fieldTemplate.singleColumn = BoolCast(fieldTemplate.singleColumn); + fieldTemplate.nativeWidth = Cast(fieldTemplate.nativeWidth, "number"); + fieldTemplate.nativeHeight = Cast(fieldTemplate.nativeHeight, "number"); fieldTemplate.showTitle = "title"; - setTimeout(() => fieldTemplate.proto = proto); + setTimeout(() => fieldTemplate.proto = templateDataDoc); } export async function ToggleDetailLayout(d: Doc) { diff --git a/src/new_fields/Types.ts b/src/new_fields/Types.ts index f8a4a30b4..565ae2ee3 100644 --- a/src/new_fields/Types.ts +++ b/src/new_fields/Types.ts @@ -78,7 +78,7 @@ export function StrCast(field: FieldResult, defaultVal: string | null = "") { return Cast(field, "string", defaultVal); } -export function BoolCast(field: FieldResult, defaultVal: boolean | null = null) { +export function BoolCast(field: FieldResult, defaultVal: boolean | null = false) { return Cast(field, "boolean", defaultVal); } export function DateCast(field: FieldResult) { -- cgit v1.2.3-70-g09d2 From 60aa214501f7932e5efee68fd878ef367fc2a31b Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 27 Jul 2019 16:18:24 -0400 Subject: switch Make Portal to act more like a prezi portal. added scale paramter to focus() --- .../collectionFreeForm/CollectionFreeFormView.tsx | 10 +++++----- src/client/views/nodes/DocumentView.tsx | 22 +++++++++++++++------- 2 files changed, 20 insertions(+), 12 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index eaad43c10..cd071fb4d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -303,7 +303,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { doc.zIndex = docs.length + 1; } - focusDocument = (doc: Doc, willZoom: boolean) => { + focusDocument = (doc: Doc, willZoom: boolean, scale?: number) => { const panX = this.Document.panX; const panY = this.Document.panY; const id = this.Document[Id]; @@ -335,20 +335,20 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { this.props.Document.panTransformType = "Ease"; this.props.focus(this.props.Document); if (willZoom) { - this.setScaleToZoom(doc); + this.setScaleToZoom(doc, scale); } } - setScaleToZoom = (doc: Doc) => { + setScaleToZoom = (doc: Doc, scale: number = 0.5) => { let p = this.props; let PanelHeight = p.PanelHeight(); let panelWidth = p.PanelWidth(); let docHeight = NumCast(doc.height); let docWidth = NumCast(doc.width); - let targetHeight = 0.5 * PanelHeight; - let targetWidth = 0.5 * panelWidth; + let targetHeight = scale * PanelHeight; + let targetWidth = scale * panelWidth; let maxScaleX: number = targetWidth / docWidth; let maxScaleY: number = targetHeight / docHeight; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index bc2af2ae8..f60be0a6a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -88,7 +88,7 @@ export interface DocumentViewProps { ContentScaling: () => number; PanelWidth: () => number; PanelHeight: () => number; - focus: (doc: Doc, willZoom: boolean) => void; + focus: (doc: Doc, willZoom: boolean, scale?: number) => void; selectOnLoad: boolean; parentActive: () => boolean; whenActiveChanged: (isActive: boolean) => void; @@ -359,7 +359,12 @@ export class DocumentView extends DocComponent(Docu if (!linkedFwdDocs.some(l => l instanceof Promise)) { let maxLocation = StrCast(linkedFwdDocs[0].maximizeLocation, "inTab"); let targetContext = !Doc.AreProtosEqual(linkedFwdContextDocs[altKey ? 1 : 0], this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document) ? linkedFwdContextDocs[altKey ? 1 : 0] : undefined; - DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, false, document => this.props.addDocTab(document, undefined, maxLocation), linkedFwdPage[altKey ? 1 : 0], targetContext); + DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, false, document => { + this.props.focus(this.props.Document, true, 1); + setTimeout(() => + this.props.addDocTab(document, undefined, maxLocation), 1000); + } + , linkedFwdPage[altKey ? 1 : 0], targetContext); } } } @@ -554,12 +559,15 @@ export class DocumentView extends DocComponent(Docu cm.addItem({ description: "Make Portal", event: () => { let portal = Docs.Create.FreeformDocument([], { width: this.props.Document[WidthSym]() + 10, height: this.props.Document[HeightSym](), title: this.props.Document.title + ".portal" }); - Doc.GetProto(this.props.Document).subBulletDocs = new List([portal]); + //Doc.GetProto(this.props.Document).subBulletDocs = new List([portal]); //summary.proto!.maximizeLocation = "inTab"; // or "inPlace", or "onRight" - Doc.GetProto(this.props.Document).templates = new List([Templates.Bullet.Layout]); - let coll = Docs.Create.StackingDocument([this.props.Document, portal], { x: NumCast(this.props.Document.x), y: NumCast(this.props.Document.y), width: this.props.Document[WidthSym]() + 10, height: this.props.Document[HeightSym](), title: this.props.Document.title + ".cont" }); - this.props.addDocument && this.props.addDocument(coll); - this.props.removeDocument && this.props.removeDocument(this.props.Document); + //Doc.GetProto(this.props.Document).templates = new List([Templates.Bullet.Layout]); + //let coll = Docs.Create.StackingDocument([this.props.Document, portal], { x: NumCast(this.props.Document.x), y: NumCast(this.props.Document.y), width: this.props.Document[WidthSym]() + 10, height: this.props.Document[HeightSym](), title: this.props.Document.title + ".cont" }); + //this.props.addDocument && this.props.addDocument(coll); + //this.props.removeDocument && this.props.removeDocument(this.props.Document); + DocUtils.MakeLink(this.props.Document, portal, undefined, this.props.Document.title + ".portal"); + this.makeBtnClicked(); + }, icon: "window-restore" }); // cm.addItem({ -- cgit v1.2.3-70-g09d2 From 27cafb6eebd1c6229c3377187a5c0043db25ba0a Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Sun, 28 Jul 2019 22:58:32 -0400 Subject: templating fixes, context menu undo streamlining --- src/.DS_Store | Bin 6148 -> 6148 bytes src/client/documents/Documents.ts | 5 ++--- src/client/views/ContextMenuItem.tsx | 4 ++-- src/client/views/collections/CollectionTreeView.tsx | 10 +++++----- src/client/views/collections/CollectionView.tsx | 14 +++++++------- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/KeyValueBox.tsx | 17 ++++++++++++----- src/scraping/buxton/scraper.py | 4 ++-- 8 files changed, 31 insertions(+), 25 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/.DS_Store b/src/.DS_Store index 071dafa1e..c544bc837 100644 Binary files a/src/.DS_Store and b/src/.DS_Store differ diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 3859f2255..30a114e33 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -163,7 +163,6 @@ export namespace Docs { [DocumentType.LINKDOC, { data: new List(), layout: { view: EmptyBox }, - options: {} }], [DocumentType.BUTTON, { layout: { view: ButtonBox }, @@ -414,8 +413,8 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Tree }); } - export function StackingDocument(documents: Array, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Stacking }); + export function StackingDocument(documents: Array, options: DocumentOptions, viewType: CollectionViewType = CollectionViewType.Stacking) { + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: viewType }); } export function ButtonDocument(options?: DocumentOptions) { diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx index 4ff0fe388..a1787e78f 100644 --- a/src/client/views/ContextMenuItem.tsx +++ b/src/client/views/ContextMenuItem.tsx @@ -37,13 +37,13 @@ export class ContextMenuItem extends React.Component) => { + handleEvent = async (e: React.MouseEvent) => { if ("event" in this.props) { let batch: UndoManager.Batch | undefined; if (this.props.undoable !== false) { batch = UndoManager.StartBatch(`Context menu event: ${this.props.description}`); } - this.props.event(); + await this.props.event(); batch && batch.end(); this.props.closeMenu && this.props.closeMenu(); } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 1c7fe4bee..3eab109bc 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -237,10 +237,10 @@ class TreeView extends React.Component { 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" }); + ContextMenu.Instance.addItem({ description: "Delete Item", event: () => this.props.deleteDoc(this.props.document), icon: "trash-alt" }); } else { - 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 as Workspace", event: () => MainView.Instance.openWorkspace(this.dataDoc), icon: "caret-square-right" }); + ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => 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" }); ContextMenu.Instance.displayMenu(e.pageX > 156 ? e.pageX - 156 : 0, e.pageY - 15); @@ -520,8 +520,8 @@ export class CollectionTreeView extends CollectionSubView(Document) { onContextMenu = (e: React.MouseEvent): void => { // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout if (!e.isPropagationStopped() && this.props.Document.workspaceLibrary) { // excludeFromLibrary means this is the user document - ContextMenu.Instance.addItem({ description: "Create Workspace", event: undoBatch(() => MainView.Instance.createNewWorkspace()), icon: "plus" }); - ContextMenu.Instance.addItem({ description: "Delete Workspace", event: undoBatch(() => this.remove(this.props.Document)), icon: "minus" }); + ContextMenu.Instance.addItem({ description: "Create Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" }); + ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.remove(this.props.Document), icon: "minus" }); e.stopPropagation(); e.preventDefault(); ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index b4f29755c..e3f5d1eb8 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -53,14 +53,14 @@ export class CollectionView extends React.Component { 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[] = []; - subItems.push({ description: "Freeform", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Freeform), icon: "signature" }); + subItems.push({ description: "Freeform", event: () => this.props.Document.viewType = CollectionViewType.Freeform, icon: "signature" }); if (CollectionBaseView.InSafeMode()) { - ContextMenu.Instance.addItem({ description: "Test Freeform", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Invalid), icon: "project-diagram" }); + ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => this.props.Document.viewType = CollectionViewType.Invalid, icon: "project-diagram" }); } - 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: "ellipsis-v" }); - subItems.push({ description: "Masonry", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Masonry), icon: "columns" }); + subItems.push({ description: "Schema", event: () => this.props.Document.viewType = CollectionViewType.Schema, icon: "th-list" }); + subItems.push({ description: "Treeview", event: () => this.props.Document.viewType = CollectionViewType.Tree, icon: "tree" }); + subItems.push({ description: "Stacking", event: () => this.props.Document.viewType = CollectionViewType.Stacking, icon: "ellipsis-v" }); + subItems.push({ description: "Masonry", event: () => this.props.Document.viewType = CollectionViewType.Masonry, icon: "columns" }); switch (this.props.Document.viewType) { case CollectionViewType.Freeform: { subItems.push({ description: "Custom", icon: "fingerprint", event: CollectionFreeFormView.AddCustomLayout(this.props.Document, this.props.fieldKey) }); @@ -68,7 +68,7 @@ export class CollectionView extends React.Component { } } ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems, icon: "eye" }); - ContextMenu.Instance.addItem({ description: "Apply Template", event: undoBatch(() => this.props.addDocTab && this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight")), icon: "project-diagram" }); + ContextMenu.Instance.addItem({ description: "Apply Template", event: () => 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 d07fc7f80..a6152e1b7 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -496,7 +496,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { let layoutItems: ContextMenuProps[] = []; layoutItems.push({ description: `${this.fitToBox ? "Unset" : "Set"} Fit To Container`, - event: undoBatch(async () => this.props.Document.fitToBox = !this.fitToBox), + event: async () => this.props.Document.fitToBox = !this.fitToBox, icon: !this.fitToBox ? "expand-arrows-alt" : "compress-arrows-alt" }); layoutItems.push({ diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 77824b4ff..b0fcc4202 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -20,6 +20,7 @@ import { RichTextField } from "../../../new_fields/RichTextField"; import { ImageField } from "../../../new_fields/URLField"; import { SelectionManager } from "../../util/SelectionManager"; import { listSpec } from "../../../new_fields/Schema"; +import { CollectionViewType } from "../collections/CollectionBaseView"; export type KVPScript = { script: CompiledScript; @@ -195,6 +196,9 @@ export class KeyValueBox extends React.Component { } let fieldTemplate = await this.inferType(sourceDoc[metaKey], metaKey); + if (!fieldTemplate) { + return; + } let previousViewType = fieldTemplate.viewType; Doc.MakeTemplate(fieldTemplate, metaKey, Doc.GetProto(parentStackingDoc)); previousViewType && (fieldTemplate.viewType = previousViewType); @@ -211,14 +215,17 @@ export class KeyValueBox extends React.Component { return Docs.Create.StackingDocument([], options); } let first = await Cast(data[0], Doc); - if (!first) { + if (!first || !first.data) { return Docs.Create.StackingDocument([], options); } - switch (first.type) { - case "image": - return Docs.Create.StackingDocument([], options); - case "text": + switch (first.data.constructor) { + case RichTextField: return Docs.Create.TreeDocument([], options); + case ImageField: + return Docs.Create.StackingDocument([], options, CollectionViewType.Masonry); + default: + console.log(`Template for ${first.data.constructor} not supported!`); + return undefined; } } else if (data instanceof ImageField) { return Docs.Create.ImageDocument("https://image.flaticon.com/icons/png/512/23/23765.png", options); diff --git a/src/scraping/buxton/scraper.py b/src/scraping/buxton/scraper.py index 182b22a1a..1ff0e3b31 100644 --- a/src/scraping/buxton/scraper.py +++ b/src/scraping/buxton/scraper.py @@ -139,7 +139,7 @@ def write_text_doc(content): data_doc = { "_id": data_doc_guid, "fields": { - "proto": protofy("commonImportProto"), + "proto": protofy("textProto"), "data": { "Data": '{"doc":{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"' + content + '"}]}]},"selection":{"type":"text","anchor":1,"head":1}' + '}', "__type": "RichTextField" @@ -235,7 +235,7 @@ def parse_document(file_name: str): count += 1 view_guids.append(write_image(pure_name, image)) copyfile(dir_path + "/" + image, dir_path + - "/" + image.replace(".", "_o.", 1)) + "/" + image.replace(".", "_o.", 1)) os.rename(dir_path + "/" + image, dir_path + "/" + image.replace(".", "_m.", 1)) print(f"extracted {count} images...") -- cgit v1.2.3-70-g09d2 From 2514917040d24c04a489905c7a1fe4d10013fd31 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 29 Jul 2019 20:50:04 -0400 Subject: not elegant, but should compute bounds for computed layouts --- .../collectionFreeForm/CollectionFreeFormView.tsx | 36 +++++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 0780320d6..d2c95532e 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -65,8 +65,20 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { return (this.props as any).ContentScaling && this.fitToBox && !this.isAnnotationOverlay ? (this.props as any).ContentScaling() : 1; } + ComputeContentBounds(boundsList: { x: number, y: number, width: number, height: number }[]) { + let bounds = boundsList.reduce((bounds, b) => { + var [sptX, sptY] = [b.x, b.y]; + let [bptX, bptY] = [sptX + b.width, sptY + b.height]; + return { + 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.MAX_VALUE, b: -Number.MAX_VALUE }); + return bounds; + } + @computed get contentBounds() { - let bounds = this.fitToBox && !this.isAnnotationOverlay ? Doc.ComputeContentBounds(DocListCast(this.props.Document.data)) : undefined; + let bounds = this.fitToBox && !this.isAnnotationOverlay ? this.ComputeContentBounds(this.elements.filter(e => e.bounds).map(e => e.bounds!)) : undefined; return { panX: bounds ? (bounds.x + bounds.r) / 2 : this.Document.panX || 0, panY: bounds ? (bounds.y + bounds.b) / 2 : this.Document.panY || 0, @@ -446,13 +458,13 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } @computed.struct - get views() { + get elements() { let curPage = FieldValue(this.Document.curPage, -1); const initScript = this.Document.arrangeInit; const script = this.Document.arrangeScript; let state: any = undefined; const docs = this.childDocs; - let elements: JSX.Element[] = []; + let elements: { ele: JSX.Element, bounds?: { x: number, y: number, width: number, height: number } }[] = []; if (initScript) { const initResult = initScript.script.run({ docs, collection: this.Document }); if (initResult.success) { @@ -460,23 +472,26 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { const { state: scriptState, views } = result; state = scriptState; if (Array.isArray(views)) { - elements = views.reduce((prev, ele) => { + elements = views.reduce((prev, ele) => { const jsx = this.viewDefToJSX(ele); - jsx && prev.push(jsx); + jsx && prev.push({ ele: jsx }); return prev; }, elements); } } } - let docviews = docs.reduce((prev, doc) => { - if (!(doc instanceof Doc)) return prev; + let docviews = docs.filter(doc => doc instanceof Doc).reduce((prev, doc) => { var page = NumCast(doc.page, -1); + let bounds: { x?: number, y?: number, width?: number, height?: number }; if ((Math.abs(Math.round(page) - Math.round(curPage)) < 3) || page === -1) { let minim = BoolCast(doc.isMinimized); if (minim === undefined || !minim) { const pos = script ? this.getCalculatedPositions(script, { doc, index: prev.length, collection: this.Document, docs, state }) : {}; state = pos.state === undefined ? state : pos.state; - prev.push(); + prev.push({ + ele: , + bounds: (pos.x !== undefined && pos.y !== undefined && pos.width !== undefined && pos.height !== undefined) ? { x: pos.x, y: pos.y, width: pos.width, height: pos.height } : undefined + }); } } return prev; @@ -487,6 +502,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { return docviews; } + @computed.struct + get views() { + return this.elements.map(ele => ele.ele); + } + @action onCursorMove = (e: React.PointerEvent) => { super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY)); -- cgit v1.2.3-70-g09d2 From e042f916375fbe4f23288ece0dcec5b61ef2fbed Mon Sep 17 00:00:00 2001 From: yipstanley Date: Mon, 29 Jul 2019 21:20:51 -0400 Subject: annotations now open the target context --- src/client/util/DragManager.ts | 1 + .../collectionFreeForm/CollectionFreeFormView.tsx | 2 ++ src/client/views/nodes/DocumentView.tsx | 4 +++- src/client/views/pdf/Annotation.tsx | 12 +++++++++--- src/client/views/pdf/Page.tsx | 2 +- 5 files changed, 16 insertions(+), 5 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 9221ef274..abcc3a4e1 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -216,6 +216,7 @@ export namespace DragManager { this.annotationDocument = annotationDoc; this.xOffset = this.yOffset = 0; } + targetContext: Doc | undefined; dragDocument: Doc; annotationDocument: Doc; dropDocument: Doc; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 0780320d6..179a65739 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -151,6 +151,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { let dropY = NumCast(de.data.dropDocument.y); dragDoc.x = x + NumCast(dragDoc.x) - dropX; dragDoc.y = y + NumCast(dragDoc.y) - dropY; + de.data.targetContext = this.props.Document; + dragDoc.targetContext = this.props.Document; this.bringToFront(dragDoc); } } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index f101222ae..b37485e62 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -439,7 +439,9 @@ export class DocumentView extends DocComponent(Docu e.stopPropagation(); let annotationDoc = de.data.annotationDocument; annotationDoc.linkedToDoc = true; + de.data.targetContext = this.props.ContainingCollectionView!.props.Document; let targetDoc = this.props.Document; + targetDoc.targetContext = de.data.targetContext; let annotations = await DocListCastAsync(annotationDoc.annotations); if (annotations) { annotations.forEach(anno => { @@ -448,7 +450,7 @@ export class DocumentView extends DocComponent(Docu } let pdfDoc = await Cast(annotationDoc.pdfDoc, Doc); if (pdfDoc) { - DocUtils.MakeLink(annotationDoc, targetDoc, undefined, `Annotation from ${StrCast(pdfDoc.title)}`, "", StrCast(pdfDoc.title)); + DocUtils.MakeLink(annotationDoc, targetDoc, this.props.ContainingCollectionView!.props.Document, `Annotation from ${StrCast(pdfDoc.title)}`, "", StrCast(pdfDoc.title)); } } if (de.data instanceof DragManager.LinkDragData) { diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index ed7081b1d..e8d1da94b 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -9,6 +9,8 @@ import { List } from "../../../new_fields/List"; import PDFMenu from "./PDFMenu"; import { DocumentManager } from "../../util/DocumentManager"; import { PresentationView } from "../presentationview/PresentationView"; +import { LinkManager } from "../../util/LinkManager"; +import { CollectionDockingView } from "../collections/CollectionDockingView"; interface IAnnotationProps { anno: Doc; @@ -110,11 +112,15 @@ class RegionAnnotation extends React.Component { } @action - onPointerDown = (e: React.PointerEvent) => { + onPointerDown = async (e: React.PointerEvent) => { if (e.button === 0) { - let targetDoc = Cast(this.props.document.target, Doc, null); + let targetDoc = await Cast(this.props.document.target, Doc); if (targetDoc) { - DocumentManager.Instance.jumpToDocument(targetDoc, false); + let context = await Cast(targetDoc.targetContext, Doc); + if (context) { + DocumentManager.Instance.jumpToDocument(targetDoc, false, undefined, + ((doc) => this.props.parent.props.parent.props.addDocTab(context, context.proto, e.ctrlKey ? "onRight" : "inTab"))); + } } } if (e.button === 2) { diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index c205617b4..c5b2a1dda 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -175,7 +175,7 @@ export default class Page extends React.Component { } let pdfDoc = await Cast(annotationDoc.pdfDoc, Doc); if (pdfDoc) { - DocUtils.MakeLink(annotationDoc, targetDoc, undefined, `Annotation from ${StrCast(pdfDoc.title)}`, "", StrCast(pdfDoc.title)); + DocUtils.MakeLink(annotationDoc, targetDoc, dragData.targetContext, `Annotation from ${StrCast(pdfDoc.title)}`, "", StrCast(pdfDoc.title)); } } } -- cgit v1.2.3-70-g09d2 From f6583e4b504a4a60f4aefc68c7a60692a92869dd Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Mon, 29 Jul 2019 22:33:35 -0400 Subject: Included extra ui elements in bounds calculation --- .../collectionFreeForm/CollectionFreeFormView.tsx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 2b2f0bcd5..8dac785e1 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -440,7 +440,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { return result.result === undefined ? {} : result.result; } - private viewDefToJSX(viewDef: any): JSX.Element | undefined { + private viewDefToJSX(viewDef: any): { ele: JSX.Element, bounds?: { x: number, y: number, width: number, height: number } } | undefined { if (viewDef.type === "text") { const text = Cast(viewDef.text, "string"); const x = Cast(viewDef.x, "number"); @@ -448,14 +448,16 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { 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)) { + if ([text, x, y, width, height].some(val => val === undefined)) { return undefined; } - return
    {text}
    ; + return { + ele:
    {text}
    , bounds: { x: x!, y: y!, width: width!, height: height! } + }; } } @@ -474,9 +476,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { const { state: scriptState, views } = result; state = scriptState; if (Array.isArray(views)) { - elements = views.reduce((prev, ele) => { + elements = views.reduce((prev, ele) => { const jsx = this.viewDefToJSX(ele); - jsx && prev.push({ ele: jsx }); + jsx && prev.push(jsx); return prev; }, elements); } -- cgit v1.2.3-70-g09d2 From 26086ee95a9a16486d637aa43c96638b6154379f Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Tue, 30 Jul 2019 16:02:42 -0400 Subject: Added document export and import --- package.json | 4 + .../collectionFreeForm/CollectionFreeFormView.tsx | 39 +++++- src/client/views/nodes/DocumentView.tsx | 10 ++ src/scraping/buxton/scraper.py | 2 + src/server/database.ts | 37 +++++- src/server/index.ts | 142 +++++++++++++++++---- 6 files changed, 202 insertions(+), 32 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/package.json b/package.json index 37052fde3..b29355738 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,9 @@ "@hig/theme-context": "^2.1.3", "@hig/theme-data": "^2.3.3", "@trendmicro/react-dropdown": "^1.3.0", + "@types/adm-zip": "^0.4.32", "@types/animejs": "^2.0.2", + "@types/archiver": "^3.0.0", "@types/async": "^2.4.1", "@types/bcrypt-nodejs": "0.0.30", "@types/bluebird": "^3.5.25", @@ -105,6 +107,8 @@ "@types/uuid": "^3.4.4", "@types/webpack": "^4.4.25", "@types/youtube": "0.0.38", + "adm-zip": "^0.4.13", + "archiver": "^3.0.3", "async": "^2.6.2", "babel-runtime": "^6.26.0", "bcrypt-nodejs": "0.0.3", diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 8dac785e1..cbab14976 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -5,7 +5,7 @@ import { Id } from "../../../../new_fields/FieldSymbols"; import { InkField, StrokeData } from "../../../../new_fields/InkField"; import { createSchema, makeInterface } from "../../../../new_fields/Schema"; import { BoolCast, Cast, FieldValue, NumCast, StrCast } from "../../../../new_fields/Types"; -import { emptyFunction, returnOne } from "../../../../Utils"; +import { emptyFunction, returnOne, Utils } from "../../../../Utils"; import { DocumentManager } from "../../../util/DocumentManager"; import { DragManager } from "../../../util/DragManager"; import { HistoryUtil } from "../../../util/History"; @@ -34,12 +34,14 @@ 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, faExpandArrowsAlt, faCompressArrowsAlt, faCompass } from "@fortawesome/free-solid-svg-icons"; +import { faTable, faPaintBrush, faAsterisk, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload } from "@fortawesome/free-solid-svg-icons"; import { undo } from "prosemirror-history"; import { number } from "prop-types"; import { ContextMenu } from "../../ContextMenu"; +import { RouteStore } from "../../../../server/RouteStore"; +import { DocServer } from "../../../DocServer"; -library.add(faEye, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass); +library.add(faEye, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload); export const panZoomSchema = createSchema({ panX: "number", @@ -516,7 +518,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY)); } - onContextMenu = () => { + onContextMenu = (e: React.MouseEvent) => { let layoutItems: ContextMenuProps[] = []; layoutItems.push({ description: `${this.fitToBox ? "Unset" : "Set"} Fit To Container`, @@ -561,6 +563,35 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { CognitiveServices.Inking.Manager.analyzer(this.fieldExtensionDoc, relevantKeys, data.inkData); }, icon: "paint-brush" }); + ContextMenu.Instance.addItem({ + description: "Import document", icon: "upload", event: () => { + const input = document.createElement("input"); + input.type = "file"; + input.accept = ".zip"; + input.onchange = async _e => { + const files = input.files; + if (!files) return; + const file = files[0]; + let formData = new FormData(); + formData.append('file', file); + formData.append('remap', "true"); + const upload = Utils.prepend("/uploadDoc"); + const response = await fetch(upload, { method: "POST", body: formData }); + const json = await response.json(); + if (json === "error") { + return; + } + const doc = await DocServer.GetRefField(json); + if (!doc || !(doc instanceof Doc)) { + return; + } + const [x, y] = this.props.ScreenToLocalTransform().transformPoint(e.pageX, e.pageY); + doc.x = x, doc.y = y; + this.addDocument(doc, false); + }; + input.click(); + } + }); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 4b5cf3a43..58e2443c2 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -45,6 +45,7 @@ const JsxParser = require('react-jsx-parser').default; //TODO Why does this need library.add(fa.faTrash); library.add(fa.faShare); +library.add(fa.faDownload); library.add(fa.faExpandArrowsAlt); library.add(fa.faCompressArrowsAlt); library.add(fa.faLayerGroup); @@ -597,6 +598,15 @@ export class DocumentView extends DocComponent(Docu copies.push({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document[Id]), icon: "fingerprint" }); cm.addItem({ description: "Copy...", subitems: copies, icon: "copy" }); } + cm.addItem({ + description: "Download document", icon: "download", event: () => { + const a = document.createElement("a"); + const url = Utils.prepend(`/downloadId/${this.props.Document[Id]}`); + a.href = url; + a.download = `DocExport-${this.props.Document[Id]}.zip`; + a.click(); + } + }); cm.addItem({ description: "Delete", event: this.deleteClicked, icon: "trash" }); type User = { email: string, userDocumentId: string }; let usersMenu: ContextMenuProps[] = []; diff --git a/src/scraping/buxton/scraper.py b/src/scraping/buxton/scraper.py index 1ff0e3b31..8ff7cb223 100644 --- a/src/scraping/buxton/scraper.py +++ b/src/scraping/buxton/scraper.py @@ -236,6 +236,8 @@ def parse_document(file_name: str): view_guids.append(write_image(pure_name, image)) copyfile(dir_path + "/" + image, dir_path + "/" + image.replace(".", "_o.", 1)) + copyfile(dir_path + "/" + image, dir_path + + "/" + image) os.rename(dir_path + "/" + image, dir_path + "/" + image.replace(".", "_m.", 1)) print(f"extracted {count} images...") diff --git a/src/server/database.ts b/src/server/database.ts index acb6ce751..a7254fb0c 100644 --- a/src/server/database.ts +++ b/src/server/database.ts @@ -17,7 +17,7 @@ export class Database { }); } - public update(id: string, value: any, callback: () => void, upsert = true, collectionName = Database.DocumentsCollection) { + public update(id: string, value: any, callback: (err: mongodb.MongoError, res: mongodb.UpdateWriteOpResult) => void, upsert = true, collectionName = Database.DocumentsCollection) { if (this.db) { let collection = this.db.collection(collectionName); const prom = this.currentWrites[id]; @@ -30,7 +30,7 @@ export class Database { delete this.currentWrites[id]; } resolve(); - callback(); + callback(err, res); }); }); }; @@ -41,6 +41,30 @@ export class Database { } } + public replace(id: string, value: any, callback: (err: mongodb.MongoError, res: mongodb.UpdateWriteOpResult) => void, upsert = true, collectionName = Database.DocumentsCollection) { + if (this.db) { + let collection = this.db.collection(collectionName); + const prom = this.currentWrites[id]; + let newProm: Promise; + const run = (): Promise => { + return new Promise(resolve => { + collection.replaceOne({ _id: id }, value, { upsert } + , (err, res) => { + if (this.currentWrites[id] === newProm) { + delete this.currentWrites[id]; + } + resolve(); + callback(err, res); + }); + }); + }; + newProm = prom ? prom.then(run) : run(); + this.currentWrites[id] = newProm; + } else { + this.onConnect.push(() => this.replace(id, value, callback, upsert, collectionName)); + } + } + public delete(query: any, collectionName?: string): Promise; public delete(id: string, collectionName?: string): Promise; public delete(id: any, collectionName = Database.DocumentsCollection) { @@ -126,7 +150,7 @@ export class Database { } } - public async visit(ids: string[], fn: (result: any) => string[], collectionName = "newDocuments") { + public async visit(ids: string[], fn: (result: any) => string[], collectionName = "newDocuments"): Promise { if (this.db) { const visited = new Set(); while (ids.length) { @@ -145,7 +169,12 @@ export class Database { } } else { - this.onConnect.push(() => this.visit(ids, fn, collectionName)); + return new Promise(res => { + this.onConnect.push(() => { + this.visit(ids, fn, collectionName); + res(); + }); + }); } } diff --git a/src/server/index.ts b/src/server/index.ts index 230c574cf..2fa5132d0 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -27,6 +27,7 @@ import { Client } from './Client'; import { Database } from './database'; import { MessageStore, Transferable, Types, Diff, Message } from "./Message"; import { RouteStore } from './RouteStore'; +import v4 = require('uuid/v4'); const app = express(); const config = require('../../webpack.config'); import { createCanvas, loadImage, Canvas } from "canvas"; @@ -39,7 +40,10 @@ import c = require("crypto"); import { Search } from './Search'; import { debug } from 'util'; import _ = require('lodash'); +import * as Archiver from 'archiver'; +import * as AdmZip from 'adm-zip'; import { Response } from 'express-serve-static-core'; +import { DocComponent } from '../client/views/DocComponent'; const MongoStore = require('connect-mongo')(session); const mongoose = require('mongoose'); const probe = require("probe-image-size"); @@ -177,16 +181,21 @@ function msToTime(duration: number) { return hoursS + ":" + minutesS + ":" + secondsS + "." + milliseconds; } -app.get("/serializeDoc/:docId", async (req, res) => { - const files: { [name: string]: string[] } = {}; +async function getDocs(id: string) { + const files = new Set(); const docs: { [id: string]: any } = {}; const fn = (doc: any): string[] => { + const id = doc.id; + if (typeof id === "string" && id.endsWith("Proto")) { + //Skip protos + return []; + } const ids: string[] = []; - for (const key in doc) { - if (!doc.hasOwnProperty(key)) { + for (const key in doc.fields) { + if (!doc.fields.hasOwnProperty(key)) { continue; } - const field = doc[key]; + const field = doc.fields[key]; if (field === undefined || field === null) { continue; } @@ -194,7 +203,7 @@ app.get("/serializeDoc/:docId", async (req, res) => { if (field.__type === "proxy" || field.__type === "prefetch_proxy") { ids.push(field.fieldId); } else if (field.__type === "list") { - ids.push(...fn(field.fields)); + ids.push(...fn(field)); } else if (typeof field === "string") { const re = /"(?:dataD|d)ocumentId"\s*:\s*"([\w\-]*)"/g; let match: string[] | null; @@ -215,35 +224,120 @@ app.get("/serializeDoc/:docId", async (req, res) => { while ((match = re2.exec(field.Data)) !== null) { const urlString = match[1]; const pathname = new URL(urlString).pathname; - const ext = path.extname(pathname); - const fileName = path.basename(pathname, ext); - let exts = files[fileName]; - if (!exts) { - files[fileName] = exts = []; - } - exts.push(ext); + files.add(pathname); } } else if (["audio", "image", "video", "pdf", "web"].includes(field.__type)) { const url = new URL(field.url); const pathname = url.pathname; - const ext = path.extname(pathname); - const fileName = path.basename(pathname, ext); - let exts = files[fileName]; - if (!exts) { - files[fileName] = exts = []; - } - exts.push(ext); + files.add(pathname); } } - docs[doc.id] = doc; + if (doc.id) { + docs[doc.id] = doc; + } return ids; }; - Database.Instance.visit([req.params.docId], fn); + await Database.Instance.visit([id], fn); + return { id, docs, files }; +} +app.get("/serializeDoc/:docId", async (req, res) => { + const { docs, files } = await getDocs(req.params.docId); + res.send({ docs, files: Array.from(files) }); }); -app.get("/downloadId/:docId", (req, res) => { - res.download(`/serializeDoc/${req.params.docId}`, `DocumentExport.zip`); +app.get("/downloadId/:docId", async (req, res) => { + res.set('Content-disposition', `attachment;`); + res.set('Content-Type', "application/zip"); + const { id, docs, files } = await getDocs(req.params.docId); + const docString = JSON.stringify({ id, docs }); + const zip = Archiver('zip'); + zip.pipe(res); + zip.append(docString, { name: "doc.json" }); + files.forEach(val => { + zip.file(__dirname + RouteStore.public + val, { name: val.substring(1) }); + }); + zip.finalize(); +}); + +app.post("/uploadDoc", (req, res) => { + let form = new formidable.IncomingForm(); + form.keepExtensions = true; + // let path = req.body.path; + const ids: { [id: string]: string } = {}; + let remap = true; + const getId = (id: string): string => { + if (!remap) return id; + if (id.endsWith("Proto")) return id; + if (id in ids) { + return ids[id]; + } else { + return ids[id] = v4(); + } + }; + const mapFn = (doc: any) => { + if (doc.id) { + doc.id = getId(doc.id); + } + for (const key in doc.fields) { + if (!doc.fields.hasOwnProperty(key)) { + continue; + } + const field = doc.fields[key]; + if (field === undefined || field === null) { + continue; + } + + if (field.__type === "proxy" || field.__type === "prefetch_proxy") { + field.fieldId = getId(field.fieldId); + } else if (field.__type === "list") { + mapFn(field); + } else if (typeof field === "string") { + const re = /("(?:dataD|d)ocumentId"\s*:\s*")([\w\-]*)"/g; + doc.fields[key] = (field as any).replace(re, (match: any, p1: string, p2: string) => { + return `${p1}${getId(p2)}"`; + }); + } else if (field.__type === "RichTextField") { + const re = /("href"\s*:\s*")(.*?)"/g; + field.Data = field.Data.replace(re, (match: any, p1: string, p2: string) => { + return `${p1}${getId(p2)}"`; + }); + } + } + }; + form.parse(req, async (err, fields, files) => { + remap = fields.remap !== "false"; + let id: string = ""; + try { + for (const name in files) { + const path = files[name].path; + const zip = new AdmZip(path); + zip.getEntries().forEach(entry => { + if (!entry.name.startsWith("files/")) return; + zip.extractEntryTo(entry.name, __dirname + RouteStore.public, true, false); + }); + const json = zip.getEntry("doc.json"); + let docs: any; + try { + let data = JSON.parse(json.getData().toString("utf8")); + docs = data.docs; + id = data.id; + docs = Object.keys(docs).map(key => docs[key]); + docs.forEach(mapFn); + await Promise.all(docs.map((doc: any) => new Promise(res => Database.Instance.replace(doc.id, doc, (err, r) => { + err && console.log(err); + res(); + }, true, "newDocuments")))); + } catch (e) { console.log(e); } + fs.unlink(path, () => { }); + } + if (id) { + res.send(JSON.stringify(getId(id))); + } else { + res.send(JSON.stringify("error")); + } + } catch (e) { console.log(e); } + }); }); app.get("/whosOnline", (req, res) => { -- cgit v1.2.3-70-g09d2 From f953ab92c8e0d4bf54f90a6b7f22a86cf5985c38 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 30 Jul 2019 22:39:52 -0400 Subject: removed chrome (scrollbar) of collectionChrome when collapsed. fixed make background. --- src/client/views/collections/CollectionView.tsx | 14 ++++++-------- src/client/views/collections/CollectionViewChromes.scss | 1 + src/client/views/collections/CollectionViewChromes.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 6 +++++- .../views/collections/collectionFreeForm/MarqueeView.tsx | 16 +++++++++++++--- src/client/views/nodes/DocumentView.tsx | 9 +++++---- 6 files changed, 31 insertions(+), 17 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 376aaf53f..34adc5840 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -1,11 +1,13 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faProjectDiagram, faSignature, faColumns, faSquare, faTh, faImage, faThList, faTree, faEllipsisV, faFingerprint, faLaptopCode } from '@fortawesome/free-solid-svg-icons'; +import { faEye } from '@fortawesome/free-regular-svg-icons'; +import { faColumns, faEllipsisV, faFingerprint, faImage, faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree } from '@fortawesome/free-solid-svg-icons'; +import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; import { observer } from "mobx-react"; import * as React from 'react'; -import { Doc, DocListCast, WidthSym, HeightSym } from '../../../new_fields/Doc'; +import { Doc } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; +import { StrCast } from '../../../new_fields/Types'; import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils'; -import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from '../ContextMenuItem'; import { FieldView, FieldViewProps } from '../nodes/FieldView'; @@ -15,11 +17,7 @@ import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormV import { CollectionSchemaView } from "./CollectionSchemaView"; import { CollectionStackingView } from './CollectionStackingView'; import { CollectionTreeView } from "./CollectionTreeView"; -import { StrCast, PromiseValue } from '../../../new_fields/Types'; -import { DocumentType } from '../../documents/Documents'; -import { CollectionStackingViewChrome, CollectionViewBaseChrome } from './CollectionViewChromes'; -import { observable, action, runInAction, IReactionDisposer, reaction } from 'mobx'; -import { faEye } from '@fortawesome/free-regular-svg-icons'; +import { CollectionViewBaseChrome } from './CollectionViewChromes'; export const COLLECTION_BORDER_WIDTH = 2; library.add(faTh); diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss index 0c2665b1c..793cb7a8b 100644 --- a/src/client/views/collections/CollectionViewChromes.scss +++ b/src/client/views/collections/CollectionViewChromes.scss @@ -14,6 +14,7 @@ grid-template-columns: 1fr auto; padding-bottom: 10px; border-bottom: .5px solid rgb(180, 180, 180); + overflow: hidden; .collectionViewBaseChrome { display: flex; diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index ecf78b8c1..1b2561953 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -213,7 +213,7 @@ export class CollectionViewBaseChrome extends React.ComponentStacking View -
    +
    { } - bringToFront = (doc: Doc) => { + bringToFront = (doc: Doc, sendToBack?: boolean) => { + if (sendToBack) { + doc.zIndex = 0; + return; + } const docs = this.childDocs; docs.slice().sort((doc1, doc2) => { if (doc1 === doc) return 1; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 07d06d053..b9ee588dd 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -370,15 +370,25 @@ export class MarqueeView extends React.Component let selRect = this.Bounds; let selection: 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); - var w = NumCast(doc.width) / z; - var h = NumCast(doc.height) / z; + var w = NumCast(doc.width); + var h = NumCast(doc.height); if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) { selection.push(doc); } }); + if (!selection.length) { + this.props.activeDocuments().map(doc => { + var x = NumCast(doc.x); + var y = NumCast(doc.y); + var w = NumCast(doc.width); + var h = NumCast(doc.height); + if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) { + selection.push(doc); + } + }); + } return selection; } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 51662274d..14f0b5a5a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -96,7 +96,7 @@ export interface DocumentViewProps { selectOnLoad: boolean; parentActive: () => boolean; whenActiveChanged: (isActive: boolean) => void; - bringToFront: (doc: Doc) => void; + bringToFront: (doc: Doc, sendToBack?: boolean) => void; addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => void; collapseToPoint?: (scrpt: number[], expandedDocs: Doc[] | undefined) => void; zoomToScale: (scale: number) => void; @@ -529,7 +529,8 @@ export class DocumentView extends DocComponent(Docu @undoBatch @action makeBackground = (): void => { - this.props.Document.isBackground = true; + this.props.Document.isBackground = !this.props.Document.isBackground; + this.props.Document.isBackground && this.props.bringToFront(this.props.Document, true); } @undoBatch @@ -568,7 +569,7 @@ export class DocumentView extends DocComponent(Docu cm.addItem({ description: BoolCast(this.props.Document.lockedPosition) ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); cm.addItem({ description: "Transcribe Speech", event: this.listen, icon: "microphone" }); let makes: ContextMenuProps[] = []; - makes.push({ description: "Make Background", event: this.makeBackground, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); + makes.push({ description: this.props.Document.isBackground ? "Remove Background" : "Make Background", event: this.makeBackground, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); makes.push({ description: this.props.Document.isButton ? "Remove Button" : "Make Button", event: this.makeBtnClicked, icon: "concierge-bell" }); makes.push({ description: "Make Portal", event: () => { @@ -708,7 +709,7 @@ export class DocumentView extends DocComponent(Docu
    Date: Tue, 30 Jul 2019 23:52:44 -0400 Subject: fixed collection chrome with tree views. fixed fitToBounds calculation --- .../views/collections/CollectionTreeView.scss | 3 ++ .../collectionFreeForm/CollectionFreeFormView.tsx | 37 +++++++++++----------- 2 files changed, 21 insertions(+), 19 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index db3652ff6..990979109 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -7,6 +7,9 @@ border-radius: inherit; box-sizing: border-box; height: 100%; + width:100%; + position: absolute; + top:0; padding-top: 20px; padding-left: 10px; padding-right: 0px; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 3cead34e6..a1b1871fe 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,25 +1,35 @@ -import { action, computed, trace } from "mobx"; +import { library } from "@fortawesome/fontawesome-svg-core"; +import { faEye } from "@fortawesome/free-regular-svg-icons"; +import { faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faPaintBrush, faTable, faUpload } from "@fortawesome/free-solid-svg-icons"; +import { action, computed } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCastAsync, HeightSym, WidthSym, DocListCast } from "../../../../new_fields/Doc"; +import { Doc, DocListCastAsync, HeightSym, WidthSym } from "../../../../new_fields/Doc"; import { Id } from "../../../../new_fields/FieldSymbols"; import { InkField, StrokeData } from "../../../../new_fields/InkField"; import { createSchema, makeInterface } from "../../../../new_fields/Schema"; +import { ScriptField } from "../../../../new_fields/ScriptField"; import { BoolCast, Cast, FieldValue, NumCast, StrCast } from "../../../../new_fields/Types"; import { emptyFunction, returnOne, Utils } from "../../../../Utils"; +import { CognitiveServices } from "../../../cognitive_services/CognitiveServices"; +import { DocServer } from "../../../DocServer"; import { DocumentManager } from "../../../util/DocumentManager"; import { DragManager } from "../../../util/DragManager"; import { HistoryUtil } from "../../../util/History"; +import { CompileScript } from "../../../util/Scripting"; import { SelectionManager } from "../../../util/SelectionManager"; import { Transform } from "../../../util/Transform"; import { undoBatch, UndoManager } from "../../../util/UndoManager"; import { COLLECTION_BORDER_WIDTH } from "../../../views/globalCssVariables.scss"; -import { SubmenuProps, ContextMenuProps } from "../../ContextMenuItem"; +import { ContextMenu } from "../../ContextMenu"; +import { ContextMenuProps } from "../../ContextMenuItem"; import { InkingCanvas } from "../../InkingCanvas"; import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView"; import { DocumentContentsView } from "../../nodes/DocumentContentsView"; import { DocumentViewProps, positionSchema } from "../../nodes/DocumentView"; import { pageSchema } from "../../nodes/ImageBox"; +import { OverlayElementOptions, OverlayView } from "../../OverlayView"; import PDFMenu from "../../pdf/PDFMenu"; +import { ScriptBox } from "../../ScriptBox"; import { CollectionSubView } from "../CollectionSubView"; import { CollectionFreeFormLinksView } from "./CollectionFreeFormLinksView"; import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors"; @@ -27,19 +37,6 @@ import "./CollectionFreeFormView.scss"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); import v5 = require("uuid/v5"); -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"; -import { library } from "@fortawesome/fontawesome-svg-core"; -import { faEye } from "@fortawesome/free-regular-svg-icons"; -import { faTable, faPaintBrush, faAsterisk, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload } from "@fortawesome/free-solid-svg-icons"; -import { undo } from "prosemirror-history"; -import { number } from "prop-types"; -import { ContextMenu } from "../../ContextMenu"; -import { RouteStore } from "../../../../server/RouteStore"; -import { DocServer } from "../../../DocServer"; library.add(faEye, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload); @@ -81,11 +78,13 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @computed get contentBounds() { let bounds = this.fitToBox && !this.isAnnotationOverlay ? this.ComputeContentBounds(this.elements.filter(e => e.bounds).map(e => e.bounds!)) : undefined; - return { + let res = { 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.parentScaling }; + if (res.scale === 0) res.scale = 1; + return res; } @computed get fitToBox() { return this.props.fitToBox || this.props.Document.fitToBox; } @@ -492,11 +491,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } let docviews = docs.filter(doc => doc instanceof Doc).reduce((prev, doc) => { var page = NumCast(doc.page, -1); - let bounds: { x?: number, y?: number, width?: number, height?: number }; if ((Math.abs(Math.round(page) - Math.round(curPage)) < 3) || page === -1) { let minim = BoolCast(doc.isMinimized); if (minim === undefined || !minim) { - const pos = script ? this.getCalculatedPositions(script, { doc, index: prev.length, collection: this.Document, docs, state }) : {}; + const pos = script ? this.getCalculatedPositions(script, { doc, index: prev.length, collection: this.Document, docs, state }) : + { x: Cast(doc.x, "number"), y: Cast(doc.y, "number"), width: Cast(doc.width, "number"), height: Cast(doc.height, "number") }; state = pos.state === undefined ? state : pos.state; prev.push({ ele: , -- cgit v1.2.3-70-g09d2 From b6fa309cea934d250fe992e70e1e268f344659b5 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 31 Jul 2019 00:19:13 -0400 Subject: fixed transitions on drag --- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index a1b1871fe..33a584c6f 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -498,7 +498,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { { x: Cast(doc.x, "number"), y: Cast(doc.y, "number"), width: Cast(doc.width, "number"), height: Cast(doc.height, "number") }; state = pos.state === undefined ? state : pos.state; prev.push({ - ele: , + ele: , bounds: (pos.x !== undefined && pos.y !== undefined && pos.width !== undefined && pos.height !== undefined) ? { x: pos.x, y: pos.y, width: pos.width, height: pos.height } : undefined }); } -- cgit v1.2.3-70-g09d2 From 49faa4e76f91fed04bb1923d81dd23d57a157a63 Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 31 Jul 2019 07:35:41 -0400 Subject: grouping experiment --- .../collectionFreeForm/CollectionFreeFormView.tsx | 49 +++++++++++++++++++++- src/client/views/nodes/DocumentView.tsx | 1 + 2 files changed, 49 insertions(+), 1 deletion(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 8dac785e1..c4c3ba084 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -38,6 +38,7 @@ import { faTable, faPaintBrush, faAsterisk, faExpandArrowsAlt, faCompressArrowsA import { undo } from "prosemirror-history"; import { number } from "prop-types"; import { ContextMenu } from "../../ContextMenu"; +import { SwatchesPicker } from "react-color"; library.add(faEye, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass); @@ -125,7 +126,24 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { return Doc.resolvedFieldDataDoc(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, "true"); } - + intersectRect(r1: { left: number, top: number, width: number, height: number }, + r2: { left: number, top: number, width: number, height: number }) { + return !(r2.left > r1.left + r1.width || r2.left + r2.width < r1.left || r2.top > r1.top + r1.height || r2.top + r2.height < r1.top); + } + bounsdSelect(doc: Doc, doc2: Doc) { + var x2 = NumCast(doc2.x) - 25; + var y2 = NumCast(doc2.y) - 25; + var w2 = NumCast(doc2.width) + 25; + var h2 = NumCast(doc2.height) + 25; + var x = NumCast(doc.x) - 25; + var y = NumCast(doc.y) - 25; + var w = NumCast(doc.width) + 25; + var h = NumCast(doc.height) + 25; + if (this.intersectRect({ left: x, top: y, width: w, height: h }, { left: x2, top: y2, width: w2, height: h2 })) { + return true; + } + return false; + } @undoBatch @action drop = (e: Event, de: DragManager.DropEvent) => { @@ -151,6 +169,35 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } this.bringToFront(d); }); + + let sets: (Doc[])[] = [] + this.childDocs.map(c => { + let included = [] + for (let i = 0; i < sets.length; i++) { + for (let j = 0; j < sets[i].length; j++) { + if (this.bounsdSelect(c, sets[i][j])) { + included.push(i); + break; + } + } + } + if (included.length === 0) + sets.push([c]); + else if (included.length === 1) + sets[included[0]].push(c); + else { + sets[included[0]].push(c); + for (let s = 1; s < included.length; s++) { + sets[included[0]].push(...sets[included[s]]); + sets[included[s]].length = 0; + } + } + }); + for (let s = 0; s < sets.length; s++) { + for (let i = 0; i < sets[s].length; i++) { + Doc.GetProto(sets[s][i]).cluster = s; + } + } } } else if (de.data instanceof DragManager.AnnotationDragData) { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 4b5cf3a43..2dc2c18a3 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -694,6 +694,7 @@ export class DocumentView extends DocComponent(Docu color: foregroundColor, outlineColor: "maroon", outlineStyle: "dashed", + boxShadow: `#9c9396 ${StrCast(this.props.Document.boxShadow, "0vw 0vw 11vw")}`, outlineWidth: BoolCast(this.layoutDoc.libraryBrush) && !StrCast(Doc.GetProto(this.props.Document).borderRounding) ? `${this.props.ScreenToLocalTransform().Scale}px` : "0px", marginLeft: BoolCast(this.layoutDoc.libraryBrush) && StrCast(Doc.GetProto(this.props.Document).borderRounding) ? -- cgit v1.2.3-70-g09d2 From a4000aa57c9934ff3a68ba17474936836a5f235e Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 1 Aug 2019 10:35:02 -0400 Subject: tweaks --- src/client/views/ContextMenu.tsx | 2 +- src/client/views/DocumentDecorations.tsx | 1 - .../collections/collectionFreeForm/CollectionFreeFormView.tsx | 11 +++++------ src/client/views/nodes/DocumentView.tsx | 6 ------ 4 files changed, 6 insertions(+), 14 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index 98025ac31..1bf6e383d 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -42,7 +42,7 @@ export class ContextMenu extends React.Component { return this._items.find(menuItem => { let reference = menuItem.description; toLowerCase && (reference = reference.toLowerCase()); - reference === target; + return reference === target; }); } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index eccfc7c30..15471371a 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -541,7 +541,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> if (fixedAspect && (!nwidth || !nheight)) { proto.nativeWidth = nwidth = doc.width || 0; proto.nativeHeight = nheight = doc.height || 0; - proto.ignoreAspect = true; } if (nwidth > 0 && nheight > 0 && !BoolCast(proto.ignoreAspect)) { if (Math.abs(dW) > Math.abs(dH)) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 33a584c6f..29f9b1429 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -131,10 +131,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @action drop = (e: Event, de: DragManager.DropEvent) => { let xf = this.getTransform(); + let [xp, yp] = xf.transformPoint(de.x, de.y); if (super.drop(e, de)) { if (de.data instanceof DragManager.DocumentDragData) { if (de.data.droppedDocuments.length) { - 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); @@ -157,7 +157,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { else if (de.data instanceof DragManager.AnnotationDragData) { if (de.data.dropDocument) { let dragDoc = de.data.dropDocument; - let [xp, yp] = this.getTransform().transformPoint(de.x, de.y); let x = xp - de.data.xOffset; let y = yp - de.data.yOffset; let dropX = NumCast(de.data.dropDocument.x); @@ -224,10 +223,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; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 14f0b5a5a..d4adcfca1 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -586,12 +586,6 @@ export class DocumentView extends DocComponent(Docu }, icon: "window-restore" }); cm.addItem({ description: "Make...", subitems: makes, icon: "hand-point-right" }); - // cm.addItem({ - // description: "Find aliases", event: async () => { - // const aliases = await SearchUtil.GetAliasesOfDocument(this.props.Document); - // this.props.addDocTab && this.props.addDocTab(Docs.Create.SchemaDocument(["title"], aliases, {}), undefined, "onRight"); // bcz: dataDoc? - // }, icon: "search" - // }); if (this.props.Document.detailedLayout && !this.props.Document.isTemplate) { cm.addItem({ description: "Toggle detail", event: () => Doc.ToggleDetailLayout(this.props.Document), icon: "image" }); } -- cgit v1.2.3-70-g09d2 From 6f3d4a7015e15e0523fc194f7f911f6d45259165 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 1 Aug 2019 13:57:59 -0400 Subject: added floating docs --- src/client/util/DragManager.ts | 7 +++- src/client/views/TemplateMenu.tsx | 44 ++++++++++++++++++++- .../collectionFreeForm/CollectionFreeFormView.scss | 1 + .../collectionFreeForm/CollectionFreeFormView.tsx | 46 +++++++++++++++------- .../views/nodes/CollectionFreeFormDocumentView.tsx | 1 + src/client/views/nodes/DocumentView.tsx | 42 +++++++++----------- 6 files changed, 100 insertions(+), 41 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index abcc3a4e1..0b5c785a4 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -10,6 +10,7 @@ import { LinkManager } from "./LinkManager"; import { SelectionManager } from "./SelectionManager"; import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField"; import { DocumentDecorations } from "../views/DocumentDecorations"; +import { NumberLiteralType } from "typescript"; export type dropActionType = "alias" | "copy" | undefined; export function SetupDrag( @@ -140,6 +141,10 @@ export namespace DragManager { dragHasStarted?: () => void; withoutShiftDrag?: boolean; + + offsetX?: number; + + offsetY?: number; } export interface DragDropDisposer { @@ -423,7 +428,7 @@ export namespace DragManager { lastX = e.pageX; lastY = e.pageY; dragElements.map((dragElement, i) => (dragElement.style.transform = - `translate(${(xs[i] += moveX)}px, ${(ys[i] += moveY)}px) scale(${scaleXs[i]}, ${scaleYs[i]})`) + `translate(${(xs[i] += moveX) + (options ? (options.offsetX || 0) : 0)}px, ${(ys[i] += moveY) + (options ? (options.offsetY || 0) : 0)}px) scale(${scaleXs[i]}, ${scaleYs[i]})`) ); }; diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index 1b32f0ddd..c413650f0 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -7,6 +7,10 @@ import { DocumentView } from "./nodes/DocumentView"; import { Template } from "./Templates"; import React = require("react"); import { undoBatch } from "../util/UndoManager"; +import { DocumentManager } from "../util/DocumentManager"; +import { NumCast } from "../../new_fields/Types"; +import { DragManager } from "../util/DragManager"; +import { SelectionManager } from "../util/SelectionManager"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -35,11 +39,46 @@ export interface TemplateMenuProps { @observer export class TemplateMenu extends React.Component { @observable private _hidden: boolean = true; + dragRef = React.createRef(); constructor(props: TemplateMenuProps) { super(props); } + toggleFloat = (e: React.MouseEvent): void => { + SelectionManager.DeselectAll(); + let topDocView = this.props.docs[0]; + let topDoc = topDocView.props.Document; + let xf = topDocView.props.ScreenToLocalTransform(); + let ex = e.clientX; + let ey = e.clientY; + undoBatch(action(() => topDoc.z = topDoc.z ? 0 : 1))(); + if (!topDoc.z) { + setTimeout(() => { + let newDocView = DocumentManager.Instance.getDocumentView(topDoc); + if (newDocView) { + let de = new DragManager.DocumentDragData([topDoc], [undefined]); + de.moveDocument = topDocView.props.moveDocument; + let xf = newDocView.ContentDiv!.getBoundingClientRect(); + console.log("ex = " + ex + " " + xf.left + " " + (ex - xf.left)); + DragManager.StartDocumentDrag([newDocView.ContentDiv!], de, ex, ey, { + offsetX: (ex - xf.left), offsetY: (ey - xf.top), + handlers: { + dragComplete: () => { }, + }, + hideSource: false + }); + } + }, 10); + } else if (topDocView.props.ContainingCollectionView) { + let collView = topDocView.props.ContainingCollectionView!; + let [sx, sy] = xf.inverse().transformPoint(0, 0); + let [x, y] = collView.props.ScreenToLocalTransform().transformPoint(sx, sy); + topDoc.x = x; + topDoc.y = y; + } + } + @undoBatch @action toggleTemplate = (event: React.ChangeEvent, template: Template): void => { @@ -89,9 +128,10 @@ export class TemplateMenu extends React.Component { return (
    this.toggleTemplateActivity()}>+
    -
      +
        {templateMenu} - + +
    ); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss index cca199afa..c4311fa52 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss @@ -46,6 +46,7 @@ border-radius: inherit; box-sizing: border-box; position: absolute; + overflow: hidden; .marqueeView { overflow: hidden; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 29f9b1429..9cbc652b9 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -77,7 +77,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } @computed get contentBounds() { - let bounds = this.fitToBox && !this.isAnnotationOverlay ? this.ComputeContentBounds(this.elements.filter(e => e.bounds).map(e => e.bounds!)) : undefined; + let bounds = this.fitToBox && !this.isAnnotationOverlay ? this.ComputeContentBounds(this.elements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!)) : undefined; let res = { panX: bounds ? (bounds.x + bounds.r) / 2 : this.Document.panX || 0, panY: bounds ? (bounds.y + bounds.b) / 2 : this.Document.panY || 0, @@ -98,6 +98,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { 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 getTransformOverlay = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth + 1, -this.borderWidth + 1); 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()); private addLiveTextBox = (newBox: Doc) => { @@ -131,12 +132,15 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @action drop = (e: Event, de: DragManager.DropEvent) => { let xf = this.getTransform(); + let xfo = this.getTransformOverlay(); let [xp, yp] = xf.transformPoint(de.x, de.y); + let [xpo, ypo] = xfo.transformPoint(de.x, de.y); if (super.drop(e, de)) { if (de.data instanceof DragManager.DocumentDragData) { if (de.data.droppedDocuments.length) { - let x = xp - de.data.xOffset; - let y = yp - de.data.yOffset; + let z = NumCast(de.data.draggedDocuments[0].z); + let x = (z ? xpo : xp) - de.data.xOffset; + let y = (z ? ypo : yp) - de.data.yOffset; let dropX = NumCast(de.data.droppedDocuments[0].x); let dropY = NumCast(de.data.droppedDocuments[0].y); de.data.droppedDocuments.forEach(d => { @@ -292,8 +296,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { const newPanY = Math.min((1 - 1 / scale) * this.nativeHeight, Math.max(0, panY)); this.props.Document.panX = this.isAnnotationOverlay ? newPanX : panX; this.props.Document.panY = this.isAnnotationOverlay && StrCast(this.props.Document.backgroundLayout).indexOf("PDFBox") === -1 ? newPanY : panY; - // this.props.Document.panX = panX; - // this.props.Document.panY = panY; if (this.props.Document.scrollY) { this.props.Document.scrollY = panY - scale * this.props.Document[HeightSym](); } @@ -396,7 +398,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { addDocument: this.props.addDocument, removeDocument: this.props.removeDocument, moveDocument: this.props.moveDocument, - ScreenToLocalTransform: this.getTransform, + ScreenToLocalTransform: pair.layout.z ? this.getTransformOverlay : this.getTransform, renderDepth: this.props.renderDepth + 1, selectOnLoad: pair.layout[Id] === this._selectOnLoaded, PanelWidth: pair.layout[WidthSym], @@ -436,23 +438,25 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { }; } - getCalculatedPositions(script: ScriptField, params: { doc: Doc, index: number, collection: Doc, docs: Doc[], state: any }): { x?: number, y?: number, width?: number, height?: number, state?: any } { + getCalculatedPositions(script: ScriptField, params: { doc: Doc, index: number, collection: Doc, docs: Doc[], state: any }): { x?: number, y?: number, z?: number, width?: number, height?: number, state?: any } { const result = script.script.run(params); if (!result.success) { return {}; } - return result.result === undefined ? {} : result.result; + let doc = params.doc; + return result.result === undefined ? { x: Cast(doc.x, "number"), y: Cast(doc.y, "number"), z: Cast(doc.z, "number"), width: Cast(doc.width, "number"), height: Cast(doc.height, "number") } : result.result; } - private viewDefToJSX(viewDef: any): { ele: JSX.Element, bounds?: { x: number, y: number, width: number, height: number } } | undefined { + private viewDefToJSX(viewDef: any): { ele: JSX.Element, bounds?: { x: number, y: number, z?: number, width: number, height: number } } | undefined { if (viewDef.type === "text") { const text = Cast(viewDef.text, "string"); const x = Cast(viewDef.x, "number"); const y = Cast(viewDef.y, "number"); + const z = Cast(viewDef.z, "number"); const width = Cast(viewDef.width, "number"); const height = Cast(viewDef.height, "number"); const fontSize = Cast(viewDef.fontSize, "number"); - if ([text, x, y, width, height].some(val => val === undefined)) { + if ([text, x, y, z, width, height].some(val => val === undefined)) { return undefined; } @@ -460,7 +464,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { ele:
    {text}
    , bounds: { x: x!, y: y!, width: width!, height: height! } + }}>{text}
    , bounds: { x: x!, y: y!, z: z, width: width!, height: height! } }; } } @@ -472,7 +476,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { const script = this.Document.arrangeScript; let state: any = undefined; const docs = this.childDocs; - let elements: { ele: JSX.Element, bounds?: { x: number, y: number, width: number, height: number } }[] = []; + let elements: { ele: JSX.Element, bounds?: { x: number, y: number, z?: number, width: number, height: number } }[] = []; if (initScript) { const initResult = initScript.script.run({ docs, collection: this.Document }); if (initResult.success) { @@ -494,13 +498,13 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { let minim = BoolCast(doc.isMinimized); if (minim === undefined || !minim) { const pos = script ? this.getCalculatedPositions(script, { doc, index: prev.length, collection: this.Document, docs, state }) : - { x: Cast(doc.x, "number"), y: Cast(doc.y, "number"), width: Cast(doc.width, "number"), height: Cast(doc.height, "number") }; + { x: Cast(doc.x, "number"), y: Cast(doc.y, "number"), z: Cast(doc.z, "number"), width: Cast(doc.width, "number"), height: Cast(doc.height, "number") }; state = pos.state === undefined ? state : pos.state; prev.push({ ele: , - bounds: (pos.x !== undefined && pos.y !== undefined && pos.width !== undefined && pos.height !== undefined) ? { x: pos.x, y: pos.y, width: pos.width, height: pos.height } : undefined + bounds: (pos.x !== undefined && pos.y !== undefined && pos.width !== undefined && pos.height !== undefined) ? { x: pos.x, y: pos.y, z: pos.z, width: pos.width, height: pos.height } : undefined }); } } @@ -514,8 +518,13 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @computed.struct get views() { - return this.elements.map(ele => ele.ele); + return this.elements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele); } + @computed.struct + get overlayViews() { + return this.elements.filter(ele => ele.bounds && ele.bounds.z).map(ele => ele.ele); + } + @action onCursorMove = (e: React.PointerEvent) => { @@ -603,6 +612,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { , ...this.views ] + private overlayChildViews = () => { + console.log(this.overlayViews.length); + return [ + ...this.overlayViews + ]; + } public static AddCustomLayout(doc: Doc, dataKey: string): () => void { return () => { @@ -651,6 +666,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { + {this.overlayChildViews()}
    ); diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 7ffd760e0..3b6c443c2 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -77,6 +77,7 @@ export class CollectionFreeFormDocumentView extends DocComponent; -- cgit v1.2.3-70-g09d2 From 65f23d993c091ee7fa61becea85b5f60ec2e9104 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 1 Aug 2019 15:44:22 -0400 Subject: added dragging of clusters. --- .../collectionFreeForm/CollectionFreeFormView.tsx | 37 +++++++++++++++++----- src/client/views/nodes/DocumentView.tsx | 3 +- 2 files changed, 31 insertions(+), 9 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 8cac60edb..12e5924b4 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -131,15 +131,16 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { r2: { left: number, top: number, width: number, height: number }) { return !(r2.left > r1.left + r1.width || r2.left + r2.width < r1.left || r2.top > r1.top + r1.height || r2.top + r2.height < r1.top); } + _groupingBorder = 150; bounsdSelect(doc: Doc, doc2: Doc) { - var x2 = NumCast(doc2.x) - 25; - var y2 = NumCast(doc2.y) - 25; - var w2 = NumCast(doc2.width) + 25; - var h2 = NumCast(doc2.height) + 25; - var x = NumCast(doc.x) - 25; - var y = NumCast(doc.y) - 25; - var w = NumCast(doc.width) + 25; - var h = NumCast(doc.height) + 25; + var x2 = NumCast(doc2.x) - this._groupingBorder; + var y2 = NumCast(doc2.y) - this._groupingBorder; + var w2 = NumCast(doc2.width) + this._groupingBorder; + var h2 = NumCast(doc2.height) + this._groupingBorder; + var x = NumCast(doc.x) - this._groupingBorder; + var y = NumCast(doc.y) - this._groupingBorder; + var w = NumCast(doc.width) + this._groupingBorder; + var h = NumCast(doc.height) + this._groupingBorder; if (this.intersectRect({ left: x, top: y, width: w, height: h }, { left: x2, top: y2, width: w2, height: h2 })) { return true; } @@ -242,6 +243,26 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @action onPointerMove = (e: PointerEvent): void => { if (!e.cancelBubble) { + let probe = this.getTransform().transformPoint(e.clientX, e.clientY); + let cluster = this.childDocs.reduce((cluster, cd) => { + let cx = NumCast(cd.x) - this._groupingBorder; + let cy = NumCast(cd.y) - this._groupingBorder; + let cw = NumCast(cd.width) + this._groupingBorder; + let ch = NumCast(cd.height) + this._groupingBorder; + if (this.intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 })) + return NumCast(cd.cluster); + return cluster; + }, -1); + if (cluster !== -1) { + let eles = this.childDocs.filter(cd => NumCast(cd.cluster) === cluster); + this.selectDocuments(eles); + DragManager.StartDocumentDrag(SelectionManager.SelectedDocuments().map(v => v.ContentDiv!), new DragManager.DocumentDragData(eles, eles.map(d => undefined)), e.clientX, e.clientY); + e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers + e.preventDefault(); + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + return; + } let x = this.Document.panX || 0; let y = this.Document.panY || 0; let docs = this.childDocs || []; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index c5cf2e23a..fa1e824f8 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -695,6 +695,7 @@ export class DocumentView extends DocComponent(Docu }); } let showTextTitle = showTitle && StrCast(this.layoutDoc.layout).startsWith("(Docu color: foregroundColor, outlineColor: "maroon", outlineStyle: "dashed", - boxShadow: `#9c9396 ${StrCast(this.props.Document.boxShadow, "0vw 0vw 11vw")}`, + boxShadow: `${colors[NumCast(this.props.Document.cluster) % colors.length]} ${StrCast(this.props.Document.boxShadow, "0vw 0vw 11vw")}`, outlineWidth: BoolCast(this.layoutDoc.libraryBrush) && !StrCast(Doc.GetProto(this.props.Document).borderRounding) ? `${this.props.ScreenToLocalTransform().Scale}px` : "0px", marginLeft: BoolCast(this.layoutDoc.libraryBrush) && StrCast(Doc.GetProto(this.props.Document).borderRounding) ? -- cgit v1.2.3-70-g09d2 From 64d25bd7a92daa4a8b9ee69f0eabd88e5cbeeca5 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 1 Aug 2019 16:04:43 -0400 Subject: cleaned up detaillayout for pivot viewer. --- .../collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/new_fields/Doc.ts | 9 ++------- 2 files changed, 3 insertions(+), 8 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 9cbc652b9..90f67c54e 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -456,7 +456,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { const width = Cast(viewDef.width, "number"); const height = Cast(viewDef.height, "number"); const fontSize = Cast(viewDef.fontSize, "number"); - if ([text, x, y, z, width, height].some(val => val === undefined)) { + if ([text, x, y, width, height].some(val => val === undefined)) { return undefined; } diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 6e2c5e697..b70951040 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -525,19 +525,14 @@ export namespace Doc { } export function UseDetailLayout(d: Doc) { runInAction(async () => { - const dl1 = d.detailedLayout; - let detailLayout1 = await PromiseValue(dl1); - const dl2 = d.detailedLayout; - let detailLayout = await PromiseValue(dl2); + let detailLayout = await d.detailedLayout; if (detailLayout) { d.layout = detailLayout; d.nativeWidth = d.nativeHeight = undefined; if (detailLayout instanceof Doc) { let delegDetailLayout = Doc.MakeDelegate(detailLayout) as Doc; d.layout = delegDetailLayout; - let subDetailLayout1 = await PromiseValue(delegDetailLayout.detailedLayout); - let subDetailLayout = await PromiseValue(delegDetailLayout.detailedLayout); - delegDetailLayout.layout = subDetailLayout; + delegDetailLayout.layout = await delegDetailLayout.detailedLayout; } } }); -- cgit v1.2.3-70-g09d2 From 8cd098ab0e348c614640bc0971f3859161b8c2b4 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 2 Aug 2019 16:39:03 -0400 Subject: fixed drag interactions with groups. --- .../collectionFreeForm/CollectionFreeFormView.tsx | 18 ++++++++++++++++-- src/client/views/nodes/DocumentView.tsx | 4 ++-- 2 files changed, 18 insertions(+), 4 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 12e5924b4..3ff816c46 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -131,7 +131,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { r2: { left: number, top: number, width: number, height: number }) { return !(r2.left > r1.left + r1.width || r2.left + r2.width < r1.left || r2.top > r1.top + r1.height || r2.top + r2.height < r1.top); } - _groupingBorder = 150; + _groupingBorder = 100; bounsdSelect(doc: Doc, doc2: Doc) { var x2 = NumCast(doc2.x) - this._groupingBorder; var y2 = NumCast(doc2.y) - this._groupingBorder; @@ -256,7 +256,21 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { if (cluster !== -1) { let eles = this.childDocs.filter(cd => NumCast(cd.cluster) === cluster); this.selectDocuments(eles); - DragManager.StartDocumentDrag(SelectionManager.SelectedDocuments().map(v => v.ContentDiv!), new DragManager.DocumentDragData(eles, eles.map(d => undefined)), e.clientX, e.clientY); + let clusterDocs = SelectionManager.SelectedDocuments(); + SelectionManager.DeselectAll(); + let de = new DragManager.DocumentDragData(eles, eles.map(d => undefined)); + de.moveDocument = this.props.moveDocument; + const [left, top] = clusterDocs[0].props.ScreenToLocalTransform().scale(clusterDocs[0].props.ContentScaling()).inverse().transformPoint(0, 0); + const [xoff, yoff] = this.getTransform().transformDirection(e.x - left, e.y - top); + de.dropAction = e.ctrlKey || e.altKey ? "alias" : undefined; + de.xOffset = xoff; + de.yOffset = yoff; + DragManager.StartDocumentDrag(clusterDocs.map(v => v.ContentDiv!), de, e.clientX, e.clientY, { + handlers: { + dragComplete: action(emptyFunction) + }, + hideSource: !de.dropAction + }); e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers e.preventDefault(); document.removeEventListener("pointermove", this.onPointerMove); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index fa1e824f8..d79b58a7b 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -695,7 +695,7 @@ export class DocumentView extends DocComponent(Docu }); } let showTextTitle = showTitle && StrCast(this.layoutDoc.layout).startsWith("(Docu color: foregroundColor, outlineColor: "maroon", outlineStyle: "dashed", - boxShadow: `${colors[NumCast(this.props.Document.cluster) % colors.length]} ${StrCast(this.props.Document.boxShadow, "0vw 0vw 11vw")}`, + boxShadow: `${colors[NumCast(this.props.Document.cluster) % colors.length]} ${StrCast(this.props.Document.boxShadow, `0vw 0vw ${50 / this.props.ContentScaling()}px`)}`, outlineWidth: BoolCast(this.layoutDoc.libraryBrush) && !StrCast(Doc.GetProto(this.props.Document).borderRounding) ? `${this.props.ScreenToLocalTransform().Scale}px` : "0px", marginLeft: BoolCast(this.layoutDoc.libraryBrush) && StrCast(Doc.GetProto(this.props.Document).borderRounding) ? -- cgit v1.2.3-70-g09d2 From fe9017c3cdc2481f4bcce40dcc1514a6f7af37dc Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 2 Aug 2019 18:21:23 -0400 Subject: improvements to grouping with backgrounds --- .../views/collections/CollectionBaseView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 68 ++++++++++++---------- src/client/views/nodes/DocumentView.tsx | 9 ++- 3 files changed, 44 insertions(+), 35 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index c595a4c56..6801b94fd 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -147,7 +147,7 @@ export class CollectionBaseView extends React.Component {
    diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 232ac22c9..bc1aee159 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -108,6 +108,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { private addDocument = (newBox: Doc, allowDuplicates: boolean) => { this.props.addDocument(newBox, false); this.bringToFront(newBox); + this.updateClusters(); return true; } private selectDocuments = (docs: Doc[]) => { @@ -175,34 +176,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { this.bringToFront(d); }); - let sets: (Doc[])[] = [] - this.childDocs.map(c => { - let included = [] - for (let i = 0; i < sets.length; i++) { - for (let j = 0; j < sets[i].length; j++) { - if (this.bounsdSelect(c, sets[i][j])) { - included.push(i); - break; - } - } - } - if (included.length === 0) - sets.push([c]); - else if (included.length === 1) - sets[included[0]].push(c); - else { - sets[included[0]].push(c); - for (let s = 1; s < included.length; s++) { - sets[included[0]].push(...sets[included[s]]); - sets[included[s]].length = 0; - } - } - }); - for (let s = 0; s < sets.length; s++) { - for (let i = 0; i < sets[s].length; i++) { - Doc.GetProto(sets[s][i]).cluster = s; - } - } + this.updateClusters(); } } else if (de.data instanceof DragManager.AnnotationDragData) { @@ -223,6 +197,38 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { return false; } + @action + updateClusters() { + let sets: (Doc[])[] = [] + this.childDocs.map(c => { + let included = [] + for (let i = 0; i < sets.length; i++) { + for (let j = 0; j < sets[i].length; j++) { + if (this.bounsdSelect(c, sets[i][j])) { + included.push(i); + break; + } + } + } + if (included.length === 0) + sets.push([c]); + else if (included.length === 1) + sets[included[0]].push(c); + else { + sets[included[0]].push(c); + for (let s = 1; s < included.length; s++) { + sets[included[0]].push(...sets[included[s]]); + sets[included[s]].length = 0; + } + } + }); + for (let s = 0; s < sets.length; s++) { + for (let i = 0; i < sets[s].length; i++) { + Doc.GetProto(sets[s][i]).cluster = s; + } + } + } + @action onPointerDown = (e: React.PointerEvent): void => { if (e.button === 0 && !e.shiftKey && !e.altKey && (!this.isAnnotationOverlay || this.zoomScaling() !== 1) && this.props.active()) { @@ -247,8 +253,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { let cluster = this.childDocs.reduce((cluster, cd) => { let cx = NumCast(cd.x) - this._groupingBorder; let cy = NumCast(cd.y) - this._groupingBorder; - let cw = NumCast(cd.width) + this._groupingBorder; - let ch = NumCast(cd.height) + this._groupingBorder; + let cw = NumCast(cd.width) + 2 * this._groupingBorder; + let ch = NumCast(cd.height) + 2 * this._groupingBorder; if (this.intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 })) return NumCast(cd.cluster); return cluster; @@ -392,7 +398,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } bringToFront = (doc: Doc, sendToBack?: boolean) => { - if (sendToBack) { + if (sendToBack || doc.isBackground) { doc.zIndex = 0; return; } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index d79b58a7b..16ae5471d 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -695,7 +695,8 @@ export class DocumentView extends DocComponent(Docu }); } let showTextTitle = showTitle && StrCast(this.layoutDoc.layout).startsWith("(Docu color: foregroundColor, outlineColor: "maroon", outlineStyle: "dashed", - boxShadow: `${colors[NumCast(this.props.Document.cluster) % colors.length]} ${StrCast(this.props.Document.boxShadow, `0vw 0vw ${50 / this.props.ContentScaling()}px`)}`, + boxShadow: this.layoutDoc.isBackground ? + `0px 0px 50px 50px ${groupCol}` : + `${groupCol} ${StrCast(this.props.Document.boxShadow, `0vw 0vw ${50 / this.props.ContentScaling()}px`)}`, outlineWidth: BoolCast(this.layoutDoc.libraryBrush) && !StrCast(Doc.GetProto(this.props.Document).borderRounding) ? `${this.props.ScreenToLocalTransform().Scale}px` : "0px", marginLeft: BoolCast(this.layoutDoc.libraryBrush) && StrCast(Doc.GetProto(this.props.Document).borderRounding) ? @@ -714,7 +717,7 @@ export class DocumentView extends DocComponent(Docu border: BoolCast(this.layoutDoc.libraryBrush) && StrCast(Doc.GetProto(this.props.Document).borderRounding) ? `dashed maroon ${this.props.ScreenToLocalTransform().Scale}px` : undefined, borderRadius: "inherit", - background: backgroundColor, + background: this.layoutDoc.isBackground ? groupCol : backgroundColor, width: nativeWidth, height: nativeHeight, transform: `scale(${this.props.ContentScaling()})`, -- cgit v1.2.3-70-g09d2 From 753615c80d4cf08605ebaaeeaf0a44a0fd88d898 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 3 Aug 2019 15:21:10 -0400 Subject: working version of clustering --- src/Utils.ts | 2 + src/client/documents/Documents.ts | 3 +- src/client/util/DragManager.ts | 8 +- src/client/views/MainView.tsx | 4 +- src/client/views/TemplateMenu.tsx | 5 +- .../views/collections/CollectionDockingView.tsx | 3 +- .../views/collections/CollectionSchemaView.tsx | 3 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 142 +++++++++++++-------- .../collections/collectionFreeForm/MarqueeView.tsx | 1 + .../views/nodes/CollectionFreeFormDocumentView.tsx | 12 +- src/client/views/nodes/DocumentView.tsx | 17 +-- .../views/presentationview/PresentationElement.tsx | 3 +- src/client/views/search/SearchItem.tsx | 3 +- 13 files changed, 125 insertions(+), 81 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/Utils.ts b/src/Utils.ts index 8df67df5d..502540eb0 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -140,6 +140,8 @@ export function returnOne() { return 1; } export function returnZero() { return 0; } +export function returnEmptyString() { return ""; } + export function emptyFunction() { } export type Without = Pick>; diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 09bafcf43..07e38a4c0 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -84,6 +84,7 @@ export interface DocumentOptions { templates?: List; viewType?: number; backgroundColor?: string; + defaultBackgroundColor?: string; dropAction?: dropActionType; backgroundLayout?: string; chromeStatus?: string; @@ -124,7 +125,7 @@ export namespace Docs { const TemplateMap: TemplateMap = new Map([ [DocumentType.TEXT, { layout: { view: FormattedTextBox }, - options: { height: 150, backgroundColor: "#f1efeb" } + options: { height: 150, backgroundColor: "#f1efeb", defaultBackgroundColor: "#f1efeb" } }], [DocumentType.HIST, { layout: { view: HistogramBox, collectionView: [CollectionView, data] as CollectionViewType }, diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 0b5c785a4..a7aaaed7c 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -404,7 +404,8 @@ export namespace DragManager { hideSource = options.hideSource(); } } - eles.map(ele => (ele.hidden = hideSource)); + eles.map(ele => (ele.hidden = hideSource) && + (ele.parentElement && ele.parentElement.className.indexOf("collectionFreeFormDocumentView") !== -1 && (ele.parentElement.hidden = hideSource))); let lastX = downX; let lastY = downY; @@ -434,7 +435,10 @@ export namespace DragManager { let hideDragElements = () => { dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement)); - eles.map(ele => (ele.hidden = false)); + eles.map(ele => { + ele.hidden = false; + (ele.parentElement && ele.parentElement.className.indexOf("collectionFreeFormDocumentView") !== -1 && (ele.parentElement.hidden = false)); + }); }; let endDrag = () => { document.removeEventListener("pointermove", moveHandler, true); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 2ecf5fd85..53f589684 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -15,7 +15,7 @@ import { listSpec } from '../../new_fields/Schema'; import { Cast, FieldValue, NumCast, BoolCast, StrCast } from '../../new_fields/Types'; import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; import { RouteStore } from '../../server/RouteStore'; -import { emptyFunction, returnOne, returnTrue, Utils } from '../../Utils'; +import { emptyFunction, returnOne, returnTrue, Utils, returnEmptyString } from '../../Utils'; import { DocServer } from '../DocServer'; import { Docs } from '../documents/Documents'; import { SetupDrag } from '../util/DragManager'; @@ -270,6 +270,7 @@ export class MainView extends React.Component { PanelWidth={this.getPWidth} PanelHeight={this.getPHeight} renderDepth={0} + backgroundColor={returnEmptyString} selectOnLoad={false} focus={emptyFunction} parentActive={returnTrue} @@ -334,6 +335,7 @@ export class MainView extends React.Component { renderDepth={0} selectOnLoad={false} focus={emptyFunction} + backgroundColor={returnEmptyString} parentActive={returnTrue} whenActiveChanged={emptyFunction} bringToFront={emptyFunction} diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index e654a0644..6dd908445 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -60,12 +60,9 @@ export class TemplateMenu extends React.Component { let de = new DragManager.DocumentDragData([topDoc], [undefined]); de.moveDocument = topDocView.props.moveDocument; let xf = newDocView.ContentDiv!.getBoundingClientRect(); - console.log("ex = " + ex + " " + xf.left + " " + (ex - xf.left)); DragManager.StartDocumentDrag([newDocView.ContentDiv!], de, ex, ey, { offsetX: (ex - xf.left), offsetY: (ey - xf.top), - handlers: { - dragComplete: () => { }, - }, + handlers: { dragComplete: () => { }, }, hideSource: false }); } diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 588102f01..f559480ed 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -10,7 +10,7 @@ import { Id } from '../../../new_fields/FieldSymbols'; import { FieldId } from "../../../new_fields/RefField"; import { listSpec } from "../../../new_fields/Schema"; import { Cast, NumCast, StrCast, BoolCast } from "../../../new_fields/Types"; -import { emptyFunction, returnTrue, Utils, returnOne } from "../../../Utils"; +import { emptyFunction, returnTrue, Utils, returnOne, returnEmptyString } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { DocumentManager } from '../../util/DocumentManager'; import { DragLinksAsDocuments, DragManager } from "../../util/DragManager"; @@ -607,6 +607,7 @@ export class DockedFrameRenderer extends React.Component { parentActive={returnTrue} whenActiveChanged={emptyFunction} focus={emptyFunction} + backgroundColor={returnEmptyString} addDocTab={this.addDocTab} ContainingCollectionView={undefined} zoomToScale={emptyFunction} diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 9efd0d3ec..8218877ba 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -6,7 +6,7 @@ import { action, computed, observable, trace, untracked } from "mobx"; import { observer } from "mobx-react"; import ReactTable, { CellInfo, ComponentPropsGetterR, Column, RowInfo, ResizedChangeFunction, Resize } from "react-table"; import "react-table/react-table.css"; -import { emptyFunction, returnOne } from "../../../Utils"; +import { emptyFunction, returnOne, returnEmptyString } from "../../../Utils"; import { Doc, DocListCast, Field, Opt } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; @@ -999,6 +999,7 @@ export class CollectionSchemaPreview extends React.Component r1.left + r1.width || r2.left + r2.width < r1.left || r2.top > r1.top + r1.height || r2.top + r2.height < r1.top); } - _groupingBorder = 100; + _clusterDistance = 75; bounsdSelect(doc: Doc, doc2: Doc) { - var x2 = NumCast(doc2.x) - this._groupingBorder; - var y2 = NumCast(doc2.y) - this._groupingBorder; - var w2 = NumCast(doc2.width) + this._groupingBorder; - var h2 = NumCast(doc2.height) + this._groupingBorder; - var x = NumCast(doc.x) - this._groupingBorder; - var y = NumCast(doc.y) - this._groupingBorder; - var w = NumCast(doc.width) + this._groupingBorder; - var h = NumCast(doc.height) + this._groupingBorder; - if (this.intersectRect({ left: x, top: y, width: w, height: h }, { left: x2, top: y2, width: w2, height: h2 })) { + var x2 = NumCast(doc2.x) - this._clusterDistance; + var y2 = NumCast(doc2.y) - this._clusterDistance; + var w2 = NumCast(doc2.width) + this._clusterDistance; + var h2 = NumCast(doc2.height) + this._clusterDistance; + var x = NumCast(doc.x) - this._clusterDistance; + var y = NumCast(doc.y) - this._clusterDistance; + var w = NumCast(doc.width) + this._clusterDistance; + var h = NumCast(doc.height) + this._clusterDistance; + if (doc.z === doc2.z && this.intersectRect({ left: x, top: y, width: w, height: h }, { left: x2, top: y2, width: w2, height: h2 })) { return true; } return false; @@ -197,36 +199,83 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { return false; } + tryDragCluster(e: PointerEvent) { + let probe = this.getTransform().transformPoint(e.clientX, e.clientY); + let cluster = this.childDocs.reduce((cluster, cd) => { + let cx = NumCast(cd.x) - this._clusterDistance; + let cy = NumCast(cd.y) - this._clusterDistance; + let cw = NumCast(cd.width) + 2 * this._clusterDistance; + let ch = NumCast(cd.height) + 2 * this._clusterDistance; + if (!cd.z && this.intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 })) + return NumCast(cd.cluster); + return cluster; + }, -1); + if (cluster !== -1) { + let eles = this.childDocs.filter(cd => NumCast(cd.cluster) === cluster); + this.selectDocuments(eles); + let clusterDocs = SelectionManager.SelectedDocuments(); + SelectionManager.DeselectAll(); + let de = new DragManager.DocumentDragData(eles, eles.map(d => undefined)); + de.moveDocument = this.props.moveDocument; + const [left, top] = clusterDocs[0].props.ScreenToLocalTransform().scale(clusterDocs[0].props.ContentScaling()).inverse().transformPoint(0, 0); + const [xoff, yoff] = this.getTransform().transformDirection(e.x - left, e.y - top); + de.dropAction = e.ctrlKey || e.altKey ? "alias" : undefined; + de.xOffset = xoff; + de.yOffset = yoff; + DragManager.StartDocumentDrag(clusterDocs.map(v => v.ContentDiv!), de, e.clientX, e.clientY, { + handlers: { dragComplete: action(emptyFunction) }, + hideSource: !de.dropAction + }); + return true; + } + + return false; + } + @observable sets: (Doc[])[] = []; @action updateClusters() { - let sets: (Doc[])[] = [] + this.sets.length = 0; this.childDocs.map(c => { let included = [] - for (let i = 0; i < sets.length; i++) { - for (let j = 0; j < sets[i].length; j++) { - if (this.bounsdSelect(c, sets[i][j])) { + for (let i = 0; i < this.sets.length; i++) { + for (let j = 0; j < this.sets[i].length; j++) { + if (this.bounsdSelect(c, this.sets[i][j])) { included.push(i); break; } } } if (included.length === 0) - sets.push([c]); + this.sets.push([c]); else if (included.length === 1) - sets[included[0]].push(c); + this.sets[included[0]].push(c); else { - sets[included[0]].push(c); + this.sets[included[0]].push(c); for (let s = 1; s < included.length; s++) { - sets[included[0]].push(...sets[included[s]]); - sets[included[s]].length = 0; + this.sets[included[0]].push(...this.sets[included[s]]); + this.sets[included[s]].length = 0; } } }); - for (let s = 0; s < sets.length; s++) { - for (let i = 0; i < sets[s].length; i++) { - Doc.GetProto(sets[s][i]).cluster = s; + for (let s = 0; s < this.sets.length; s++) { + for (let i = 0; i < this.sets[s].length; i++) { + this.sets[s][i].cluster = s; + } + } + } + + getClusterColor = (doc: Doc) => { + if (this.props.Document.useClusters) { + let cluster = NumCast(doc.cluster); + let set = this.sets.length > cluster ? this.sets[NumCast(doc.cluster)] : undefined; + let colors = ["#da42429e", "#31ea318c", "#8c4000", "#4a7ae2c4", "#d809ff", "#ff7601", "#1dffff", "yellow", "#1b8231f2", "#000000ad"]; + let clusterColor = colors[cluster % colors.length]; + for (let i = 0; set && i < set.length; i++) { + if (set[i].backgroundColor && set[i].backgroundColor !== set[i].defaultBackgroundColor) clusterColor = StrCast(set[i].backgroundColor); } + return clusterColor; } + return ""; } @action @@ -249,34 +298,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @action onPointerMove = (e: PointerEvent): void => { if (!e.cancelBubble) { - let probe = this.getTransform().transformPoint(e.clientX, e.clientY); - let cluster = this.childDocs.reduce((cluster, cd) => { - let cx = NumCast(cd.x) - this._groupingBorder; - let cy = NumCast(cd.y) - this._groupingBorder; - let cw = NumCast(cd.width) + 2 * this._groupingBorder; - let ch = NumCast(cd.height) + 2 * this._groupingBorder; - if (this.intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 })) - return NumCast(cd.cluster); - return cluster; - }, -1); - if (cluster !== -1) { - let eles = this.childDocs.filter(cd => NumCast(cd.cluster) === cluster); - this.selectDocuments(eles); - let clusterDocs = SelectionManager.SelectedDocuments(); - SelectionManager.DeselectAll(); - let de = new DragManager.DocumentDragData(eles, eles.map(d => undefined)); - de.moveDocument = this.props.moveDocument; - const [left, top] = clusterDocs[0].props.ScreenToLocalTransform().scale(clusterDocs[0].props.ContentScaling()).inverse().transformPoint(0, 0); - const [xoff, yoff] = this.getTransform().transformDirection(e.x - left, e.y - top); - de.dropAction = e.ctrlKey || e.altKey ? "alias" : undefined; - de.xOffset = xoff; - de.yOffset = yoff; - DragManager.StartDocumentDrag(clusterDocs.map(v => v.ContentDiv!), de, e.clientX, e.clientY, { - handlers: { - dragComplete: action(emptyFunction) - }, - hideSource: !de.dropAction - }); + if (this.props.Document.useClusters && this.tryDragCluster(e)) { e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers e.preventDefault(); document.removeEventListener("pointermove", this.onPointerMove); @@ -493,6 +515,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { ContentScaling: returnOne, ContainingCollectionView: this.props.CollectionView, focus: this.focusDocument, + backgroundColor: this.getClusterColor, parentActive: this.props.active, whenActiveChanged: this.props.whenActiveChanged, bringToFront: this.bringToFront, @@ -516,6 +539,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { ContentScaling: returnOne, ContainingCollectionView: this.props.CollectionView, focus: this.focusDocument, + backgroundColor: returnEmptyString, parentActive: this.props.active, whenActiveChanged: this.props.whenActiveChanged, bringToFront: this.bringToFront, @@ -625,6 +649,15 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { event: async () => this.props.Document.fitToBox = !this.fitToBox, icon: !this.fitToBox ? "expand-arrows-alt" : "compress-arrows-alt" }); + layoutItems.push({ + description: `${this.props.Document.useClusters ? "Uncluster" : "Use Clusters"}`, + event: async () => { + Docs.Prototypes.get(DocumentType.TEXT).defaultBackgroundColor = "#f1efeb"; + Docs.Prototypes.get(DocumentType.COL).defaultBackgroundColor = "white"; + this.props.Document.useClusters = !this.props.Document.useClusters; + }, + icon: !this.props.Document.useClusters ? "expand-arrows-alt" : "compress-arrows-alt" + }); layoutItems.push({ description: "Arrange contents in grid", icon: "table", @@ -700,10 +733,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { ...this.views ] private overlayChildViews = () => { - console.log(this.overlayViews.length); - return [ - ...this.overlayViews - ]; + return [...this.overlayViews]; } public static AddCustomLayout(doc: Doc, dataKey: string): () => void { diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index b9ee588dd..ff96bd993 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -278,6 +278,7 @@ export class MarqueeView extends React.Component panX: 0, panY: 0, backgroundColor: this.props.container.isAnnotationOverlay ? undefined : "white", + defaultBackgroundColor: this.props.container.isAnnotationOverlay ? undefined : "white", width: bounds.width, height: bounds.height, title: e.key === "s" || e.key === "S" ? "-summary-" : "a nested collection", diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 3b6c443c2..ee596c841 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -8,6 +8,7 @@ import { DocumentView, DocumentViewProps, positionSchema } from "./DocumentView" import "./DocumentView.scss"; import React = require("react"); import { Doc } from "../../../new_fields/Doc"; +import { returnEmptyString } from "../../../Utils"; export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { x?: number; @@ -69,6 +70,11 @@ export class CollectionFreeFormDocumentView extends DocComponent this.clusterColor; + render() { const hasPosition = this.props.x !== undefined || this.props.y !== undefined; return ( @@ -77,7 +83,10 @@ export class CollectionFreeFormDocumentView extends DocComponent void; collapseToPoint?: (scrpt: number[], expandedDocs: Doc[] | undefined) => void; zoomToScale: (scale: number) => void; + backgroundColor: (doc: Doc) => string; getScale: () => number; animateBetweenIcon?: (iconPos: number[], startTime: number, maximizing: boolean) => void; ChromeHeight?: () => number; @@ -675,12 +676,9 @@ export class DocumentView extends DocComponent(Docu // 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 = StrCast(this.layoutDoc.backgroundColor); + let backgroundColor = this.props.backgroundColor(this.props.Document) || StrCast(this.layoutDoc.backgroundColor); let foregroundColor = StrCast(this.layoutDoc.color); var nativeWidth = this.nativeWidth > 0 && !BoolCast(this.props.Document.ignoreAspect) ? `${this.nativeWidth}px` : "100%"; var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; @@ -695,8 +693,6 @@ export class DocumentView extends DocComponent(Docu }); } let showTextTitle = showTitle && StrCast(this.layoutDoc.layout).startsWith("(Docu color: foregroundColor, outlineColor: "maroon", outlineStyle: "dashed", - boxShadow: this.layoutDoc.isBackground ? - `0px 0px 50px 50px ${groupCol}` : - `${groupCol} ${StrCast(this.props.Document.boxShadow, `0vw 0vw ${50 / this.props.ContentScaling()}px`)}`, outlineWidth: BoolCast(this.layoutDoc.libraryBrush) && !StrCast(Doc.GetProto(this.props.Document).borderRounding) ? `${this.props.ScreenToLocalTransform().Scale}px` : "0px", marginLeft: BoolCast(this.layoutDoc.libraryBrush) && StrCast(Doc.GetProto(this.props.Document).borderRounding) ? @@ -717,7 +710,7 @@ export class DocumentView extends DocComponent(Docu border: BoolCast(this.layoutDoc.libraryBrush) && StrCast(Doc.GetProto(this.props.Document).borderRounding) ? `dashed maroon ${this.props.ScreenToLocalTransform().Scale}px` : undefined, borderRadius: "inherit", - background: this.layoutDoc.isBackground ? groupCol : backgroundColor, + background: backgroundColor, width: nativeWidth, height: nativeHeight, transform: `scale(${this.props.ContentScaling()})`, diff --git a/src/client/views/presentationview/PresentationElement.tsx b/src/client/views/presentationview/PresentationElement.tsx index 11f3eb846..e2d8daea9 100644 --- a/src/client/views/presentationview/PresentationElement.tsx +++ b/src/client/views/presentationview/PresentationElement.tsx @@ -9,7 +9,7 @@ import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; import { listSpec } from "../../../new_fields/Schema"; import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types"; -import { Utils, returnFalse, emptyFunction, returnOne } from "../../../Utils"; +import { Utils, returnFalse, emptyFunction, returnOne, returnEmptyString } from "../../../Utils"; import { DragManager, dropActionType, SetupDrag } from "../../util/DragManager"; import { SelectionManager } from "../../util/SelectionManager"; import { ContextMenu } from "../ContextMenu"; @@ -843,6 +843,7 @@ export default class PresentationElement extends React.Component 350} PanelHeight={() => 90} focus={emptyFunction} + backgroundColor={returnEmptyString} selectOnLoad={false} parentActive={returnFalse} whenActiveChanged={returnFalse} diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx index 0390359b3..1b9bba5c6 100644 --- a/src/client/views/search/SearchItem.tsx +++ b/src/client/views/search/SearchItem.tsx @@ -7,7 +7,7 @@ import { observer } from "mobx-react"; import { Doc, DocListCast, HeightSym, WidthSym } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; -import { emptyFunction, returnFalse, returnOne, Utils } from "../../../Utils"; +import { emptyFunction, returnFalse, returnOne, Utils, returnEmptyString } from "../../../Utils"; import { DocumentType } from "../../documents/Documents"; import { DocumentManager } from "../../util/DocumentManager"; import { SetupDrag, DragManager } from "../../util/DragManager"; @@ -223,6 +223,7 @@ export class SearchItem extends React.Component { PanelWidth={returnXDimension} PanelHeight={returnYDimension} focus={emptyFunction} + backgroundColor={returnEmptyString} selectOnLoad={false} parentActive={returnFalse} whenActiveChanged={returnFalse} -- cgit v1.2.3-70-g09d2 From 098e3780c9d16738070a92dbfcdf68372a9d18a9 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 3 Aug 2019 15:35:51 -0400 Subject: from last --- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index ee21c5a4d..24439ef4c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -135,7 +135,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { return !(r2.left > r1.left + r1.width || r2.left + r2.width < r1.left || r2.top > r1.top + r1.height || r2.top + r2.height < r1.top); } _clusterDistance = 75; - bounsdSelect(doc: Doc, doc2: Doc) { + boundsOverlap(doc: Doc, doc2: Doc) { var x2 = NumCast(doc2.x) - this._clusterDistance; var y2 = NumCast(doc2.y) - this._clusterDistance; var w2 = NumCast(doc2.width) + this._clusterDistance; @@ -239,7 +239,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { let included = [] for (let i = 0; i < this.sets.length; i++) { for (let j = 0; j < this.sets[i].length; j++) { - if (this.bounsdSelect(c, this.sets[i][j])) { + if (this.boundsOverlap(c, this.sets[i][j])) { included.push(i); break; } -- cgit v1.2.3-70-g09d2 From b7c70cf115ea3d23e06fc7a3837e983b62993334 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 3 Aug 2019 17:30:58 -0400 Subject: a few updates and an option to switch between default backgrounds and cluster backgrounds. --- .../collectionFreeForm/CollectionFreeFormView.tsx | 20 +++++++++++++++----- src/client/views/nodes/DocumentView.tsx | 4 +++- 2 files changed, 18 insertions(+), 6 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 24439ef4c..b4d065d26 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -267,12 +267,17 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { getClusterColor = (doc: Doc) => { if (this.props.Document.useClusters) { let cluster = NumCast(doc.cluster); - let set = this.sets.length > cluster ? this.sets[NumCast(doc.cluster)] : undefined; + if (this.sets.length <= cluster) { + setTimeout(() => this.updateClusters(), 0); + return; + } + let set = this.sets.length > cluster ? this.sets[cluster] : undefined; let colors = ["#da42429e", "#31ea318c", "#8c4000", "#4a7ae2c4", "#d809ff", "#ff7601", "#1dffff", "yellow", "#1b8231f2", "#000000ad"]; let clusterColor = colors[cluster % colors.length]; - for (let i = 0; set && i < set.length; i++) { - if (set[i].backgroundColor && set[i].backgroundColor !== set[i].defaultBackgroundColor) clusterColor = StrCast(set[i].backgroundColor); - } + set && set.filter(s => !s.isBackground).map(s => + s.backgroundColor && s.backgroundColor !== s.defaultBackgroundColor && (clusterColor = StrCast(s.backgroundColor))); + set && set.filter(s => s.isBackground).map(s => + s.backgroundColor && s.backgroundColor !== s.defaultBackgroundColor && (clusterColor = StrCast(s.backgroundColor))); return clusterColor; } return ""; @@ -652,12 +657,17 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { layoutItems.push({ description: `${this.props.Document.useClusters ? "Uncluster" : "Use Clusters"}`, event: async () => { - Docs.Prototypes.get(DocumentType.TEXT).defaultBackgroundColor = "#f1efeb"; + Docs.Prototypes.get(DocumentType.TEXT).defaultBackgroundColor = "#f1efeb"; // backward compatibility with databases that didn't have a default background color on prototypes Docs.Prototypes.get(DocumentType.COL).defaultBackgroundColor = "white"; this.props.Document.useClusters = !this.props.Document.useClusters; }, icon: !this.props.Document.useClusters ? "expand-arrows-alt" : "compress-arrows-alt" }); + layoutItems.push({ + description: `${this.props.Document.clusterOverridesDefaultBackground ? "Use Default Backgrounds" : "Clusters Override Defaults"}`, + event: async () => this.props.Document.clusterOverridesDefaultBackground = !this.props.Document.clusterOverridesDefaultBackground, + icon: !this.props.Document.useClusters ? "expand-arrows-alt" : "compress-arrows-alt" + }); layoutItems.push({ description: "Arrange contents in grid", icon: "table", diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index ae6a01ab4..0347fc9b2 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -678,7 +678,9 @@ export class DocumentView extends DocComponent(Docu } render() { - let backgroundColor = this.props.backgroundColor(this.props.Document) || StrCast(this.layoutDoc.backgroundColor); + let backgroundColor = this.layoutDoc.isBackground || (this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document.clusterOverridesDefaultBackground && this.layoutDoc.backgroundColor === this.layoutDoc.defaultBackgroundColor) ? + this.props.backgroundColor(this.layoutDoc) || StrCast(this.layoutDoc.backgroundColor) : + StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor(this.layoutDoc); let foregroundColor = StrCast(this.layoutDoc.color); var nativeWidth = this.nativeWidth > 0 && !BoolCast(this.props.Document.ignoreAspect) ? `${this.nativeWidth}px` : "100%"; var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; -- cgit v1.2.3-70-g09d2 From 02346eabdefd428ca23d6a3fbefdcd51ef62b738 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 4 Aug 2019 01:17:29 -0400 Subject: fixed errors/warnings --- src/client/apis/youtube/YoutubeBox.tsx | 8 +- src/client/views/InkingControl.tsx | 4 +- src/client/views/TemplateMenu.tsx | 2 +- .../views/collections/CollectionSchemaCells.tsx | 4 +- .../views/collections/CollectionSchemaHeaders.tsx | 2 +- .../views/collections/CollectionSchemaView.tsx | 5 +- .../views/collections/CollectionStackingView.tsx | 116 +++++++++------------ src/client/views/collections/CollectionView.tsx | 11 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 23 ++-- src/client/views/nodes/ButtonBox.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 4 +- src/client/views/search/FilterBox.tsx | 2 +- src/client/views/search/SearchItem.tsx | 2 +- src/client/views/search/ToggleBar.tsx | 2 +- src/new_fields/Doc.ts | 4 +- 16 files changed, 82 insertions(+), 111 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index dc142802c..d73988bb8 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -57,7 +57,7 @@ export class YoutubeBox extends React.Component { if (awaitedBackUp) { - let jsonList = await DocListCastAsync(awaitedBackUp!.json); + let jsonList = await DocListCastAsync(awaitedBackUp.json); let jsonDetailList = await DocListCastAsync(awaitedDetails!.json); if (jsonList!.length !== 0) { @@ -76,8 +76,8 @@ export class YoutubeBox extends React.Component { let videoDescription = StrCast(snippet!.description); let pusblishDate = (this.roundPublishTime(StrCast(snippet!.publishedAt)))!; let channelTitle = StrCast(snippet!.channelTitle); - let duration: string; - let viewCount: string; + let duration: string = ""; + let viewCount: string = ""; if (jsonDetailList!.length !== 0) { let contentDetails = await Cast(jsonDetailList![index].contentDetails, Doc); let statistics = await Cast(jsonDetailList![index].statistics, Doc); @@ -85,7 +85,7 @@ export class YoutubeBox extends React.Component { viewCount = this.abbreviateViewCount(parseInt(StrCast(statistics!.viewCount)))!; } index = index + 1; - let newTemplate: VideoTemplate = { videoId: id, videoTitle: videoTitle, thumbnailUrl: thumbnailUrl, publishDate: pusblishDate, channelTitle: channelTitle, videoDescription: videoDescription, duration: duration!, viewCount: viewCount! }; + let newTemplate: VideoTemplate = { videoId: id, videoTitle: videoTitle, thumbnailUrl: thumbnailUrl, publishDate: pusblishDate, channelTitle: channelTitle, videoDescription: videoDescription, duration: duration, viewCount: viewCount }; runInAction(() => this.curVideoTemplates.push(newTemplate)); } } diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index 58c83915b..3f40642b5 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -1,5 +1,5 @@ import { observable, action, computed, runInAction } from "mobx"; -import { ColorState } from 'react-color'; +import { ColorResult } from 'react-color'; import React = require("react"); import { observer } from "mobx-react"; import "./InkingControl.scss"; @@ -41,7 +41,7 @@ export class InkingControl extends React.Component { } @undoBatch - switchColor = action((color: ColorState): void => { + switchColor = action((color: ColorResult): void => { this._selectedColor = color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff"); if (InkingControl.Instance.selectedTool === InkTool.None) { if (MainOverlayTextBox.Instance.SetColor(color.hex)) return; diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index 6dd908445..393e97a7e 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -68,7 +68,7 @@ export class TemplateMenu extends React.Component { } }, 10); } else if (topDocView.props.ContainingCollectionView) { - let collView = topDocView.props.ContainingCollectionView!; + let collView = topDocView.props.ContainingCollectionView; let [sx, sy] = xf.inverse().transformPoint(0, 0); let [x, y] = collView.props.ScreenToLocalTransform().transformPoint(sx, sy); topDoc.x = x; diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index 4ff65b277..7e3061354 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -175,11 +175,11 @@ export class CollectionSchemaCell extends React.Component { }; let onPointerEnter = (e: React.PointerEvent): void => { if (e.buttons === 1 && SelectionManager.GetIsDragging() && (type === "document" || type === undefined)) { - dragRef!.current!.className = "collectionSchemaView-cellContainer doc-drag-over"; + dragRef.current!.className = "collectionSchemaView-cellContainer doc-drag-over"; } }; let onPointerLeave = (e: React.PointerEvent): void => { - dragRef!.current!.className = "collectionSchemaView-cellContainer"; + dragRef.current!.className = "collectionSchemaView-cellContainer"; }; let contents: any = "incorrect type"; diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx index dfd65770e..d24f63fbb 100644 --- a/src/client/views/collections/CollectionSchemaHeaders.tsx +++ b/src/client/views/collections/CollectionSchemaHeaders.tsx @@ -13,7 +13,7 @@ import { faFile } from "@fortawesome/free-regular-svg-icons"; import { SchemaHeaderField, RandomPastel, PastelSchemaPalette } from "../../../new_fields/SchemaHeaderField"; import { undoBatch } from "../../util/UndoManager"; -library.add(faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faFile, faSortAmountDown, faSortAmountUp, faTimes); +library.add(faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faFile as any, faSortAmountDown, faSortAmountUp, faTimes); export interface HeaderProps { keyValue: SchemaHeaderField; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 2d4c88b94..75787c0a8 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -303,14 +303,13 @@ export class SchemaTable extends React.Component { return resized; }, [] as { "id": string, "value": number }[]); } - - @computed get sorted(): { "id": string, "desc": boolean }[] { + @computed get sorted(): { "id": string, "desc"?: true }[] { return this.columns.reduce((sorted, shf) => { if (shf.desc) { sorted.push({ "id": shf.heading, "desc": shf.desc }); } return sorted; - }, [] as { "id": string, "desc": boolean }[]); + }, [] as { "id": string, "desc"?: true }[]); } @computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); } diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index d7c3ac3b8..4a751c84c 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -1,26 +1,25 @@ import React = require("react"); import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, computed, IReactionDisposer, reaction, untracked, observable, runInAction } from "mobx"; +import { CursorProperty } from "csstype"; +import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, HeightSym, WidthSym, DocListCast } from "../../../new_fields/Doc"; +import Switch from 'rc-switch'; +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, returnTrue } from "../../../Utils"; -import { CollectionSchemaPreview } from "./CollectionSchemaView"; -import "./CollectionStackingView.scss"; -import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView"; -import { undoBatch } from "../../util/UndoManager"; -import { DragManager } from "../../util/DragManager"; +import { List } from "../../../new_fields/List"; +import { listSpec } from "../../../new_fields/Schema"; +import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; +import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types"; +import { emptyFunction } from "../../../Utils"; import { DocumentType } from "../../documents/Documents"; +import { DragManager } from "../../util/DragManager"; import { Transform } from "../../util/Transform"; -import { CursorProperty } from "csstype"; -import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn"; -import { listSpec } from "../../../new_fields/Schema"; -import { SchemaHeaderField, RandomPastel } from "../../../new_fields/SchemaHeaderField"; -import { List } from "../../../new_fields/List"; +import { undoBatch } from "../../util/UndoManager"; import { EditableView } from "../EditableView"; -import { CollectionViewProps } from "./CollectionBaseView"; -import Switch from 'rc-switch'; +import { CollectionSchemaPreview } from "./CollectionSchemaView"; +import "./CollectionStackingView.scss"; +import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn"; +import { CollectionSubView } from "./CollectionSubView"; @observer export class CollectionStackingView extends CollectionSubView(doc => doc) { @@ -32,13 +31,13 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { _columnStart: number = 0; @observable private cursor: CursorProperty = "grab"; get sectionHeaders() { return Cast(this.props.Document.sectionHeaders, listSpec(SchemaHeaderField)); } - @computed get chromeCollapsed() { return this.props.chromeCollapsed; } @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 filteredChildren() { return this.childDocs.filter(d => !d.isMinimized); } + @computed get sectionFilter() { return this.singleColumn ? StrCast(this.props.Document.sectionFilter) : ""; } get layoutDoc() { // if this document's layout field contains a document (ie, a rendering template), then we will use that @@ -46,37 +45,32 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { return this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document; } - @computed - get SectionFilter() { return this.singleColumn ? StrCast(this.props.Document.sectionFilter) : "" } get Sections() { - let sectionHeaders = this.sectionHeaders; - if (!sectionHeaders) { - this.props.Document.sectionHeaders = sectionHeaders = new List(); + if (!this.sectionFilter) return new Map(); + + if (this.sectionHeaders === undefined) { + this.props.Document.sectionHeaders = new List(); } - let fields = new Map(sectionHeaders.map(sh => [sh, []])); - if (this.SectionFilter) { - this.filteredChildren.map(d => { - let sectionValue = (d[this.SectionFilter] ? d[this.SectionFilter] : `NO ${this.SectionFilter.toUpperCase()} VALUE`) as object; - // the next five lines ensures that floating point rounding errors don't create more than one section -syip - let parsed = parseInt(sectionValue.toString()); - let castedSectionValue: any = sectionValue; - if (!isNaN(parsed)) { - castedSectionValue = parsed; - } + const sectionHeaders = this.sectionHeaders!; + let fields = new Map(sectionHeaders.map(sh => [sh, []] as [SchemaHeaderField, []])); + this.filteredChildren.map(d => { + let sectionValue = (d[this.sectionFilter] ? d[this.sectionFilter] : `NO ${this.sectionFilter.toUpperCase()} VALUE`) as object; + // the next five lines ensures that floating point rounding errors don't create more than one section -syip + let parsed = parseInt(sectionValue.toString()); + let castedSectionValue = !isNaN(parsed) ? parsed : sectionValue; - // look for if header exists already - let existingHeader = sectionHeaders!.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.SectionFilter.toUpperCase()} VALUE`)); - if (existingHeader) { - fields.get(existingHeader)!.push(d); - } - else { - let newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.SectionFilter.toUpperCase()} VALUE`); - fields.set(newSchemaHeader, [d]); - sectionHeaders!.push(newSchemaHeader); - } - }); - } else fields.clear(); + // look for if header exists already + let existingHeader = sectionHeaders.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.sectionFilter.toUpperCase()} VALUE`)); + if (existingHeader) { + fields.get(existingHeader)!.push(d); + } + else { + let newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.sectionFilter.toUpperCase()} VALUE`); + fields.set(newSchemaHeader, [d]); + sectionHeaders.push(newSchemaHeader); + } + }); return fields; } @@ -96,10 +90,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { // reset section headers when a new filter is inputted this._sectionFilterDisposer = reaction( - () => this.SectionFilter, - () => { - this.props.Document.sectionHeaders = new List(); - } + () => this.sectionFilter, + () => this.props.Document.sectionHeaders = new List() ); } componentWillUnmount() { @@ -186,8 +178,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { @undoBatch @action drop = (e: Event, de: DragManager.DropEvent) => { - let targInd = -1; let where = [de.x, de.y]; + let targInd = -1; if (de.data instanceof DragManager.DocumentDragData) { this._docXfs.map((cd, i) => { let pos = cd.dxf().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap); @@ -233,8 +225,9 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } }); } + headings = () => Array.from(this.Sections.keys()); section = (heading: SchemaHeaderField | undefined, docList: Doc[]) => { - let key = this.SectionFilter; + let key = this.sectionFilter; let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined; let types = docList.length ? docList.map(d => typeof d[key]) : this.childDocs.map(d => typeof d[key]); if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) { @@ -245,7 +238,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { return Array.from(this.Sections.keys())} + headings={this.headings} heading={heading ? heading.heading : ""} headingObject={heading} docList={docList} @@ -254,16 +247,13 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { createDropTarget={this.createDropTarget} screenToLocalTransform={this.props.ScreenToLocalTransform} />; - } @action addGroup = (value: string) => { - if (value) { - if (this.sectionHeaders) { - this.sectionHeaders.push(new SchemaHeaderField(value)); - return true; - } + if (value && this.sectionHeaders) { + this.sectionHeaders.push(new SchemaHeaderField(value)); + return true; } return false; } @@ -276,11 +266,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } onToggle = (checked: Boolean) => { - if (checked) { - this.props.CollectionView.props.Document.chromeStatus = 'collapsed'; - } else { - this.props.CollectionView.props.Document.chromeStatus = 'view-mode'; - } + this.props.CollectionView.props.Document.chromeSatus = checked ? "collapsed" : "view-mode"; } render() { @@ -296,10 +282,10 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { return (
    e.stopPropagation()} > - {this.SectionFilter ? Array.from(this.Sections.entries()).sort(this.sortFunc). - map(section => this.section(section[0], section[1])) : + {this.sectionFilter ? Array.from(this.Sections.entries()).sort(this.sortFunc). + map((section: [SchemaHeaderField, Doc[]]) => this.section(section[0], section[1])) : this.section(undefined, this.filteredChildren)} - {(this.SectionFilter && (this.props.CollectionView.props.Document.chromeStatus !== 'view-mode' && this.props.CollectionView.props.Document.chromeStatus !== 'disabled')) ? + {(this.sectionFilter && (this.props.CollectionView.props.Document.chromeStatus !== 'view-mode' && this.props.CollectionView.props.Document.chromeStatus !== 'disabled')) ?
    diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 57dc5879b..f59fee985 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -20,16 +20,7 @@ import { CollectionTreeView } from "./CollectionTreeView"; import { CollectionViewBaseChrome } from './CollectionViewChromes'; export const COLLECTION_BORDER_WIDTH = 2; -library.add(faTh); -library.add(faTree); -library.add(faSquare); -library.add(faProjectDiagram); -library.add(faSignature); -library.add(faThList); -library.add(faFingerprint); -library.add(faColumns); -library.add(faEllipsisV); -library.add(faImage, faEye); +library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faEllipsisV, faImage, faEye as any); @observer export class CollectionView extends React.Component { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index b4d065d26..c943fac74 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -40,7 +40,7 @@ import v5 = require("uuid/v5"); import { setScheduler } from "bluebird"; import { DocumentType, Docs } from "../../../documents/Documents"; -library.add(faEye, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload); +library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload); export const panZoomSchema = createSchema({ panX: "number", @@ -206,8 +206,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { let cy = NumCast(cd.y) - this._clusterDistance; let cw = NumCast(cd.width) + 2 * this._clusterDistance; let ch = NumCast(cd.height) + 2 * this._clusterDistance; - if (!cd.z && this.intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 })) + if (!cd.z && this.intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 })) { return NumCast(cd.cluster); + } return cluster; }, -1); if (cluster !== -1) { @@ -236,20 +237,20 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { updateClusters() { this.sets.length = 0; this.childDocs.map(c => { - let included = [] + let included = []; for (let i = 0; i < this.sets.length; i++) { - for (let j = 0; j < this.sets[i].length; j++) { - if (this.boundsOverlap(c, this.sets[i][j])) { + for (let member of this.sets[i]) { + if (this.boundsOverlap(c, member)) { included.push(i); break; } } } - if (included.length === 0) + if (included.length === 0) { this.sets.push([c]); - else if (included.length === 1) + } else if (included.length === 1) { this.sets[included[0]].push(c); - else { + } else { this.sets[included[0]].push(c); for (let s = 1; s < included.length; s++) { this.sets[included[0]].push(...this.sets[included[s]]); @@ -257,11 +258,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } } }); - for (let s = 0; s < this.sets.length; s++) { - for (let i = 0; i < this.sets[s].length; i++) { - this.sets[s][i].cluster = s; - } - } + this.sets.map((set, i) => set.map(member => member.cluster = i)); } getClusterColor = (doc: Doc) => { diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx index e2c559c9a..640795789 100644 --- a/src/client/views/nodes/ButtonBox.tsx +++ b/src/client/views/nodes/ButtonBox.tsx @@ -16,7 +16,7 @@ import './ButtonBox.scss'; import { observer } from 'mobx-react'; import { DocumentIconContainer } from './DocumentIcon'; -library.add(faEdit); +library.add(faEdit as any); const ButtonSchema = createSchema({ onClick: ScriptField, diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 0347fc9b2..4528cf94d 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -95,7 +95,7 @@ export interface DocumentViewProps { addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => void; collapseToPoint?: (scrpt: number[], expandedDocs: Doc[] | undefined) => void; zoomToScale: (scale: number) => void; - backgroundColor: (doc: Doc) => string; + backgroundColor: (doc: Doc) => string | undefined; getScale: () => number; animateBetweenIcon?: (iconPos: number[], startTime: number, maximizing: boolean) => void; ChromeHeight?: () => number; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index fa072aecf..a49709e83 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -152,9 +152,7 @@ export class PDFBox extends DocComponent(PdfDocumen } scrollTo(y: number) { - if (this._mainCont.current) { - this._mainCont.current.scrollTo({ top: Math.max(y - (this._mainCont.current!.offsetHeight / 2), 0), behavior: "auto" }); - } + this._mainCont.current && this._mainCont.current.scrollTo({ top: Math.max(y - (this._mainCont.current.offsetHeight / 2), 0), behavior: "auto" }); } settingsPanel() { diff --git a/src/client/views/search/FilterBox.tsx b/src/client/views/search/FilterBox.tsx index 995ddd5c3..3e8582d61 100644 --- a/src/client/views/search/FilterBox.tsx +++ b/src/client/views/search/FilterBox.tsx @@ -384,7 +384,7 @@ export class FilterBox extends React.Component {
    Collection Filters Active
    : undefined}
    - ) + ); } // Useful queries: diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx index 1b9bba5c6..48eb87251 100644 --- a/src/client/views/search/SearchItem.tsx +++ b/src/client/views/search/SearchItem.tsx @@ -134,7 +134,7 @@ export class LinkContextMenu extends React.Component {
    - ) + ); } } diff --git a/src/client/views/search/ToggleBar.tsx b/src/client/views/search/ToggleBar.tsx index a30104089..ed5ecd3ba 100644 --- a/src/client/views/search/ToggleBar.tsx +++ b/src/client/views/search/ToggleBar.tsx @@ -59,7 +59,7 @@ export class ToggleBar extends React.Component{ this._forwardTimeline.play(); this._forwardTimeline.reverse(); this.props.handleChange(); - console.log(this.props.getStatus()) + console.log(this.props.getStatus()); } @action.bound diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index b081588a3..c01f4e8cf 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -358,7 +358,7 @@ export namespace Doc { let docExtensionForField = doc[fieldKey + "_ext"] as Doc; if (docExtensionForField === undefined) { setTimeout(() => { - CreateDocumentExtensionForField(doc, field); + CreateDocumentExtensionForField(doc, fieldKey); }, 0); } else if (doc instanceof Doc) { // backward compatibility -- add fields for docs that don't have them already docExtensionForField.extendsDoc === undefined && setTimeout(() => docExtensionForField.extendsDoc = doc, 0); @@ -535,7 +535,7 @@ export namespace Doc { d.layout = detailLayout; d.nativeWidth = d.nativeHeight = undefined; if (detailLayout instanceof Doc) { - let delegDetailLayout = Doc.MakeDelegate(detailLayout) as Doc; + let delegDetailLayout = Doc.MakeDelegate(detailLayout); d.layout = delegDetailLayout; delegDetailLayout.layout = await delegDetailLayout.detailedLayout; } -- cgit v1.2.3-70-g09d2 From a8aa834d8e08bddde705ce90ef4dee8be3791862 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 4 Aug 2019 21:56:11 -0400 Subject: fixed dropping of PDFs (or anything that doesn't have a height). --- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index c943fac74..2bdbb6749 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -69,7 +69,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { ComputeContentBounds(boundsList: { x: number, y: number, width: number, height: number }[]) { let bounds = boundsList.reduce((bounds, b) => { var [sptX, sptY] = [b.x, b.y]; - let [bptX, bptY] = [sptX + b.width, sptY + b.height]; + let [bptX, bptY] = [sptX + NumCast(b.width, 1), sptY + NumCast(b.height, 1)]; return { x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y), r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b) @@ -617,7 +617,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { ele: , - bounds: (pos.x !== undefined && pos.y !== undefined && pos.width !== undefined && pos.height !== undefined) ? { x: pos.x, y: pos.y, z: pos.z, width: pos.width, height: pos.height } : undefined + bounds: (pos.x !== undefined && pos.y !== undefined) ? { x: pos.x, y: pos.y, z: pos.z, width: NumCast(pos.width), height: NumCast(pos.height) } : undefined }); } } -- cgit v1.2.3-70-g09d2 From 298d1c9b29d6ce2171fd9ac8274b64583b73f6f5 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 5 Aug 2019 18:32:53 -0400 Subject: added reset view menu option --- .../collectionFreeForm/CollectionFreeFormView.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 2bdbb6749..a08a12426 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,6 +1,6 @@ import { library } from "@fortawesome/fontawesome-svg-core"; import { faEye } from "@fortawesome/free-regular-svg-icons"; -import { faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faPaintBrush, faTable, faUpload } from "@fortawesome/free-solid-svg-icons"; +import { faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faPaintBrush, faTable, faUpload, faChalkboard, faBraille } from "@fortawesome/free-solid-svg-icons"; import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCastAsync, HeightSym, WidthSym } from "../../../../new_fields/Doc"; @@ -36,11 +36,9 @@ import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCurso import "./CollectionFreeFormView.scss"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); -import v5 = require("uuid/v5"); -import { setScheduler } from "bluebird"; import { DocumentType, Docs } from "../../../documents/Documents"; -library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload); +library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard); export const panZoomSchema = createSchema({ panX: "number", @@ -651,6 +649,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { event: async () => this.props.Document.fitToBox = !this.fitToBox, icon: !this.fitToBox ? "expand-arrows-alt" : "compress-arrows-alt" }); + layoutItems.push({ + description: "reset view", event: () => { + this.props.Document.panX = this.props.Document.panY = 0; + this.props.Document.scale = 1; + }, icon: "compress-arrows-alt" + }); layoutItems.push({ description: `${this.props.Document.useClusters ? "Uncluster" : "Use Clusters"}`, event: async () => { @@ -658,12 +662,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { Docs.Prototypes.get(DocumentType.COL).defaultBackgroundColor = "white"; this.props.Document.useClusters = !this.props.Document.useClusters; }, - icon: !this.props.Document.useClusters ? "expand-arrows-alt" : "compress-arrows-alt" + icon: !this.props.Document.useClusters ? "braille" : "braille" }); layoutItems.push({ description: `${this.props.Document.clusterOverridesDefaultBackground ? "Use Default Backgrounds" : "Clusters Override Defaults"}`, event: async () => this.props.Document.clusterOverridesDefaultBackground = !this.props.Document.clusterOverridesDefaultBackground, - icon: !this.props.Document.useClusters ? "expand-arrows-alt" : "compress-arrows-alt" + icon: !this.props.Document.useClusters ? "chalkboard" : "chalkboard" }); layoutItems.push({ description: "Arrange contents in grid", -- cgit v1.2.3-70-g09d2