From 7d35ba0df5be5f3c2cd95c0f0d148c0c9c04de7a Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 5 May 2019 00:54:44 -0400 Subject: fix duplicate goldenlayout caused by presentation view --- src/client/views/PresentationView.tsx | 62 ++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 22 deletions(-) (limited to 'src/client/views/PresentationView.tsx') diff --git a/src/client/views/PresentationView.tsx b/src/client/views/PresentationView.tsx index b4c12d057..f2d685443 100644 --- a/src/client/views/PresentationView.tsx +++ b/src/client/views/PresentationView.tsx @@ -1,18 +1,19 @@ import { observer } from "mobx-react"; import React = require("react") -import { observable, action } from "mobx"; +import { observable, action, runInAction, reaction } from "mobx"; import "./PresentationView.scss" import "./Main.tsx"; import { DocumentManager } from "../util/DocumentManager"; import { Utils } from "../../Utils"; import { Doc } from "../../new_fields/Doc"; import { listSpec } from "../../new_fields/Schema"; -import { Cast, NumCast, FieldValue } from "../../new_fields/Types"; +import { Cast, NumCast, FieldValue, PromiseValue } from "../../new_fields/Types"; import { Id } from "../../new_fields/RefField"; import { List } from "../../new_fields/List"; +import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; export interface PresViewProps { - Document: Doc; + //Document: Doc; } @@ -22,6 +23,11 @@ export interface PresViewProps { */ class PresentationViewItem extends React.Component { + @observable Document?: Doc; + constructor(props: PresViewProps) { + super(props); + this.Document = FieldValue(Cast(FieldValue(Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc))!.presentationView, Doc))!; + } //look at CollectionFreeformView.focusDocument(d) @action openDoc = (doc: Doc) => { @@ -36,7 +42,7 @@ class PresentationViewItem extends React.Component { **/ @action public RemoveDoc(doc: Doc) { - const value = Cast(this.props.Document.data, listSpec(Doc), []); + const value = Cast(this.Document!.data, listSpec(Doc), []); let index = -1; for (let i = 0; i < value.length; i++) { if (value[i][Id] === doc[Id]) { @@ -57,10 +63,10 @@ class PresentationViewItem extends React.Component { let title = document.title; //to get currently selected presentation doc - let selected = NumCast(this.props.Document.selectedDoc, 0); + let selected = NumCast(this.Document!.selectedDoc, 0); // finally, if it's a normal document, then render it as such. - const children = Cast(this.props.Document.data, listSpec(Doc)); + const children = Cast(this.Document!.data, listSpec(Doc)); const styles: any = {}; if (children && children[selected] === document) { //this doc is selected @@ -76,7 +82,7 @@ class PresentationViewItem extends React.Component { } render() { - const children = Cast(this.props.Document.data, listSpec(Doc), []); + const children = Cast(this.Document!.data, listSpec(Doc), []); return (
@@ -94,13 +100,13 @@ export class PresentationView extends React.Component { //observable means render is re-called every time variable is changed @observable collapsed: boolean = false; - closePresentation = action(() => this.props.Document.width = 0); + closePresentation = action(() => this.Document!.width = 0); next = () => { - const current = NumCast(this.props.Document.selectedDoc); - const allDocs = FieldValue(Cast(this.props.Document.data, listSpec(Doc))); + const current = NumCast(this.Document!.selectedDoc); + const allDocs = FieldValue(Cast(this.Document!.data, listSpec(Doc))); if (allDocs && current < allDocs.length + 1) { //can move forwards - this.props.Document.selectedDoc = current + 1; + this.Document!.selectedDoc = current + 1; const doc = allDocs[current + 1]; let docView = DocumentManager.Instance.getDocumentView(doc); if (docView) { @@ -110,11 +116,11 @@ export class PresentationView extends React.Component { } back = () => { - const current = NumCast(this.props.Document.selectedDoc); - const allDocs = FieldValue(Cast(this.props.Document.data, listSpec(Doc))); + const current = NumCast(this.Document!.selectedDoc); + const allDocs = FieldValue(Cast(this.Document!.data, listSpec(Doc))); if (allDocs && current - 1 >= 0) { //can move forwards - this.props.Document.selectedDoc = current - 1; + this.Document!.selectedDoc = current - 1; const doc = allDocs[current - 1]; let docView = DocumentManager.Instance.getDocumentView(doc); if (docView) { @@ -125,9 +131,21 @@ export class PresentationView extends React.Component { private ref = React.createRef(); + @observable Document?: Doc; //initilize class variables constructor(props: PresViewProps) { super(props); + let self = this; + reaction(() => + CurrentUserUtils.UserDocument.activeWorkspace, + (activeW) => { + if (activeW && activeW instanceof Doc) { + PromiseValue(Cast(activeW.presentationView, Doc)). + then(pv => runInAction(() => + self.Document = pv ? pv : (activeW.presentationView = new Doc()))) + } + }, + { fireImmediately: true }); PresentationView.Instance = this; } @@ -137,19 +155,21 @@ export class PresentationView extends React.Component { @action public PinDoc(doc: Doc) { //add this new doc to props.Document - const data = Cast(this.props.Document.data, listSpec(Doc)); + const data = Cast(this.Document!.data, listSpec(Doc)); if (data) { data.push(doc); } else { - this.props.Document.data = new List([doc]); + this.Document!.data = new List([doc]); } - this.props.Document.width = 300; + this.Document!.width = 300; } render() { - let titleStr = this.props.Document.Title; - let width = NumCast(this.props.Document.width); + if (!this.Document) + return (null); + let titleStr = this.Document.Title; + let width = NumCast(this.Document.width); //TODO: next and back should be icons return ( @@ -163,9 +183,7 @@ export class PresentationView extends React.Component {
    - +
); -- cgit v1.2.3-70-g09d2 From f63217890738e738dfa0e8eca3f8a8996440373e Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Mon, 6 May 2019 00:25:12 -0400 Subject: Small change in presentation view --- src/client/views/PresentationView.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/client/views/PresentationView.tsx') diff --git a/src/client/views/PresentationView.tsx b/src/client/views/PresentationView.tsx index f2d685443..4853eb151 100644 --- a/src/client/views/PresentationView.tsx +++ b/src/client/views/PresentationView.tsx @@ -23,7 +23,7 @@ export interface PresViewProps { */ class PresentationViewItem extends React.Component { - @observable Document?: Doc; + @observable Document: Doc; constructor(props: PresViewProps) { super(props); this.Document = FieldValue(Cast(FieldValue(Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc))!.presentationView, Doc))!; @@ -42,7 +42,7 @@ class PresentationViewItem extends React.Component { **/ @action public RemoveDoc(doc: Doc) { - const value = Cast(this.Document!.data, listSpec(Doc), []); + const value = Cast(this.Document.data, listSpec(Doc), []); let index = -1; for (let i = 0; i < value.length; i++) { if (value[i][Id] === doc[Id]) { @@ -63,10 +63,10 @@ class PresentationViewItem extends React.Component { let title = document.title; //to get currently selected presentation doc - let selected = NumCast(this.Document!.selectedDoc, 0); + let selected = NumCast(this.Document.selectedDoc, 0); // finally, if it's a normal document, then render it as such. - const children = Cast(this.Document!.data, listSpec(Doc)); + const children = Cast(this.Document.data, listSpec(Doc)); const styles: any = {}; if (children && children[selected] === document) { //this doc is selected @@ -82,7 +82,7 @@ class PresentationViewItem extends React.Component { } render() { - const children = Cast(this.Document!.data, listSpec(Doc), []); + const children = Cast(this.Document.data, listSpec(Doc), []); return (
-- cgit v1.2.3-70-g09d2 From 7da76d2538ebde21d7a878b5096d5a673e5d6375 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 10 May 2019 23:01:08 -0400 Subject: added summary screen grab icon as a template view of a text document. converted Cast(...ListSpec(Doc)) pattern to DocListCast --- src/client/northstar/dash-nodes/HistogramBox.tsx | 10 +++---- src/client/util/DocumentManager.ts | 6 ++-- src/client/util/DragManager.ts | 6 ++-- src/client/views/PresentationView.tsx | 11 +++++-- src/client/views/Templates.tsx | 35 +++++++++++++++------- .../views/collections/CollectionBaseView.tsx | 8 ++--- .../views/collections/CollectionSchemaView.tsx | 6 ++-- src/client/views/collections/CollectionSubView.tsx | 4 +-- .../views/collections/CollectionTreeView.tsx | 22 +++++++------- .../CollectionFreeFormLinksView.tsx | 12 ++++---- .../collections/collectionFreeForm/MarqueeView.tsx | 24 +++++++++++---- .../views/nodes/CollectionFreeFormDocumentView.tsx | 28 +++++++++-------- src/client/views/nodes/DocumentView.tsx | 23 +++++++------- src/client/views/nodes/IconBox.tsx | 4 +-- src/client/views/nodes/ImageBox.tsx | 14 ++++++--- src/client/views/nodes/LinkMenu.tsx | 6 ++-- src/new_fields/Doc.ts | 10 +++++-- 17 files changed, 137 insertions(+), 92 deletions(-) (limited to 'src/client/views/PresentationView.tsx') diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx index 5e7b867b3..ed556cf45 100644 --- a/src/client/northstar/dash-nodes/HistogramBox.tsx +++ b/src/client/northstar/dash-nodes/HistogramBox.tsx @@ -19,7 +19,7 @@ import { HistogramLabelPrimitives } from "./HistogramLabelPrimitives"; import { StyleConstants } from "../utils/StyleContants"; import { NumCast, Cast } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; -import { Doc } from "../../../new_fields/Doc"; +import { Doc, DocListCast } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/RefField"; @@ -117,15 +117,15 @@ export class HistogramBox extends React.Component { runInAction(() => { this.HistoOp = histoOp ? histoOp.HistoOp : HistogramOperation.Empty; if (this.HistoOp !== HistogramOperation.Empty) { - reaction(() => Cast(this.props.Document.linkedFromDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc), (docs) => this.HistoOp.Links.splice(0, this.HistoOp.Links.length, ...docs), { fireImmediately: true }); - reaction(() => Cast(this.props.Document.brushingDocs, listSpec(Doc), []).length, + reaction(() => DocListCast(this.props.Document.linkedFromDocs), (docs) => this.HistoOp.Links.splice(0, this.HistoOp.Links.length, ...docs), { fireImmediately: true }); + reaction(() => DocListCast(this.props.Document.brushingDocs).length, () => { - let brushingDocs = Cast(this.props.Document.brushingDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc); + let brushingDocs = DocListCast(this.props.Document.brushingDocs); const proto = this.props.Document.proto; if (proto) { this.HistoOp.BrushLinks.splice(0, this.HistoOp.BrushLinks.length, ...brushingDocs.map((brush, i) => { brush.backgroundColor = StyleConstants.BRUSH_COLORS[i % StyleConstants.BRUSH_COLORS.length]; - let brushed = Cast(brush.brushingDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc); + let brushed = DocListCast(brush.brushingDocs); return { l: brush, b: brushed[0][Id] === proto[Id] ? brushed[1] : brushed[0] }; })); } diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index a8b643d4d..9a7a94228 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,6 +1,6 @@ import { computed, observable } from 'mobx'; import { DocumentView } from '../views/nodes/DocumentView'; -import { Doc } from '../../new_fields/Doc'; +import { Doc, DocListCast } from '../../new_fields/Doc'; import { FieldValue, Cast, NumCast, BoolCast } from '../../new_fields/Types'; import { listSpec } from '../../new_fields/Schema'; import { undoBatch } from './UndoManager'; @@ -73,7 +73,7 @@ export class DocumentManager { @computed public get LinkedDocumentViews() { return DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)).reduce((pairs, dv) => { - let linksList = Cast(dv.props.Document.linkedToDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc); + let linksList = DocListCast(dv.props.Document.linkedToDocs); if (linksList && linksList.length) { pairs.push(...linksList.reduce((pairs, link) => { if (link) { @@ -86,7 +86,7 @@ export class DocumentManager { return pairs; }, [] as { a: DocumentView, b: DocumentView, l: Doc }[])); } - linksList = Cast(dv.props.Document.linkedFromDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc); + linksList = DocListCast(dv.props.Document.linkedFromDocs); if (linksList && linksList.length) { pairs.push(...linksList.reduce((pairs, link) => { if (link) { diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index c0402f0c9..2da0d5b51 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -1,5 +1,5 @@ import { action, runInAction } from "mobx"; -import { Doc, DocListCast } from "../../new_fields/Doc"; +import { Doc, DocListCastAsync } from "../../new_fields/Doc"; import { Cast } from "../../new_fields/Types"; import { emptyFunction } from "../../Utils"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; @@ -43,8 +43,8 @@ export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: n let draggedDocs: Doc[] = []; let draggedFromDocs: Doc[] = [] if (srcTarg) { - let linkToDocs = await DocListCast(srcTarg.linkedToDocs); - let linkFromDocs = await DocListCast(srcTarg.linkedFromDocs); + let linkToDocs = await DocListCastAsync(srcTarg.linkedToDocs); + let linkFromDocs = await DocListCastAsync(srcTarg.linkedFromDocs); if (linkToDocs) draggedDocs = linkToDocs.map(linkDoc => Cast(linkDoc.linkedTo, Doc) as Doc); if (linkFromDocs) draggedFromDocs = linkFromDocs.map(linkDoc => Cast(linkDoc.linkedFrom, Doc) as Doc); } diff --git a/src/client/views/PresentationView.tsx b/src/client/views/PresentationView.tsx index 4853eb151..3fb24a339 100644 --- a/src/client/views/PresentationView.tsx +++ b/src/client/views/PresentationView.tsx @@ -141,8 +141,15 @@ export class PresentationView extends React.Component { (activeW) => { if (activeW && activeW instanceof Doc) { PromiseValue(Cast(activeW.presentationView, Doc)). - then(pv => runInAction(() => - self.Document = pv ? pv : (activeW.presentationView = new Doc()))) + then(pv => runInAction(() => { + if (pv) self.Document = pv; + else { + pv = new Doc(); + pv.title = "Presentation Doc"; + activeW.presentationView = pv; + self.Document = pv; + } + })) } }, { fireImmediately: true }); diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx index bbedc95f1..a98870b04 100644 --- a/src/client/views/Templates.tsx +++ b/src/client/views/Templates.tsx @@ -5,6 +5,7 @@ export enum TemplatePosition { InnerBottom, InnerRight, InnerLeft, + TopRight, OutterTop, OutterBottom, OutterRight, @@ -39,30 +40,42 @@ export namespace Templates { // export const BasicLayout = new Template("Basic layout", "{layout}"); export const Caption = new Template("Caption", TemplatePosition.OutterBottom, - `
` - ); + `
+ +
` ); + export const TitleOverlay = new Template("TitleOverlay", TemplatePosition.InnerTop, - `
{layout}
-
- {props.Document.title} -
` - ); + `
+
{layout}
+
+ {props.Document.title} +
+
` ); + export const Title = new Template("Title", TemplatePosition.InnerTop, `
{layout}
{props.Document.title} -
` - ); +
` ); export const Bullet = new Template("Bullet", TemplatePosition.InnerTop, `
{layout}
-
- +
` ); + export function ImageOverlay(width: number, height: number, field: string = "thumbnail") { + return (`
+
{layout}
+
+ +
+
`); + } + export const TemplateList: Template[] = [Title, TitleOverlay, Caption, Bullet]; export function sortTemplates(a: Template, b: Template) { diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index 2b1f7bb37..645296d27 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import { ContextMenu } from '../ContextMenu'; import { FieldViewProps } from '../nodes/FieldView'; import { Cast, FieldValue, PromiseValue, NumCast } from '../../../new_fields/Types'; -import { Doc, FieldResult, Opt } from '../../../new_fields/Doc'; +import { Doc, FieldResult, Opt, DocListCast } from '../../../new_fields/Doc'; import { listSpec } from '../../../new_fields/Schema'; import { List } from '../../../new_fields/List'; import { Id } from '../../../new_fields/RefField'; @@ -63,13 +63,13 @@ export class CollectionBaseView extends React.Component { if (!(documentToAdd instanceof Doc)) { return false; } - let data = Cast(documentToAdd.data, listSpec(Doc), []).filter(d => d).map(d => d as Doc); - for (const doc of data.filter(d => d instanceof Document)) { + let data = DocListCast(documentToAdd.data); + for (const doc of data) { if (this.createsCycle(doc, containerDocument)) { return true; } } - let annots = Cast(documentToAdd.annotations, listSpec(Doc), []).filter(d => d).map(d => d as Doc); + let annots = DocListCast(documentToAdd.annotations); for (const annot of annots) { if (this.createsCycle(annot, containerDocument)) { return true; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index ae949b2ed..506e60a65 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -19,7 +19,7 @@ import { DocumentView } from "../nodes/DocumentView"; import { FieldView, FieldViewProps } from "../nodes/FieldView"; import "./CollectionSchemaView.scss"; import { CollectionSubView } from "./CollectionSubView"; -import { Opt, Field, Doc, DocListCast } from "../../../new_fields/Doc"; +import { Opt, Field, Doc, DocListCastAsync, DocListCast } from "../../../new_fields/Doc"; import { Cast, FieldValue, NumCast } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; import { List } from "../../../new_fields/List"; @@ -118,7 +118,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { } const run = script.run; //TODO This should be able to be refactored to compile the script once - const val = await DocListCast(this.props.Document[this.props.fieldKey]) + const val = await DocListCastAsync(this.props.Document[this.props.fieldKey]) val && val.forEach(doc => applyToDoc(doc, run)); }}> @@ -276,7 +276,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { } get documentKeysCheckList() { - const docs = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []).filter(d => d).map(d => d as Doc); + const docs = DocListCast(this.props.Document[this.props.fieldKey]); let keys: { [key: string]: boolean } = {}; // bcz: ugh. this is untracked since otherwise a large collection of documents will blast the server for all their fields. // then as each document's fields come back, we update the documents _proxies. Each time we do this, the whole schema will be diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 0b08e150a..a86d250bd 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -10,7 +10,7 @@ import * as rp from 'request-promise'; import { CollectionView } from "./CollectionView"; import { CollectionPDFView } from "./CollectionPDFView"; import { CollectionVideoView } from "./CollectionVideoView"; -import { Doc, Opt, FieldResult } from "../../../new_fields/Doc"; +import { Doc, Opt, FieldResult, DocListCast } from "../../../new_fields/Doc"; import { DocComponent } from "../DocComponent"; import { listSpec } from "../../../new_fields/Schema"; import { Cast, PromiseValue, FieldValue, ListSpec } from "../../../new_fields/Types"; @@ -49,7 +49,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { get children() { //TODO tfs: This might not be what we want? //This linter error can't be fixed because of how js arguments work, so don't switch this to filter(FieldValue) - return Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []).filter(doc => FieldValue(doc)).map(doc => doc as Doc); + return DocListCast(this.props.Document[this.props.fieldKey]); } @action diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 33787f06b..78c84cc89 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -18,9 +18,7 @@ import { Main } from '../Main'; import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils'; import { CollectionDockingView } from './CollectionDockingView'; import { DocumentManager } from '../../util/DocumentManager'; -import { Utils } from '../../../Utils'; import { List } from '../../../new_fields/List'; -import { indexOf } from 'typescript-collections/dist/lib/arrays'; export interface TreeViewProps { @@ -155,16 +153,21 @@ class TreeView extends React.Component { let keys = Array.from(Object.keys(this.props.document)); if (this.props.document.proto instanceof Doc) { keys.push(...Array.from(Object.keys(this.props.document.proto))); + while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1); } keys.map(key => { - let docList = Cast(this.props.document[key], listSpec(Doc)); - if (docList instanceof List && docList.length && docList[0] instanceof Doc) { + let docList = DocListCast(this.props.document[key]); + let doc = Cast(this.props.document[key], Doc); + if (doc instanceof Doc || docList.length) { if (!this._collapsed) { bulletType = BulletType.Collapsible; + let spacing = (key === "data") ? 0 : -10; contentElement.push(
    {(key === "data") ? (null) : - {key}} - {TreeView.GetChildElements(docList, key !== "data", (doc: Doc) => this.remove(doc, key), this.move, this.props.dropAction)} + {key}} +
    + {TreeView.GetChildElements(doc instanceof Doc ? [doc] : docList, key !== "data", (doc: Doc) => this.remove(doc, key), this.move, this.props.dropAction)} +
); } else bulletType = BulletType.Collapsed; @@ -179,7 +182,7 @@ class TreeView extends React.Component { ; } - public static GetChildElements(docs: (Doc | Promise)[], allowMinimized: boolean, remove: ((doc: Doc) => void), move: DragManager.MoveFunction, dropAction: dropActionType) { + public static GetChildElements(docs: Doc[], allowMinimized: boolean, remove: ((doc: Doc) => void), move: DragManager.MoveFunction, dropAction: dropActionType) { return docs.filter(child => child instanceof Doc && !child.excludeFromLibrary && (allowMinimized || !child.isMinimized)).filter(doc => FieldValue(doc)).map(child => ); } @@ -203,12 +206,11 @@ export class CollectionTreeView extends CollectionSubView(Document) { } } render() { - const children = this.children; let dropAction = StrCast(this.props.Document.dropAction, "alias") as dropActionType; - if (!children) { + if (!this.children) { return (null); } - let childElements = TreeView.GetChildElements(children, false, this.remove, this.props.moveDocument, dropAction); + let childElements = TreeView.GetChildElements(this.children, false, this.remove, this.props.moveDocument, dropAction); return (
{ - let doclist = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []); - return { doclist: doclist ? doclist : [], xs: doclist instanceof List ? doclist.map(d => d instanceof Doc && d.x) : [] }; + let doclist = DocListCast(this.props.Document[this.props.fieldKey]); + return { doclist: doclist ? doclist : [], xs: doclist.map(d => d.x) }; }, - async () => { - let doclist = await DocListCast(this.props.Document[this.props.fieldKey]); + () => { + let doclist = DocListCast(this.props.Document[this.props.fieldKey]); let views = doclist ? doclist.filter(doc => StrCast(doc.backgroundLayout).indexOf("istogram") !== -1) : []; views.forEach((dstDoc, i) => { views.forEach((srcDoc, j) => { @@ -84,7 +84,7 @@ export class CollectionFreeFormLinksView extends React.Component d).map(d => d as Doc). + DocListCast(this.props.Document[this.props.fieldKey]). filter(child => child[Id] === collid).map(view => DocumentManager.Instance.getDocumentViews(view).map(view => diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 1bf39e335..9ace0272a 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -1,3 +1,4 @@ +import * as htmlToImage from "html-to-image"; import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import { Docs } from "../../../documents/Documents"; @@ -14,6 +15,8 @@ import { Doc } from "../../../../new_fields/Doc"; import { NumCast, Cast } from "../../../../new_fields/Types"; import { InkField, StrokeData } from "../../../../new_fields/InkField"; import { List } from "../../../../new_fields/List"; +import { ImageField } from "../../../../new_fields/URLField"; +import { Template, Templates } from "../../Templates"; interface MarqueeViewProps { getContainerTransform: () => Transform; @@ -30,6 +33,7 @@ interface MarqueeViewProps { @observer export class MarqueeView extends React.Component { + private _mainCont = React.createRef(); @observable _lastX: number = 0; @observable _lastY: number = 0; @observable _downX: number = 0; @@ -166,7 +170,7 @@ export class MarqueeView extends React.Component @undoBatch @action - marqueeCommand = (e: KeyboardEvent) => { + marqueeCommand = async (e: KeyboardEvent) => { if (this._commandExecuted) { return; } @@ -224,13 +228,17 @@ export class MarqueeView extends React.Component let scrpt = this.props.getTransform().inverse().transformPoint(bounds.left, bounds.top); let summary = Docs.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "yellow", title: "-summary-" }); + let dataUrl = await htmlToImage.toPng(this._mainCont.current!, { width: bounds.width, height: bounds.height, quality: 1 }); + summary.proto!.thumbnail = new ImageField(new URL(dataUrl)); + + summary.proto!.templates = new List([Templates.ImageOverlay(Math.min(50, bounds.width), bounds.height * Math.min(50, bounds.width) / bounds.width, "thumbnail")]); if (e.key === "s" || e.key === "p") { summary.proto!.maximizeOnRight = true; newCollection.proto!.summaryDoc = summary; selected = [newCollection]; } summary.proto!.summarizedDocs = new List(selected); - summary.proto!.isButton = true; + //summary.proto!.isButton = true; selected.map(summarizedDoc => { let maxx = NumCast(summarizedDoc.x, undefined); let maxy = NumCast(summarizedDoc.y, undefined); @@ -313,17 +321,21 @@ export class MarqueeView extends React.Component @computed get marqueeDiv() { - let p = this.props.getContainerTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY); let v = this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY); - return
+ return
; } render() { + let p = this.props.getContainerTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY); return
- {this.props.children} - {!this._visible ? (null) : this.marqueeDiv} +
+
+ {this.props.children} +
+ {!this._visible ? null : this.marqueeDiv} +
; } } \ No newline at end of file diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index b05f2eea2..39d216da0 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -9,10 +9,10 @@ import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schem import { FieldValue, Cast, NumCast, BoolCast, StrCast } from "../../../new_fields/Types"; import { OmitKeys, Utils } from "../../../Utils"; import { SelectionManager } from "../../util/SelectionManager"; -import { Doc, DocListCast, HeightSym } from "../../../new_fields/Doc"; +import { Doc, DocListCastAsync, DocListCast, } from "../../../new_fields/Doc"; import { List } from "../../../new_fields/List"; import { CollectionDockingView } from "../collections/CollectionDockingView"; -import { undoBatch, UndoManager } from "../../util/UndoManager"; +import { UndoManager } from "../../util/UndoManager"; export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { } @@ -65,7 +65,7 @@ export class CollectionFreeFormDocumentView extends DocComponent this.nativeWidth > 0 ? this.width / this.nativeWidth : 1; panelWidth = () => this.props.PanelWidth(); panelHeight = () => this.props.PanelHeight(); - toggleMinimized = async () => this.toggleIcon(await DocListCast(this.props.Document.maximizedDocs)); + toggleMinimized = async () => this.toggleIcon(await DocListCastAsync(this.props.Document.maximizedDocs)); getTransform = (): Transform => this.props.ScreenToLocalTransform() .translate(-this.X, -this.Y) .scale(1 / this.contentScaling()).scale(1 / this.zoom) @@ -132,7 +132,7 @@ export class CollectionFreeFormDocumentView extends DocComponent { + expandedDocs.forEach(maxDoc => { maxDoc.isMinimized = false; if (!dataDocs || dataDocs.indexOf(maxDoc) == -1) { CollectionDockingView.Instance.AddRightSplit(maxDoc); @@ -195,8 +197,8 @@ export class CollectionFreeFormDocumentView extends DocComponent this.props.addDocument!(await maxDoc, false)); - this.toggleIcon(maximizedDocs); + this.props.addDocument && expandedDocs.forEach(async maxDoc => this.props.addDocument!(await maxDoc, false)); + this.toggleIcon(expandedDocs); } } } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 5aa74c703..90f67db7c 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -12,13 +12,13 @@ import { CollectionPDFView } from "../collections/CollectionPDFView"; import { CollectionVideoView } from "../collections/CollectionVideoView"; import { CollectionView } from "../collections/CollectionView"; import { ContextMenu } from "../ContextMenu"; -import { Template, Templates } from "./../Templates"; +import { Template } from "./../Templates"; import { DocumentContentsView } from "./DocumentContentsView"; import "./DocumentView.scss"; import React = require("react"); -import { Opt, Doc, WidthSym, HeightSym, DocListCast } from "../../../new_fields/Doc"; +import { Opt, Doc, WidthSym, HeightSym, DocListCastAsync, DocListCast } from "../../../new_fields/Doc"; import { DocComponent } from "../DocComponent"; -import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema"; +import { createSchema, makeInterface } from "../../../new_fields/Schema"; import { FieldValue, StrCast, BoolCast, Cast } from "../../../new_fields/Types"; import { List } from "../../../new_fields/List"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; @@ -109,8 +109,8 @@ export class DocumentView extends DocComponent(Docu } // bcz: kind of ugly .. setup a reaction to update the title of a summary document's target (maximizedDocs) whenver the summary doc's title changes this._reactionDisposer = reaction(() => [this.props.Document.maximizedDocs, this.props.Document.summaryDoc, this.props.Document.summaryDoc instanceof Doc ? this.props.Document.summaryDoc.title : ""], - async () => { - let maxDoc = await DocListCast(this.props.Document.maximizedDocs); + () => { + let maxDoc = DocListCast(this.props.Document.maximizedDocs); if (maxDoc && StrCast(this.props.Document.layout).indexOf("IconBox") !== -1) { this.props.Document.title = (maxDoc && maxDoc.length === 1 ? maxDoc[0].title + ".icon" : ""); } @@ -145,7 +145,7 @@ export class DocumentView extends DocComponent(Docu startDragging(x: number, y: number, dropAction: dropActionType, dragSubBullets: boolean) { if (this._mainCont.current) { - let allConnected = dragSubBullets ? [this.props.Document, ...Cast(this.props.Document.subBulletDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc)] : [this.props.Document]; + let allConnected = [this.props.Document, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])]; const [left, top] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(0, 0); let dragData = new DragManager.DocumentDragData(allConnected); const [xoff, yoff] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top); @@ -169,17 +169,17 @@ export class DocumentView extends DocComponent(Docu SelectionManager.SelectDoc(this, e.ctrlKey); } } - _hitIsBullet = false; + _hitExpander = false; onPointerDown = (e: React.PointerEvent): void => { this._downX = e.clientX; this._downY = e.clientY; if (CollectionFreeFormView.RIGHT_BTN_DRAG && (e.button === 2 || (e.button === 0 && e.altKey)) && !this.isSelected()) { return; } - this._hitIsBullet = (e.target && (e.target as any).id === "isBullet") || Cast(this.props.Document.subBulletDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc).length > 0; + this._hitExpander = DocListCast(this.props.Document.subBulletDocs).length > 0; if (e.shiftKey && e.buttons === 1) { if (this.props.isTopMost) { - this.startDragging(e.pageX, e.pageY, e.altKey || e.ctrlKey ? "alias" : undefined, this._hitIsBullet); + this.startDragging(e.pageX, e.pageY, e.altKey || e.ctrlKey ? "alias" : undefined, this._hitExpander); } else if (this.props.Document) { CollectionDockingView.Instance.StartOtherDrag([Doc.MakeAlias(this.props.Document)], e); } @@ -197,7 +197,7 @@ export class DocumentView extends DocComponent(Docu document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); if (!e.altKey && !this.topMost && (!CollectionFreeFormView.RIGHT_BTN_DRAG && e.buttons === 1) || (CollectionFreeFormView.RIGHT_BTN_DRAG && e.buttons === 2)) { - this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined, this._hitIsBullet); + this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined, this._hitExpander); } } e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers @@ -232,6 +232,7 @@ export class DocumentView extends DocComponent(Docu ContextMenu.Instance.clearItems(); SelectionManager.DeselectAll(); } + @undoBatch @action drop = async (e: Event, de: DragManager.DropEvent) => { @@ -259,7 +260,6 @@ export class DocumentView extends DocComponent(Docu } } - @action addTemplate = (template: Template) => { this.templates.push(template.Layout); @@ -307,7 +307,6 @@ export class DocumentView extends DocComponent(Docu } } - isSelected = () => SelectionManager.IsSelected(this); select = (ctrlPressed: boolean) => SelectionManager.SelectDoc(this, ctrlPressed); diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx index 4bcb4c636..48aecdcd0 100644 --- a/src/client/views/nodes/IconBox.tsx +++ b/src/client/views/nodes/IconBox.tsx @@ -2,12 +2,12 @@ import React = require("react"); import { library } from '@fortawesome/fontawesome-svg-core'; import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { computed, observable, runInAction, reaction, IReactionDisposer } from "mobx"; +import { computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; import { FieldView, FieldViewProps } from './FieldView'; import "./IconBox.scss"; import { Cast, StrCast, BoolCast } from "../../../new_fields/Types"; -import { Doc, DocListCast } from "../../../new_fields/Doc"; +import { Doc } from "../../../new_fields/Doc"; import { IconField } from "../../../new_fields/IconField"; import { ContextMenu } from "../ContextMenu"; import Measure from "react-measure"; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 0e9e904a8..f9659a4b2 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -1,5 +1,5 @@ -import { action, observable } from 'mobx'; +import { action, observable, trace } from 'mobx'; import { observer } from "mobx-react"; import Lightbox from 'react-image-lightbox'; import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app @@ -157,15 +157,21 @@ export class ImageBox extends DocComponent(ImageD } render() { + trace(); let field = this.Document[this.props.fieldKey]; let paths: string[] = ["http://www.cs.brown.edu/~bcz/face.gif"]; if (field instanceof ImageField) paths = [field.url.href]; else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => (p as ImageField).url.href); - let nativeWidth = FieldValue(this.Document.nativeWidth, 1); + let nativeWidth = FieldValue(this.Document.nativeWidth, (this.props.PanelWidth as any) as string ? Number((this.props.PanelWidth as any) as string) : 50); let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive"; + let id = this.props.id; return ( -
- Image not found +
+ {paths.length > 1 ? this.dots(paths) : (null)} {this.lightbox(paths)}
); diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index 24901913d..7bf13d5f9 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -5,7 +5,7 @@ import { LinkBox } from "./LinkBox"; import { LinkEditor } from "./LinkEditor"; import './LinkMenu.scss'; import React = require("react"); -import { Doc } from "../../../new_fields/Doc"; +import { Doc, DocListCast } from "../../../new_fields/Doc"; import { Cast, FieldValue } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; import { Id } from "../../../new_fields/RefField"; @@ -31,8 +31,8 @@ export class LinkMenu extends React.Component { render() { //get list of links from document - let linkFrom = Cast(this.props.docView.props.Document.linkedFromDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc); - let linkTo = Cast(this.props.docView.props.Document.linkedToDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc); + let linkFrom = DocListCast(this.props.docView.props.Document.linkedFromDocs); + let linkTo = DocListCast(this.props.docView.props.Document.linkedToDoc); if (this._editingLink === undefined) { return (
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 2ab145fa3..46ccb3e90 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -29,13 +29,17 @@ export const SelfProxy = Symbol("SelfProxy"); export const WidthSym = Symbol("Width"); export const HeightSym = Symbol("Height"); -export function DocListCast(field: FieldResult): Promise; -export function DocListCast(field: FieldResult, defaultValue: Doc[]): Promise; -export function DocListCast(field: FieldResult, defaultValue?: Doc[]) { +export function DocListCastAsync(field: FieldResult): Promise; +export function DocListCastAsync(field: FieldResult, defaultValue: Doc[]): Promise; +export function DocListCastAsync(field: FieldResult, defaultValue?: Doc[]) { const list = Cast(field, listSpec(Doc)); return list ? Promise.all(list) : Promise.resolve(defaultValue); } +export function DocListCast(field: FieldResult) { + return Cast(field, listSpec(Doc), []).filter(d => d && d instanceof Doc).map(d => d as Doc) +} + @Deserializable("doc").withFields(["id"]) export class Doc extends RefField { constructor(id?: FieldId, forceSave?: boolean) { -- cgit v1.2.3-70-g09d2 From c3a24f3cd2d1d3baf5738c649552baadf3677385 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Tue, 14 May 2019 01:31:11 -0400 Subject: Refactored most of presentation view --- src/client/views/Main.scss | 19 --- src/client/views/Main.tsx | 57 ++++--- src/client/views/PresentationView.scss | 71 +++++---- src/client/views/PresentationView.tsx | 166 ++++++++------------- src/client/views/TemplateMenu.tsx | 1 - .../views/collections/CollectionDockingView.tsx | 13 +- src/client/views/nodes/LinkMenu.tsx | 2 +- src/new_fields/Doc.ts | 10 +- 8 files changed, 155 insertions(+), 184 deletions(-) (limited to 'src/client/views/PresentationView.tsx') diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss index 2430e8f6c..d63b0147b 100644 --- a/src/client/views/Main.scss +++ b/src/client/views/Main.scss @@ -27,25 +27,6 @@ div { z-index: 9999; } -h1 { - font-size: 50px; - position: fixed; - top: 30px; - left: 50%; - transform: translateX(-50%); - color: $dark-color; - text-shadow: -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff; - z-index: 9999; - font-family: $sans-serif; - font-weight: 700; - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - .jsx-parser { width: 100%; pointer-events: none; diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 158de31f5..66205f8ca 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -1,7 +1,7 @@ import { IconName, library } from '@fortawesome/fontawesome-svg-core'; import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faTree, faUndoAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, configure, observable, runInAction } from 'mobx'; +import { action, computed, configure, observable, runInAction, trace } from 'mobx'; import { observer } from 'mobx-react'; import "normalize.css"; import * as React from 'react'; @@ -51,6 +51,9 @@ export class Main extends React.Component { } private set mainContainer(doc: Opt) { if (doc) { + if (!("presentationView" in doc)) { + doc.presentationView = new Doc(); + } CurrentUserUtils.UserDocument.activeWorkspace = doc; } } @@ -174,32 +177,42 @@ export class Main extends React.Component { } }, 100); } + @action + onResize = (r: any) => { + this.pwidth = r.offset.width; + this.pheight = r.offset.height; + } + getPWidth = () => { + return this.pwidth; + } + getPHeight = () => { + return this.pheight; + } @computed get mainContent() { - let pwidthFunc = () => this.pwidth; - let pheightFunc = () => this.pheight; - let noScaling = () => 1; let mainCont = this.mainContainer; - return { this.pwidth = r.offset.width; this.pheight = r.offset.height; })}> + let content = !mainCont ? (null) : + ; + const pres = mainCont ? FieldValue(Cast(mainCont.presentationView, Doc)) : undefined; + return {({ measureRef }) =>
- {!mainCont ? (null) : - } - + {content} + {pres ? : null}
}
; diff --git a/src/client/views/PresentationView.scss b/src/client/views/PresentationView.scss index 7c5677f0d..fb4a851c4 100644 --- a/src/client/views/PresentationView.scss +++ b/src/client/views/PresentationView.scss @@ -4,15 +4,14 @@ z-index: 1; box-shadow: #AAAAAA .2vw .2vw .4vw; right: 0; - top:0; - bottom:0; + top: 0; + bottom: 0; } .presentationView-item { - width: 220px; - height: 40px; - vertical-align: center; - padding-top: 15px; + padding: 10px; + display: inline-block; + width: 100%; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; @@ -22,47 +21,59 @@ transition: all .1s; } +.presentationView-listCont { + padding-left: 10px; + padding-right: 10px; +} + .presentationView-item:hover { transition: all .1s; background: #AAAAAA } +.presentationView-selected { + background: gray; +} + .presentationView-heading { - margin-top: 0px; - height: 40px; background: lightseagreen; - padding: 30px; + padding: 10px; + display: inline-block; + width: 100%; } + .presentationView-title { - padding-top: 3px; - padding-bottom: 3px; - font-size: 25px; - float:left; + padding-top: 3px; + padding-bottom: 3px; + font-size: 25px; + display: inline-block; } -.presentation-icon{ + +.presentation-icon { float: right; - display: inline; - width: 10px; - margin-top: 7px; } -.presentationView-header { - padding-top: 1px; - padding-bottom: 1px; + +.presentationView-name { font-size: 15px; - float:left; - } + display: inline-block; +} + +.presentation-button { + margin-right: 12.5%; + margin-left: 12.5%; + width: 25%; +} - .presentation-next{ - float: right; - } - .presentation-back{ - float: left; - } - .presentation-next:hover{ +.presentation-buttons { + padding: 10px; +} + +.presentation-next:hover { transition: all .1s; background: #AAAAAA } -.presentation-back:hover{ + +.presentation-back:hover { transition: all .1s; background: #AAAAAA } \ No newline at end of file diff --git a/src/client/views/PresentationView.tsx b/src/client/views/PresentationView.tsx index 3fb24a339..098e725c7 100644 --- a/src/client/views/PresentationView.tsx +++ b/src/client/views/PresentationView.tsx @@ -5,15 +5,20 @@ import "./PresentationView.scss" import "./Main.tsx"; import { DocumentManager } from "../util/DocumentManager"; import { Utils } from "../../Utils"; -import { Doc } from "../../new_fields/Doc"; +import { Doc, DocListCast, DocListCastAsync } from "../../new_fields/Doc"; import { listSpec } from "../../new_fields/Schema"; -import { Cast, NumCast, FieldValue, PromiseValue } from "../../new_fields/Types"; +import { Cast, NumCast, FieldValue, PromiseValue, StrCast } from "../../new_fields/Types"; import { Id } from "../../new_fields/RefField"; import { List } from "../../new_fields/List"; import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; export interface PresViewProps { - //Document: Doc; + Document: Doc; +} + +interface PresListProps extends PresViewProps { + deleteDocument(index: number): void; + gotoDocument(index: number): void; } @@ -21,72 +26,40 @@ export interface PresViewProps { /** * Component that takes in a document prop and a boolean whether it's collapsed or not. */ -class PresentationViewItem extends React.Component { - - @observable Document: Doc; - constructor(props: PresViewProps) { - super(props); - this.Document = FieldValue(Cast(FieldValue(Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc))!.presentationView, Doc))!; - } - //look at CollectionFreeformView.focusDocument(d) - @action - openDoc = (doc: Doc) => { - let docView = DocumentManager.Instance.getDocumentView(doc); - if (docView) { - docView.props.focus(docView.props.Document); - } - } - - /** - * Removes a document from the presentation view - **/ - @action - public RemoveDoc(doc: Doc) { - const value = Cast(this.Document.data, listSpec(Doc), []); - let index = -1; - for (let i = 0; i < value.length; i++) { - if (value[i][Id] === doc[Id]) { - index = i; - break; - } - } - if (index !== -1) { - value.splice(index, 1); - } - } +class PresentationViewList extends React.Component { /** * Renders a single child document. It will just append a list element. * @param document The document to render. */ - renderChild(document: Doc) { + renderChild = (document: Doc, index: number) => { let title = document.title; //to get currently selected presentation doc - let selected = NumCast(this.Document.selectedDoc, 0); + let selected = NumCast(this.props.Document.selectedDoc, 0); - // finally, if it's a normal document, then render it as such. - const children = Cast(this.Document.data, listSpec(Doc)); - const styles: any = {}; - if (children && children[selected] === document) { + let className = "presentationView-item"; + if (selected === index) { //this doc is selected - styles.background = "gray"; + className += " presentationView-selected"; } return ( -
  • -
    this.openDoc(document)}>{title}
    -
    this.RemoveDoc(document)}>X
    -
  • +
    { this.props.gotoDocument(index); e.stopPropagation(); }}> + + {`${index + 1}. ${title}`} + + +
    ); } render() { - const children = Cast(this.Document.data, listSpec(Doc), []); + const children = DocListCast(this.props.Document.data); return ( -
    - {children.map(value => this.renderChild(value))} +
    + {children.map(this.renderChild)}
    ); } @@ -100,59 +73,42 @@ export class PresentationView extends React.Component { //observable means render is re-called every time variable is changed @observable collapsed: boolean = false; - closePresentation = action(() => this.Document!.width = 0); + closePresentation = action(() => this.props.Document.width = 0); next = () => { - const current = NumCast(this.Document!.selectedDoc); - const allDocs = FieldValue(Cast(this.Document!.data, listSpec(Doc))); - if (allDocs && current < allDocs.length + 1) { - //can move forwards - this.Document!.selectedDoc = current + 1; - const doc = allDocs[current + 1]; - let docView = DocumentManager.Instance.getDocumentView(doc); - if (docView) { - docView.props.focus(docView.props.Document); - } - } + const current = NumCast(this.props.Document.selectedDoc); + this.gotoDocument(current + 1); } back = () => { - const current = NumCast(this.Document!.selectedDoc); - const allDocs = FieldValue(Cast(this.Document!.data, listSpec(Doc))); - if (allDocs && current - 1 >= 0) { - //can move forwards - this.Document!.selectedDoc = current - 1; - const doc = allDocs[current - 1]; - let docView = DocumentManager.Instance.getDocumentView(doc); - if (docView) { - docView.props.focus(docView.props.Document); - } + const current = NumCast(this.props.Document.selectedDoc); + this.gotoDocument(current - 1); + } + + @action + public RemoveDoc = (index: number) => { + const value = FieldValue(Cast(this.props.Document.data, listSpec(Doc))); + if (value) { + value.splice(index, 1); } } - private ref = React.createRef(); + public gotoDocument = async (index: number) => { + const list = FieldValue(Cast(this.props.Document.data, listSpec(Doc))); + if (!list) { + return; + } + if (index < 0 || index >= list.length) { + return; + } + + this.props.Document.selectedDoc = index; + const doc = await list[index]; + DocumentManager.Instance.jumpToDocument(doc); + } - @observable Document?: Doc; //initilize class variables constructor(props: PresViewProps) { super(props); - let self = this; - reaction(() => - CurrentUserUtils.UserDocument.activeWorkspace, - (activeW) => { - if (activeW && activeW instanceof Doc) { - PromiseValue(Cast(activeW.presentationView, Doc)). - then(pv => runInAction(() => { - if (pv) self.Document = pv; - else { - pv = new Doc(); - pv.title = "Presentation Doc"; - activeW.presentationView = pv; - self.Document = pv; - } - })) - } - }, - { fireImmediately: true }); PresentationView.Instance = this; } @@ -162,36 +118,32 @@ export class PresentationView extends React.Component { @action public PinDoc(doc: Doc) { //add this new doc to props.Document - const data = Cast(this.Document!.data, listSpec(Doc)); + const data = Cast(this.props.Document.data, listSpec(Doc)); if (data) { data.push(doc); } else { - this.Document!.data = new List([doc]); + this.props.Document.data = new List([doc]); } - this.Document!.width = 300; + this.props.Document.width = 300; } render() { - if (!this.Document) - return (null); - let titleStr = this.Document.Title; - let width = NumCast(this.Document.width); + let titleStr = StrCast(this.props.Document.title); + let width = NumCast(this.props.Document.width); //TODO: next and back should be icons return (
    {titleStr}
    -
    X
    -
    -
    back
    -
    next
    - + +
    +
    + +
    -
      - -
    +
    ); } diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index e2b3bd07a..22c4edc25 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -38,7 +38,6 @@ export class TemplateMenu extends React.Component { constructor(props: TemplateMenuProps) { super(props); - console.log(""); } @action diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index a755e0f91..6651a834d 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -1,6 +1,6 @@ import 'golden-layout/src/css/goldenlayout-base.css'; import 'golden-layout/src/css/goldenlayout-dark-theme.css'; -import { action, observable, reaction } from "mobx"; +import { action, observable, reaction, Lambda } from "mobx"; import { observer } from "mobx-react"; import * as ReactDOM from 'react-dom'; import Measure from "react-measure"; @@ -188,12 +188,16 @@ export class CollectionDockingView extends React.Component void = () => { + console.log("Docking mount"); if (this._containerRef.current) { - reaction( + this.reactionDisposer = reaction( () => StrCast(this.props.Document.dockingConfig), () => { if (!this._goldenLayout || this._ignoreStateChange !== JSON.stringify(this._goldenLayout.toConfig())) { + // Because this is in a set timeout, if this component unmounts right after mounting, + // we will leak a GoldenLayout, because we try to destroy it before we ever create it setTimeout(() => this.setupGoldenLayout(), 1); } this._ignoreStateChange = ""; @@ -203,6 +207,7 @@ export class CollectionDockingView extends React.Component void = () => { + console.log("Docking unmount"); try { this._goldenLayout.unbind('itemDropped', this.itemDropped); this._goldenLayout.unbind('tabCreated', this.tabCreated); @@ -214,6 +219,10 @@ export class CollectionDockingView extends React.Component { diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index 5dabfc30d..11117122d 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -36,7 +36,7 @@ export class LinkMenu extends React.Component { if (this._editingLink === undefined) { return (
    - + {/* */}
    {this.renderLinkItems(linkTo, "linkedTo", "Destination: ")} {this.renderLinkItems(linkFrom, "linkedFrom", "Source: ")} diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 4c837fcbd..89901490d 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -29,15 +29,21 @@ export const SelfProxy = Symbol("SelfProxy"); export const WidthSym = Symbol("Width"); export const HeightSym = Symbol("Height"); +/** + * Cast any field to either a List of Docs or undefined if the given field isn't a List of Docs. + * If a default value is given, that will be returned instead of undefined. + * If a default value is given, the returned value should not be modified as it might be a temporary value. + * If no default value is given, and the returned value is not undefined, it can be safely modified. + */ export function DocListCastAsync(field: FieldResult): Promise; export function DocListCastAsync(field: FieldResult, defaultValue: Doc[]): Promise; export function DocListCastAsync(field: FieldResult, defaultValue?: Doc[]) { const list = Cast(field, listSpec(Doc)); - return list ? Promise.all(list) : Promise.resolve(defaultValue); + return list ? Promise.all(list).then(() => list) : Promise.resolve(defaultValue); } export function DocListCast(field: FieldResult) { - return Cast(field, listSpec(Doc), []).filter(d => d && d instanceof Doc).map(d => d as Doc) + return Cast(field, listSpec(Doc), []).filter(d => d && d instanceof Doc).map(d => d as Doc); } @Deserializable("doc").withFields(["id"]) -- cgit v1.2.3-70-g09d2 From b29a0d1cef60b55f609fcd94ad38232ae557e681 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Thu, 16 May 2019 18:32:57 -0400 Subject: Fixed linter errors --- src/client/goldenLayout.d.ts | 3 +++ src/client/northstar/dash-fields/HistogramField.ts | 2 +- src/client/northstar/model/ModelHelpers.ts | 9 +++---- src/client/util/DragManager.ts | 2 +- src/client/util/RichTextSchema.tsx | 16 +++++------ src/client/util/TooltipTextMenu.tsx | 13 ++++----- src/client/views/DocumentDecorations.tsx | 31 +++++++++++----------- src/client/views/PresentationView.tsx | 4 +-- src/client/views/TemplateMenu.tsx | 4 +-- .../views/collections/CollectionDockingView.tsx | 5 ++-- .../views/collections/CollectionSchemaView.tsx | 10 +++---- .../views/collections/CollectionTreeView.tsx | 9 ++++--- .../collections/collectionFreeForm/MarqueeView.tsx | 13 ++++----- .../views/nodes/CollectionFreeFormDocumentView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 8 +++--- src/client/views/nodes/ImageBox.tsx | 2 +- src/client/views/nodes/KeyValueBox.tsx | 2 +- src/client/views/nodes/KeyValuePair.tsx | 3 ++- src/client/views/nodes/VideoBox.tsx | 22 +++++++-------- src/new_fields/CursorField.ts | 6 ++--- src/new_fields/Doc.ts | 4 +-- src/new_fields/ObjectField.ts | 2 +- .../authentication/models/current_user_utils.ts | 2 +- 24 files changed, 91 insertions(+), 85 deletions(-) create mode 100644 src/client/goldenLayout.d.ts (limited to 'src/client/views/PresentationView.tsx') diff --git a/src/client/goldenLayout.d.ts b/src/client/goldenLayout.d.ts new file mode 100644 index 000000000..b50240563 --- /dev/null +++ b/src/client/goldenLayout.d.ts @@ -0,0 +1,3 @@ + +declare const GoldenLayout: any; +export = GoldenLayout; \ No newline at end of file diff --git a/src/client/northstar/dash-fields/HistogramField.ts b/src/client/northstar/dash-fields/HistogramField.ts index aabc77bb2..1ee2189b9 100644 --- a/src/client/northstar/dash-fields/HistogramField.ts +++ b/src/client/northstar/dash-fields/HistogramField.ts @@ -52,7 +52,7 @@ export class HistogramField extends ObjectField { [Copy]() { let y = this.HistoOp; - let z = this.HistoOp["Copy"]; + let z = this.HistoOp.Copy; return new HistogramField(HistogramOperation.Duplicate(this.HistoOp)); } } \ No newline at end of file diff --git a/src/client/northstar/model/ModelHelpers.ts b/src/client/northstar/model/ModelHelpers.ts index 80bb71224..88e6e72b8 100644 --- a/src/client/northstar/model/ModelHelpers.ts +++ b/src/client/northstar/model/ModelHelpers.ts @@ -32,12 +32,9 @@ export class ModelHelpers { public static GetAggregateParametersIndex(histogramResult: HistogramResult, aggParameters?: AggregateParameters): number { return Array.from(histogramResult.aggregateParameters!).findIndex((value, i, set) => { - if (set[i] instanceof CountAggregateParameters && value instanceof CountAggregateParameters) - return true; - if (set[i] instanceof MarginAggregateParameters && value instanceof MarginAggregateParameters) - return true; - if (set[i] instanceof SumAggregateParameters && value instanceof SumAggregateParameters) - return true; + if (set[i] instanceof CountAggregateParameters && value instanceof CountAggregateParameters) return true; + if (set[i] instanceof MarginAggregateParameters && value instanceof MarginAggregateParameters) return true; + if (set[i] instanceof SumAggregateParameters && value instanceof SumAggregateParameters) return true; return false; }); } diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 26da34e67..05eb5c38a 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -41,7 +41,7 @@ export function SetupDrag(_reference: React.RefObject, docFunc: () export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: number, sourceDoc: Doc) { let srcTarg = sourceDoc.proto; let draggedDocs: Doc[] = []; - let draggedFromDocs: Doc[] = [] + let draggedFromDocs: Doc[] = []; if (srcTarg) { let linkToDocs = await DocListCastAsync(srcTarg.linkedToDocs); let linkFromDocs = await DocListCastAsync(srcTarg.linkedFromDocs); diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index c0e6f7899..3e3e98206 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -97,13 +97,13 @@ export const nodes: { [index: string]: NodeSpec } = { title: dom.getAttribute("title"), alt: dom.getAttribute("alt"), width: Math.min(100, Number(dom.getAttribute("width"))), - } + }; } }], // TODO if we don't define toDom, something weird happens: dragging the image will not move it but clone it. Why? toDOM(node) { - const attrs = { style: `width: ${node.attrs.width}` } - return ["img", { ...node.attrs, ...attrs }] + const attrs = { style: `width: ${node.attrs.width}` }; + return ["img", { ...node.attrs, ...attrs }]; } }, @@ -375,7 +375,7 @@ export class ImageResizeView { const currentX = e.pageX; const diffInPx = currentX - startX; self._outer.style.width = `${startWidth + diffInPx}`; - } + }; const onpointerup = () => { document.removeEventListener("pointermove", onpointermove); @@ -384,11 +384,11 @@ export class ImageResizeView { view.state.tr.setNodeMarkup(getPos(), null, { src: node.attrs.src, width: self._outer.style.width }) .setSelection(view.state.selection)); - } + }; - document.addEventListener("pointermove", onpointermove) - document.addEventListener("pointerup", onpointerup) - } + document.addEventListener("pointermove", onpointermove); + document.addEventListener("pointerup", onpointerup); + }; this._outer.appendChild(this._handle); this._outer.appendChild(this._img); diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 6eb654319..223921428 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -175,7 +175,7 @@ export class TooltipTextMenu { this.linkText.style.width = "150px"; this.linkText.style.overflow = "hidden"; this.linkText.style.color = "white"; - this.linkText.onpointerdown = (e: PointerEvent) => { e.stopPropagation(); } + this.linkText.onpointerdown = (e: PointerEvent) => { e.stopPropagation(); }; let linkBtn = document.createElement("div"); linkBtn.textContent = ">>"; linkBtn.style.width = "20px"; @@ -192,8 +192,9 @@ export class TooltipTextMenu { let docid = href.replace(DocServer.prepend("/doc/"), ""); DocServer.GetRefField(docid).then(action((f: Opt) => { if (f instanceof Doc) { - if (DocumentManager.Instance.getDocumentView(f)) + if (DocumentManager.Instance.getDocumentView(f)) { DocumentManager.Instance.getDocumentView(f)!.props.focus(f); + } else CollectionDockingView.Instance.AddRightSplit(f); } })); @@ -201,7 +202,7 @@ export class TooltipTextMenu { e.stopPropagation(); e.preventDefault(); } - } + }; this.linkDrag = document.createElement("img"); this.linkDrag.src = "https://seogurusnyc.com/wp-content/uploads/2016/12/link-1.png"; this.linkDrag.style.width = "20px"; @@ -216,12 +217,12 @@ export class TooltipTextMenu { { handlers: { dragComplete: action(() => { - let m = dragData.droppedDocuments as Doc[]; + let m = dragData.droppedDocuments; this.makeLink(DocServer.prepend("/doc/" + m[0][Id])); }), }, hideSource: false - }) + }); }; this.linkEditor.appendChild(this.linkDrag); this.linkEditor.appendChild(this.linkText); @@ -239,7 +240,7 @@ export class TooltipTextMenu { e.stopPropagation(); e.preventDefault(); } - } + }; this.tooltip.appendChild(this.linkEditor); } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index b2c65a31f..7083b1003 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -250,22 +250,21 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> selectedDocs[0].props.removeDocument && selectedDocs[0].props.removeDocument(this._iconDoc); } if (!this._removeIcon) { - if (selectedDocs.length === 1) + if (selectedDocs.length === 1) { this.getIconDoc(selectedDocs[0]).then(icon => selectedDocs[0].props.toggleMinimized()); - else - if (Math.abs(e.pageX - this._downX) < Utils.DRAG_THRESHOLD && - Math.abs(e.pageY - this._downY) < Utils.DRAG_THRESHOLD) { - let docViews = SelectionManager.ViewsSortedVertically(); - let topDocView = docViews[0]; - let ind = topDocView.templates.indexOf(Templates.Bullet.Layout); - if (ind !== -1) { - topDocView.templates.splice(ind, 1); - topDocView.props.Document.subBulletDocs = undefined; - } else { - topDocView.addTemplate(Templates.Bullet); - topDocView.props.Document.subBulletDocs = new List(docViews.filter(v => v !== topDocView).map(v => v.props.Document.proto!)); - } + } else if (Math.abs(e.pageX - this._downX) < Utils.DRAG_THRESHOLD && + Math.abs(e.pageY - this._downY) < Utils.DRAG_THRESHOLD) { + let docViews = SelectionManager.ViewsSortedVertically(); + let topDocView = docViews[0]; + let ind = topDocView.templates.indexOf(Templates.Bullet.Layout); + if (ind !== -1) { + topDocView.templates.splice(ind, 1); + topDocView.props.Document.subBulletDocs = undefined; + } else { + topDocView.addTemplate(Templates.Bullet); + topDocView.props.Document.subBulletDocs = new List(docViews.filter(v => v !== topDocView).map(v => v.props.Document.proto!)); } + } } this._removeIcon = false; } @@ -537,9 +536,9 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> if (temp !== Templates.Bullet.Layout || i === 0) { res.push(temp); } - }) + }); } - return res + return res; }, [] as string[]); let checked = false; docTemps.forEach(temp => { diff --git a/src/client/views/PresentationView.tsx b/src/client/views/PresentationView.tsx index 098e725c7..7d0dc2913 100644 --- a/src/client/views/PresentationView.tsx +++ b/src/client/views/PresentationView.tsx @@ -1,7 +1,7 @@ import { observer } from "mobx-react"; -import React = require("react") +import React = require("react"); import { observable, action, runInAction, reaction } from "mobx"; -import "./PresentationView.scss" +import "./PresentationView.scss"; import "./Main.tsx"; import { DocumentManager } from "../util/DocumentManager"; import { Utils } from "../../Utils"; diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index cfe1b0663..e5b679e24 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -43,7 +43,7 @@ export class TemplateMenu extends React.Component { @action toggleTemplate = (event: React.ChangeEvent, template: Template): void => { if (event.target.checked) { - if (template.Name == "Bullet") { + if (template.Name === "Bullet") { let topDocView = this.props.docs[0]; topDocView.addTemplate(template); topDocView.props.Document.subBulletDocs = new List(this.props.docs.filter(v => v !== topDocView).map(v => v.props.Document.proto!)); @@ -52,7 +52,7 @@ export class TemplateMenu extends React.Component { } this.props.templates.set(template, true); } else { - if (template.Name == "Bullet") { + if (template.Name === "Bullet") { let topDocView = this.props.docs[0]; topDocView.removeTemplate(template); topDocView.props.Document.subBulletDocs = undefined; diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 58f1e33a1..deec64225 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -83,7 +83,7 @@ export class CollectionDockingView extends React.Component tab.config.component === "DocumentFrameRenderer").some((tab: any, j: number) => { if (Doc.AreProtosEqual(DocumentManager.Instance.getDocumentViewById(tab.config.props.documentId)!.Document, document)) { child.contentItems[j].remove(); @@ -94,8 +94,9 @@ export class CollectionDockingView extends React.Component doc) { let fieldContentView = ; let reference = React.createRef(); let onItemDown = (e: React.PointerEvent) => - (this.props.CollectionView!.props.isSelected() ? + (this.props.CollectionView.props.isSelected() ? SetupDrag(reference, () => props.Document, this.props.moveDocument)(e) : undefined); let applyToDoc = (doc: Doc, run: (args?: { [name: string]: any }) => any) => { const res = run({ this: doc }); @@ -127,7 +127,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { } 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]) + const val = await DocListCastAsync(this.props.Document[this.props.fieldKey]); val && val.forEach(doc => applyToDoc(doc, run)); }}> @@ -230,14 +230,14 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { DocListCast(this.props.Document.data).map(doc => { csv += self.columns.reduce((val, col) => val + (doc[col] ? doc[col]!.toString() : "") + ",", ""); csv = csv.substr(0, csv.length - 1) + "\n"; - }) + }); csv.substring(0, csv.length - 1); let dbName = StrCast(this.props.Document.title); let res = await Gateway.Instance.PostSchema(csv, dbName); if (self.props.CollectionView.props.addDocument) { let schemaDoc = await Docs.DBDocument("https://www.cs.brown.edu/" + dbName, { title: dbName }); if (schemaDoc) { - self.props.CollectionView.props.addDocument(schemaDoc, false); + self.props.CollectionView.props.addDocument(schemaDoc, false); } } } @@ -263,7 +263,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { get previewDocument(): Doc | undefined { const children = DocListCast(this.props.Document[this.props.fieldKey]); const selected = children.length > this._selectedIndex ? FieldValue(children[this._selectedIndex]) : undefined; - return selected ? (this.previewScript && this.previewScript != "this" ? FieldValue(Cast(selected[this.previewScript], Doc)) : selected) : undefined; + return selected ? (this.previewScript && this.previewScript !== "this" ? FieldValue(Cast(selected[this.previewScript], Doc)) : selected) : undefined; } get tableWidth() { return (this.props.PanelWidth() - 2 * this.borderWidth - this.DIVIDER_WIDTH) * (1 - this.splitPercentage / 100); } get previewRegionHeight() { return this.props.PanelHeight() - 2 * this.borderWidth; } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 70c09d97c..6acef434e 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -56,7 +56,7 @@ class TreeView extends React.Component { } else { CollectionDockingView.Instance.AddRightSplit(this.props.document); } - }; + } get children() { return Cast(this.props.document.data, listSpec(Doc), []); // bcz: needed? .filter(doc => FieldValue(doc)); @@ -184,8 +184,9 @@ class TreeView extends React.Component { {TreeView.GetChildElements(doc instanceof Doc ? [doc] : docList, key !== "data", (doc: Doc) => this.remove(doc, key), this.move, this.props.dropAction)}
    ); - } else + } else { bulletType = BulletType.Collapsed; + } } }); return
    {
    ; } public static GetChildElements(docs: Doc[], allowMinimized: boolean, remove: ((doc: Doc) => void), move: DragManager.MoveFunction, dropAction: dropActionType) { - return docs.filter(child => child instanceof Doc && !child.excludeFromLibrary && (allowMinimized || !child.isMinimized)).filter(doc => FieldValue(doc)).map(child => - ); + return docs.filter(child => !child.excludeFromLibrary && (allowMinimized || !child.isMinimized)).map(child => + ); } } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 12edb2c2a..c3c4115b8 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -63,7 +63,7 @@ export class MarqueeView extends React.Component e.preventDefault(); (async () => { let text: string = await navigator.clipboard.readText(); - let ns = text.split("\n").filter(t => t.trim() != "\r" && t.trim() != ""); + let ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== ""); for (let i = 0; i < ns.length - 1; i++) { while (!(ns[i].trim() === "" || ns[i].endsWith("-\r") || ns[i].endsWith("-") || ns[i].endsWith(";\r") || ns[i].endsWith(";") || @@ -80,7 +80,7 @@ export class MarqueeView extends React.Component let newBox = Docs.TextDocument({ width: 200, height: 35, x: x + indent / 3 * 10, y: y, documentText: "@@@" + line, title: line }); this.props.addDocument(newBox, false); y += 40 * this.props.getTransform().Scale; - }) + }); })(); } else if (e.key === "b" && e.ctrlKey) { //heuristically converts pasted text into a table. @@ -93,9 +93,10 @@ export class MarqueeView extends React.Component e.preventDefault(); (async () => { let text: string = await navigator.clipboard.readText(); - let ns = text.split("\n").filter(t => t.trim() != "\r" && t.trim() != ""); - while (ns.length > 0 && ns[0].split("\t").length < 2) + let ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== ""); + while (ns.length > 0 && ns[0].split("\t").length < 2) { ns.splice(0, 1); + } if (ns.length > 0) { let columns = ns[0].split("\t"); let docList: Doc[] = []; @@ -109,7 +110,7 @@ export class MarqueeView extends React.Component let doc = new Doc(); columns.forEach((col, i) => doc[columns[i]] = (values.length > i ? ((values[i].indexOf(Number(values[i]).toString()) !== -1) ? Number(values[i]) : values[i]) : undefined)); if (groupAttr) { - doc["_group"] = groupAttr; + doc._group = groupAttr; } doc.title = i.toString(); docList.push(doc); @@ -284,7 +285,7 @@ export class MarqueeView extends React.Component // summarizedDoc.isIconAnimating = new List([scrpt[0], scrpt[1], maxx, maxy, maxw, maxh, Date.now(), 0]); // }); this.props.addLiveTextDocument(summary); - }) + }); } else { this.props.addDocument(newCollection, false); diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 7c7ca9e25..d4f660b3f 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -149,7 +149,7 @@ export class CollectionFreeFormDocumentView extends DocComponent([scrpt[0], scrpt[1], Date.now(), isMinimized ? 1 : 0]) + maximizedDoc.isIconAnimating = new List([scrpt[0], scrpt[1], Date.now(), isMinimized ? 1 : 0]); } } }); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index cf8bcbb42..428dd9b36 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -244,7 +244,7 @@ export class DocumentView extends DocComponent(Docu const protoDest = destDoc.proto; const protoSrc = sourceDoc.proto; - if (de.mods == "Control") { + if (de.mods === "Control") { let src = protoSrc ? protoSrc : sourceDoc; let dst = protoDest ? protoDest : destDoc; dst.data = src; diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index f24d4ae88..f30022508 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -184,7 +184,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe state: field && field.Data ? EditorState.fromJSON(config, JSON.parse(field.Data)) : EditorState.create(config), dispatchTransaction: this.dispatchTransaction, nodeViews: { - image(node, view, getPos) { return new ImageResizeView(node, view, getPos) } + image(node, view, getPos) { return new ImageResizeView(node, view, getPos); } } }); let text = StrCast(this.props.Document.documentText); @@ -232,9 +232,11 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe let docid = href.replace(DocServer.prepend("/doc/"), "").split("?")[0]; DocServer.GetRefField(docid).then(action((f: Opt) => { if (f instanceof Doc) { - if (DocumentManager.Instance.getDocumentView(f)) + if (DocumentManager.Instance.getDocumentView(f)) { DocumentManager.Instance.getDocumentView(f)!.props.focus(f); - else CollectionDockingView.Instance.AddRightSplit(f); + } else { + CollectionDockingView.Instance.AddRightSplit(f); + } } })); } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 6472ae711..3cc60a6c5 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -165,7 +165,7 @@ export class ImageBox extends DocComponent(ImageD else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => (p as ImageField).url.href); let nativeWidth = FieldValue(this.Document.nativeWidth, (this.props.PanelWidth as any) as string ? Number((this.props.PanelWidth as any) as string) : 50); let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive"; - let id = this.props.id; // bcz: used to set id = "isExpander" in templates.tsx + let id = (this.props as any).id; // bcz: used to set id = "isExpander" in templates.tsx return (
    { @observable private _keyInput: string = ""; @observable private _valueInput: string = ""; @computed get splitPercentage() { return NumCast(this.props.Document.schemaSplitPercentage, 50); } - get fieldDocToLayout() { return this.props.fieldKey ? FieldValue(Cast(this.props.Document[this.props.fieldKey], Doc)) : this.props.Document } + get fieldDocToLayout() { return this.props.fieldKey ? FieldValue(Cast(this.props.Document[this.props.fieldKey], Doc)) : this.props.Document; } constructor(props: FieldViewProps) { super(props); diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index 5de660d57..4f7919f50 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -46,8 +46,9 @@ export class KeyValuePair extends React.Component {
    -
    )} - -
    -
    ; - } - - /* @TODO this should really be moved into a moveable toolbar component, but for now let's put it here to meet the deadline */ - @computed - get miscButtons() { - let logoutRef = React.createRef(); - - return [ - , -
    - - - -
    , -
    , -
    -
    - ]; - - } - - render() { - return ( -
    - - {this.mainContent} - - - {this.nodesMenu()} - {this.miscButtons} - - -
    - ); - } -} +import * as React from 'react'; (async () => { await Docs.initProtos(); await CurrentUserUtils.loadCurrentUser(); - ReactDOM.render(
    , document.getElementById('root')); + ReactDOM.render(, document.getElementById('root')); })(); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx new file mode 100644 index 000000000..d0d77bbf4 --- /dev/null +++ b/src/client/views/MainView.tsx @@ -0,0 +1,274 @@ +import { IconName, library } from '@fortawesome/fontawesome-svg-core'; +import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faTree, faUndoAlt } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { action, computed, configure, observable, runInAction, trace } from 'mobx'; +import { observer } from 'mobx-react'; +import "normalize.css"; +import * as React from 'react'; +import Measure from 'react-measure'; +import * as request from 'request'; +import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; +import { RouteStore } from '../../server/RouteStore'; +import { emptyFunction, returnTrue, Utils, returnOne, returnZero } from '../../Utils'; +import { Docs } from '../documents/Documents'; +import { SetupDrag, DragManager } from '../util/DragManager'; +import { Transform } from '../util/Transform'; +import { UndoManager } from '../util/UndoManager'; +import { PresentationView } from './PresentationView'; +import { CollectionDockingView } from './collections/CollectionDockingView'; +import { ContextMenu } from './ContextMenu'; +import { DocumentDecorations } from './DocumentDecorations'; +import { InkingControl } from './InkingControl'; +import "./Main.scss"; +import { MainOverlayTextBox } from './MainOverlayTextBox'; +import { DocumentView } from './nodes/DocumentView'; +import { PreviewCursor } from './PreviewCursor'; +import { SearchBox } from './SearchBox'; +import { SelectionManager } from '../util/SelectionManager'; +import { FieldResult, Field, Doc, Opt } from '../../new_fields/Doc'; +import { Cast, FieldValue, StrCast } from '../../new_fields/Types'; +import { DocServer } from '../DocServer'; +import { listSpec } from '../../new_fields/Schema'; +import { Id } from '../../new_fields/RefField'; +import { HistoryUtil } from '../util/History'; + + +@observer +export class MainView extends React.Component { + public static Instance: MainView; + @observable private _workspacesShown: boolean = false; + @observable public pwidth: number = 0; + @observable public pheight: number = 0; + + @computed private get mainContainer(): Opt { + return FieldValue(Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc)); + } + private set mainContainer(doc: Opt) { + if (doc) { + if (!("presentationView" in doc)) { + doc.presentationView = new Doc(); + } + CurrentUserUtils.UserDocument.activeWorkspace = doc; + } + } + + constructor(props: Readonly<{}>) { + super(props); + MainView.Instance = this; + // causes errors to be generated when modifying an observable outside of an action + configure({ enforceActions: "observed" }); + if (window.location.pathname !== RouteStore.home) { + let pathname = window.location.pathname.split("/"); + if (pathname.length > 1 && pathname[pathname.length - 2] === 'doc') { + CurrentUserUtils.MainDocId = pathname[pathname.length - 1]; + } + } + + library.add(faFont); + library.add(faImage); + library.add(faFilePdf); + library.add(faObjectGroup); + library.add(faTable); + library.add(faGlobeAsia); + library.add(faUndoAlt); + library.add(faRedoAlt); + library.add(faPenNib); + library.add(faFilm); + library.add(faMusic); + library.add(faTree); + this.initEventListeners(); + this.initAuthenticationRouters(); + } + + initEventListeners = () => { + // window.addEventListener("pointermove", (e) => this.reportLocation(e)) + window.addEventListener("drop", (e) => e.preventDefault(), false); // drop event handler + window.addEventListener("dragover", (e) => e.preventDefault(), false); // drag event handler + window.addEventListener("keydown", (e) => { + if (e.key === "Escape") { + DragManager.AbortDrag(); + SelectionManager.DeselectAll(); + } + }, false); // drag event handler + // click interactions for the context menu + document.addEventListener("pointerdown", action(function (e: PointerEvent) { + if (!ContextMenu.Instance.intersects(e.pageX, e.pageY)) { + ContextMenu.Instance.clearItems(); + } + }), true); + } + + initAuthenticationRouters = async () => { + // Load the user's active workspace, or create a new one if initial session after signup + if (!CurrentUserUtils.MainDocId) { + const doc = await Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc); + if (doc) { + this.openWorkspace(doc); + } else { + this.createNewWorkspace(); + } + } else { + DocServer.GetRefField(CurrentUserUtils.MainDocId).then(field => + field instanceof Doc ? this.openWorkspace(field) : + this.createNewWorkspace(CurrentUserUtils.MainDocId)); + } + } + + @action + createNewWorkspace = async (id?: string) => { + const list = Cast(CurrentUserUtils.UserDocument.data, listSpec(Doc)); + if (list) { + let freeformDoc = Docs.FreeformDocument([], { x: 0, y: 400, title: `WS collection ${list.length + 1}` }); + var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(CurrentUserUtils.UserDocument, 150), CollectionDockingView.makeDocumentConfig(freeformDoc, 600)] }] }; + let mainDoc = Docs.DockDocument([CurrentUserUtils.UserDocument, freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${list.length + 1}` }); + list.push(mainDoc); + // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container) + setTimeout(() => { + this.openWorkspace(mainDoc); + let pendingDocument = Docs.SchemaDocument(["title"], [], { title: "New Mobile Uploads" }); + mainDoc.optionalRightCollection = pendingDocument; + }, 0); + } + } + + @action + openWorkspace = async (doc: Doc, fromHistory = false) => { + CurrentUserUtils.MainDocId = doc[Id]; + this.mainContainer = doc; + fromHistory || HistoryUtil.pushState({ type: "doc", docId: doc[Id], initializers: {} }); + const col = await Cast(CurrentUserUtils.UserDocument.optionalRightCollection, Doc); + // if there is a pending doc, and it has new data, show it (syip: we use a timeout to prevent collection docking view from being uninitialized) + setTimeout(async () => { + if (col) { + const l = Cast(col.data, listSpec(Doc)); + if (l && l.length > 0) { + CollectionDockingView.Instance.AddRightSplit(col); + } + } + }, 100); + } + @action + onResize = (r: any) => { + this.pwidth = r.offset.width; + this.pheight = r.offset.height; + } + getPWidth = () => { + return this.pwidth; + } + getPHeight = () => { + return this.pheight; + } + @computed + get mainContent() { + let mainCont = this.mainContainer; + let content = !mainCont ? (null) : + ; + const pres = mainCont ? FieldValue(Cast(mainCont.presentationView, Doc)) : undefined; + return + {({ measureRef }) => +
    + {content} + {pres ? : null} +
    + } +
    ; + } + + /* for the expandable add nodes menu. Not included with the miscbuttons because once it expands it expands the whole div with it, making canvas interactions limited. */ + nodesMenu() { + + let imgurl = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg"; + let pdfurl = "http://www.adobe.com/support/products/enterprise/knowledgecenter/media/c27211_sample_explain.pdf"; + let weburl = "https://cs.brown.edu/courses/cs166/"; + let audiourl = "http://techslides.com/demos/samples/sample.mp3"; + let videourl = "http://techslides.com/demos/sample-videos/small.mp4"; + + let addTextNode = action(() => Docs.TextDocument({ borderRounding: -1, width: 200, height: 200, title: "a text note" })); + let addColNode = action(() => Docs.FreeformDocument([], { width: 200, height: 200, title: "a freeform collection" })); + let addSchemaNode = action(() => Docs.SchemaDocument(["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 addVideoNode = action(() => Docs.VideoDocument(videourl, { width: 200, title: "video node" })); + let addPDFNode = action(() => Docs.PdfDocument(pdfurl, { width: 200, height: 200, title: "a pdf doc" })); + let addImageNode = action(() => Docs.ImageDocument(imgurl, { width: 200, title: "an image of a cat" })); + let addWebNode = action(() => Docs.WebDocument(weburl, { width: 200, height: 200, title: "a sample web page" })); + let addAudioNode = action(() => Docs.AudioDocument(audiourl, { width: 200, height: 200, title: "audio node" })); + + let btns: [React.RefObject, IconName, string, () => Doc][] = [ + [React.createRef(), "font", "Add Textbox", addTextNode], + [React.createRef(), "image", "Add Image", addImageNode], + [React.createRef(), "file-pdf", "Add PDF", addPDFNode], + [React.createRef(), "film", "Add Video", addVideoNode], + [React.createRef(), "music", "Add Audio", addAudioNode], + [React.createRef(), "globe-asia", "Add Web Clipping", addWebNode], + [React.createRef(), "object-group", "Add Collection", addColNode], + [React.createRef(), "tree", "Add Tree", addTreeNode], + [React.createRef(), "table", "Add Schema", addSchemaNode], + ]; + + return < div id="add-nodes-menu" > + + + +
    +
      + {btns.map(btn => +
    • + +
    • )} +
    +
    +
    ; + } + + /* @TODO this should really be moved into a moveable toolbar component, but for now let's put it here to meet the deadline */ + @computed + get miscButtons() { + let logoutRef = React.createRef(); + + return [ + , +
    + + + +
    , +
    , +
    +
    + ]; + + } + + render() { + return ( +
    + + {this.mainContent} + + + {this.nodesMenu()} + {this.miscButtons} + + +
    + ); + } +} diff --git a/src/client/views/PresentationView.tsx b/src/client/views/PresentationView.tsx index 7d0dc2913..9c37e9000 100644 --- a/src/client/views/PresentationView.tsx +++ b/src/client/views/PresentationView.tsx @@ -2,7 +2,6 @@ import { observer } from "mobx-react"; import React = require("react"); import { observable, action, runInAction, reaction } from "mobx"; import "./PresentationView.scss"; -import "./Main.tsx"; import { DocumentManager } from "../util/DocumentManager"; import { Utils } from "../../Utils"; import { Doc, DocListCast, DocListCastAsync } from "../../new_fields/Doc"; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 35a9f34cf..4cc4ae6b6 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -14,12 +14,12 @@ import { Doc, DocListCast } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/RefField'; import { ContextMenu } from '../ContextMenu'; import { undoBatch } from '../../util/UndoManager'; -import { Main } from '../Main'; import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils'; import { CollectionDockingView } from './CollectionDockingView'; import { DocumentManager } from '../../util/DocumentManager'; import { List } from '../../../new_fields/List'; import { Docs } from '../../documents/Documents'; +import { MainView } from '../MainView'; export interface TreeViewProps { @@ -52,7 +52,7 @@ class TreeView extends React.Component { @undoBatch openRight = async () => { if (this.props.document.dockingConfig) { - Main.Instance.openWorkspace(this.props.document); + MainView.Instance.openWorkspace(this.props.document); } else { CollectionDockingView.Instance.AddRightSplit(this.props.document); } @@ -140,7 +140,7 @@ class TreeView extends React.Component { onWorkspaceContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped() && this.props.document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 - ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => Main.Instance.openWorkspace(this.props.document)) }); + ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.props.document)) }); ContextMenu.Instance.addItem({ description: "Open Right", event: () => CollectionDockingView.Instance.AddRightSplit(this.props.document) }); ContextMenu.Instance.addItem({ description: "Open Fields", event: () => CollectionDockingView.Instance.AddRightSplit(Docs.KVPDocument(this.props.document, @@ -215,7 +215,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { } onContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 - ContextMenu.Instance.addItem({ description: "Create Workspace", event: undoBatch(() => Main.Instance.createNewWorkspace()) }); + ContextMenu.Instance.addItem({ description: "Create Workspace", event: undoBatch(() => MainView.Instance.createNewWorkspace()) }); } if (!ContextMenu.Instance.getItems().some(item => item.description === "Delete")) { ContextMenu.Instance.addItem({ description: "Delete", event: undoBatch(() => this.remove(this.props.Document)) }); diff --git a/src/debug/Viewer.tsx b/src/debug/Viewer.tsx index 4cac09dee..d9b07aac6 100644 --- a/src/debug/Viewer.tsx +++ b/src/debug/Viewer.tsx @@ -3,184 +3,186 @@ import "normalize.css"; import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { observer } from 'mobx-react'; - -// configure({ -// enforceActions: "observed" -// }); - -// @observer -// class FieldViewer extends React.Component<{ field: BasicField }> { -// render() { -// return {JSON.stringify(this.props.field.Data)} ({this.props.field.Id}); -// } -// } - -// @observer -// class KeyViewer extends React.Component<{ field: Key }> { -// render() { -// return this.props.field.Name; -// } -// } - -// @observer -// class ListViewer extends React.Component<{ field: ListField }>{ -// @observable -// expanded = false; - -// render() { -// let content; -// if (this.expanded) { -// content = ( -//
    -// {this.props.field.Data.map(field => )} -//
    -// ); -// } else { -// content = <>[...] ({this.props.field.Id}); -// } -// return ( -//
    -// -// {content} -//
    -// ); -// } -// } - -// @observer -// class DocumentViewer extends React.Component<{ field: Document }> { -// private keyMap: ObservableMap = new ObservableMap; - -// private disposer?: Lambda; - -// componentDidMount() { -// let f = () => { -// Array.from(this.props.field._proxies.keys()).forEach(id => { -// if (!this.keyMap.has(id)) { -// Server.GetField(id, (field) => { -// if (field && field instanceof Key) { -// this.keyMap.set(id, field); -// } -// }); -// } -// }); -// }; -// this.disposer = this.props.field._proxies.observe(f); -// f(); -// } - -// componentWillUnmount() { -// if (this.disposer) { -// this.disposer(); -// } -// } - -// render() { -// let fields = Array.from(this.props.field._proxies.entries()).map(kv => { -// let key = this.keyMap.get(kv[0]); -// return ( -//
    -// ({key ? key.Name : kv[0]}): -// -//
    -// ); -// }); -// return ( -//
    -// Document ({this.props.field.Id}) -//
    -// {fields} -//
    -//
    -// ); -// } -// } - -// @observer -// class DebugViewer extends React.Component<{ fieldId: string }> { -// @observable -// private field?: Field; - -// @observable -// private error?: string; - -// constructor(props: { fieldId: string }) { -// super(props); -// this.update(); -// } - -// update() { -// Server.GetField(this.props.fieldId, action((field: Opt) => { -// this.field = field; -// if (!field) { -// this.error = `Field with id ${this.props.fieldId} not found`; -// } -// })); - -// } - -// render() { -// let content; -// if (this.field) { -// // content = this.field.ToJson(); -// if (this.field instanceof ListField) { -// content = (); -// } else if (this.field instanceof Document) { -// content = (); -// } else if (this.field instanceof BasicField) { -// content = (); -// } else if (this.field instanceof Key) { -// content = (); -// } else { -// content = (Unrecognized field type); -// } -// } else if (this.error) { -// content = Field {this.props.fieldId} not found ; -// } else { -// content = Field loading: {this.props.fieldId}; -// } -// return content; -// } -// } - -// @observer -// class Viewer extends React.Component { -// @observable -// private idToAdd: string = ''; - -// @observable -// private ids: string[] = []; - -// @action -// inputOnChange = (e: React.ChangeEvent) => { -// this.idToAdd = e.target.value; -// } - -// @action -// onKeyPress = (e: React.KeyboardEvent) => { -// if (e.key === "Enter") { -// this.ids.push(this.idToAdd); -// this.idToAdd = ""; -// } -// } - -// render() { -// return ( -// <> -// -//
    -// {this.ids.map(id => )} -//
    -// -// ); -// } -// } - -// ReactDOM.render(( -//
    -// -//
    ), -// document.getElementById('root') -// ); \ No newline at end of file +import { CurrentUserUtils } from '../server/authentication/models/current_user_utils'; +import { RouteStore } from '../server/RouteStore'; +import { emptyFunction } from '../Utils'; +import { Docs } from '../client/documents/Documents'; +import { SetupDrag } from '../client/util/DragManager'; +import { Transform } from '../client/util/Transform'; +import { UndoManager } from '../client/util/UndoManager'; +import { PresentationView } from '../client/views/PresentationView'; +import { CollectionDockingView } from '../client/views/collections/CollectionDockingView'; +import { ContextMenu } from '../client/views/ContextMenu'; +import { DocumentDecorations } from '../client/views/DocumentDecorations'; +import { InkingControl } from '../client/views/InkingControl'; +import { MainOverlayTextBox } from '../client/views/MainOverlayTextBox'; +import { DocumentView } from '../client/views/nodes/DocumentView'; +import { PreviewCursor } from '../client/views/PreviewCursor'; +import { SearchBox } from '../client/views/SearchBox'; +import { SelectionManager } from '../client/util/SelectionManager'; +import { Doc, Field, FieldResult } from '../new_fields/Doc'; +import { Cast } from '../new_fields/Types'; +import { DocServer } from '../client/DocServer'; +import { listSpec } from '../new_fields/Schema'; +import { Id } from '../new_fields/RefField'; +import { HistoryUtil } from '../client/util/History'; +import { List } from '../new_fields/List'; +import { URLField } from '../new_fields/URLField'; + +CurrentUserUtils; +RouteStore; +emptyFunction; +Docs; +SetupDrag; +Transform; +UndoManager; +PresentationView; +CollectionDockingView; +ContextMenu; +DocumentDecorations; +InkingControl; +MainOverlayTextBox; +DocumentView; +PreviewCursor; +SearchBox; +SelectionManager; +Doc; +Cast; +DocServer; +listSpec; +Id; +HistoryUtil; + +configure({ + enforceActions: "observed" +}); + +@observer +class ListViewer extends React.Component<{ field: List }>{ + @observable + expanded = false; + + render() { + let content; + if (this.expanded) { + content = ( +
    + {this.props.field.map((field, index) => )} +
    + ); + } else { + content = <>[...]; + } + return ( +
    + + {content} +
    + ); + } +} + +@observer +class DocumentViewer extends React.Component<{ field: Doc }> { + @observable + expanded = false; + render() { + let content; + if (this.expanded) { + const keys = Object.keys(this.props.field); + let fields = keys.map(key => { + return ( +
    + ({key}): + +
    + ); + }); + content = ( +
    + Document ({this.props.field[Id]}) +
    + {fields} +
    +
    + ); + } else { + content = <>[...] ({this.props.field[Id]}); + } + return ( +
    + + {content} +
    + ); + } +} + +@observer +class DebugViewer extends React.Component<{ field: FieldResult }> { + + render() { + let content; + const field = this.props.field; + if (field instanceof List) { + content = (); + } else if (field instanceof Doc) { + content = (); + } else if (typeof field === "string") { + content =

    "{field}"

    ; + } else if (typeof field === "number" || typeof field === "boolean") { + content =

    {field}

    ; + } else if (field instanceof URLField) { + content =

    {field.url.href}

    ; + } else { + content =

    Unrecognized field type

    ; + } + return content; + } +} + +@observer +class Viewer extends React.Component { + @observable + private idToAdd: string = ''; + + @observable + private fields: Field[] = []; + + @action + inputOnChange = (e: React.ChangeEvent) => { + this.idToAdd = e.target.value; + } + + @action + onKeyPress = (e: React.KeyboardEvent) => { + if (e.key === "Enter") { + DocServer.GetRefField(this.idToAdd).then(action((field: any) => { + if (field !== undefined) { + this.fields.push(field); + } + })); + this.idToAdd = ""; + } + } + + render() { + return ( + <> + +
    + {this.fields.map((field, index) => )} +
    + + ); + } +} + +ReactDOM.render(( +
    + +
    ), + document.getElementById('root') +); \ No newline at end of file -- cgit v1.2.3-70-g09d2 From af2346983eae1145167b70faf96a9aec0ca82427 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Mon, 20 May 2019 00:09:47 -0400 Subject: Fixed a bunch of demo bugs Moved Field Symbols to separate file Editing is mostly working in debug viewer --- deploy/debug/viewer.html | 1 + src/client/DocServer.ts | 3 +- src/client/northstar/dash-fields/HistogramField.ts | 7 +- src/client/northstar/dash-nodes/HistogramBox.tsx | 2 +- src/client/util/DocumentManager.ts | 2 +- src/client/util/SearchUtil.ts | 2 +- src/client/util/TooltipTextMenu.tsx | 2 +- src/client/util/type_decls.d | 128 +++++++-------------- src/client/views/EditableView.tsx | 10 +- src/client/views/MainView.tsx | 2 +- src/client/views/PresentationView.tsx | 2 +- src/client/views/SearchBox.tsx | 2 +- .../views/collections/CollectionBaseView.tsx | 2 +- .../views/collections/CollectionDockingView.tsx | 14 ++- src/client/views/collections/CollectionPDFView.tsx | 2 +- .../views/collections/CollectionSchemaView.tsx | 2 +- .../views/collections/CollectionTreeView.tsx | 2 +- .../views/collections/CollectionVideoView.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 2 +- .../views/collections/ParentDocumentSelector.tsx | 2 +- .../CollectionFreeFormLinksView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- .../collections/collectionFreeForm/MarqueeView.tsx | 3 - src/client/views/nodes/DocumentView.tsx | 4 +- src/client/views/nodes/KeyValueBox.tsx | 4 +- src/client/views/nodes/KeyValuePair.tsx | 4 +- src/client/views/nodes/LinkMenu.tsx | 2 +- src/debug/Viewer.tsx | 53 +++++++-- src/new_fields/CursorField.ts | 7 +- src/new_fields/DateField.ts | 7 +- src/new_fields/Doc.ts | 43 ++++--- src/new_fields/FieldSymbols.ts | 10 ++ src/new_fields/HtmlField.ts | 7 +- src/new_fields/IconField.ts | 7 +- src/new_fields/InkField.ts | 7 +- src/new_fields/List.ts | 16 ++- src/new_fields/ObjectField.ts | 7 +- src/new_fields/Proxy.ts | 9 +- src/new_fields/RefField.ts | 5 +- src/new_fields/RichTextField.ts | 7 +- src/new_fields/URLField.ts | 9 +- src/new_fields/util.ts | 7 +- 42 files changed, 240 insertions(+), 173 deletions(-) create mode 100644 src/new_fields/FieldSymbols.ts (limited to 'src/client/views/PresentationView.tsx') diff --git a/deploy/debug/viewer.html b/deploy/debug/viewer.html index 3785a6602..8c265ccb8 100644 --- a/deploy/debug/viewer.html +++ b/deploy/debug/viewer.html @@ -3,6 +3,7 @@ Document Debugger + diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index f1b50d5a0..cbcf751ee 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -3,7 +3,8 @@ import { MessageStore } from "./../server/Message"; import { Opt } from '../new_fields/Doc'; import { Utils, emptyFunction } from '../Utils'; import { SerializationHelper } from './util/SerializationHelper'; -import { RefField, HandleUpdate, Id } from '../new_fields/RefField'; +import { RefField } from '../new_fields/RefField'; +import { Id, HandleUpdate } from '../new_fields/FieldSymbols'; export namespace DocServer { const _cache: { [id: string]: RefField | Promise> } = {}; diff --git a/src/client/northstar/dash-fields/HistogramField.ts b/src/client/northstar/dash-fields/HistogramField.ts index 1ee2189b9..31040a474 100644 --- a/src/client/northstar/dash-fields/HistogramField.ts +++ b/src/client/northstar/dash-fields/HistogramField.ts @@ -3,10 +3,11 @@ import { custom, serializable } from "serializr"; import { ColumnAttributeModel } from "../../../client/northstar/core/attribute/AttributeModel"; import { AttributeTransformationModel } from "../../../client/northstar/core/attribute/AttributeTransformationModel"; import { HistogramOperation } from "../../../client/northstar/operations/HistogramOperation"; -import { ObjectField, Copy } from "../../../new_fields/ObjectField"; +import { ObjectField } from "../../../new_fields/ObjectField"; import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; import { OmitKeys } from "../../../Utils"; import { Deserializable } from "../../util/SerializationHelper"; +import { Copy, ToScriptString } from "../../../new_fields/FieldSymbols"; function serialize(field: HistogramField) { let obj = OmitKeys(field, ['Links', 'BrushLinks', 'Result', 'BrushColors', 'FilterModels', 'FilterOperand']).omit; @@ -55,4 +56,8 @@ export class HistogramField extends ObjectField { let z = this.HistoOp.Copy; return new HistogramField(HistogramOperation.Duplicate(this.HistoOp)); } + + [ToScriptString]() { + return "invalid"; + } } \ No newline at end of file diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx index eb1ad69b7..a9646ed31 100644 --- a/src/client/northstar/dash-nodes/HistogramBox.tsx +++ b/src/client/northstar/dash-nodes/HistogramBox.tsx @@ -19,7 +19,7 @@ import { HistogramLabelPrimitives } from "./HistogramLabelPrimitives"; import { StyleConstants } from "../utils/StyleContants"; import { Cast } from "../../../new_fields/Types"; import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc"; -import { Id } from "../../../new_fields/RefField"; +import { Id } from "../../../new_fields/FieldSymbols"; @observer diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index be448098a..a5e768dcf 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -5,10 +5,10 @@ import { FieldValue, Cast, NumCast, BoolCast } from '../../new_fields/Types'; import { listSpec } from '../../new_fields/Schema'; import { undoBatch } from './UndoManager'; import { CollectionDockingView } from '../views/collections/CollectionDockingView'; -import { Id } from '../../new_fields/RefField'; import { CollectionView } from '../views/collections/CollectionView'; import { CollectionPDFView } from '../views/collections/CollectionPDFView'; import { CollectionVideoView } from '../views/collections/CollectionVideoView'; +import { Id } from '../../new_fields/FieldSymbols'; export class DocumentManager { diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts index 4ccff0d1b..e8eb70837 100644 --- a/src/client/util/SearchUtil.ts +++ b/src/client/util/SearchUtil.ts @@ -1,7 +1,7 @@ import * as rp from 'request-promise'; import { DocServer } from '../DocServer'; import { Doc } from '../../new_fields/Doc'; -import { Id } from '../../new_fields/RefField'; +import { Id } from '../../new_fields/FieldSymbols'; export namespace SearchUtil { export function Search(query: string, returnDocs: true): Promise; diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 4d40d09b2..a1f80120f 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -22,12 +22,12 @@ import { throwStatement } from "babel-types"; import { View } from "@react-pdf/renderer"; import { DragManager } from "./DragManager"; import { Doc, Opt, Field } from "../../new_fields/Doc"; -import { Id } from "../../new_fields/RefField"; import { Utils } from "../northstar/utils/Utils"; import { DocServer } from "../DocServer"; import { CollectionFreeFormDocumentView } from "../views/nodes/CollectionFreeFormDocumentView"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import { DocumentManager } from "./DocumentManager"; +import { Id } from "../../new_fields/FieldSymbols"; const SVG = "http://www.w3.org/2000/svg"; diff --git a/src/client/util/type_decls.d b/src/client/util/type_decls.d index 47c3481b2..51114d0e2 100644 --- a/src/client/util/type_decls.d +++ b/src/client/util/type_decls.d @@ -119,104 +119,54 @@ interface URL { username: string; toJSON(): string; } +interface PromiseLike { + then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): PromiseLike; +} +interface Promise { + then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise; + catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise; +} -declare type FieldId = string; +declare const Update: unique symbol; +declare const Self: unique symbol; +declare const SelfProxy: unique symbol; +declare const HandleUpdate: unique symbol; +declare const Id: unique symbol; +declare const OnUpdate: unique symbol; +declare const Parent: unique symbol; +declare const Copy: unique symbol; +declare const ToScriptString: unique symbol; -declare abstract class Field { - Id: FieldId; - abstract ToScriptString(): string; - abstract TrySetValue(value: any): boolean; - abstract GetValue(): any; - abstract Copy(): Field; -} +declare abstract class RefField { + readonly [Id]: FieldId; + + constructor(id?: FieldId); + protected [HandleUpdate]?(diff: any): void; -declare abstract class BasicField extends Field { - constructor(data: T); - Data: T; - TrySetValue(value: any): boolean; - GetValue(): any; + abstract [ToScriptString](): string; } -declare class TextField extends BasicField{ - constructor(); - constructor(data: string); - ToScriptString(): string; - Copy(): Field; -} -declare class ImageField extends BasicField{ - constructor(); - constructor(data: URL); - ToScriptString(): string; - Copy(): Field; -} -declare class HtmlField extends BasicField{ - constructor(); - constructor(data: string); - ToScriptString(): string; - Copy(): Field; -} -declare class NumberField extends BasicField{ - constructor(); - constructor(data: number); - ToScriptString(): string; - Copy(): Field; -} -declare class WebField extends BasicField{ - constructor(); - constructor(data: URL); - ToScriptString(): string; - Copy(): Field; -} -declare class ListField extends BasicField{ - constructor(); - constructor(data: T[]); - ToScriptString(): string; - Copy(): Field; -} -declare class Key extends Field { - constructor(name:string); - Name: string; - TrySetValue(value: any): boolean; - GetValue(): any; - Copy(): Field; - ToScriptString(): string; -} -declare type FIELD_WAITING = null; -declare type Opt = T | undefined; -declare type FieldValue = Opt | FIELD_WAITING; -// @ts-ignore -declare class Document extends Field { - TrySetValue(value: any): boolean; - GetValue(): any; - Copy(): Field; - ToScriptString(): string; +declare abstract class ObjectField { + protected [OnUpdate](diff?: any): void; + private [Parent]?: RefField | ObjectField; + abstract [Copy](): ObjectField; - Width(): number; - Height(): number; - Scale(): number; - Title: string; + abstract [ToScriptString](): string; +} +declare type FieldId = string; - Get(key: Key): FieldValue; - GetAsync(key: Key, callback: (field: Field) => void): boolean; - GetOrCreateAsync(key: Key, ctor: { new(): T }, callback: (field: T) => void): void; - GetT(key: Key, ctor: { new(): T }): FieldValue; - GetOrCreate(key: Key, ctor: { new(): T }): T; - GetData(key: Key, ctor: { new(): U }, defaultVal: T): T; - GetHtml(key: Key, defaultVal: string): string; - GetNumber(key: Key, defaultVal: number): number; - GetText(key: Key, defaultVal: string): string; - GetList(key: Key, defaultVal: T[]): T[]; - Set(key: Key, field: Field | undefined): void; - SetData(key: Key, value: T, ctor: { new(): U }): void; - SetText(key: Key, value: string): void; - SetNumber(key: Key, value: number): void; - GetPrototype(): FieldValue; - GetAllPrototypes(): Document[]; - MakeDelegate(): Document; +declare type Field = number | string | boolean | ObjectField | RefField; + +declare type Opt = T | undefined; +declare class Doc extends RefField { + [key: string]: Field | undefined; + [ToScriptString](): string; } -declare const KeyStore: { - [name: string]: Key; +declare class ListImpl extends ObjectField { + [index: number]: T | (T extends RefField ? Promise : never); + [ToScriptString](): string; + [Copy](): ObjectField; } // @ts-ignore diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index 78143ccda..c946d68e1 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -22,7 +22,7 @@ export interface EditableProps { * The contents to render when not editing */ contents: any; - height: number; + height?: number; display?: string; oneLine?: boolean; } @@ -53,6 +53,12 @@ export class EditableView extends React.Component { } } + @action + onClick = (e: React.MouseEvent) => { + this.editing = true; + e.stopPropagation(); + } + render() { if (this.editing) { return this.editing = false)} @@ -60,7 +66,7 @@ export class EditableView extends React.Component { } else { return (
    this.editing = true)} > + onClick={this.onClick} > {this.props.contents}
    ); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 400562f12..9edbba997 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -29,7 +29,7 @@ import { FieldResult, Field, Doc, Opt } from '../../new_fields/Doc'; import { Cast, FieldValue, StrCast } from '../../new_fields/Types'; import { DocServer } from '../DocServer'; import { listSpec } from '../../new_fields/Schema'; -import { Id } from '../../new_fields/RefField'; +import { Id } from '../../new_fields/FieldSymbols'; import { HistoryUtil } from '../util/History'; import { CollectionBaseView } from './collections/CollectionBaseView'; diff --git a/src/client/views/PresentationView.tsx b/src/client/views/PresentationView.tsx index 9c37e9000..ce679aa0a 100644 --- a/src/client/views/PresentationView.tsx +++ b/src/client/views/PresentationView.tsx @@ -7,7 +7,7 @@ import { Utils } from "../../Utils"; import { Doc, DocListCast, DocListCastAsync } from "../../new_fields/Doc"; import { listSpec } from "../../new_fields/Schema"; import { Cast, NumCast, FieldValue, PromiseValue, StrCast } from "../../new_fields/Types"; -import { Id } from "../../new_fields/RefField"; +import { Id } from "../../new_fields/FieldSymbols"; import { List } from "../../new_fields/List"; import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; diff --git a/src/client/views/SearchBox.tsx b/src/client/views/SearchBox.tsx index 6e64e1af1..8efd8d266 100644 --- a/src/client/views/SearchBox.tsx +++ b/src/client/views/SearchBox.tsx @@ -16,7 +16,7 @@ import { isString } from 'util'; import { constant } from 'async'; import { DocServer } from '../DocServer'; import { Doc } from '../../new_fields/Doc'; -import { Id } from '../../new_fields/RefField'; +import { Id } from '../../new_fields/FieldSymbols'; import { DocumentManager } from '../util/DocumentManager'; import { SetupDrag } from '../util/DragManager'; import { Docs } from '../documents/Documents'; diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index 84ffbac36..5686ccfef 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -7,8 +7,8 @@ import { Cast, FieldValue, PromiseValue, NumCast } from '../../../new_fields/Typ import { Doc, FieldResult, Opt, DocListCast } from '../../../new_fields/Doc'; import { listSpec } from '../../../new_fields/Schema'; import { List } from '../../../new_fields/List'; -import { Id } from '../../../new_fields/RefField'; import { SelectionManager } from '../../util/SelectionManager'; +import { Id } from '../../../new_fields/FieldSymbols'; export enum CollectionViewType { Invalid, diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 483209f86..e904358a9 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -6,7 +6,7 @@ import * as ReactDOM from 'react-dom'; import Measure from "react-measure"; import * as GoldenLayout from "../../../client/goldenLayout"; import { Doc, Field, Opt, DocListCast } from "../../../new_fields/Doc"; -import { FieldId, Id } from "../../../new_fields/RefField"; +import { FieldId } from "../../../new_fields/RefField"; import { listSpec } from "../../../new_fields/Schema"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { emptyFunction, returnTrue, Utils } from "../../../Utils"; @@ -21,6 +21,7 @@ import React = require("react"); import { ParentDocSelector } from './ParentDocumentSelector'; import { DocumentManager } from '../../util/DocumentManager'; import { CollectionViewType } from './CollectionBaseView'; +import { Id } from '../../../new_fields/FieldSymbols'; @observer export class CollectionDockingView extends React.Component { @@ -75,7 +76,7 @@ export class CollectionDockingView extends React.Component { let retVal = false; if (this._goldenLayout.root.contentItems[0].isRow) { retVal = Array.from(this._goldenLayout.root.contentItems[0].contentItems).some((child: any) => { @@ -118,7 +119,7 @@ export class CollectionDockingView extends React.Component { let docs = Cast(this.props.Document.data, listSpec(Doc)); if (docs) { docs.push(document); @@ -155,7 +156,7 @@ export class CollectionDockingView extends React.Component { let docs = Cast(this.props.Document.data, listSpec(Doc)); if (docs) { docs.push(document); @@ -412,10 +413,11 @@ export class DockedFrameRenderer extends React.Component { @observable private _panelWidth = 0; @observable private _panelHeight = 0; @observable private _document: Opt; - _stack: any; + get _stack(): any { + return (this.props as any).glContainer.parent.parent; + } constructor(props: any) { super(props); - this._stack = (this.props as any).glContainer.parent.parent; DocServer.GetRefField(this.props.documentId).then(action((f: Opt) => this._document = f as Doc)); } diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx index a6614da21..5e51437a4 100644 --- a/src/client/views/collections/CollectionPDFView.tsx +++ b/src/client/views/collections/CollectionPDFView.tsx @@ -8,7 +8,7 @@ import { FieldView, FieldViewProps } from "../nodes/FieldView"; import { CollectionRenderProps, CollectionBaseView, CollectionViewType } from "./CollectionBaseView"; import { emptyFunction } from "../../../Utils"; import { NumCast } from "../../../new_fields/Types"; -import { Id } from "../../../new_fields/RefField"; +import { Id } from "../../../new_fields/FieldSymbols"; @observer diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index f15da41ff..b25b48339 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -23,7 +23,7 @@ import { Opt, Field, Doc, DocListCastAsync, DocListCast } from "../../../new_fie import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; import { List } from "../../../new_fields/List"; -import { Id } from "../../../new_fields/RefField"; +import { Id } from "../../../new_fields/FieldSymbols"; import { Gateway } from "../../northstar/manager/Gateway"; import { Docs } from "../../documents/Documents"; import { ContextMenu } from "../ContextMenu"; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 72fa69cb1..8ad495762 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -11,7 +11,7 @@ import React = require("react"); import { Document, listSpec } from '../../../new_fields/Schema'; import { Cast, StrCast, BoolCast, FieldValue } from '../../../new_fields/Types'; import { Doc, DocListCast } from '../../../new_fields/Doc'; -import { Id } from '../../../new_fields/RefField'; +import { Id } from '../../../new_fields/FieldSymbols'; import { ContextMenu } from '../ContextMenu'; import { undoBatch } from '../../util/UndoManager'; import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils'; diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx index 9ab959f3c..27f23a1a8 100644 --- a/src/client/views/collections/CollectionVideoView.tsx +++ b/src/client/views/collections/CollectionVideoView.tsx @@ -7,7 +7,7 @@ import "./CollectionVideoView.scss"; import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; import { FieldView, FieldViewProps } from "../nodes/FieldView"; import { emptyFunction } from "../../../Utils"; -import { Id } from "../../../new_fields/RefField"; +import { Id } from "../../../new_fields/FieldSymbols"; import { VideoBox } from "../nodes/VideoBox"; import { NumCast } from "../../../new_fields/Types"; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 59c763be8..bfdef8e8c 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -2,7 +2,7 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faProjectDiagram, faSquare, faTh, faTree, faSignature, faThList } from '@fortawesome/free-solid-svg-icons'; import { observer } from "mobx-react"; import * as React from 'react'; -import { Id } from '../../../new_fields/RefField'; +import { Id } from '../../../new_fields/FieldSymbols'; import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils'; import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from "../ContextMenu"; diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx index 52f7914f3..4d07c31a7 100644 --- a/src/client/views/collections/ParentDocumentSelector.tsx +++ b/src/client/views/collections/ParentDocumentSelector.tsx @@ -3,7 +3,7 @@ import './ParentDocumentSelector.scss'; import { Doc } from "../../../new_fields/Doc"; import { observer } from "mobx-react"; import { observable, action, runInAction } from "mobx"; -import { Id } from "../../../new_fields/RefField"; +import { Id } from "../../../new_fields/FieldSymbols"; import { SearchUtil } from "../../util/SearchUtil"; import { CollectionDockingView } from "./CollectionDockingView"; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index d5ce4e1e7..e1ff715d1 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -11,7 +11,7 @@ import { Doc, DocListCastAsync, DocListCast } from "../../../../new_fields/Doc"; import { Cast, FieldValue, NumCast, StrCast } from "../../../../new_fields/Types"; import { listSpec } from "../../../../new_fields/Schema"; import { List } from "../../../../new_fields/List"; -import { Id } from "../../../../new_fields/RefField"; +import { Id } from "../../../../new_fields/FieldSymbols"; @observer export class CollectionFreeFormLinksView extends React.Component { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 9cb8443f4..ee6f4821f 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -22,9 +22,9 @@ import { createSchema, makeInterface, listSpec } from "../../../../new_fields/Sc import { Doc, WidthSym, HeightSym } from "../../../../new_fields/Doc"; import { FieldValue, Cast, NumCast, BoolCast } from "../../../../new_fields/Types"; import { pageSchema } from "../../nodes/ImageBox"; -import { Id } from "../../../../new_fields/RefField"; import { InkField, StrokeData } from "../../../../new_fields/InkField"; import { HistoryUtil } from "../../../util/History"; +import { Id } from "../../../../new_fields/FieldSymbols"; export const panZoomSchema = createSchema({ panX: "number", diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 4587c2227..2029b91e5 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -17,9 +17,6 @@ import { InkField, StrokeData } from "../../../../new_fields/InkField"; import { List } from "../../../../new_fields/List"; import { ImageField } from "../../../../new_fields/URLField"; import { Template, Templates } from "../../Templates"; -import { Gateway } from "../../../northstar/manager/Gateway"; -import { DocServer } from "../../../DocServer"; -import { Id } from "../../../../new_fields/RefField"; interface MarqueeViewProps { getContainerTransform: () => Transform; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 5f218fd1f..87c88f57c 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -4,8 +4,7 @@ import { action, computed, IReactionDisposer, reaction } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc"; import { List } from "../../../new_fields/List"; -import { Copy, ObjectField } from "../../../new_fields/ObjectField"; -import { Id } from "../../../new_fields/RefField"; +import { ObjectField } from "../../../new_fields/ObjectField"; import { createSchema, makeInterface } from "../../../new_fields/Schema"; import { BoolCast, Cast, FieldValue, StrCast } from "../../../new_fields/Types"; import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; @@ -29,6 +28,7 @@ import { Template } from "./../Templates"; import { DocumentContentsView } from "./DocumentContentsView"; import "./DocumentView.scss"; import React = require("react"); +import { Id, Copy } from '../../../new_fields/FieldSymbols'; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? library.add(faTrash); diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 86437a6c1..8cb576786 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -8,7 +8,7 @@ import "./KeyValueBox.scss"; import { KeyValuePair } from "./KeyValuePair"; import React = require("react"); import { NumCast, Cast, FieldValue } from "../../../new_fields/Types"; -import { Doc, IsField } from "../../../new_fields/Doc"; +import { Doc, Field } from "../../../new_fields/Doc"; @observer export class KeyValueBox extends React.Component { @@ -41,7 +41,7 @@ export class KeyValueBox extends React.Component { let res = script.run(); if (!res.success) return; const field = res.result; - if (IsField(field)) { + if (Field.IsField(field)) { realDoc[this._keyInput] = field; } this._keyInput = ""; diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index 4f7919f50..7a88985c0 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -9,7 +9,7 @@ import { FieldView, FieldViewProps } from './FieldView'; import "./KeyValueBox.scss"; import "./KeyValuePair.scss"; import React = require("react"); -import { Doc, Opt, IsField } from '../../../new_fields/Doc'; +import { Doc, Opt, Field } from '../../../new_fields/Doc'; import { FieldValue } from '../../../new_fields/Types'; // Represents one row in a key value plane @@ -75,7 +75,7 @@ export class KeyValuePair extends React.Component { let res = script.run(); if (!res.success) return false; const field = res.result; - if (IsField(field)) { + if (Field.IsField(field)) { props.Document[props.fieldKey] = field; return true; } diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index 4cf798249..3f09d6214 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -7,7 +7,7 @@ import './LinkMenu.scss'; import React = require("react"); import { Doc, DocListCast } from "../../../new_fields/Doc"; import { Cast, FieldValue, StrCast } from "../../../new_fields/Types"; -import { Id } from "../../../new_fields/RefField"; +import { Id } from "../../../new_fields/FieldSymbols"; interface Props { docView: DocumentView; diff --git a/src/debug/Viewer.tsx b/src/debug/Viewer.tsx index 720e1640a..4314e2132 100644 --- a/src/debug/Viewer.tsx +++ b/src/debug/Viewer.tsx @@ -3,11 +3,27 @@ import "normalize.css"; import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { observer } from 'mobx-react'; -import { Doc, Field, FieldResult } from '../new_fields/Doc'; +import { Doc, Field, FieldResult, Opt } from '../new_fields/Doc'; import { DocServer } from '../client/DocServer'; -import { Id } from '../new_fields/RefField'; +import { Id } from '../new_fields/FieldSymbols'; import { List } from '../new_fields/List'; import { URLField } from '../new_fields/URLField'; +import { EditableView } from '../client/views/EditableView'; +import { CompileScript } from '../client/util/Scripting'; + +function applyToDoc(doc: { [index: string]: FieldResult }, key: string, scriptString: string): boolean; +function applyToDoc(doc: { [index: number]: FieldResult }, key: number, scriptString: string): boolean; +function applyToDoc(doc: any, key: string | number, scriptString: string): boolean { + let script = CompileScript(scriptString, { addReturn: true, params: { this: doc instanceof Doc ? Doc.name : List.name } }); + if (!script.compiled) { + return false; + } + const res = script.run({ this: doc }); + if (!res.success) return false; + if (!Field.IsField(res.result)) return false; + doc[key] = res.result; + return true; +} configure({ enforceActions: "observed" @@ -18,12 +34,18 @@ class ListViewer extends React.Component<{ field: List }>{ @observable expanded = false; + @action + onClick = (e: React.MouseEvent) => { + this.expanded = !this.expanded; + e.stopPropagation(); + } + render() { let content; if (this.expanded) { content = (
    - {this.props.field.map((field, index) => )} + {this.props.field.map((field, index) => applyToDoc(this.props.field, index, value)} />)}
    ); } else { @@ -31,7 +53,7 @@ class ListViewer extends React.Component<{ field: List }>{ } return (
    - + {content}
    ); @@ -42,6 +64,13 @@ class ListViewer extends React.Component<{ field: List }>{ class DocumentViewer extends React.Component<{ field: Doc }> { @observable expanded = false; + + @action + onClick = (e: React.MouseEvent) => { + this.expanded = !this.expanded; + e.stopPropagation(); + } + render() { let content; if (this.expanded) { @@ -50,7 +79,7 @@ class DocumentViewer extends React.Component<{ field: Doc }> { return (
    ({key}): - + applyToDoc(this.props.field, key, value)}>
    ); }); @@ -67,7 +96,7 @@ class DocumentViewer extends React.Component<{ field: Doc }> { } return (
    - + {content}
    ); @@ -75,7 +104,7 @@ class DocumentViewer extends React.Component<{ field: Doc }> { } @observer -class DebugViewer extends React.Component<{ field: FieldResult }> { +class DebugViewer extends React.Component<{ field: FieldResult, setValue(value: string): boolean }> { render() { let content; @@ -90,10 +119,14 @@ class DebugViewer extends React.Component<{ field: FieldResult }> { content =

    {field}

    ; } else if (field instanceof URLField) { content =

    {field.url.href}

    ; + } else if (field instanceof Promise) { + return

    Field loading

    ; } else { - content =

    Unrecognized field type

    ; + return

    Unrecognized field type

    ; } - return content; + + return Field.toScriptString(field)} SetValue={this.props.setValue} + contents={content}>; } } @@ -129,7 +162,7 @@ class Viewer extends React.Component { onChange={this.inputOnChange} onKeyDown={this.onKeyPress} />
    - {this.fields.map((field, index) => )} + {this.fields.map((field, index) => false}>)}
    ); diff --git a/src/new_fields/CursorField.ts b/src/new_fields/CursorField.ts index fc144222c..1be1ec3e0 100644 --- a/src/new_fields/CursorField.ts +++ b/src/new_fields/CursorField.ts @@ -1,7 +1,8 @@ -import { ObjectField, Copy, OnUpdate } from "./ObjectField"; +import { ObjectField } from "./ObjectField"; import { observable } from "mobx"; import { Deserializable } from "../client/util/SerializationHelper"; import { serializable, createSimpleSchema, object } from "serializr"; +import { OnUpdate, ToScriptString, Copy } from "./FieldSymbols"; export type CursorPosition = { x: number, @@ -52,4 +53,8 @@ export default class CursorField extends ObjectField { [Copy]() { return new CursorField(this.data); } + + [ToScriptString]() { + return "invalid"; + } } \ No newline at end of file diff --git a/src/new_fields/DateField.ts b/src/new_fields/DateField.ts index c0a79f267..fc8abb9d9 100644 --- a/src/new_fields/DateField.ts +++ b/src/new_fields/DateField.ts @@ -1,6 +1,7 @@ import { Deserializable } from "../client/util/SerializationHelper"; import { serializable, date } from "serializr"; -import { ObjectField, Copy } from "./ObjectField"; +import { ObjectField } from "./ObjectField"; +import { Copy, ToScriptString } from "./FieldSymbols"; @Deserializable("date") export class DateField extends ObjectField { @@ -15,4 +16,8 @@ export class DateField extends ObjectField { [Copy]() { return new DateField(this.date); } + + [ToScriptString]() { + return `new DateField(new Date(${this.date.toISOString()}))`; + } } diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 02dd34cb4..f4514c33e 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -5,27 +5,33 @@ import { DocServer } from "../client/DocServer"; import { setter, getter, getField, updateFunction, deleteProperty } from "./util"; import { Cast, ToConstructor, PromiseValue, FieldValue, NumCast } from "./Types"; import { listSpec } from "./Schema"; -import { ObjectField, Parent, OnUpdate } from "./ObjectField"; -import { RefField, FieldId, Id, HandleUpdate } from "./RefField"; - -export function IsField(field: any): field is Field { - return (typeof field === "string") - || (typeof field === "number") - || (typeof field === "boolean") - || (field instanceof ObjectField) - || (field instanceof RefField); +import { ObjectField } from "./ObjectField"; +import { RefField, FieldId } from "./RefField"; +import { ToScriptString, SelfProxy, Parent, OnUpdate, Self, HandleUpdate, Update, Id } from "./FieldSymbols"; + +export namespace Field { + export function toScriptString(field: Field): string { + if (typeof field === "string") { + return `"${field}"`; + } else if (typeof field === "number" || typeof field === "boolean") { + return String(field); + } else { + return field[ToScriptString](); + } + } + export function IsField(field: any): field is Field { + return (typeof field === "string") + || (typeof field === "number") + || (typeof field === "boolean") + || (field instanceof ObjectField) + || (field instanceof RefField); + } } export type Field = number | string | boolean | ObjectField | RefField; export type Opt = T | undefined; export type FieldWaiting = T extends undefined ? never : Promise; export type FieldResult = Opt | FieldWaiting>; -export const Update = Symbol("Update"); -export const Self = Symbol("Self"); -export const SelfProxy = Symbol("SelfProxy"); -export const WidthSym = Symbol("Width"); -export const HeightSym = Symbol("Height"); - /** * Cast any field to either a List of Docs or undefined if the given field isn't a List of Docs. * If a default value is given, that will be returned instead of undefined. @@ -43,6 +49,9 @@ export function DocListCast(field: FieldResult): Doc[] { return Cast(field, listSpec(Doc), []).filter(d => d instanceof Doc) as Doc[]; } +export const WidthSym = Symbol("Width"); +export const HeightSym = Symbol("Height"); + @Deserializable("doc").withFields(["id"]) export class Doc extends RefField { constructor(id?: FieldId, forceSave?: boolean) { @@ -102,6 +111,10 @@ export class Doc extends RefField { public [WidthSym] = () => NumCast(this[SelfProxy].width); // bcz: is this the right way to access width/height? it didn't work with : this.width public [HeightSym] = () => NumCast(this[SelfProxy].height); + [ToScriptString]() { + return "invalid"; + } + public [HandleUpdate](diff: any) { console.log(diff); const set = diff.$set; diff --git a/src/new_fields/FieldSymbols.ts b/src/new_fields/FieldSymbols.ts new file mode 100644 index 000000000..a436dcf2b --- /dev/null +++ b/src/new_fields/FieldSymbols.ts @@ -0,0 +1,10 @@ + +export const Update = Symbol("Update"); +export const Self = Symbol("Self"); +export const SelfProxy = Symbol("SelfProxy"); +export const HandleUpdate = Symbol("HandleUpdate"); +export const Id = Symbol("Id"); +export const OnUpdate = Symbol("OnUpdate"); +export const Parent = Symbol("Parent"); +export const Copy = Symbol("Copy"); +export const ToScriptString = Symbol("Copy"); \ No newline at end of file diff --git a/src/new_fields/HtmlField.ts b/src/new_fields/HtmlField.ts index d998746bb..f952acff9 100644 --- a/src/new_fields/HtmlField.ts +++ b/src/new_fields/HtmlField.ts @@ -1,6 +1,7 @@ import { Deserializable } from "../client/util/SerializationHelper"; import { serializable, primitive } from "serializr"; -import { ObjectField, Copy } from "./ObjectField"; +import { ObjectField } from "./ObjectField"; +import { Copy, ToScriptString } from "./FieldSymbols"; @Deserializable("html") export class HtmlField extends ObjectField { @@ -15,4 +16,8 @@ export class HtmlField extends ObjectField { [Copy]() { return new HtmlField(this.html); } + + [ToScriptString]() { + return "invalid"; + } } diff --git a/src/new_fields/IconField.ts b/src/new_fields/IconField.ts index 1a928389d..62b2cd254 100644 --- a/src/new_fields/IconField.ts +++ b/src/new_fields/IconField.ts @@ -1,6 +1,7 @@ import { Deserializable } from "../client/util/SerializationHelper"; import { serializable, primitive } from "serializr"; -import { ObjectField, Copy } from "./ObjectField"; +import { ObjectField } from "./ObjectField"; +import { Copy, ToScriptString } from "./FieldSymbols"; @Deserializable("icon") export class IconField extends ObjectField { @@ -15,4 +16,8 @@ export class IconField extends ObjectField { [Copy]() { return new IconField(this.icon); } + + [ToScriptString]() { + return "invalid"; + } } diff --git a/src/new_fields/InkField.ts b/src/new_fields/InkField.ts index 2d75f8a19..4e3b7abe0 100644 --- a/src/new_fields/InkField.ts +++ b/src/new_fields/InkField.ts @@ -1,6 +1,7 @@ import { Deserializable } from "../client/util/SerializationHelper"; import { serializable, custom, createSimpleSchema, list, object, map } from "serializr"; -import { ObjectField, Copy } from "./ObjectField"; +import { ObjectField } from "./ObjectField"; +import { Copy, ToScriptString } from "./FieldSymbols"; import { deepCopy } from "../Utils"; export enum InkTool { @@ -40,4 +41,8 @@ export class InkField extends ObjectField { [Copy]() { return new InkField(deepCopy(this.inkData)); } + + [ToScriptString]() { + return "invalid"; + } } diff --git a/src/new_fields/List.ts b/src/new_fields/List.ts index 70e36f911..f1e4c4721 100644 --- a/src/new_fields/List.ts +++ b/src/new_fields/List.ts @@ -1,11 +1,12 @@ import { Deserializable, autoObject } from "../client/util/SerializationHelper"; -import { Field, Update, Self, FieldResult, SelfProxy } from "./Doc"; +import { Field } from "./Doc"; import { setter, getter, deleteProperty, updateFunction } from "./util"; import { serializable, alias, list } from "serializr"; import { observable, action } from "mobx"; -import { ObjectField, OnUpdate, Copy, Parent } from "./ObjectField"; +import { ObjectField } from "./ObjectField"; import { RefField } from "./RefField"; import { ProxyField } from "./Proxy"; +import { Self, Update, Parent, OnUpdate, SelfProxy, ToScriptString, Copy } from "./FieldSymbols"; const listHandlers: any = { /// Mutator methods @@ -225,7 +226,7 @@ type StoredType = T extends RefField ? ProxyField : T; @Deserializable("list") class ListImpl extends ObjectField { - constructor(fields: T[] = []) { + constructor(fields?: T[]) { super(); const list = new Proxy(this, { set: setter, @@ -244,7 +245,9 @@ class ListImpl extends ObjectField { defineProperty: () => { throw new Error("Currently properties can't be defined on documents using Object.defineProperty"); }, }); this[SelfProxy] = list; - (list as any).push(...fields); + if (fields) { + (list as any).push(...fields); + } return list; } @@ -284,6 +287,11 @@ class ListImpl extends ObjectField { private [Self] = this; private [SelfProxy]: any; + + [ToScriptString]() { + return "invalid"; + // return `new List([${(this as any).map((field => Field.toScriptString(field))}])`; + } } export type List = ListImpl & (T | (T extends RefField ? Promise : never))[]; export const List: { new (fields?: T[]): List } = ListImpl as any; \ No newline at end of file diff --git a/src/new_fields/ObjectField.ts b/src/new_fields/ObjectField.ts index 51768c6db..5f4a6f8fb 100644 --- a/src/new_fields/ObjectField.ts +++ b/src/new_fields/ObjectField.ts @@ -1,14 +1,13 @@ import { Doc } from "./Doc"; import { RefField } from "./RefField"; - -export const OnUpdate = Symbol("OnUpdate"); -export const Parent = Symbol("Parent"); -export const Copy = Symbol("Copy"); +import { OnUpdate, Parent, Copy, ToScriptString } from "./FieldSymbols"; export abstract class ObjectField { protected [OnUpdate](diff?: any) { } private [Parent]?: RefField | ObjectField; abstract [Copy](): ObjectField; + + abstract [ToScriptString](): string; } export namespace ObjectField { diff --git a/src/new_fields/Proxy.ts b/src/new_fields/Proxy.ts index fd99ae1c0..130ec066e 100644 --- a/src/new_fields/Proxy.ts +++ b/src/new_fields/Proxy.ts @@ -3,8 +3,9 @@ import { FieldWaiting } from "./Doc"; import { primitive, serializable } from "serializr"; import { observable, action } from "mobx"; import { DocServer } from "../client/DocServer"; -import { RefField, Id } from "./RefField"; -import { ObjectField, Copy } from "./ObjectField"; +import { RefField } from "./RefField"; +import { ObjectField } from "./ObjectField"; +import { Id, Copy, ToScriptString } from "./FieldSymbols"; @Deserializable("proxy") export class ProxyField extends ObjectField { @@ -26,6 +27,10 @@ export class ProxyField extends ObjectField { return new ProxyField(this.fieldId); } + [ToScriptString]() { + return "invalid"; + } + @serializable(primitive()) readonly fieldId: string = ""; diff --git a/src/new_fields/RefField.ts b/src/new_fields/RefField.ts index 202c65f21..75ce4287f 100644 --- a/src/new_fields/RefField.ts +++ b/src/new_fields/RefField.ts @@ -1,9 +1,8 @@ import { serializable, primitive, alias } from "serializr"; import { Utils } from "../Utils"; +import { Id, HandleUpdate, ToScriptString } from "./FieldSymbols"; export type FieldId = string; -export const HandleUpdate = Symbol("HandleUpdate"); -export const Id = Symbol("Id"); export abstract class RefField { @serializable(alias("id", primitive())) private __id: FieldId; @@ -15,4 +14,6 @@ export abstract class RefField { } protected [HandleUpdate]?(diff: any): void; + + abstract [ToScriptString](): string; } diff --git a/src/new_fields/RichTextField.ts b/src/new_fields/RichTextField.ts index eb30e76de..89d077a47 100644 --- a/src/new_fields/RichTextField.ts +++ b/src/new_fields/RichTextField.ts @@ -1,6 +1,7 @@ -import { ObjectField, Copy } from "./ObjectField"; +import { ObjectField } from "./ObjectField"; import { serializable } from "serializr"; import { Deserializable } from "../client/util/SerializationHelper"; +import { Copy, ToScriptString } from "./FieldSymbols"; @Deserializable("RichTextField") export class RichTextField extends ObjectField { @@ -15,4 +16,8 @@ export class RichTextField extends ObjectField { [Copy]() { return new RichTextField(this.Data); } + + [ToScriptString]() { + return "invalid"; + } } \ No newline at end of file diff --git a/src/new_fields/URLField.ts b/src/new_fields/URLField.ts index d00a95a16..a6f8f1cc5 100644 --- a/src/new_fields/URLField.ts +++ b/src/new_fields/URLField.ts @@ -1,6 +1,7 @@ import { Deserializable } from "../client/util/SerializationHelper"; import { serializable, custom } from "serializr"; -import { ObjectField, Copy } from "./ObjectField"; +import { ObjectField } from "./ObjectField"; +import { ToScriptString, Copy } from "./FieldSymbols"; function url() { return custom( @@ -13,7 +14,7 @@ function url() { ); } -export class URLField extends ObjectField { +export abstract class URLField extends ObjectField { @serializable(url()) readonly url: URL; @@ -22,6 +23,10 @@ export class URLField extends ObjectField { this.url = url; } + [ToScriptString]() { + return `new ${this.constructor.name}(new URL(${this.url.href}))`; + } + [Copy](): this { return new (this.constructor as any)(this.url); } diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts index d94994a07..65a37a0d1 100644 --- a/src/new_fields/util.ts +++ b/src/new_fields/util.ts @@ -1,11 +1,12 @@ import { UndoManager } from "../client/util/UndoManager"; -import { Update, Doc, Field } from "./Doc"; +import { Doc, Field } from "./Doc"; import { SerializationHelper } from "../client/util/SerializationHelper"; import { ProxyField } from "./Proxy"; import { FieldValue } from "./Types"; -import { RefField, Id } from "./RefField"; -import { ObjectField, Parent, OnUpdate } from "./ObjectField"; +import { RefField } from "./RefField"; +import { ObjectField } from "./ObjectField"; import { action } from "mobx"; +import { Parent, OnUpdate, Update, Id } from "./FieldSymbols"; export const setter = action(function (target: any, prop: string | symbol | number, value: any, receiver: any): boolean { if (SerializationHelper.IsSerializing()) { -- cgit v1.2.3-70-g09d2 From e32ccd138ff70f2225e5c2c13e0003e02593ef09 Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 20 May 2019 14:14:19 -0400 Subject: minimal change to brushing presentation view. --- src/client/views/PresentationView.tsx | 14 ++++++++++++-- .../views/collections/collectionFreeForm/MarqueeView.tsx | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) (limited to 'src/client/views/PresentationView.tsx') diff --git a/src/client/views/PresentationView.tsx b/src/client/views/PresentationView.tsx index ce679aa0a..9d5798ff1 100644 --- a/src/client/views/PresentationView.tsx +++ b/src/client/views/PresentationView.tsx @@ -6,7 +6,7 @@ import { DocumentManager } from "../util/DocumentManager"; import { Utils } from "../../Utils"; import { Doc, DocListCast, DocListCastAsync } from "../../new_fields/Doc"; import { listSpec } from "../../new_fields/Schema"; -import { Cast, NumCast, FieldValue, PromiseValue, StrCast } from "../../new_fields/Types"; +import { Cast, NumCast, FieldValue, PromiseValue, StrCast, BoolCast } from "../../new_fields/Types"; import { Id } from "../../new_fields/FieldSymbols"; import { List } from "../../new_fields/List"; import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; @@ -27,6 +27,7 @@ interface PresListProps extends PresViewProps { */ class PresentationViewList extends React.Component { + /** * Renders a single child document. It will just append a list element. * @param document The document to render. @@ -42,8 +43,17 @@ class PresentationViewList extends React.Component { //this doc is selected className += " presentationView-selected"; } + let onEnter = (e: React.PointerEvent) => { document.libraryBrush = true; } + let onLeave = (e: React.PointerEvent) => { document.libraryBrush = false; } return ( -
    { this.props.gotoDocument(index); e.stopPropagation(); }}> +
    { this.props.gotoDocument(index); e.stopPropagation(); }}> {`${index + 1}. ${title}`} diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 6ea230aab..97e610e13 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -135,7 +135,7 @@ export class MarqueeView extends React.Component document.addEventListener("pointerup", this.onPointerUp, true); document.addEventListener("keydown", this.marqueeCommand, true); if (e.altKey) { - e.stopPropagation(); + //e.stopPropagation(); // bcz: removed so that you can alt-click on button in a collection to switch link following behaviors. e.preventDefault(); } // bcz: do we need this? it kills the context menu on the main collection if !altKey -- cgit v1.2.3-70-g09d2