From f38d2de249ddf2cad533e2f92197738835688a73 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 10 May 2019 11:54:33 -0400 Subject: fixed dragging search results. --- src/new_fields/Doc.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/new_fields') diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index f844dad6e..2ab145fa3 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -92,8 +92,8 @@ export class Doc extends RefField { private [Self] = this; private [SelfProxy]: any; - public [WidthSym] = () => NumCast(this.__fields.width); // bcz: is this the right way to access width/height? it didn't work with : this.width - public [HeightSym] = () => NumCast(this.__fields.height); + 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); public [HandleUpdate](diff: any) { console.log(diff); -- 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/new_fields') 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 9047fbe7dc69572bd8178bc29616ed2c855933ce Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 13 May 2019 00:37:03 -0400 Subject: fixed putting link info on protoype. making images and videos show up reasonably in schema and kvp. dragging of docs from schema. --- .../views/collections/CollectionSchemaView.scss | 4 +++ .../views/collections/CollectionSchemaView.tsx | 32 +++++++++++----------- .../views/collections/CollectionVideoView.scss | 2 +- src/client/views/nodes/FieldView.tsx | 10 ++++--- src/client/views/nodes/IconBox.scss | 4 +-- src/client/views/nodes/KeyValuePair.scss | 8 ++++++ src/new_fields/Doc.ts | 8 +++--- 7 files changed, 41 insertions(+), 27 deletions(-) (limited to 'src/new_fields') diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss index cfdb3ab22..b9ed99155 100644 --- a/src/client/views/collections/CollectionSchemaView.scss +++ b/src/client/views/collections/CollectionSchemaView.scss @@ -14,6 +14,10 @@ .collectionSchemaView-cellContents { height: $MAX_ROW_HEIGHT; + img { + width:auto; + max-height: $MAX_ROW_HEIGHT; + } } .collectionSchemaView-previewRegion { diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 18319dc77..4984e26d1 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -24,6 +24,7 @@ import { Cast, FieldValue, NumCast } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; import { List } from "../../../new_fields/List"; import { Id } from "../../../new_fields/RefField"; +import { isUndefined } from "typescript-collections/dist/lib/util"; // bcz: need to add drag and drop of rows and columns. This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657 @@ -77,23 +78,22 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { PanelHeight: returnZero, PanelWidth: returnZero, }; - let contents = ( - - ); + let fieldContentView = ; let reference = React.createRef(); - let onItemDown = SetupDrag(reference, () => props.Document, this.props.moveDocument); + let onItemDown = (e: React.PointerEvent) => + (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 }); if (!res.success) return false; - const field = res.result; - doc[props.fieldKey] = field; + doc[props.fieldKey] = res.result; return true; }; return (
{ let field = props.Document[props.fieldKey]; @@ -224,10 +224,11 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { this.previewScript = e.currentTarget.value; } + @computed get previewDocument(): Doc | undefined { - const children = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []); + const children = DocListCast(this.props.Document[this.props.fieldKey]); const selected = children.length > this._selectedIndex ? FieldValue(children[this._selectedIndex]) : undefined; - return selected ? (this.previewScript ? 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; } @@ -253,8 +254,8 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { get previewPanel() { // let doc = CompileScript(this.previewScript, { this: selected }, true)(); const previewDoc = this.previewDocument; - return !previewDoc || !this.previewRegionWidth ? (null) : ( -
+ return (
+ {!previewDoc || !this.previewRegionWidth ? (null) : (
doc) { whenActiveChanged={this.props.whenActiveChanged} bringToFront={emptyFunction} /> -
- -
- ); +
)} + +
); } get documentKeysCheckList() { diff --git a/src/client/views/collections/CollectionVideoView.scss b/src/client/views/collections/CollectionVideoView.scss index ed56ad268..db8b84832 100644 --- a/src/client/views/collections/CollectionVideoView.scss +++ b/src/client/views/collections/CollectionVideoView.scss @@ -2,7 +2,7 @@ .collectionVideoView-cont{ width: 100%; height: 100%; - position: absolute; + position: inherit; top: 0; left:0; diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 8bdf34181..34b6c5e70 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -7,7 +7,7 @@ import { VideoBox } from "./VideoBox"; import { AudioBox } from "./AudioBox"; import { DocumentContentsView } from "./DocumentContentsView"; import { Transform } from "../../util/Transform"; -import { returnFalse, emptyFunction } from "../../../Utils"; +import { returnFalse, emptyFunction, returnOne } from "../../../Utils"; import { CollectionView } from "../collections/CollectionView"; import { CollectionPDFView } from "../collections/CollectionPDFView"; import { CollectionVideoView } from "../collections/CollectionVideoView"; @@ -18,6 +18,7 @@ import { ImageField, VideoField, AudioField } from "../../../new_fields/URLField import { IconField } from "../../../new_fields/IconField"; import { RichTextField } from "../../../new_fields/RichTextField"; import { DateField } from "../../../new_fields/DateField"; +import { NumCast } from "../../../new_fields/Types"; // @@ -82,14 +83,15 @@ export class FieldView extends React.Component { return

{field.date.toLocaleString()}

; } else if (field instanceof Doc) { + let returnHundred = () => 100; return ( 1} - PanelWidth={() => 100} - PanelHeight={() => 100} + ContentScaling={returnOne} + PanelWidth={returnHundred} + PanelHeight={returnHundred} isTopMost={true} //TODO Why is this top most? selectOnLoad={false} focus={emptyFunction} diff --git a/src/client/views/nodes/IconBox.scss b/src/client/views/nodes/IconBox.scss index 85bbdeb59..f6d9860a3 100644 --- a/src/client/views/nodes/IconBox.scss +++ b/src/client/views/nodes/IconBox.scss @@ -1,7 +1,7 @@ @import "../globalCssVariables"; .iconBox-container { - position: absolute; + position: inherit; left:0; top:0; height: 100%; @@ -14,7 +14,7 @@ background: white; } .iconBox-label { - position: inherit; + position: absolute; width:max-content; font-size: 14px; margin-top: 3px; diff --git a/src/client/views/nodes/KeyValuePair.scss b/src/client/views/nodes/KeyValuePair.scss index ff6885965..4f305dc91 100644 --- a/src/client/views/nodes/KeyValuePair.scss +++ b/src/client/views/nodes/KeyValuePair.scss @@ -26,4 +26,12 @@ .keyValuePair-td-value { display:inline-block; overflow: scroll; + img { + max-height: 36px; + width: auto; + } + .videobox-cont{ + width: auto; + max-height: 36px; + } } \ No newline at end of file diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 46ccb3e90..42d04e93f 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -198,11 +198,11 @@ export namespace Doc { let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 }); //let linkDoc = new Doc; linkDoc.proto!.title = "-link name-"; - linkDoc.linkDescription = ""; - linkDoc.linkTags = "Default"; + linkDoc.proto!.linkDescription = ""; + linkDoc.proto!.linkTags = "Default"; - linkDoc.linkedTo = target; - linkDoc.linkedFrom = source; + linkDoc.proto!.linkedTo = target; + linkDoc.proto!.linkedFrom = source; let linkedFrom = Cast(target.linkedFromDocs, listSpec(Doc)); if (!linkedFrom) { -- cgit v1.2.3-70-g09d2 From 2767d6b439dd9e90543f955c68e521e03c690af0 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Mon, 13 May 2019 02:09:08 -0400 Subject: Added "find aliases" and "see collections that contain me" --- src/client/util/SearchUtil.ts | 25 +++++++++++++++++ .../views/collections/ParentDocumentSelector.tsx | 31 ++++++++++++++++++++-- src/client/views/nodes/DocumentView.tsx | 27 ++++++++++++------- src/new_fields/Doc.ts | 4 +-- 4 files changed, 73 insertions(+), 14 deletions(-) create mode 100644 src/client/util/SearchUtil.ts (limited to 'src/new_fields') diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts new file mode 100644 index 000000000..4ccff0d1b --- /dev/null +++ b/src/client/util/SearchUtil.ts @@ -0,0 +1,25 @@ +import * as rp from 'request-promise'; +import { DocServer } from '../DocServer'; +import { Doc } from '../../new_fields/Doc'; +import { Id } from '../../new_fields/RefField'; + +export namespace SearchUtil { + export function Search(query: string, returnDocs: true): Promise; + export function Search(query: string, returnDocs: false): Promise; + export async function Search(query: string, returnDocs: boolean) { + const ids = JSON.parse(await rp.get(DocServer.prepend("/search"), { + qs: { query } + })); + if (!returnDocs) { + return ids; + } + const docMap = await DocServer.GetRefFields(ids); + return ids.map((id: string) => docMap[id]).filter((doc: any) => doc instanceof Doc); + } + + export async function GetAliasesOfDocument(doc: Doc): Promise { + const proto = await Doc.GetT(doc, "proto", Doc, true); + const protoId = (proto || doc)[Id]; + return Search(`{!join from=id to=proto_i}id:${protoId}`, true); + } +} \ No newline at end of file diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx index 1fdb9d4d9..dd1516da7 100644 --- a/src/client/views/collections/ParentDocumentSelector.tsx +++ b/src/client/views/collections/ParentDocumentSelector.tsx @@ -2,7 +2,34 @@ import * as React from "react"; import './ParentDocumentSelector.scss'; import { Doc } from "../../../new_fields/Doc"; import { observer } from "mobx-react"; -import { observable, action } from "mobx"; +import { observable, action, runInAction } from "mobx"; +import { Id } from "../../../new_fields/RefField"; +import { SearchUtil } from "../../util/SearchUtil"; +import { CollectionDockingView } from "./CollectionDockingView"; + +@observer +export class SelectorContextMenu extends React.Component<{ Document: Doc }> { + @observable private _docs: Doc[] = []; + + constructor(props: { Document: Doc }) { + super(props); + + this.fetchDocuments(); + } + + async fetchDocuments() { + const docs = await SearchUtil.Search(`data_l:${this.props.Document[Id]}`, true); + runInAction(() => this._docs = docs); + } + + render() { + return ( + <> + {this._docs.map(doc =>

CollectionDockingView.Instance.AddRightSplit(doc)}>{doc.title}

)} + + ); + } +} @observer export class ParentDocSelector extends React.Component<{ Document: Doc }> { @@ -23,7 +50,7 @@ export class ParentDocSelector extends React.Component<{ Document: Doc }> { if (this.hover) { flyout = (
-

Hello world

+
); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 90f67db7c..edc2158f0 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -26,6 +26,7 @@ import { CurrentUserUtils } from "../../../server/authentication/models/current_ import { DocServer } from "../../DocServer"; import { Id } from "../../../new_fields/RefField"; import { PresentationView } from "../PresentationView"; +import { SearchUtil } from "../../util/SearchUtil"; const linkSchema = createSchema({ title: "string", @@ -287,16 +288,22 @@ export class DocumentView extends DocComponent(Docu } e.preventDefault(); - ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked }); - ContextMenu.Instance.addItem({ description: this.props.Document.isButton ? "Remove Button" : "Make Button", event: this.makeButton }); - ContextMenu.Instance.addItem({ description: "Fields", event: this.fieldsClicked }); - ContextMenu.Instance.addItem({ description: "Center", event: () => this.props.focus(this.props.Document) }); - ContextMenu.Instance.addItem({ description: "Open Right", event: () => CollectionDockingView.Instance.AddRightSplit(this.props.Document) }); - ContextMenu.Instance.addItem({ description: "Copy URL", event: () => Utils.CopyText(DocServer.prepend("/doc/" + this.props.Document[Id])) }); - ContextMenu.Instance.addItem({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document[Id]) }); - //ContextMenu.Instance.addItem({ description: "Docking", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Docking) }) - ContextMenu.Instance.addItem({ description: "Pin to Presentation", event: () => PresentationView.Instance.PinDoc(this.props.Document) }); - ContextMenu.Instance.addItem({ description: "Delete", event: this.deleteClicked }); + const cm = ContextMenu.Instance; + cm.addItem({ description: "Full Screen", event: this.fullScreenClicked }); + cm.addItem({ description: this.props.Document.isButton ? "Remove Button" : "Make Button", event: this.makeButton }); + cm.addItem({ description: "Fields", event: this.fieldsClicked }); + cm.addItem({ description: "Center", event: () => this.props.focus(this.props.Document) }); + cm.addItem({ description: "Open Right", event: () => CollectionDockingView.Instance.AddRightSplit(this.props.Document) }); + cm.addItem({ + description: "Find aliases", event: async () => { + const aliases = await SearchUtil.GetAliasesOfDocument(this.props.Document); + CollectionDockingView.Instance.AddRightSplit(Docs.SchemaDocument(aliases, {})); + } + }); + cm.addItem({ description: "Copy URL", event: () => Utils.CopyText(DocServer.prepend("/doc/" + this.props.Document[Id])) }); + cm.addItem({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document[Id]) }); + cm.addItem({ description: "Pin to Presentation", event: () => PresentationView.Instance.PinDoc(this.props.Document) }); + cm.addItem({ description: "Delete", event: this.deleteClicked }); if (!this.topMost) { // DocumentViews should stop propagation of this event e.stopPropagation(); diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 46ccb3e90..c08049a39 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -130,8 +130,8 @@ export namespace Doc { const self = doc[Self]; return getField(self, key, ignoreProto); } - export function GetT(doc: Doc, key: string, ctor: ToConstructor, ignoreProto: boolean = false): T | null | undefined { - return Cast(Get(doc, key, ignoreProto), ctor) as T | null | undefined; + export function GetT(doc: Doc, key: string, ctor: ToConstructor, ignoreProto: boolean = false): FieldResult { + return Cast(Get(doc, key, ignoreProto), ctor) as FieldResult; } export async function SetOnPrototype(doc: Doc, key: string, value: Field) { const proto = doc.proto; -- cgit v1.2.3-70-g09d2 From 48b0f98ea2519d861a0eecee541dc0986a2c2f12 Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 13 May 2019 10:56:12 -0400 Subject: small fixes --- src/client/util/DocumentManager.ts | 10 +++++++--- src/client/views/InkingControl.tsx | 9 +++------ .../views/collections/CollectionDockingView.tsx | 22 +++++++++++++++------- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 6 ++---- src/new_fields/Doc.ts | 12 +++++++++++- 6 files changed, 39 insertions(+), 22 deletions(-) (limited to 'src/new_fields') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 9a7a94228..47bcb153f 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -5,6 +5,7 @@ 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'; export class DocumentManager { @@ -26,13 +27,13 @@ export class DocumentManager { // this.DocumentViews = new Array(); } - public getDocumentView(toFind: Doc): DocumentView | null { + public getDocumentViewById(id: string): DocumentView | null { let toReturn: DocumentView | null = null; //gets document view that is in a freeform canvas collection DocumentManager.Instance.DocumentViews.map(view => { - if (view.props.Document === toFind) { + if (view.props.Document[Id] === id) { toReturn = view; return; } @@ -40,7 +41,7 @@ export class DocumentManager { if (!toReturn) { DocumentManager.Instance.DocumentViews.map(view => { let doc = view.props.Document.proto; - if (doc && Object.is(doc, toFind)) { + if (doc && doc.Id === id) { toReturn = view; } }); @@ -48,6 +49,9 @@ export class DocumentManager { return toReturn; } + + public getDocumentView(toFind: Doc): DocumentView | null { return this.getDocumentViewById(toFind[Id]); } + public getDocumentViews(toFind: Doc): DocumentView[] { let toReturn: DocumentView[] = []; diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index 4b3dbd4e0..17d4a1e49 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -35,12 +35,9 @@ export class InkingControl extends React.Component { @action switchColor = (color: ColorResult): void => { this._selectedColor = color.hex; - if (SelectionManager.SelectedDocuments().length === 1) { - var sdoc = SelectionManager.SelectedDocuments()[0]; - if (sdoc.props.ContainingCollectionView) { - Doc.SetOnPrototype(sdoc.props.Document, "backgroundColor", color.hex); - } - } + SelectionManager.SelectedDocuments().forEach(doc => + doc.props.ContainingCollectionView && Doc.SetOnPrototype(doc.props.Document, "backgroundColor", color.hex) + ); } @action diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 8739a213f..0aa067972 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -19,6 +19,7 @@ import "./CollectionDockingView.scss"; import { SubCollectionViewProps } from "./CollectionSubView"; import React = require("react"); import { ParentDocSelector } from './ParentDocumentSelector'; +import { DocumentManager } from '../../util/DocumentManager'; @observer export class CollectionDockingView extends React.Component { @@ -73,26 +74,33 @@ export class CollectionDockingView extends React.Component { + retVal = Array.from(this._goldenLayout.root.contentItems[0].contentItems).some((child: any) => { if (child.contentItems.length === 1 && child.contentItems[0].config.component === "DocumentFrameRenderer" && - child.contentItems[0].config.props.documentId == document[Id]) { + Doc.AreProtosEqual(DocumentManager.Instance.getDocumentViewById(child.contentItems[0].config.props.documentId)!.Document, document)) { child.contentItems[0].remove(); this.layoutChanged(document); - this.stateChanged(); + return true; } else - child.contentItems.map((tab: any, j: number) => { - if (tab.config.component === "DocumentFrameRenderer" && tab.config.props.documentId === document[Id]) { + Array.from(child.contentItems).filter((tab: any) => 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(); child.config.activeItemIndex = Math.max(child.contentItems.length - 1, 0); let docs = Cast(this.props.Document.data, listSpec(Doc)); docs && docs.indexOf(document) !== -1 && docs.splice(docs.indexOf(document), 1); - this.stateChanged(); + return true; } + return false; }); + return false; }) } + if (retVal) { + this.stateChanged(); + } + return retVal; } @action diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 080c484f4..1c29ebaae 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -79,7 +79,7 @@ export class MarqueeView extends React.Component y += 40 * this.props.getTransform().Scale; }) })(); - } else if (e.key === "t" && e.ctrlKey) { + } else if (e.key === "b" && e.ctrlKey) { //heuristically converts pasted text into a table. // assumes each entry is separated by a tab // skips all rows until it gets to a row with more than one entry diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 817a23ce8..2a041bf70 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -189,10 +189,8 @@ export class CollectionFreeFormDocumentView extends DocComponent { maxDoc.isMinimized = false; - if (!dataDocs || dataDocs.indexOf(maxDoc) == -1) { - CollectionDockingView.Instance.AddRightSplit(maxDoc); - } else { - CollectionDockingView.Instance.CloseRightSplit(maxDoc); + if (!CollectionDockingView.Instance.CloseRightSplit(maxDoc)) { + CollectionDockingView.Instance.AddRightSplit(Doc.MakeCopy(maxDoc)); } }); } diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 625ba0d6a..a8c9d28e1 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -134,7 +134,8 @@ export namespace Doc { return Cast(Get(doc, key, ignoreProto), ctor) as FieldResult; } export async function SetOnPrototype(doc: Doc, key: string, value: Field) { - const proto = doc.proto; + const proto = Object.getOwnPropertyNames(doc).indexOf("isProto") == -1 ? doc.proto : doc; + if (proto) { proto[key] = value; } @@ -160,6 +161,15 @@ export namespace Doc { return doc; } + // compare whether documents or their protos match + export function AreProtosEqual(doc: Doc, other: Doc) { + let r = (doc[Id] === other[Id]); + let r2 = (doc.proto && doc.proto.Id === other[Id]); + let r3 = (other.proto && other.proto.Id === doc[Id]); + let r4 = (doc.proto && other.proto && doc.proto[Id] === other.proto[Id]); + return r || r2 || r3 || r4 ? true : false; + } + export function MakeAlias(doc: Doc) { const alias = new Doc; -- cgit v1.2.3-70-g09d2 From 3be42131bc08024f06e0daec8d09e45bf3f1ddab Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 13 May 2019 16:21:28 -0400 Subject: a bunch of fixes to schemas, marquees, and an experimental feature to set a document's data with linking UI --- src/client/util/DragManager.ts | 6 ++++-- src/client/views/DocumentDecorations.tsx | 2 ++ src/client/views/collections/CollectionDockingView.tsx | 7 ++----- src/client/views/collections/CollectionSchemaView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 ++-- .../views/collections/collectionFreeForm/MarqueeView.tsx | 8 +++----- src/client/views/nodes/DocumentContentsView.tsx | 9 +++++++-- src/client/views/nodes/DocumentView.tsx | 13 +++++++++++-- src/client/views/nodes/ImageBox.tsx | 3 ++- src/client/views/nodes/KeyValueBox.tsx | 6 +++--- src/client/views/nodes/LinkMenu.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 4 ++-- src/new_fields/Doc.ts | 14 +++++++++----- 13 files changed, 49 insertions(+), 31 deletions(-) (limited to 'src/new_fields') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 2da0d5b51..26da34e67 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -105,7 +105,8 @@ export namespace DragManager { constructor( readonly x: number, readonly y: number, - readonly data: { [id: string]: any } + readonly data: { [id: string]: any }, + readonly mods: string ) { } } @@ -334,7 +335,8 @@ export namespace DragManager { detail: { x: e.x, y: e.y, - data: dragData + data: dragData, + mods: e.ctrlKey ? "Control" : "" } }) ); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 4786b4de6..5aa3d804d 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -239,6 +239,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> this._removeIcon = snapped; } } + @undoBatch @action onMinimizeUp = (e: PointerEvent): void => { e.stopPropagation(); @@ -270,6 +271,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> runInAction(() => this._minimizedX = this._minimizedY = 0); } + @undoBatch @action createIcon = (selected: DocumentView[], layoutString: string): Doc => { let doc = selected[0].props.Document; let iconDoc = Docs.IconDocument(layoutString); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 0aa067972..a755e0f91 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -5,7 +5,7 @@ import { observer } from "mobx-react"; import * as ReactDOM from 'react-dom'; import Measure from "react-measure"; import * as GoldenLayout from "../../../client/goldenLayout"; -import { Doc, Field, Opt } from "../../../new_fields/Doc"; +import { Doc, Field, Opt, DocListCast } from "../../../new_fields/Doc"; import { FieldId, Id } from "../../../new_fields/RefField"; import { listSpec } from "../../../new_fields/Schema"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; @@ -313,10 +313,7 @@ export class CollectionDockingView extends React.Component [doc.linkedFromDocs, doc.LinkedToDocs, doc.title], () => { - const lf = Cast(doc.linkedFromDocs, listSpec(Doc), []); - const lt = Cast(doc.linkedToDocs, listSpec(Doc), []); - let count = (lf ? lf.length : 0) + (lt ? lt.length : 0); - counter.innerHTML = count; + counter.innerHTML = DocListCast(doc.linkedFromDocs).length + DocListCast(doc.linkedToDocs).length; tab.titleElement[0].textContent = doc.title; }, { fireImmediately: true }); tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 4984e26d1..9ecccc559 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -254,7 +254,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { get previewPanel() { // let doc = CompileScript(this.previewScript, { this: selected }, true)(); const previewDoc = this.previewDocument; - return (
+ return (
{!previewDoc || !this.previewRegionWidth ? (null) : (
); } render() { - return this.overlayView; + return this.props.Document.overlayLayout ? this.overlayView : (null); } } @@ -350,7 +350,7 @@ class CollectionFreeFormBackgroundView extends React.Component); } render() { - return this.backgroundView; + return this.props.Document.backgroundLayout ? this.backgroundView : (null); } } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 1c29ebaae..24dea200e 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -104,10 +104,7 @@ export class MarqueeView extends React.Component continue; } let doc = new Doc(); - columns.forEach((col, i) => { - console.log(values[i] + " " + Number(values[i]).toString()); - doc[columns[i]] = (values.length > i ? ((values[i].indexOf(Number(values[i]).toString()) !== -1) ? Number(values[i]) : values[i]) : undefined); - }); + 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; } @@ -207,7 +204,7 @@ export class MarqueeView extends React.Component @undoBatch @action marqueeCommand = async (e: KeyboardEvent) => { - if (this._commandExecuted) { + if (this._commandExecuted || (e as any).propagationIsStopped) { return; } if (e.key === "Backspace" || e.key === "Delete" || e.key === "d") { @@ -224,6 +221,7 @@ export class MarqueeView extends React.Component if (e.key === "c" || e.key === "r" || e.key === "s" || e.key === "e" || e.key === "p") { this._commandExecuted = true; e.stopPropagation(); + (e as any).propagationIsStopped = true; let bounds = this.Bounds; let selected = this.marqueeSelect().map(d => { if (e.key === "s") { diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index f404b7bc6..3ddf8eb00 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -43,9 +43,14 @@ const ObserverJsxParser: typeof JsxParser = ObserverJsxParser1 as any; export class DocumentContentsView extends React.Component boolean, select: (ctrl: boolean) => void, - layoutKey: string + layoutKey: string, }> { - @computed get layout(): string { return Cast(this.props.Document[this.props.layoutKey], "string", this.props.layoutKey === "layout" ? "

Error loading layout data

" : ""); } + @computed get layout(): string { + return StrCast(this.props.Document[this.props.layoutKey], + this.props.Document.data ? + "" : + KeyValueBox.LayoutString(this.props.Document.proto ? "proto" : "")); + } CreateBindings(): JsxBindings { return { props: OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit }; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index edc2158f0..b7ad9249a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -243,8 +243,17 @@ export class DocumentView extends DocComponent(Docu const protoDest = destDoc.proto; const protoSrc = sourceDoc.proto; - Doc.MakeLink(protoSrc ? protoSrc : sourceDoc, protoDest ? protoDest : destDoc); - de.data.droppedDocuments.push(destDoc); + if (de.mods == "Control") { + let src = protoSrc ? protoSrc : sourceDoc; + let dst = protoDest ? protoDest : destDoc; + dst.data = src; + dst.nativeWidth = src.nativeWidth; + dst.nativeHeight = src.nativeHeight; + } + else { + Doc.MakeLink(protoSrc ? protoSrc : sourceDoc, protoDest ? protoDest : destDoc); + de.data.droppedDocuments.push(destDoc); + } e.stopPropagation(); } } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index f9659a4b2..05742f8d1 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -42,8 +42,9 @@ export class ImageBox extends DocComponent(ImageD onLoad = (target: any) => { var h = this._imgRef.current!.naturalHeight; var w = this._imgRef.current!.naturalWidth; + console.log("title: " + this.Document.title); if (this._photoIndex === 0) { - this.Document.nativeHeight = FieldValue(this.Document.nativeWidth, 0) * h / w; + Doc.SetOnPrototype(this.Document, "nativeHeight", FieldValue(this.Document.nativeWidth, 0) * h / w); this.Document.height = FieldValue(this.Document.width, 0) * h / w; } } diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 876a3c173..c9d665ceb 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -18,7 +18,7 @@ export class KeyValueBox extends React.Component { @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 } constructor(props: FieldViewProps) { super(props); @@ -28,7 +28,7 @@ export class KeyValueBox extends React.Component { onEnterKey = (e: React.KeyboardEvent): void => { if (e.key === 'Enter') { if (this._keyInput && this._valueInput) { - let doc = FieldValue(Cast(this.props.Document.data, Doc)); + let doc = this.fieldDocToLayout; if (!doc) { return; } @@ -60,7 +60,7 @@ export class KeyValueBox extends React.Component { } createTable = () => { - let doc = FieldValue(Cast(this.props.Document.data, Doc)); + let doc = this.fieldDocToLayout; if (!doc) { return Loading...; } diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index 7bf13d5f9..5dabfc30d 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -32,7 +32,7 @@ export class LinkMenu extends React.Component { render() { //get list of links from document let linkFrom = DocListCast(this.props.docView.props.Document.linkedFromDocs); - let linkTo = DocListCast(this.props.docView.props.Document.linkedToDoc); + let linkTo = DocListCast(this.props.docView.props.Document.linkedToDocs); if (this._editingLink === undefined) { return (
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index cb27b3f1b..ff8737192 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -1,5 +1,5 @@ import * as htmlToImage from "html-to-image"; -import { action, computed, IReactionDisposer, observable, reaction, Reaction, trace } from 'mobx'; +import { action, computed, IReactionDisposer, observable, reaction, Reaction, trace, runInAction } from 'mobx'; import { observer } from "mobx-react"; import 'react-image-lightbox/style.css'; import Measure from "react-measure"; @@ -223,7 +223,7 @@ export class PDFBox extends DocComponent(PdfDocumen document.addEventListener("pointerup", this.onPointerUp); } if (this.props.isSelected() && e.buttons === 2) { - this._alt = true; + runInAction(() => this._alt = true); document.removeEventListener("pointerup", this.onPointerUp); document.addEventListener("pointerup", this.onPointerUp); } diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index a8c9d28e1..4c837fcbd 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -173,11 +173,15 @@ export namespace Doc { export function MakeAlias(doc: Doc) { const alias = new Doc; - PromiseValue(Cast(doc.proto, Doc)).then(proto => { - if (proto) { - alias.proto = proto; - } - }); + if (!doc.proto) { + alias.proto = doc; + } else { + PromiseValue(Cast(doc.proto, Doc)).then(proto => { + if (proto) { + alias.proto = proto; + } + }); + } return alias; } -- 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/new_fields') 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 1ae0370e6a116404085f6864c8b644fcde80f460 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Tue, 14 May 2019 19:59:23 -0400 Subject: Added History module and ability to go back and forward when following links --- src/client/util/History.ts | 122 +++++++++++++++++++++ src/client/views/Main.tsx | 19 +--- .../views/collections/CollectionDockingView.tsx | 2 - .../collectionFreeForm/CollectionFreeFormView.tsx | 36 +++++- src/new_fields/Doc.ts | 7 +- 5 files changed, 159 insertions(+), 27 deletions(-) create mode 100644 src/client/util/History.ts (limited to 'src/new_fields') diff --git a/src/client/util/History.ts b/src/client/util/History.ts new file mode 100644 index 000000000..92d2b2b44 --- /dev/null +++ b/src/client/util/History.ts @@ -0,0 +1,122 @@ +import { Doc, Opt, Field } from "../../new_fields/Doc"; +import { DocServer } from "../DocServer"; +import { Main } from "../views/Main"; +import { RouteStore } from "../../server/RouteStore"; + +export namespace HistoryUtil { + export interface DocInitializerList { + [key: string]: string | number; + } + + export interface DocUrl { + type: "doc"; + docId: string; + initializers: { + [docId: string]: DocInitializerList; + }; + } + + export type ParsedUrl = DocUrl; + + // const handlers: ((state: ParsedUrl | null) => void)[] = []; + function onHistory(e: PopStateEvent) { + if (window.location.pathname !== RouteStore.home) { + const url = e.state as ParsedUrl || parseUrl(window.location.pathname); + if (url) { + switch (url.type) { + case "doc": + onDocUrl(url); + break; + } + } + } + // for (const handler of handlers) { + // handler(e.state); + // } + } + + export function pushState(state: ParsedUrl) { + history.pushState(state, "", createUrl(state)); + } + + export function replaceState(state: ParsedUrl) { + history.replaceState(state, "", createUrl(state)); + } + + function copyState(state: ParsedUrl): ParsedUrl { + return JSON.parse(JSON.stringify(state)); + } + + export function getState(): ParsedUrl { + return copyState(history.state); + } + + // export function addHandler(handler: (state: ParsedUrl | null) => void) { + // handlers.push(handler); + // } + + // export function removeHandler(handler: (state: ParsedUrl | null) => void) { + // const index = handlers.indexOf(handler); + // if (index !== -1) { + // handlers.splice(index, 1); + // } + // } + + export function parseUrl(pathname: string): ParsedUrl | undefined { + let pathnameSplit = pathname.split("/"); + if (pathnameSplit.length !== 2) { + return undefined; + } + const type = pathnameSplit[0]; + const data = pathnameSplit[1]; + + if (type === "doc") { + const s = data.split("?"); + if (s.length < 1 || s.length > 2) { + return undefined; + } + const docId = s[0]; + const initializers = s.length === 2 ? JSON.parse(decodeURIComponent(s[1])) : {}; + return { + type: "doc", + docId, + initializers + }; + } + + return undefined; + } + + export function createUrl(params: ParsedUrl): string { + let baseUrl = DocServer.prepend(`/${params.type}`); + switch (params.type) { + case "doc": + const initializers = encodeURIComponent(JSON.stringify(params.initializers)); + const id = params.docId; + let url = baseUrl + `/${id}`; + if (Object.keys(params.initializers).length) { + url += `?${initializers}`; + } + return url; + } + return ""; + } + + export async function initDoc(id: string, initializer: DocInitializerList) { + const doc = await DocServer.GetRefField(id); + if (!(doc instanceof Doc)) { + return; + } + Doc.assign(doc, initializer); + } + + async function onDocUrl(url: DocUrl) { + const field = await DocServer.GetRefField(url.docId); + await Promise.all(Object.keys(url.initializers).map(id => initDoc(id, url.initializers[id]))); + if (field instanceof Doc) { + Main.Instance.openWorkspace(field, true); + } + } + + window.onpopstate = onHistory; +} diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 66205f8ca..55252ab1d 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -38,6 +38,8 @@ 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 Main extends React.Component { @@ -95,21 +97,6 @@ export class Main extends React.Component { // } } - componentDidMount() { window.onpopstate = this.onHistory; } - - componentWillUnmount() { window.onpopstate = null; } - - onHistory = () => { - if (window.location.pathname !== RouteStore.home) { - let pathname = window.location.pathname.split("/"); - DocServer.GetRefField(pathname[pathname.length - 1]).then(action((field: Opt) => { - if (field instanceof Doc) { - this.openWorkspace(field, true); - } - })); - } - } - initEventListeners = () => { // window.addEventListener("pointermove", (e) => this.reportLocation(e)) window.addEventListener("drop", (e) => e.preventDefault(), false); // drop event handler @@ -165,7 +152,7 @@ export class Main extends React.Component { openWorkspace = async (doc: Doc, fromHistory = false) => { CurrentUserUtils.MainDocId = doc[Id]; this.mainContainer = doc; - fromHistory || window.history.pushState(null, StrCast(doc.title), "/doc/" + doc[Id]); + 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 () => { diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 6651a834d..58f1e33a1 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -190,7 +190,6 @@ export class CollectionDockingView extends React.Component void = () => { - console.log("Docking mount"); if (this._containerRef.current) { this.reactionDisposer = reaction( () => StrCast(this.props.Document.dockingConfig), @@ -207,7 +206,6 @@ export class CollectionDockingView extends React.Component void = () => { - console.log("Docking unmount"); try { this._goldenLayout.unbind('itemDropped', this.itemDropped); this._goldenLayout.unbind('tabCreated', this.tabCreated); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index c36c708db..e60bb2fb2 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -24,6 +24,7 @@ import { FieldValue, Cast, NumCast } 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"; export const panZoomSchema = createSchema({ panX: "number", @@ -219,6 +220,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @action setPan(panX: number, panY: number) { + this.panDisposer && clearTimeout(this.panDisposer); + this.props.Document.panTransformType = "None"; var scale = this.getLocalTransform().inverse().Scale; const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX)); const newPanY = Math.min((1 - 1 / scale) * this.nativeHeight, Math.max(0, panY)); @@ -245,16 +248,37 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { doc.zIndex = docs.length + 1; } + panDisposer?: NodeJS.Timeout; focusDocument = (doc: Doc) => { + const panX = this.Document.panX; + const panY = this.Document.panY; + const id = this.Document[Id]; + const state = HistoryUtil.getState(); + // TODO This technically isn't correct if type !== "doc", as + // currently nothing is done, but we should probably push a new state + if (state.type === "doc" && panX !== undefined && panY !== undefined) { + const init = state.initializers[id]; + if (!init) { + state.initializers[id] = { + panX, panY + }; + HistoryUtil.pushState(state); + } else if (init.panX !== panX || init.panY !== panY) { + init.panX = panX; + init.panY = panY; + HistoryUtil.pushState(state); + } + } SelectionManager.DeselectAll(); + const newPanX = NumCast(doc.x) + NumCast(doc.width) / 2; + const newPanY = NumCast(doc.y) + NumCast(doc.height) / 2; + const newState = HistoryUtil.getState(); + newState.initializers[id] = { panX: newPanX, panY: newPanY }; + HistoryUtil.pushState(newState); + this.setPan(newPanX, newPanY); this.props.Document.panTransformType = "Ease"; - this.setPan( - NumCast(doc.x) + NumCast(doc.width) / 2, - NumCast(doc.y) + NumCast(doc.height) / 2); this.props.focus(this.props.Document); - if (this.props.Document.panTransformType === "Ease") { - setTimeout(() => this.props.Document.panTransformType = "None", 2000); // wait 3 seconds, then reset to false - } + this.panDisposer = setTimeout(() => this.props.Document.panTransformType = "None", 2000); // wait 3 seconds, then reset to false } diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 89901490d..d6043ef7a 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -159,9 +159,10 @@ export namespace Doc { for (const key in fields) { if (fields.hasOwnProperty(key)) { const value = fields[key]; - if (value !== undefined) { - doc[key] = value; - } + // Do we want to filter out undefineds? + // if (value !== undefined) { + doc[key] = value; + // } } } return doc; -- cgit v1.2.3-70-g09d2 From dae85deb62168cbfae4557aa8632896592d71cf9 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 15 May 2019 09:10:41 -0400 Subject: set hover highlight for search results --- src/client/views/SearchItem.tsx | 12 +++++++++++- src/client/views/collections/CollectionTreeView.tsx | 2 +- src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 4 +++- src/new_fields/Doc.ts | 2 +- 4 files changed, 16 insertions(+), 4 deletions(-) (limited to 'src/new_fields') diff --git a/src/client/views/SearchItem.tsx b/src/client/views/SearchItem.tsx index 0da0bdae8..01c7316d6 100644 --- a/src/client/views/SearchItem.tsx +++ b/src/client/views/SearchItem.tsx @@ -40,6 +40,14 @@ export class SearchItem extends React.Component { faCaretUp; return ; } + onPointerEnter = (e: React.PointerEvent) => { + this.props.doc.libraryBrush = true; + Doc.SetOnPrototype(this.props.doc, "protoBrush", true); + } + onPointerLeave = (e: React.PointerEvent) => { + this.props.doc.libraryBrush = false; + Doc.SetOnPrototype(this.props.doc, "protoBrush", false); + } collectionRef = React.createRef(); startDocDrag = () => { @@ -53,7 +61,9 @@ export class SearchItem extends React.Component { } render() { return ( -
    +
    title: {this.props.doc.title}
    {/*
    Type: {this.props.doc.layout}
    */} {/*
    {SearchItem.DocumentIcon(this.layout)}
    */} diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index d22418b2c..70c09d97c 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -130,7 +130,7 @@ class TreeView extends React.Component {
    ); return (
    {editableView(StrCast(this.props.document.title))} {openRight} diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index fc1dc2b1e..925945b17 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -247,7 +247,9 @@ export class CollectionFreeFormDocumentView extends DocComponent; } export async function SetOnPrototype(doc: Doc, key: string, value: Field) { - const proto = Object.getOwnPropertyNames(doc).indexOf("isProto") == -1 ? doc.proto : doc; + const proto = Object.getOwnPropertyNames(doc).indexOf("isPrototype") == -1 ? doc.proto : doc; if (proto) { proto[key] = value; -- cgit v1.2.3-70-g09d2 From d79d721baa25947dfbbd17eda173835d9ae93fa6 Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 15 May 2019 15:05:57 -0400 Subject: several smaller fixes. --- src/client/views/DocumentDecorations.tsx | 30 +++++++------ .../views/collections/ParentDocumentSelector.tsx | 2 +- .../collections/collectionFreeForm/MarqueeView.tsx | 17 ++----- .../views/nodes/CollectionFreeFormDocumentView.tsx | 52 ++++++++++++---------- src/client/views/nodes/DocumentView.tsx | 8 ++-- src/client/views/nodes/FormattedTextBox.tsx | 2 +- src/client/views/nodes/IconBox.scss | 4 +- src/client/views/nodes/IconBox.tsx | 27 ++++++++--- src/new_fields/List.ts | 10 +++++ 9 files changed, 87 insertions(+), 65 deletions(-) (limited to 'src/new_fields') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 627bedde2..8d85f7e2c 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -252,18 +252,20 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> if (!this._removeIcon) { if (selectedDocs.length === 1) this.getIconDoc(selectedDocs[0]).then(icon => selectedDocs[0].props.toggleMinimized()); - else { - 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)); + 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)); + } } - } } this._removeIcon = false; } @@ -275,9 +277,9 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> let doc = selected[0].props.Document; let iconDoc = Docs.IconDocument(layoutString); iconDoc.isButton = true; - iconDoc.title = selected.length > 1 ? "ICONset" : "ICON" + StrCast(doc.title); - iconDoc.labelField = this._fieldKey; - iconDoc[this._fieldKey] = selected.length > 1 ? "collection" : undefined; + iconDoc.proto!.title = selected.length > 1 ? "ICONset" : "ICON" + StrCast(doc.title); + iconDoc.labelField = selected.length > 1 ? undefined : this._fieldKey; + iconDoc.proto![this._fieldKey] = selected.length > 1 ? "collection" : undefined; iconDoc.isMinimized = false; iconDoc.width = Number(MINIMIZED_ICON_SIZE); iconDoc.height = Number(MINIMIZED_ICON_SIZE); diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx index dd1516da7..52f7914f3 100644 --- a/src/client/views/collections/ParentDocumentSelector.tsx +++ b/src/client/views/collections/ParentDocumentSelector.tsx @@ -18,7 +18,7 @@ export class SelectorContextMenu extends React.Component<{ Document: Doc }> { } async fetchDocuments() { - const docs = await SearchUtil.Search(`data_l:${this.props.Document[Id]}`, true); + const docs = await SearchUtil.Search(`data_l:"${this.props.Document[Id]}"`, true); runInAction(() => this._docs = docs); } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index be7cddca6..ae4852aa2 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -212,6 +212,8 @@ export class MarqueeView extends React.Component } if (e.key === "Backspace" || e.key === "Delete" || e.key === "d") { this._commandExecuted = true; + e.stopPropagation(); + (e as any).propagationIsStopped = true; this.marqueeSelect().map(d => this.props.removeDocument(d)); let ink = Cast(this.props.container.props.Document.ink, InkField); if (ink) { @@ -291,20 +293,7 @@ export class MarqueeView extends React.Component this.props.selectDocuments([newCollection]); } this.cleanupInteractions(false); - } else - if (e.key === "s") { - // this._commandExecuted = true; - // e.stopPropagation(); - // e.preventDefault(); - // let bounds = this.Bounds; - // let selected = this.marqueeSelect(); - // SelectionManager.DeselectAll(); - // let summary = Docs.TextDocument({ x: bounds.left + bounds.width + 25, y: bounds.top, width: 300, height: 100, backgroundColor: "yellow", title: "-summary-" }); - // this.props.addLiveTextDocument(summary); - // selected.forEach(select => Doc.MakeLink(summary.proto!, select.proto!)); - - // this.cleanupInteractions(false); - } + } } @action marqueeInkSelect(ink: Map) { diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 925945b17..38ac5cb90 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -1,19 +1,19 @@ -import { computed, trace, action, reaction, IReactionDisposer } from "mobx"; +import { action, computed, IReactionDisposer, reaction } from "mobx"; import { observer } from "mobx-react"; +import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc"; +import { List } from "../../../new_fields/List"; +import { createSchema, listSpec, makeInterface } from "../../../new_fields/Schema"; +import { BoolCast, Cast, FieldValue, NumCast } from "../../../new_fields/Types"; +import { OmitKeys, Utils } from "../../../Utils"; +import { DocumentManager } from "../../util/DocumentManager"; +import { SelectionManager } from "../../util/SelectionManager"; import { Transform } from "../../util/Transform"; +import { UndoManager } from "../../util/UndoManager"; +import { CollectionDockingView } from "../collections/CollectionDockingView"; +import { DocComponent } from "../DocComponent"; import { DocumentView, DocumentViewProps, positionSchema } from "./DocumentView"; import "./DocumentView.scss"; import React = require("react"); -import { DocComponent } from "../DocComponent"; -import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema"; -import { FieldValue, Cast, NumCast, BoolCast, StrCast } from "../../../new_fields/Types"; -import { OmitKeys, Utils } from "../../../Utils"; -import { SelectionManager } from "../../util/SelectionManager"; -import { Doc, DocListCastAsync, DocListCast, } from "../../../new_fields/Doc"; -import { List } from "../../../new_fields/List"; -import { CollectionDockingView } from "../collections/CollectionDockingView"; -import { UndoManager } from "../../util/UndoManager"; -import { DocumentManager } from "../../util/DocumentManager"; export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { } @@ -140,7 +140,7 @@ export class CollectionFreeFormDocumentView extends DocComponent { + maximizedDocs.map(maximizedDoc => { let iconAnimating = Cast(maximizedDoc.isIconAnimating, List); if (!iconAnimating || (Date.now() - iconAnimating[6] > 1000)) { if (isMinimized === undefined) { @@ -182,12 +182,16 @@ export class CollectionFreeFormDocumentView extends DocComponent(Docu this._reactionDisposer = reaction(() => [this.props.Document.maximizedDocs, this.props.Document.summaryDoc, this.props.Document.summaryDoc instanceof Doc ? this.props.Document.summaryDoc.title : ""], () => { 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" : ""); + if (maxDoc.length === 1 && StrCast(maxDoc[0].title).startsWith("-") && StrCast(this.props.Document.layout).indexOf("IconBox") !== -1) { + this.props.Document.proto!.title = maxDoc[0].title + ".icon"; } let sumDoc = Cast(this.props.Document.summaryDoc, Doc); if (sumDoc instanceof Doc) { @@ -332,8 +332,8 @@ export class DocumentView extends DocComponent(Docu render() { var scaling = this.props.ContentScaling(); - var nativeHeight = this.nativeHeight > 0 ? this.nativeHeight.toString() + "px" : "100%"; - var nativeWidth = this.nativeWidth > 0 ? this.nativeWidth.toString() + "px" : "100%"; + var nativeHeight = this.nativeHeight > 0 ? `${this.nativeHeight}px` : (StrCast(this.props.Document.layout).indexOf("IconBox") === -1 ? "100%" : "auto"); + var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; return (
    ) => { if (f instanceof Doc) { if (DocumentManager.Instance.getDocumentView(f)) diff --git a/src/client/views/nodes/IconBox.scss b/src/client/views/nodes/IconBox.scss index f6d9860a3..893dc2d36 100644 --- a/src/client/views/nodes/IconBox.scss +++ b/src/client/views/nodes/IconBox.scss @@ -4,13 +4,13 @@ position: inherit; left:0; top:0; - height: 100%; + height: auto; width: max-content; // overflow: hidden; pointer-events: all; svg { width: $MINIMIZED_ICON_SIZE !important; - height: 100%; + height: auto; background: white; } .iconBox-label { diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx index 071930633..b42eb44a5 100644 --- a/src/client/views/nodes/IconBox.tsx +++ b/src/client/views/nodes/IconBox.tsx @@ -7,7 +7,7 @@ import { observer } from "mobx-react"; import { FieldView, FieldViewProps } from './FieldView'; import "./IconBox.scss"; import { Cast, StrCast, BoolCast } from "../../../new_fields/Types"; -import { Doc } from "../../../new_fields/Doc"; +import { Doc, DocListCast } from "../../../new_fields/Doc"; import { IconField } from "../../../new_fields/IconField"; import { ContextMenu } from "../ContextMenu"; import Measure from "react-measure"; @@ -41,25 +41,40 @@ export class IconBox extends React.Component { setLabelField = (e: React.MouseEvent): void => { this.props.Document.hideLabel = !BoolCast(this.props.Document.hideLabel); } + setUseOwnTitleField = (e: React.MouseEvent): void => { + this.props.Document.useOwnTitle = !BoolCast(this.props.Document.useTargetTitle); + } specificContextMenu = (e: React.MouseEvent): void => { ContextMenu.Instance.addItem({ - description: BoolCast(this.props.Document.hideLabel) ? "show label" : "hide label", + description: BoolCast(this.props.Document.hideLabel) ? "Show label with icon" : "Remove label from icon", event: this.setLabelField }); + let maxDocs = DocListCast(this.props.Document.maximizedDocs); + if (maxDocs.length === 1 && !BoolCast(this.props.Document.hideLabel)) { + ContextMenu.Instance.addItem({ + description: BoolCast(this.props.Document.useOwnTitle) ? "Use target title for label" : "Use own title label", + event: this.setUseOwnTitleField + }); + } } @observable _panelWidth: number = 0; @observable _panelHeight: number = 0; render() { let labelField = StrCast(this.props.Document.labelField); let hideLabel = BoolCast(this.props.Document.hideLabel); - let maxDoc = Cast(this.props.Document.maximizedDocs, listSpec(Doc), []); - let firstDoc = maxDoc && maxDoc.length > 0 && maxDoc[0] instanceof Doc ? maxDoc[0] as Doc : undefined; - let label = !hideLabel && firstDoc && labelField ? firstDoc[labelField] : ""; + let maxDocs = DocListCast(this.props.Document.maximizedDocs); + let firstDoc = maxDocs.length ? maxDocs[0] : undefined; + let label = hideLabel ? "" : (firstDoc && labelField && !BoolCast(this.props.Document.useOwnTitle, false) ? firstDoc[labelField] : this.props.Document.title); return (
    {this.minimizedIcon} - runInAction(() => { if (r.offset!.width || BoolCast(this.props.Document.hideLabel)) this.props.Document.nativeWidth = this.props.Document.width = (r.offset!.width + Number(MINIMIZED_ICON_SIZE)); })}> + runInAction(() => { + if (r.offset!.width || BoolCast(this.props.Document.hideLabel)) { + this.props.Document.nativeWidth = (r.offset!.width + Number(MINIMIZED_ICON_SIZE)); + if (this.props.Document.height === Number(MINIMIZED_ICON_SIZE)) this.props.Document.width = this.props.Document.nativeWidth; + } + })}> {({ measureRef }) => {label} } diff --git a/src/new_fields/List.ts b/src/new_fields/List.ts index 88a65eba4..70e36f911 100644 --- a/src/new_fields/List.ts +++ b/src/new_fields/List.ts @@ -230,6 +230,16 @@ class ListImpl extends ObjectField { const list = new Proxy(this, { set: setter, get: listGetter, + ownKeys: target => Object.keys(target.__fields), + getOwnPropertyDescriptor: (target, prop) => { + if (prop in target.__fields) { + return { + configurable: true,//TODO Should configurable be true? + enumerable: true, + }; + } + return Reflect.getOwnPropertyDescriptor(target, prop); + }, deleteProperty: deleteProperty, defineProperty: () => { throw new Error("Currently properties can't be defined on documents using Object.defineProperty"); }, }); -- cgit v1.2.3-70-g09d2