From 8a1be635352177ba05845851289d1a67b4060708 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Thu, 18 Jul 2019 19:24:58 -0400 Subject: schema cols can be moved by dragging --- src/client/util/DragManager.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'src/client/util') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 323908302..f9f6b05c0 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -288,6 +288,15 @@ export namespace DragManager { [id: string]: any; } + // for column dragging in schema view + export class ColumnDragData { + constructor(colKey: string) { + this.colKey = colKey; + } + colKey: string; + [id: string]: any; + } + export function StartLinkDrag(ele: HTMLElement, dragData: LinkDragData, downX: number, downY: number, options?: DragOptions) { StartDrag([ele], dragData, downX, downY, options); } @@ -296,6 +305,10 @@ export namespace DragManager { StartDrag([ele], dragData, downX, downY, options); } + export function StartColumnDrag(ele: HTMLElement, dragData: ColumnDragData, downX: number, downY: number, options?:DragOptions) { + StartDrag([ele], dragData, downX, downY, options); + } + export let AbortDrag: () => void = emptyFunction; function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: { [id: string]: any }) => void) { -- 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/util') 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 93c81b9fcc93dd05d723f7f103bc38fff9e21786 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Tue, 23 Jul 2019 15:19:34 -0400 Subject: Added async deserialization and got captures for script fields working again --- src/client/DocServer.ts | 11 +++++------ src/client/util/SerializationHelper.ts | 15 +++++++++------ src/client/views/InkingControl.tsx | 4 ++-- src/new_fields/Doc.ts | 13 ++++++++++--- src/new_fields/Proxy.ts | 4 ++-- src/new_fields/ScriptField.ts | 22 ++++++++++++++++++++-- 6 files changed, 48 insertions(+), 21 deletions(-) (limited to 'src/client/util') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 8c64d2b2f..077c8e5ba 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -126,12 +126,11 @@ export namespace DocServer { // future .proto calls on the Doc won't have to go farther than the cache to get their actual value. const deserializeField = getSerializedField.then(async fieldJson => { // deserialize - const field = SerializationHelper.Deserialize(fieldJson); + const field = await SerializationHelper.Deserialize(fieldJson); // either way, overwrite or delete any promises cached at this id (that we inserted as flags // to indicate that the field was in the process of being fetched). Now everything // should be an actual value within or entirely absent from the cache. if (field !== undefined) { - await field.proto; _cache[id] = field; } else { delete _cache[id]; @@ -202,18 +201,18 @@ export namespace DocServer { // future .proto calls on the Doc won't have to go farther than the cache to get their actual value. const deserializeFields = getSerializedFields.then(async fields => { const fieldMap: { [id: string]: RefField } = {}; - const protosToLoad: any = []; + // const protosToLoad: any = []; for (const field of fields) { if (field !== undefined) { // deserialize - let deserialized: any = SerializationHelper.Deserialize(field); + let deserialized = await SerializationHelper.Deserialize(field); fieldMap[field.id] = deserialized; // adds to a list of promises that will be awaited asynchronously - protosToLoad.push(deserialized.proto); + // protosToLoad.push(deserialized.proto); } } // this actually handles the loading of prototypes - await Promise.all(protosToLoad); + // await Promise.all(protosToLoad); return fieldMap; }); diff --git a/src/client/util/SerializationHelper.ts b/src/client/util/SerializationHelper.ts index dca539f3b..692d07c74 100644 --- a/src/client/util/SerializationHelper.ts +++ b/src/client/util/SerializationHelper.ts @@ -28,7 +28,7 @@ export namespace SerializationHelper { return json; } - export function Deserialize(obj: any): any { + export async function Deserialize(obj: any): Promise { if (obj === undefined || obj === null) { return undefined; } @@ -52,16 +52,19 @@ export namespace SerializationHelper { } const type = serializationTypes[obj.__type]; - const value = deserialize(type.ctor, obj); + const value = await new Promise(res => deserialize(type.ctor, obj, (err, result) => res(result))); if (type.afterDeserialize) { - type.afterDeserialize(value); + const currentSerialization = serializing; + serializing = 0; + await type.afterDeserialize(value); + serializing = currentSerialization; } serializing -= 1; return value; } } -let serializationTypes: { [name: string]: { ctor: { new(): any }, afterDeserialize?: (obj: any) => void } } = {}; +let serializationTypes: { [name: string]: { ctor: { new(): any }, afterDeserialize?: (obj: any) => void | Promise } } = {}; let reverseMap: { [ctor: string]: string } = {}; export interface DeserializableOpts { @@ -69,7 +72,7 @@ export interface DeserializableOpts { withFields(fields: string[]): Function; } -export function Deserializable(name: string, afterDeserialize?: (obj: any) => void): DeserializableOpts; +export function Deserializable(name: string, afterDeserialize?: (obj: any) => void | Promise): DeserializableOpts; export function Deserializable(constructor: { new(...args: any[]): any }): void; export function Deserializable(constructor: { new(...args: any[]): any } | string, afterDeserialize?: (obj: any) => void): DeserializableOpts | void { function addToMap(name: string, ctor: { new(...args: any[]): any }) { @@ -135,6 +138,6 @@ export namespace Deserializable { export function autoObject(): PropSchema { return custom( (s) => SerializationHelper.Serialize(s), - (s) => SerializationHelper.Deserialize(s) + (json: any, context: any, oldValue: any, cb: (err: any, result: any) => void) => SerializationHelper.Deserialize(json).then(res => cb(null, res)) ); } \ No newline at end of file diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index c7f7bdb66..1910e409b 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 { ColorResult } from 'react-color'; +import { ColorState } 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: ColorResult): void => { + switchColor = action((color: ColorState): 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/new_fields/Doc.ts b/src/new_fields/Doc.ts index 59e61023f..45eb2e514 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -67,8 +67,15 @@ export function DocListCast(field: FieldResult): Doc[] { export const WidthSym = Symbol("Width"); export const HeightSym = Symbol("Height"); +function fetchProto(doc: Doc) { + const proto = doc.proto; + if (proto instanceof Promise) { + return proto; + } +} + @scriptingGlobal -@Deserializable("doc").withFields(["id"]) +@Deserializable("doc", fetchProto).withFields(["id"]) export class Doc extends RefField { constructor(id?: FieldId, forceSave?: boolean) { super(id); @@ -133,14 +140,14 @@ export class Doc extends RefField { return "invalid"; } - public [HandleUpdate](diff: any) { + public async [HandleUpdate](diff: any) { const set = diff.$set; if (set) { for (const key in set) { if (!key.startsWith("fields.")) { continue; } - const value = SerializationHelper.Deserialize(set[key]); + const value = await SerializationHelper.Deserialize(set[key]); const fKey = key.substring(7); this[fKey] = value; } diff --git a/src/new_fields/Proxy.ts b/src/new_fields/Proxy.ts index 38d874a68..14f08814e 100644 --- a/src/new_fields/Proxy.ts +++ b/src/new_fields/Proxy.ts @@ -48,7 +48,7 @@ export class ProxyField extends ObjectField { private failed = false; private promise?: Promise; - value(): T | undefined | FieldWaiting { + value(): T | undefined | FieldWaiting { if (this.cache) { return this.cache; } @@ -63,6 +63,6 @@ export class ProxyField extends ObjectField { return field; })); } - return this.promise; + return this.promise as any; } } diff --git a/src/new_fields/ScriptField.ts b/src/new_fields/ScriptField.ts index e8a1ea28a..e1f65fc9c 100644 --- a/src/new_fields/ScriptField.ts +++ b/src/new_fields/ScriptField.ts @@ -2,10 +2,11 @@ import { ObjectField } from "./ObjectField"; import { CompiledScript, CompileScript, scriptingGlobal } from "../client/util/Scripting"; import { Copy, ToScriptString, Parent, SelfProxy } from "./FieldSymbols"; import { serializable, createSimpleSchema, map, primitive, object, deserialize, PropSchema, custom, SKIP } from "serializr"; -import { Deserializable } from "../client/util/SerializationHelper"; +import { Deserializable, autoObject } from "../client/util/SerializationHelper"; import { Doc } from "../new_fields/Doc"; import { Plugins } from "./util"; import { computedFn } from "mobx-utils"; +import { ProxyField } from "./Proxy"; function optional(propSchema: PropSchema) { return custom(value => { @@ -34,7 +35,16 @@ const scriptSchema = createSimpleSchema({ originalScript: true }); -function deserializeScript(script: ScriptField) { +async function deserializeScript(script: ScriptField) { + const captures: ProxyField = (script as any).captures; + if (captures) { + const doc = (await captures.value())!; + const captured: any = {}; + const keys = Object.keys(doc); + const vals = await Promise.all(keys.map(key => doc[key]) as any); + keys.forEach((key, i) => captured[key] = vals[i]); + (script.script.options as any).capturedVariables = captured; + } const comp = CompileScript(script.script.originalScript, script.script.options); if (!comp.compiled) { throw new Error("Couldn't compile loaded script"); @@ -48,9 +58,17 @@ export class ScriptField extends ObjectField { @serializable(object(scriptSchema)) readonly script: CompiledScript; + @serializable(autoObject()) + private captures?: ProxyField; + constructor(script: CompiledScript) { super(); + if (script && script.options.capturedVariables) { + const doc = Doc.assign(new Doc, script.options.capturedVariables); + this.captures = new ProxyField(doc); + } + this.script = script; } -- cgit v1.2.3-70-g09d2 From 35b1450709c751ae9782f60140d31718fed8146d Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Tue, 23 Jul 2019 18:22:19 -0400 Subject: Fixed async deserialization --- src/client/util/SerializationHelper.ts | 18 +++++++++--------- src/new_fields/Doc.ts | 8 ++++---- src/new_fields/List.ts | 4 ++-- src/new_fields/RefField.ts | 3 ++- 4 files changed, 17 insertions(+), 16 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/SerializationHelper.ts b/src/client/util/SerializationHelper.ts index 692d07c74..94b640afa 100644 --- a/src/client/util/SerializationHelper.ts +++ b/src/client/util/SerializationHelper.ts @@ -1,9 +1,14 @@ import { PropSchema, serialize, deserialize, custom, setDefaultModelSchema, getDefaultModelSchema, primitive, SKIP } from "serializr"; -import { Field } from "../../new_fields/Doc"; +import { Field, Doc } from "../../new_fields/Doc"; import { ClientUtils } from "./ClientUtils"; +let serializing = 0; +export function afterDocDeserialize(cb: (err: any, val: any) => void, err: any, newValue: any) { + serializing++; + cb(err, newValue); + serializing--; +} export namespace SerializationHelper { - let serializing: number = 0; export function IsSerializing() { return serializing > 0; } @@ -17,14 +22,14 @@ export namespace SerializationHelper { return obj; } - serializing += 1; + serializing++; if (!(obj.constructor.name in reverseMap)) { throw Error(`type '${obj.constructor.name}' not registered. Make sure you register it using a @Deserializable decorator`); } const json = serialize(obj); json.__type = reverseMap[obj.constructor.name]; - serializing -= 1; + serializing--; return json; } @@ -37,7 +42,6 @@ export namespace SerializationHelper { return obj; } - serializing += 1; if (!obj.__type) { if (ClientUtils.RELEASE) { console.warn("No property 'type' found in JSON."); @@ -54,12 +58,8 @@ export namespace SerializationHelper { const type = serializationTypes[obj.__type]; const value = await new Promise(res => deserialize(type.ctor, obj, (err, result) => res(result))); if (type.afterDeserialize) { - const currentSerialization = serializing; - serializing = 0; await type.afterDeserialize(value); - serializing = currentSerialization; } - serializing -= 1; return value; } } diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 45eb2e514..f0a2f7e07 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -1,6 +1,6 @@ import { observable, action } from "mobx"; -import { serializable, primitive, map, alias, list } from "serializr"; -import { autoObject, SerializationHelper, Deserializable } from "../client/util/SerializationHelper"; +import { serializable, primitive, map, alias, list, PropSchema, custom } from "serializr"; +import { autoObject, SerializationHelper, Deserializable, afterDocDeserialize } from "../client/util/SerializationHelper"; import { DocServer } from "../client/DocServer"; import { setter, getter, getField, updateFunction, deleteProperty, makeEditable, makeReadOnly } from "./util"; import { Cast, ToConstructor, PromiseValue, FieldValue, NumCast, BoolCast, StrCast } from "./Types"; @@ -61,7 +61,7 @@ export function DocListCastAsync(field: FieldResult, defaultValue?: Doc[]) { } export function DocListCast(field: FieldResult): Doc[] { - return Cast(field, listSpec(Doc), []).filter(d => d instanceof Doc) as Doc[]; + return Cast(field, listSpec(Doc), []).filter(d => d instanceof Doc) as Doc[]; j } export const WidthSym = Symbol("Width"); @@ -108,7 +108,7 @@ export class Doc extends RefField { proto: Opt; [key: string]: FieldResult; - @serializable(alias("fields", map(autoObject()))) + @serializable(alias("fields", map(autoObject(), { afterDeserialize: afterDocDeserialize }))) private get __fields() { return this.___fields; } diff --git a/src/new_fields/List.ts b/src/new_fields/List.ts index a2133a990..0c7b77fa5 100644 --- a/src/new_fields/List.ts +++ b/src/new_fields/List.ts @@ -1,4 +1,4 @@ -import { Deserializable, autoObject } from "../client/util/SerializationHelper"; +import { Deserializable, autoObject, afterDocDeserialize } from "../client/util/SerializationHelper"; import { Field } from "./Doc"; import { setter, getter, deleteProperty, updateFunction } from "./util"; import { serializable, alias, list } from "serializr"; @@ -254,7 +254,7 @@ class ListImpl extends ObjectField { [key: number]: T | (T extends RefField ? Promise : never); - @serializable(alias("fields", list(autoObject()))) + @serializable(alias("fields", list(autoObject(), { afterDeserialize: afterDocDeserialize }))) private get __fields() { return this.___fields; } diff --git a/src/new_fields/RefField.ts b/src/new_fields/RefField.ts index 75ce4287f..5414df2b9 100644 --- a/src/new_fields/RefField.ts +++ b/src/new_fields/RefField.ts @@ -1,10 +1,11 @@ import { serializable, primitive, alias } from "serializr"; import { Utils } from "../Utils"; import { Id, HandleUpdate, ToScriptString } from "./FieldSymbols"; +import { afterDocDeserialize } from "../client/util/SerializationHelper"; export type FieldId = string; export abstract class RefField { - @serializable(alias("id", primitive())) + @serializable(alias("id", primitive({ afterDeserialize: afterDocDeserialize }))) private __id: FieldId; readonly [Id]: FieldId; -- cgit v1.2.3-70-g09d2 From c96ba0c2e8ce67e16b613f6a48e6b8e6fd5daa68 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Wed, 24 Jul 2019 12:56:23 -0400 Subject: Fixed List name in type_decls --- src/client/util/type_decls.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/util') diff --git a/src/client/util/type_decls.d b/src/client/util/type_decls.d index 1f95af00c..79a4e50d5 100644 --- a/src/client/util/type_decls.d +++ b/src/client/util/type_decls.d @@ -179,7 +179,7 @@ declare class Doc extends RefField { // [ToScriptString](): string; } -declare class ListImpl extends ObjectField { +declare class List extends ObjectField { constructor(fields?: T[]); [index: number]: T | (T extends RefField ? Promise : never); [Copy](): ObjectField; -- cgit v1.2.3-70-g09d2 From 75070c4c2188823ed9a09816861d4f873574c9db Mon Sep 17 00:00:00 2001 From: Fawn Date: Wed, 24 Jul 2019 17:48:11 -0400 Subject: can move rows within expanded collection --- src/client/util/DragManager.ts | 4 +-- .../views/collections/CollectionSchemaCells.tsx | 42 ++++++++++++---------- .../CollectionSchemaMovableTableHOC.tsx | 18 +++++----- .../views/collections/CollectionSchemaView.scss | 24 ++++++------- .../views/collections/CollectionSchemaView.tsx | 17 +++++---- src/new_fields/Doc.ts | 9 ++++- 6 files changed, 64 insertions(+), 50 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index f9f6b05c0..0299b1d90 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -1,6 +1,6 @@ import { action, runInAction } from "mobx"; import { Doc } from "../../new_fields/Doc"; -import { Cast } from "../../new_fields/Types"; +import { Cast, StrCast } from "../../new_fields/Types"; import { URLField } from "../../new_fields/URLField"; import { emptyFunction } from "../../Utils"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; @@ -305,7 +305,7 @@ export namespace DragManager { StartDrag([ele], dragData, downX, downY, options); } - export function StartColumnDrag(ele: HTMLElement, dragData: ColumnDragData, downX: number, downY: number, options?:DragOptions) { + export function StartColumnDrag(ele: HTMLElement, dragData: ColumnDragData, downX: number, downY: number, options?: DragOptions) { StartDrag([ele], dragData, downX, downY, options); } diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index abb2203a0..bc84da140 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -34,7 +34,7 @@ export interface CellProps { renderDepth: number; addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void; moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; - isFocused: boolean; + isFocused: boolean; changeFocusedCellByIndex: (row: number, col: number) => void; setIsEditing: (isEditing: boolean) => void; isEditable: boolean; @@ -42,22 +42,22 @@ export interface CellProps { @observer export class CollectionSchemaCell extends React.Component { - @observable protected _isEditing: boolean = false; + @observable protected _isEditing: boolean = this.props.isEditing ? true : false; protected _focusRef = React.createRef(); protected _document = this.props.rowProps.original; private _dropDisposer?: DragManager.DragDropDisposer; componentDidMount() { - if (this._focusRef.current) { - if (this.props.isFocused) { - this._focusRef.current.className += " focused"; - if (!this.props.isEditable) { - this._focusRef.current.className += " inactive"; - } - } else { - this._focusRef.current.className = "collectionSchemaView-cellWrapper"; - } - } + // if (this._focusRef.current) { + // if (this.props.isFocused) { + // this._focusRef.current.className += " focused"; + // if (!this.props.isEditable) { + // this._focusRef.current.className += " inactive"; + // } + // } else { + // this._focusRef.current.className = "collectionSchemaView-cellWrapper"; + // } + // } document.addEventListener("keydown", this.onKeyDown); @@ -69,6 +69,7 @@ export class CollectionSchemaCell extends React.Component { @action onKeyDown = (e: KeyboardEvent): void => { + console.log("CELL keydown"); if (this.props.isFocused && this.props.isEditable) { document.removeEventListener("keydown", this.onKeyDown); this._isEditing = true; @@ -139,10 +140,10 @@ export class CollectionSchemaCell extends React.Component { addDocTab: this.props.addDocTab, }; - let onItemDown = (e: React.PointerEvent) => { - SetupDrag(this._focusRef, () => this._document[props.fieldKey] instanceof Doc ? this._document[props.fieldKey] : this._document, - this._document[props.fieldKey] instanceof Doc ? (doc: Doc, target: Doc, addDoc: (newDoc: Doc) => any) => addDoc(doc) : this.props.moveDocument, this._document[props.fieldKey] instanceof Doc ? "alias" : this.props.Document.schemaDoc ? "copy" : undefined)(e); - }; + // let onItemDown = (e: React.PointerEvent) => { + // SetupDrag(this._focusRef, () => this._document[props.fieldKey] instanceof Doc ? this._document[props.fieldKey] : this._document, + // this._document[props.fieldKey] instanceof Doc ? (doc: Doc, target: Doc, addDoc: (newDoc: Doc) => any) => addDoc(doc) : this.props.moveDocument, this._document[props.fieldKey] instanceof Doc ? "alias" : this.props.Document.schemaDoc ? "copy" : undefined)(e); + // }; let onPointerEnter = (e: React.PointerEvent): void => { if (e.buttons === 1 && SelectionManager.GetIsDragging() && (type === "document" || type === undefined)) { dragRef!.current!.className = "doc-drag-over"; @@ -163,10 +164,15 @@ export class CollectionSchemaCell extends React.Component { contents = typeof field === "object" ? doc ? StrCast(doc.title) === "" ? "--" : StrCast(doc.title) : `--${typeof field}--` : `--${typeof field}--`; } + let className = "collectionSchemaView-cellWrapper"; + if (this._isEditing) className += " editing"; + if (this.props.isFocused && this.props.isEditable) className += " focused"; + if (this.props.isFocused && !this.props.isEditable) className += " inactive"; + return (
-
-
+
+
Transform; addDoc: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean; - moveDoc: DragManager.MoveFunction; + removeDoc: (doc: Doc) => boolean; rowFocused: boolean; textWrapRow: (doc: Doc) => void; rowWrapped: boolean; @@ -145,9 +146,6 @@ export class MovableRow extends React.Component { } rowDrop = (e: Event, de: DragManager.DropEvent) => { - // const { children = null, rowInfo } = this.props; - // if (!rowInfo) return false; - const rowDoc = FieldValue(Cast(this.props.rowInfo.original, Doc)); if (!rowDoc) return false; @@ -160,24 +158,26 @@ export class MovableRow extends React.Component { e.stopPropagation(); if (de.data.draggedDocuments[0] === rowDoc) return true; let addDocument = (doc: Doc) => this.props.addDoc(doc, rowDoc, before); - let movedDocs = de.data.draggedDocuments; //(de.data.options === this.props.treeViewId ? de.data.draggedDocuments : de.data.droppedDocuments); + let movedDocs = de.data.draggedDocuments; return (de.data.dropAction || de.data.userDropAction) ? de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before) || added, false) : (de.data.moveDocument) ? movedDocs.reduce((added: boolean, d) => de.data.moveDocument(d, rowDoc, addDocument) || added, false) - // movedDocs.reduce((added: boolean, d) => this.props.moveDoc(d, rowDoc, addDocument) || added, false) : de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before), false); } return false; } onRowContextMenu = (e: React.MouseEvent): void => { - // const { rowInfo } = this.props; - // const { textWrapRow, original } = rowInfo; let description = this.props.rowWrapped ? "Unwrap text on row" : "Text wrap row"; ContextMenu.Instance.addItem({ description: description, event: () => this.props.textWrapRow(this.props.rowInfo.original) }); } + @action + move: DragManager.MoveFunction = (doc: Doc, target: Doc, addDoc) => { + return doc !== target && this.props.removeDoc(doc) && addDoc(doc); + } + render() { const { children = null, rowInfo } = this.props; if (!rowInfo) { @@ -189,7 +189,7 @@ export class MovableRow extends React.Component { if (!doc) return <>; let reference = React.createRef(); - let onItemDown = SetupDrag(reference, () => doc, this.props.moveDoc); + let onItemDown = SetupDrag(reference, () => doc, this.move); let className = "collectionSchema-row"; if (this.props.rowFocused) className += " row-focused"; diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss index 410790197..c2b0d8f42 100644 --- a/src/client/views/collections/CollectionSchemaView.scss +++ b/src/client/views/collections/CollectionSchemaView.scss @@ -148,7 +148,7 @@ padding: 0; font-size: 13px; text-align: center; - // white-space: normal; + white-space: normal; .imageBox-cont { position: relative; @@ -196,8 +196,6 @@ .collectionSchemaView-header { height: 100%; color: gray; - letter-spacing: 2px; - text-transform: uppercase; .collectionSchema-header-menu { height: 100%; @@ -206,16 +204,18 @@ width: 100%; height: 100%; padding: 4px; + letter-spacing: 2px; + text-transform: uppercase; svg { margin-right: 4px; } } - div[class*="css"] { - width: 100%; - height: 100%; - } + // div[class*="css"] { + // width: 100%; + // height: 100%; + // } } } @@ -287,12 +287,6 @@ button.add-column { background-color: $light-color-secondary; } - &.row-wrapped { - .rt-td { - white-space: normal; - } - } - .row-dragger { // height: $MAX_ROW_HEIGHT; } @@ -309,6 +303,10 @@ button.add-column { &.row-inside { border: 1px solid red; } + + .row-dragging { + background-color: blue; + } } } diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 62e4ceb54..a7e435ac6 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -344,7 +344,7 @@ export class SchemaTable extends React.Component { addDocTab: this.props.addDocTab, moveDocument: this.props.moveDocument, setIsEditing: this.setCellIsEditing, - isEditable: isEditable + isEditable: isEditable, }; let colType = this.getColumnType(col); @@ -393,13 +393,16 @@ export class SchemaTable extends React.Component { } tableAddDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => { - console.log("table add doc"); return Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before); } - tableMoveDoc = (d: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => { - console.log("SCHEMA MOVE", StrCast(d.title), StrCast(target.title)); - this.props.moveDocument(d, target, addDoc); + tableRemoveDoc = (document: Doc): boolean => { + let index = this.props.childDocs.findIndex(d => d === document); + if (index !== -1) { + this.props.childDocs.splice(index, 1); + return true; + } + return false; } private getTrProps: ComponentPropsGetterR = (state, rowInfo) => { @@ -410,7 +413,7 @@ export class SchemaTable extends React.Component { return { ScreenToLocalTransform: this.props.ScreenToLocalTransform, addDoc: this.tableAddDoc, - moveDoc: this.tableMoveDoc, + removeDoc: this.tableRemoveDoc, rowInfo, rowFocused: !this._headerIsEditing && rowInfo.index === this._focusedCell.row && this.props.isFocused(this.props.Document), textWrapRow: this.textWrapRow, @@ -467,6 +470,7 @@ export class SchemaTable extends React.Component { } onKeyDown = (e: KeyboardEvent): void => { + console.log("schema keydown", !this._cellIsEditing, !this._headerIsEditing, this.props.isFocused(this.props.Document)); if (!this._cellIsEditing && !this._headerIsEditing && this.props.isFocused(this.props.Document)) {// && this.props.isSelected()) { let direction = e.key === "Tab" ? "tab" : e.which === 39 ? "right" : e.which === 37 ? "left" : e.which === 38 ? "up" : e.which === 40 ? "down" : ""; this.changeFocusedCellByDirection(direction); @@ -504,7 +508,6 @@ export class SchemaTable extends React.Component { changeFocusedCellByIndex = (row: number, col: number): void => { this._focusedCell = { row: row, col: col }; this.props.setFocused(this.props.Document); - // console.log("changed cell by index", StrCast(this.props.Document.title)); } @action diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 5ae0753d8..eba1d4f04 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -267,21 +267,28 @@ export namespace Doc { export function AddDocToList(target: Doc, key: string, doc: Doc, relativeTo?: Doc, before?: boolean, first?: boolean, allowDuplicates?: boolean) { if (target[key] === undefined) { + console.log("target key undefined"); Doc.GetProto(target)[key] = new List(); } let list = Cast(target[key], listSpec(Doc)); if (list) { + console.log("has list"); if (allowDuplicates !== true) { let pind = list.reduce((l, d, i) => d instanceof Doc && Doc.AreProtosEqual(d, doc) ? i : l, -1); if (pind !== -1) { list.splice(pind, 1); } } - if (first) list.splice(0, 0, doc); + if (first) { + console.log("is first"); + list.splice(0, 0, doc); + } else { + console.log("not first"); let ind = relativeTo ? list.indexOf(relativeTo) : -1; if (ind === -1) list.push(doc); else list.splice(before ? ind : ind + 1, 0, doc); + console.log("index", ind); } } return true; -- cgit v1.2.3-70-g09d2 From 18a0850353b0fc55cbf7ae1d4e2763919fc78bca Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Thu, 25 Jul 2019 12:58:34 -0400 Subject: Fixed async deserialization bug --- src/client/util/SerializationHelper.ts | 6 +++--- src/new_fields/Doc.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/SerializationHelper.ts b/src/client/util/SerializationHelper.ts index 94b640afa..034be8f67 100644 --- a/src/client/util/SerializationHelper.ts +++ b/src/client/util/SerializationHelper.ts @@ -91,15 +91,15 @@ export function Deserializable(constructor: { new(...args: any[]): any } | strin if (typeof constructor === "string") { return Object.assign((ctor: { new(...args: any[]): any }) => { addToMap(constructor, ctor); - }, { withFields: Deserializable.withFields }); + }, { withFields: (fields: string[]) => Deserializable.withFields(fields, name, afterDeserialize) }); } addToMap(constructor.name, constructor); } export namespace Deserializable { - export function withFields(fields: string[]) { + export function withFields(fields: string[], name?: string, afterDeserialize?: (obj: any) => void | Promise) { return function (constructor: { new(...fields: any[]): any }) { - Deserializable(constructor); + Deserializable(name || constructor.name, afterDeserialize)(constructor); let schema = getDefaultModelSchema(constructor); if (schema) { schema.factory = context => { diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 5e98ec48c..0a5bdc4b7 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -78,7 +78,7 @@ function fetchProto(doc: Doc) { let updatingFromServer = false; @scriptingGlobal -@Deserializable("doc", fetchProto).withFields(["id"]) +@Deserializable("Doc", fetchProto).withFields(["id"]) export class Doc extends RefField { constructor(id?: FieldId, forceSave?: boolean) { super(id); -- cgit v1.2.3-70-g09d2 From 3ee8a9ebc760e3049b692efad0d6edfb877342cf Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 25 Jul 2019 22:27:26 -0400 Subject: fixed dragging annotaitons on images. fixed border rounding of buttons. fixed dropping to ignore document titles. --- src/client/util/DragManager.ts | 3 ++- src/client/views/collections/CollectionBaseView.tsx | 2 +- src/client/views/nodes/ButtonBox.scss | 2 ++ src/client/views/nodes/DocumentView.tsx | 3 ++- src/client/views/nodes/ImageBox.tsx | 2 -- 5 files changed, 7 insertions(+), 5 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 323908302..5271f2f5d 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -412,7 +412,6 @@ export namespace DragManager { }; let hideDragElements = () => { - SelectionManager.SetIsDragging(false); dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement)); eles.map(ele => (ele.hidden = false)); }; @@ -426,11 +425,13 @@ export namespace DragManager { AbortDrag = () => { hideDragElements(); + SelectionManager.SetIsDragging(false); endDrag(); }; const upHandler = (e: PointerEvent) => { hideDragElements(); dispatchDrag(eles, e, dragData, options, finishDrag); + SelectionManager.SetIsDragging(false); endDrag(); }; document.addEventListener("pointermove", moveHandler, true); diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index 72faf52c4..c39caec24 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -124,7 +124,7 @@ export class CollectionBaseView extends React.Component { @action.bound moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean { let self = this; - let targetDataDoc = this.props.fieldExt || this.props.Document.isTemplate ? this.extensionDoc : this.props.Document; + let targetDataDoc = this.props.Document; if (Doc.AreProtosEqual(targetDataDoc, targetCollection)) { //if (Doc.AreProtosEqual(this.extensionDoc, targetCollection)) { return true; diff --git a/src/client/views/nodes/ButtonBox.scss b/src/client/views/nodes/ButtonBox.scss index 97cc91128..92beafa15 100644 --- a/src/client/views/nodes/ButtonBox.scss +++ b/src/client/views/nodes/ButtonBox.scss @@ -2,9 +2,11 @@ width: 100%; height: 100%; pointer-events: all; + border-radius: inherit; } .buttonBox-mainButton { width: 100%; height: 100%; + border-radius: inherit; } \ No newline at end of file diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index b5e64ed19..4a5e20533 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -298,6 +298,7 @@ export class DocumentView extends DocComponent(Docu let fullScreenAlias = Doc.MakeAlias(this.props.Document); fullScreenAlias.templates = new List(); Doc.UseDetailLayout(fullScreenAlias); + fullScreenAlias.showCaption = true; this.props.addDocTab(fullScreenAlias, this.dataDoc, "inTab"); SelectionManager.DeselectAll(); this.props.Document.libraryBrush = false; @@ -673,7 +674,7 @@ export class DocumentView extends DocComponent(Docu {!showTitle ? (null) :
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 6541007d0..ea167286b 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -104,8 +104,6 @@ export class ImageBox extends DocComponent(ImageD e.stopPropagation(); } } - } else if (!this.props.isSelected()) { - e.stopPropagation(); } })); // de.data.removeDocument() bcz: need to implement -- cgit v1.2.3-70-g09d2 From 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/util') 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 d009df0c7142c04947193fb5670974017919bd05 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Sat, 27 Jul 2019 16:38:55 -0400 Subject: undo for link dragging, image rotation and cognitive services --- src/client/cognitive_services/CognitiveServices.ts | 5 ++++ src/client/util/DragManager.ts | 2 ++ src/client/views/DocumentDecorations.tsx | 10 ++++++++ src/client/views/nodes/DocumentView.tsx | 1 + src/client/views/nodes/ImageBox.tsx | 29 +++++++++++----------- 5 files changed, 33 insertions(+), 14 deletions(-) (limited to 'src/client/util') diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts index d69378d0e..bbc438a9b 100644 --- a/src/client/cognitive_services/CognitiveServices.ts +++ b/src/client/cognitive_services/CognitiveServices.ts @@ -9,6 +9,7 @@ import { Utils } from "../../Utils"; import { CompileScript } from "../util/Scripting"; import { ComputedField } from "../../new_fields/ScriptField"; import { InkData } from "../../new_fields/InkField"; +import { undoBatch, UndoManager } from "../util/UndoManager"; type APIManager = { converter: BodyConverter, requester: RequestExecutor, analyzer: AnalysisApplier }; type RequestExecutor = (apiKey: string, body: string, service: Service) => Promise; @@ -103,6 +104,7 @@ export namespace CognitiveServices { }, analyzer: async (target: Doc, keys: string[], service: Service, converter: Converter) => { + let batch = UndoManager.StartBatch("Image Analysis"); let imageData = Cast(target.data, ImageField); let storageKey = keys[0]; if (!imageData || await Cast(target[storageKey], Doc)) { @@ -120,6 +122,7 @@ export namespace CognitiveServices { } } target[storageKey] = toStore; + batch.end(); } }; @@ -205,6 +208,7 @@ export namespace CognitiveServices { }, analyzer: async (target: Doc, keys: string[], inkData: InkData) => { + let batch = UndoManager.StartBatch("Ink Analysis"); let results = await executeQuery(Service.Handwriting, Manager, inkData); if (results) { results.recognitionUnits && (results = results.recognitionUnits); @@ -213,6 +217,7 @@ export namespace CognitiveServices { let individualWords = recognizedText.filter((text: string) => text && text.split(" ").length === 1); target[keys[1]] = individualWords.join(" "); } + batch.end(); } }; diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 5271f2f5d..95416cd53 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -8,6 +8,7 @@ import * as globalCssVariables from "../views/globalCssVariables.scss"; import { DocumentManager } from "./DocumentManager"; import { LinkManager } from "./LinkManager"; import { SelectionManager } from "./SelectionManager"; +import { DocumentDecorations } from "../views/DocumentDecorations"; export type dropActionType = "alias" | "copy" | undefined; export function SetupDrag( @@ -421,6 +422,7 @@ export namespace DragManager { if (options) { options.handlers.dragComplete({}); } + DocumentDecorations.Instance.endLinkDragBatch(); }; AbortDrag = () => { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 255855b45..c4d958338 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -54,6 +54,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> private _downY = 0; private _iconDoc?: Doc = undefined; private _resizeUndo?: UndoManager.Batch; + private _linkDrag?: UndoManager.Batch; @observable private _minimizedX = 0; @observable private _minimizedY = 0; @observable private _title: string = ""; @@ -376,7 +377,16 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> } } + endLinkDragBatch = () => { + if (!this._linkDrag) { + return; + } + this._linkDrag.end(); + this._linkDrag = undefined; + } + onLinkerButtonDown = (e: React.PointerEvent): void => { + this._linkDrag = UndoManager.StartBatch("Drag Link"); e.stopPropagation(); document.removeEventListener("pointermove", this.onLinkerButtonMoved); document.addEventListener("pointermove", this.onLinkerButtonMoved); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 392faf3e9..ebb2da506 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -40,6 +40,7 @@ import { ScriptingRepl } from '../ScriptingRepl'; import { ClientUtils } from '../../util/ClientUtils'; import { EditableView } from '../EditableView'; import { faHandPointer, faHandPointRight } from '@fortawesome/free-regular-svg-icons'; +import { DocumentDecorations } from '../DocumentDecorations'; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? library.add(fa.faTrash); diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index a36885616..bdb50bcf0 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -192,6 +192,20 @@ export class ImageBox extends DocComponent(ImageD }); } + @undoBatch + rotate = action(() => { + let proto = Doc.GetProto(this.props.Document); + let nw = this.props.Document.nativeWidth; + let nh = this.props.Document.nativeHeight; + let w = this.props.Document.width; + let h = this.props.Document.height; + proto.rotation = (NumCast(this.props.Document.rotation) + 90) % 360; + proto.nativeWidth = nh; + proto.nativeHeight = nw; + this.props.Document.width = h; + this.props.Document.height = w; + }); + specificContextMenu = (e: React.MouseEvent): void => { let field = Cast(this.Document[this.props.fieldKey], ImageField); if (field) { @@ -199,20 +213,7 @@ export class ImageBox extends DocComponent(ImageD let funcs: ContextMenuProps[] = []; funcs.push({ description: "Copy path", event: () => Utils.CopyText(url), icon: "expand-arrows-alt" }); funcs.push({ description: "Record 1sec audio", event: this.recordAudioAnnotation, icon: "expand-arrows-alt" }); - funcs.push({ - description: "Rotate", event: action(() => { - let proto = Doc.GetProto(this.props.Document); - let nw = this.props.Document.nativeWidth; - let nh = this.props.Document.nativeHeight; - let w = this.props.Document.width; - let h = this.props.Document.height; - proto.rotation = (NumCast(this.props.Document.rotation) + 90) % 360; - proto.nativeWidth = nh; - proto.nativeHeight = nw; - this.props.Document.width = h; - this.props.Document.height = w; - }), icon: "expand-arrows-alt" - }); + funcs.push({ description: "Rotate", event: this.rotate, icon: "expand-arrows-alt" }); let modes: ContextMenuProps[] = []; let dataDoc = Doc.GetProto(this.props.Document); -- cgit v1.2.3-70-g09d2 From d53ad748d90ca1c863a7ef52d0835573ce967a54 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Sat, 27 Jul 2019 16:49:07 -0400 Subject: init --- src/client/util/TooltipTextMenu.tsx | 6 +++++- src/client/views/GlobalKeyHandler.ts | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 30 ++++++++++++++++++++++++++++- src/client/views/nodes/PDFBox.tsx | 14 ++++++++++++++ src/client/views/pdf/Page.tsx | 11 ++++++++--- 5 files changed, 57 insertions(+), 6 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index a4c053de2..6214b568c 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -1,6 +1,6 @@ import { action } from "mobx"; import { Dropdown, MenuItem, icons, } from "prosemirror-menu"; //no import css -import { EditorState, NodeSelection, TextSelection } from "prosemirror-state"; +import { EditorState, NodeSelection, TextSelection, Transaction } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import { schema } from "./RichTextSchema"; import { Schema, NodeType, MarkType, Mark, ResolvedPos } from "prosemirror-model"; @@ -321,6 +321,10 @@ export class TooltipTextMenu { } } + makeLinkWithState = (state: EditorState, target: string, location: string) => { + let link = state.schema.mark(state.schema.marks.link, { href: target, location: location }); + } + makeLink = (target: string, location: string) => { let node = this.view.state.selection.$from.nodeAfter; let link = this.view.state.schema.mark(this.view.state.schema.marks.link, { href: target, location: location }); diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 7477c5b4f..6c80c7645 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -151,9 +151,9 @@ export default class KeyManager { stopPropagation = false; break; case "a": - case "c": case "v": case "x": + case "c": stopPropagation = false; preventDefault = false; break; diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 0a79677e2..cf913a4c7 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -6,7 +6,7 @@ import { baseKeymap } from "prosemirror-commands"; import { history } from "prosemirror-history"; import { keymap } from "prosemirror-keymap"; import { NodeType } from 'prosemirror-model'; -import { EditorState, Plugin, Transaction } from "prosemirror-state"; +import { EditorState, Plugin, Transaction, Selection } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import { Doc, Opt } from "../../../new_fields/Doc"; import { Id, Copy } from '../../../new_fields/FieldSymbols'; @@ -123,12 +123,40 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (this.props.isOverlay) { DragManager.StartDragFunctions.push(() => FormattedTextBox.InputBoxOverlay = undefined); } + + document.addEventListener("paste", this.paste); } @computed get extensionDoc() { return Doc.resolvedFieldDataDoc(this.dataDoc, this.props.fieldKey, "dummy"); } @computed get dataDoc() { return BoolCast(this.props.Document.isTemplate) && this.props.DataDoc ? this.props.DataDoc : Doc.GetProto(this.props.Document); } + paste = (e: ClipboardEvent) => { + if (e.clipboardData && this._editorView) { + let pdfPasteText = `${Utils.GenerateDeterministicGuid("pdf paste")}`; + for (let i = 0; i < e.clipboardData.items.length; i++) { + let item = e.clipboardData.items.item(i); + if (item.type === "text/plain") { + item.getAsString((text) => { + let pdfPasteIndex = text.indexOf(pdfPasteText); + if (pdfPasteIndex > -1) { + let insertText = text.substr(0, pdfPasteIndex); + const tx = this._editorView!.state.tr.insertText(insertText); + // tx.setSelection(new Selection(tx.)) + const state = this._editorView!.state; + this._editorView!.dispatch(tx); + if (this._toolTipTextMenu) { + // this._toolTipTextMenu.makeLinkWithState(state) + } + e.stopPropagation(); + e.preventDefault(); + } + }); + } + } + } + } + dispatchTransaction = (tx: Transaction) => { if (this._editorView) { const state = this._editorView.state.apply(tx); diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 5a5e6e6dd..282312dca 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -24,6 +24,8 @@ import { Flyout, anchorPoints } from '../DocumentDecorations'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { ScriptField } from '../../../new_fields/ScriptField'; import { KeyCodes } from '../../northstar/utils/KeyCodes'; +import { Utils } from '../../../Utils'; +import { Id } from '../../../new_fields/FieldSymbols'; type PdfDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>; const PdfDocument = makeInterface(positionSchema, pageSchema); @@ -32,6 +34,7 @@ export const handleBackspace = (e: React.KeyboardEvent) => { if (e.keyCode === K @observer export class PDFBox extends DocComponent(PdfDocument) { public static LayoutString() { return FieldView.LayoutString(PDFBox); } + public selectionText: string = ""; @observable private _alt = false; @observable private _scrollY: number = 0; @@ -63,6 +66,8 @@ export class PDFBox extends DocComponent(PdfDocumen this._keyRef = React.createRef(); this._valueRef = React.createRef(); this._scriptRef = React.createRef(); + + document.addEventListener("keydown", this.onKeyDown); } componentDidMount() { @@ -73,6 +78,14 @@ export class PDFBox extends DocComponent(PdfDocumen this._reactionDisposer && this._reactionDisposer(); } + onKeyDown = (e: KeyboardEvent) => { + if (e.ctrlKey && e.keyCode === KeyCodes.C) { + let text = this.selectionText; + text += `${Utils.GenerateDeterministicGuid("pdf paste")}/${this.props.Document[Id]}`; + navigator.clipboard.writeText(text); + } + } + public GetPage() { return Math.floor(NumCast(this.props.Document.scrollY) / NumCast(this.dataDoc.pdfHeight)) + 1; } @@ -241,6 +254,7 @@ export class PDFBox extends DocComponent(PdfDocumen let classname = "pdfBox-cont" + (this.props.active() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return (
{ else { let sel = window.getSelection(); if (sel && sel.type === "Range") { - this.createTextAnnotation(sel); + let selRange = sel.getRangeAt(0); + this.createTextAnnotation(sel, selRange); PDFMenu.Instance.jumpTo(e.clientX, e.clientY); } } @@ -375,8 +376,8 @@ export default class Page extends React.Component { } @action - createTextAnnotation = (sel: Selection) => { - let clientRects = sel.getRangeAt(0).getClientRects(); + createTextAnnotation = (sel: Selection, selRange: Range) => { + let clientRects = selRange.getClientRects(); if (this._textLayer.current) { let boundingRect = this._textLayer.current.getBoundingClientRect(); for (let i = 0; i < clientRects.length; i++) { @@ -393,6 +394,10 @@ export default class Page extends React.Component { } } } + let text = selRange.extractContents().textContent; + if (text) { + this.props.parent.selectionText = text; + } // clear selection if (sel.empty) { // Chrome sel.empty(); -- cgit v1.2.3-70-g09d2 From 905d77804bf9a810822e5e43f0cd06019aef7620 Mon Sep 17 00:00:00 2001 From: Fawn Date: Sat, 27 Jul 2019 17:03:20 -0400 Subject: schema headers changed to schemaheaderfields, a bug with udpating the headers (in CollectionSchemaView>ChangeColumn) --- src/client/documents/Documents.ts | 13 +-- src/client/util/DragManager.ts | 5 +- src/client/views/MainView.tsx | 3 +- .../views/collections/CollectionSchemaCells.tsx | 30 +++++- .../views/collections/CollectionSchemaHeaders.tsx | 9 +- .../CollectionSchemaMovableTableHOC.tsx | 7 +- .../views/collections/CollectionSchemaView.scss | 15 +++ .../views/collections/CollectionSchemaView.tsx | 103 +++++++++++++-------- .../views/collections/CollectionStackingView.tsx | 2 +- .../collections/collectionFreeForm/MarqueeView.tsx | 3 +- src/client/views/nodes/LinkEditor.tsx | 3 +- src/client/views/nodes/LinkMenuGroup.tsx | 3 +- src/new_fields/SchemaHeaderField.ts | 55 +++++++++-- 13 files changed, 186 insertions(+), 65 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 76ac34d85..e6fe1b8b3 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -39,6 +39,7 @@ import { DocumentManager } from "../util/DocumentManager"; import DirectoryImportBox from "../util/Import & Export/DirectoryImportBox"; import { Scripting } from "../util/Scripting"; import { ButtonBox } from "../views/nodes/ButtonBox"; +import { SchemaHeaderField, RandomPastel } from "../../new_fields/SchemaHeaderField"; var requestImageSize = require('../util/request-image-size'); var path = require('path'); @@ -84,7 +85,7 @@ export interface DocumentOptions { curPage?: number; documentText?: string; borderRounding?: string; - schemaColumns?: List; + schemaColumns?: List; dockingConfig?: string; dbDoc?: Doc; // [key: string]: Opt; @@ -403,23 +404,23 @@ export namespace Docs { } export function FreeformDocument(documents: Array, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Freeform }); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Freeform }); } - export function SchemaDocument(schemaColumns: string[], documents: Array, options: DocumentOptions) { + export function SchemaDocument(schemaColumns: SchemaHeaderField[], documents: Array, options: DocumentOptions) { return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List(schemaColumns), ...options, viewType: CollectionViewType.Schema }); } export function TreeDocument(documents: Array, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Tree }); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("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 }); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Stacking }); } export function MasonryDocument(documents: Array, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Masonry }); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Masonry }); } export function ButtonDocument(options?: DocumentOptions) { diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 0299b1d90..47d3c313d 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -8,6 +8,7 @@ import * as globalCssVariables from "../views/globalCssVariables.scss"; import { DocumentManager } from "./DocumentManager"; import { LinkManager } from "./LinkManager"; import { SelectionManager } from "./SelectionManager"; +import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField"; export type dropActionType = "alias" | "copy" | undefined; export function SetupDrag( @@ -290,10 +291,10 @@ export namespace DragManager { // for column dragging in schema view export class ColumnDragData { - constructor(colKey: string) { + constructor(colKey: SchemaHeaderField) { this.colKey = colKey; } - colKey: string; + colKey: SchemaHeaderField; [id: string]: any; } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 0e04b5e7e..282db244e 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -39,6 +39,7 @@ import { PreviewCursor } from './PreviewCursor'; import { FilterBox } from './search/FilterBox'; import { CollectionTreeView } from './collections/CollectionTreeView'; import { ClientUtils } from '../util/ClientUtils'; +import { SchemaHeaderField, RandomPastel } from '../../new_fields/SchemaHeaderField'; @observer export class MainView extends React.Component { @@ -375,7 +376,7 @@ export class MainView extends React.Component { let imgurl = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg"; // let addDockingNode = action(() => Docs.Create.StandardCollectionDockingDocument([{ doc: addColNode(), initialWidth: 200 }], { width: 200, height: 200, title: "a nested docking freeform collection" })); - let addSchemaNode = action(() => Docs.Create.SchemaDocument(["title"], [], { width: 200, height: 200, title: "a schema collection" })); + let addSchemaNode = action(() => Docs.Create.SchemaDocument([new SchemaHeaderField("title")], [], { width: 200, height: 200, title: "a schema collection" })); //let addTreeNode = action(() => Docs.TreeDocument([CurrentUserUtils.UserDocument], { width: 250, height: 400, title: "Library:" + CurrentUserUtils.email, dropAction: "alias" })); // let addTreeNode = action(() => Docs.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", dropAction: "copy" })); let addColNode = action(() => Docs.Create.FreeformDocument([], { width: this.pwidth * .7, height: this.pheight, title: "a freeform collection" })); diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index 4a618a436..c91f1017b 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -21,7 +21,12 @@ import { NumCast, StrCast, BoolCast, FieldValue, Cast } from "../../../new_field import { Docs } from "../../documents/Documents"; import { DocumentContentsView } from "../nodes/DocumentContentsView"; import { SelectionManager } from "../../util/SelectionManager"; +import { library } from '@fortawesome/fontawesome-svg-core'; +import { faExpand } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { SchemaHeaderField, RandomPastel } from "../../../new_fields/SchemaHeaderField"; +library.add(faExpand); export interface CellProps { row: number; @@ -38,6 +43,7 @@ export interface CellProps { changeFocusedCellByIndex: (row: number, col: number) => void; setIsEditing: (isEditing: boolean) => void; isEditable: boolean; + setPreviewDoc: (doc: Doc) => void; } @observer @@ -92,7 +98,7 @@ export class CollectionSchemaCell extends React.Component { this._document[fieldKey] = de.data.draggedDocuments[0]; } else { - let coll = Docs.Create.SchemaDocument(["title"], de.data.draggedDocuments, {}); + let coll = Docs.Create.SchemaDocument([new SchemaHeaderField("title")], de.data.draggedDocuments, {}); this._document[fieldKey] = coll; } e.stopPropagation(); @@ -106,6 +112,18 @@ export class CollectionSchemaCell extends React.Component { } } + expandDoc = (e: React.PointerEvent) => { + let field = this.props.rowProps.original[this.props.rowProps.column.id as string]; + let doc = FieldValue(Cast(field, Doc)); + + console.log("Expanding doc", StrCast(doc!.title)); + this.props.setPreviewDoc(doc!); + + // this.props.changeFocusedCellByIndex(this.props.row, this.props.col); + + e.stopPropagation(); + } + renderCellWithType(type: string | undefined) { let dragRef: React.RefObject = React.createRef(); @@ -157,6 +175,15 @@ export class CollectionSchemaCell extends React.Component { if (this.props.isFocused && this.props.isEditable) className += " focused"; if (this.props.isFocused && !this.props.isEditable) className += " inactive"; + let doc = FieldValue(Cast(field, Doc)); + if (type === "document") console.log("doc", typeof field); + let fieldIsDoc = (type === "document" && typeof field === "object") || (typeof field === "object" && doc); + let docExpander = ( +
+ +
+ ); + return (
@@ -193,6 +220,7 @@ export class CollectionSchemaCell extends React.Component { val && val.forEach(doc => this.applyToDoc(doc, run)); }} />
+ {fieldIsDoc ? docExpander : null}
); diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx index d1d0674c4..9fc28eafa 100644 --- a/src/client/views/collections/CollectionSchemaHeaders.tsx +++ b/src/client/views/collections/CollectionSchemaHeaders.tsx @@ -10,11 +10,12 @@ import { ColumnType } from "./CollectionSchemaView"; import { emptyFunction } from "../../../Utils"; import { contains } from "typescript-collections/dist/lib/arrays"; import { faFile } from "@fortawesome/free-regular-svg-icons"; +import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; library.add(faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faFile); export interface HeaderProps { - keyValue: string; + keyValue: SchemaHeaderField; possibleKeys: string[]; existingKeys: string[]; keyType: ColumnType; @@ -33,14 +34,14 @@ export class CollectionSchemaHeader extends React.Component { this.props.keyType === ColumnType.Boolean ? "check-square" : this.props.keyType === ColumnType.Doc ? "file" : "align-justify"; return ( -
+
{this.props.keyValue}
} + menuButtonContent={
{this.props.keyValue.heading}
} addNew={false} onSelect={this.props.onSelect} setIsEditing={this.props.setIsEditing} diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx index 0ddd56180..2349e42ca 100644 --- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx +++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx @@ -12,14 +12,15 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faGripVertical, faTrash } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { DocumentManager } from "../../util/DocumentManager"; +import { PastelSchemaPalette, SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; library.add(faGripVertical, faTrash); export interface MovableColumnProps { columnRenderer: TableCellRenderer; - columnValue: string; - allColumns: string[]; - reorderColumns: (toMove: string, relativeTo: string, before: boolean, columns: string[]) => void; + columnValue: SchemaHeaderField; + allColumns: SchemaHeaderField[]; + reorderColumns: (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columns: SchemaHeaderField[]) => void; ScreenToLocalTransform: () => Transform; } export class MovableColumn extends React.Component { diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss index 91c6e8b3c..e0de76247 100644 --- a/src/client/views/collections/CollectionSchemaView.scss +++ b/src/client/views/collections/CollectionSchemaView.scss @@ -379,6 +379,7 @@ button.add-column { .collectionSchemaView-cellWrapper { height: 100%; padding: 4px; + position: relative; &:focus { outline: none; @@ -405,6 +406,20 @@ button.add-column { height: 100%; // word-wrap: break-word; } + + &:hover .collectionSchemaView-cellContents-docExpander { + display: block; + } +} + +.collectionSchemaView-cellContents-docExpander { + height: 30px; + width: 30px; + display: none; + position: absolute; + top: 0; + right: 0; + background-color: lightgray; } .doc-drag-over { diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index c6550560a..176872503 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -38,6 +38,7 @@ import { SelectionManager } from "../../util/SelectionManager"; import { DocumentManager } from "../../util/DocumentManager"; import { ImageBox } from "../nodes/ImageBox"; import { ComputedField } from "../../../new_fields/ScriptField"; +import { SchemaHeaderField, RandomPastel } from "../../../new_fields/SchemaHeaderField"; library.add(faCog, faPlus, faSortUp, faSortDown); @@ -284,11 +285,13 @@ export class SchemaTable extends React.Component { @computed get previewWidth() { return () => NumCast(this.props.Document.schemaPreviewWidth); } @computed get previewHeight() { return () => this.props.PanelHeight() - 2 * this.borderWidth; } @computed get tableWidth() { return this.props.PanelWidth() - 2 * this.borderWidth - this.DIVIDER_WIDTH - this.previewWidth(); } - @computed get columns() { return Cast(this.props.Document.schemaColumns, listSpec("string"), []); } - set columns(columns: string[]) { this.props.Document.schemaColumns = new List(columns); } + @computed get columns() { + return Cast(this.props.Document.schemaColumns, listSpec(SchemaHeaderField), []); + } + set columns(columns: SchemaHeaderField[]) { this.props.Document.schemaColumns = new List(columns); } @computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); } @computed get tableColumns(): Column[] { - let possibleKeys = this.documentKeys.filter(key => this.columns.findIndex(existingKey => existingKey.toUpperCase() === key.toUpperCase()) === -1); + let possibleKeys = this.documentKeys.filter(key => this.columns.findIndex(existingKey => existingKey.heading.toUpperCase() === key.toUpperCase()) === -1); let columns: Column[] = []; let tableIsFocused = this.props.isFocused(this.props.Document); let focusedRow = this._focusedCell.row; @@ -321,9 +324,9 @@ export class SchemaTable extends React.Component { let header = c.heading)} keyType={this.getColumnType(col)} - typeConst={columnTypes.get(col) !== undefined} + typeConst={col.type !== undefined || columnTypes.get(col.heading) !== undefined} onSelect={this.changeColumns} setIsEditing={this.setHeaderIsEditing} deleteColumn={this.deleteColumn} @@ -334,16 +337,16 @@ export class SchemaTable extends React.Component { return { Header: , - accessor: (doc: Doc) => doc ? doc[col] : 0, - id: col, + accessor: (doc: Doc) => doc ? doc[col.heading] : 0, + id: col.heading, Cell: (rowProps: CellInfo) => { - let row = rowProps.index; - let column = this.columns.indexOf(rowProps.column.id!); - let isFocused = focusedRow === row && focusedCol === column && tableIsFocused; + let rowIndex = rowProps.index; + let columnIndex = this.columns.map(c => c.heading).indexOf(rowProps.column.id!); + let isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused; let props: CellProps = { - row: row, - col: column, + row: rowIndex, + col: columnIndex, rowProps: rowProps, isFocused: isFocused, changeFocusedCellByIndex: this.changeFocusedCellByIndex, @@ -356,6 +359,7 @@ export class SchemaTable extends React.Component { moveDocument: this.props.moveDocument, setIsEditing: this.setCellIsEditing, isEditable: isEditable, + setPreviewDoc: this.props.setPreviewDoc }; let colType = this.getColumnType(col); @@ -394,6 +398,15 @@ export class SchemaTable extends React.Component { // } // return this.props.Document; // } + constructor(props: SchemaTableProps) { + super(props); + // convert old schema columns (list of strings) into new schema columns (list of schema header fields) + let oldSchemaColumns = Cast(this.props.Document.schemaColumns, listSpec("string"), []); + if (oldSchemaColumns && oldSchemaColumns.length) { + let newSchemaColumns = oldSchemaColumns.map(i => typeof i === "string" ? new SchemaHeaderField(i) : i); + this.props.Document.schemaColumns = new List(newSchemaColumns); + } + } componentDidMount() { document.addEventListener("keydown", this.onKeyDown); @@ -440,7 +453,7 @@ export class SchemaTable extends React.Component { let row = rowInfo.index; //@ts-ignore - let col = this.columns.indexOf(column!.id); + let col = this.columns.map(c => c.heading).indexOf(column!.id); // let col = column ? this.columns.indexOf(column!) : -1; let isFocused = this._focusedCell.row === row && this._focusedCell.col === col && this.props.isFocused(this.props.Document); // let column = this.columns.indexOf(column.id!); @@ -502,6 +515,11 @@ export class SchemaTable extends React.Component { if (!this._cellIsEditing && !this._headerIsEditing && this.props.isFocused(this.props.Document)) {// && this.props.isSelected()) { let direction = e.key === "Tab" ? "tab" : e.which === 39 ? "right" : e.which === 37 ? "left" : e.which === 38 ? "up" : e.which === 40 ? "down" : ""; this.changeFocusedCellByDirection(direction); + + let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; + let children = Cast(doc[this.props.fieldKey], listSpec(Doc), []); + const pdoc = FieldValue(children[this._focusedCell.row]); + pdoc && this.props.setPreviewDoc(pdoc); } } @@ -532,8 +550,8 @@ export class SchemaTable extends React.Component { this._focusedCell = { row: this._focusedCell.row + 1 === children.length ? this._focusedCell.row : this._focusedCell.row + 1, col: this._focusedCell.col }; break; } - const pdoc = FieldValue(children[this._focusedCell.row]); - pdoc && this.props.setPreviewDoc(pdoc); + // const pdoc = FieldValue(children[this._focusedCell.row]); + // pdoc && this.props.setPreviewDoc(pdoc); } @action @@ -544,8 +562,8 @@ export class SchemaTable extends React.Component { this._focusedCell = { row: row, col: col }; this.props.setFocused(this.props.Document); - const fdoc = FieldValue(children[this._focusedCell.row]); - fdoc && this.props.setPreviewDoc(fdoc); + // const fdoc = FieldValue(children[this._focusedCell.row]); + // fdoc && this.props.setPreviewDoc(fdoc); } createRow = () => { @@ -561,25 +579,25 @@ export class SchemaTable extends React.Component { @action createColumn = () => { let index = 0; - let found = this.columns.findIndex(col => col.toUpperCase() === "New field".toUpperCase()) > -1; + let found = this.columns.findIndex(col => col.heading.toUpperCase() === "New field".toUpperCase()) > -1; if (!found) { - this.columns.push("New field"); + this.columns.push(new SchemaHeaderField("New field")); return; } while (found) { index++; - found = this.columns.findIndex(col => col.toUpperCase() === ("New field (" + index + ")").toUpperCase()) > -1; + found = this.columns.findIndex(col => col.heading.toUpperCase() === ("New field (" + index + ")").toUpperCase()) > -1; } - this.columns.push("New field (" + index + ")"); + this.columns.push(new SchemaHeaderField("New field (" + index + ")")); } @action deleteColumn = (key: string) => { - let list = Cast(this.props.Document.schemaColumns, listSpec("string")); + let list = Cast(this.props.Document.schemaColumns, listSpec(SchemaHeaderField)); if (list === undefined) { - this.props.Document.schemaColumns = list = new List([]); + this.props.Document.schemaColumns = list = new List([]); } else { - const index = list.indexOf(key); + const index = list.map(c => c.heading).indexOf(key); if (index > -1) { list.splice(index, 1); } @@ -588,26 +606,37 @@ export class SchemaTable extends React.Component { @action changeColumns = (oldKey: string, newKey: string, addNew: boolean) => { - let list = Cast(this.props.Document.schemaColumns, listSpec("string")); + let list = Cast(this.props.Document.schemaColumns, listSpec(SchemaHeaderField)); if (list === undefined) { - this.props.Document.schemaColumns = list = new List([newKey]); + this.props.Document.schemaColumns = list = new List([new SchemaHeaderField(newKey)]); } else { if (addNew) { - this.columns.push(newKey); + this.columns.push(new SchemaHeaderField(newKey)); } else { - const index = list.indexOf(oldKey); + const index = list.map(c => c.heading).indexOf(oldKey); if (index > -1) { - list[index] = newKey; + list[index] = new SchemaHeaderField(newKey); } } } } - getColumnType = (key: string): ColumnType => { - if (columnTypes.get(key)) return columnTypes.get(key)!; + getColumnType = (column: SchemaHeaderField): ColumnType => { + // added functionality to convert old column type stuff to new column type stuff -syip + if (column.type && column.type !== 0) { + return column.type; + } + if (columnTypes.get(column.heading)) { + column.type = columnTypes.get(column.heading)!; + return columnTypes.get(column.heading)!; + } const typesDoc = FieldValue(Cast(this.props.Document.schemaColumnTypes, Doc)); - if (!typesDoc) return ColumnType.Any; - return NumCast(typesDoc[key]); + if (!typesDoc) { + column.type = ColumnType.Any; + return ColumnType.Any; + } + column.type = NumCast(typesDoc[column.heading]); + return NumCast(typesDoc[column.heading]); } setColumnType = (key: string, type: ColumnType): void => { @@ -624,11 +653,11 @@ export class SchemaTable extends React.Component { } @action - setColumns = (columns: string[]) => { + setColumns = (columns: SchemaHeaderField[]) => { this.columns = columns; } - reorderColumns = (toMove: string, relativeTo: string, before: boolean, columnsValues: string[]) => { + reorderColumns = (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columnsValues: SchemaHeaderField[]) => { let columns = [...columnsValues]; let oldIndex = columns.indexOf(toMove); let relIndex = columns.indexOf(relativeTo); @@ -661,7 +690,7 @@ export class SchemaTable extends React.Component { //TODO Types untracked(() => docs.map(doc => Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => keys[key] = false)))); - this.columns.forEach(key => keys[key] = true); + this.columns.forEach(key => keys[key.heading] = true); return Array.from(Object.keys(keys)); } @@ -727,7 +756,7 @@ export class SchemaTable extends React.Component { csv = csv.substr(0, csv.length - 1) + "\n"; let self = this; DocListCast(this.props.Document.data).map(doc => { - csv += self.columns.reduce((val, col) => val + (doc[col] ? doc[col]!.toString() : "0") + ",", ""); + csv += self.columns.reduce((val, col) => val + (doc[col.heading] ? doc[col.heading]!.toString() : "0") + ",", ""); csv = csv.substr(0, csv.length - 1) + "\n"; }); csv.substring(0, csv.length - 1); diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 7677f53c1..e897c5676 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -16,7 +16,7 @@ import { Transform } from "../../util/Transform"; import { CursorProperty } from "csstype"; import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn"; import { listSpec } from "../../../new_fields/Schema"; -import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; +import { SchemaHeaderField, RandomPastel } from "../../../new_fields/SchemaHeaderField"; import { List } from "../../../new_fields/List"; import { EditableView } from "../EditableView"; import { CollectionViewProps } from "./CollectionBaseView"; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index b765517a2..2e54a9736 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -19,6 +19,7 @@ import { CollectionViewType } from "../CollectionBaseView"; import { CollectionFreeFormView } from "./CollectionFreeFormView"; import "./MarqueeView.scss"; import React = require("react"); +import { SchemaHeaderField, RandomPastel } from "../../../../new_fields/SchemaHeaderField"; interface MarqueeViewProps { getContainerTransform: () => Transform; @@ -134,7 +135,7 @@ export class MarqueeView extends React.Component doc.width = 200; docList.push(doc); } - let newCol = Docs.Create.SchemaDocument([...(groupAttr ? ["_group"] : []), ...columns.filter(c => c)], docList, { x: x, y: y, title: "droppedTable", width: 300, height: 100 }); + let newCol = Docs.Create.SchemaDocument([...(groupAttr ? [new SchemaHeaderField("_group")] : []), ...columns.filter(c => c).map(c => new SchemaHeaderField(c))], docList, { x: x, y: y, title: "droppedTable", width: 300, height: 100 }); this.props.addDocument(newCol, false); } diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index afde85b69..0ea948c81 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -11,6 +11,7 @@ import { faArrowLeft, faEllipsisV, faTable, faTrash, faCog, faExchangeAlt, faTim import { library } from "@fortawesome/fontawesome-svg-core"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { SetupDrag } from "../../util/DragManager"; +import { SchemaHeaderField, RandomPastel } from "../../../new_fields/SchemaHeaderField"; library.add(faArrowLeft, faEllipsisV, faTable, faTrash, faCog, faExchangeAlt, faTimes, faPlus); @@ -289,7 +290,7 @@ export class LinkGroupEditor extends React.Component { let keys = LinkManager.Instance.getMetadataKeysInGroup(groupType); let index = keys.indexOf(""); if (index > -1) keys.splice(index, 1); - let cols = ["anchor1", "anchor2", ...[...keys]]; + let cols = ["anchor1", "anchor2", ...[...keys]].map(c => new SchemaHeaderField(c)); let docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType); let createTable = action(() => Docs.Create.SchemaDocument(cols, docs, { width: 500, height: 300, title: groupType + " table" })); let ref = React.createRef(); diff --git a/src/client/views/nodes/LinkMenuGroup.tsx b/src/client/views/nodes/LinkMenuGroup.tsx index ae97bed2f..3637807ad 100644 --- a/src/client/views/nodes/LinkMenuGroup.tsx +++ b/src/client/views/nodes/LinkMenuGroup.tsx @@ -14,6 +14,7 @@ import { Docs } from "../../documents/Documents"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { UndoManager } from "../../util/UndoManager"; import { StrCast } from "../../../new_fields/Types"; +import { SchemaHeaderField, RandomPastel } from "../../../new_fields/SchemaHeaderField"; interface LinkMenuGroupProps { sourceDoc: Doc; @@ -70,7 +71,7 @@ export class LinkMenuGroup extends React.Component { let keys = LinkManager.Instance.getMetadataKeysInGroup(groupType); let index = keys.indexOf(""); if (index > -1) keys.splice(index, 1); - let cols = ["anchor1", "anchor2", ...[...keys]]; + let cols = ["anchor1", "anchor2", ...[...keys]].map(c => new SchemaHeaderField(c)); let docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType); let createTable = action(() => Docs.Create.SchemaDocument(cols, docs, { width: 500, height: 300, title: groupType + " table" })); let ref = React.createRef(); diff --git a/src/new_fields/SchemaHeaderField.ts b/src/new_fields/SchemaHeaderField.ts index 284de3023..a6df31e81 100644 --- a/src/new_fields/SchemaHeaderField.ts +++ b/src/new_fields/SchemaHeaderField.ts @@ -3,13 +3,42 @@ import { serializable, createSimpleSchema, primitive } from "serializr"; import { ObjectField } from "./ObjectField"; import { Copy, ToScriptString, OnUpdate } from "./FieldSymbols"; import { scriptingGlobal, Scripting } from "../client/util/Scripting"; +import { ColumnType } from "../client/views/collections/CollectionSchemaView"; export const PastelSchemaPalette = new Map([ - ["purple", "#f5b5fc"], - ["green", "#96F7D2"], - ["yellow", "#F0F696"], - ["red", "#FCB1B1"] -]) + ["pink1", "#FFB4E8"], + ["pink2", "#ff9cee"], + ["pink3", "#ffccf9"], + ["pink4", "#fcc2ff"], + ["pink5", "#f6a6ff"], + ["purple1", "#b28dff"], + ["purple2", "#c5a3ff"], + ["purple3", "#d5aaff"], + ["purple4", "#ecd4ff"], + ["purple5", "#fb34ff"], + ["purple6", "#dcd3ff"], + ["purple7", "#a79aff"], + ["purple8", "#b5b9ff"], + ["purple9", "#97a2ff"], + ["bluegreen1", "#afcbff"], + ["bluegreen2", "#aff8db"], + ["bluegreen3", "#c4faf8"], + ["bluegreen4", "#85e3ff"], + ["bluegreen5", "#ace7ff"], + ["bluegreen6", "#6eb5ff"], + ["bluegreen7", "#bffcc6"], + ["bluegreen8", "#dbffd6"], + ["yellow1", "#f3ffe3"], + ["yellow2", "#e7ffac"], + ["yellow3", "#ffffd1"], + ["yellow4", "#fff5ba"], + ["red1", "#ffc9de"], + ["red2", "#ffabab"], + ["red3", "#ffbebc"], + ["red4", "#ffcbc1"], +]); + +export const RandomPastel = () => Array.from(PastelSchemaPalette.values())[Math.floor(Math.random() * PastelSchemaPalette.size)]; @scriptingGlobal @Deserializable("schemaheader") @@ -17,12 +46,19 @@ export class SchemaHeaderField extends ObjectField { @serializable(primitive()) heading: string; color: string; + type: number; - constructor(heading: string = "", color: string = Array.from(PastelSchemaPalette.values())[Math.floor(Math.random() * 4)]) { + constructor(heading: string = "", color: string = RandomPastel(), type?: ColumnType) { super(); this.heading = heading; this.color = color; + if (type) { + this.type = type; + } + else { + this.type = 0; + } } setHeading(heading: string) { @@ -35,8 +71,13 @@ export class SchemaHeaderField extends ObjectField { this[OnUpdate](); } + setType(type: ColumnType) { + this.type = type; + this[OnUpdate](); + } + [Copy]() { - return new SchemaHeaderField(this.heading, this.color); + return new SchemaHeaderField(this.heading, this.color, this.type); } [ToScriptString]() { -- cgit v1.2.3-70-g09d2 From 3b0af6ff470539fd0a25b2ab975195ff9e269b4a Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sat, 27 Jul 2019 17:16:13 -0400 Subject: Changed link undoing --- src/client/util/DragManager.ts | 1 - src/client/views/DocumentDecorations.tsx | 17 +++++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 95416cd53..a898175b8 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -422,7 +422,6 @@ export namespace DragManager { if (options) { options.handlers.dragComplete({}); } - DocumentDecorations.Instance.endLinkDragBatch(); }; AbortDrag = () => { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index ee16938ff..a34c47fdf 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -378,16 +378,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> } } - endLinkDragBatch = () => { - if (!this._linkDrag) { - return; - } - this._linkDrag.end(); - this._linkDrag = undefined; - } - onLinkerButtonDown = (e: React.PointerEvent): void => { - this._linkDrag = UndoManager.StartBatch("Drag Link"); e.stopPropagation(); document.removeEventListener("pointermove", this.onLinkerButtonMoved); document.addEventListener("pointermove", this.onLinkerButtonMoved); @@ -424,9 +415,15 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> let container = selDoc.props.ContainingCollectionView ? selDoc.props.ContainingCollectionView.props.Document.proto : undefined; let dragData = new DragManager.LinkDragData(selDoc.props.Document, container ? [container] : []); FormattedTextBox.InputBoxOverlay = undefined; + this._linkDrag = UndoManager.StartBatch("Drag Link"); DragManager.StartLinkDrag(this._linkerButton.current, dragData, e.pageX, e.pageY, { handlers: { - dragComplete: action(emptyFunction), + dragComplete: () => { + if (this._linkDrag) { + this._linkDrag.end(); + this._linkDrag = undefined; + } + }, }, hideSource: false }); -- cgit v1.2.3-70-g09d2