From 15e8341334419142f4a54db23cc643f18ba0e0f1 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 14 Jun 2019 15:53:13 -0400 Subject: a few tweaks to fix unfreezing documents to give them a margin --- src/client/views/nodes/DocumentView.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/client/views/nodes/DocumentView.tsx') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 6fe01963a..583fa3e1a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -452,8 +452,9 @@ export class DocumentView extends DocComponent(Docu render() { var scaling = this.props.ContentScaling(); - var nativeHeight = this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; + let ph = this.props.PanelHeight(); + var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; return (
Date: Fri, 14 Jun 2019 17:27:49 -0400 Subject: added page fwd/back/goto --- src/client/views/collections/CollectionPDFView.tsx | 48 ++++++----------- src/client/views/nodes/DocumentView.tsx | 1 - src/client/views/nodes/FieldView.tsx | 2 + src/client/views/nodes/PDFBox.tsx | 62 ++++++++++++++++------ src/client/views/pdf/PDFViewer.tsx | 6 +-- src/client/views/pdf/Page.tsx | 12 ++--- 6 files changed, 72 insertions(+), 59 deletions(-) (limited to 'src/client/views/nodes/DocumentView.tsx') diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx index 4af89d780..b62d3f7bb 100644 --- a/src/client/views/collections/CollectionPDFView.tsx +++ b/src/client/views/collections/CollectionPDFView.tsx @@ -1,19 +1,21 @@ -import { action, observable, IReactionDisposer, reaction } from "mobx"; +import { action, IReactionDisposer, observable, reaction } from "mobx"; import { observer } from "mobx-react"; +import { WidthSym } from "../../../new_fields/Doc"; +import { Id } from "../../../new_fields/FieldSymbols"; +import { NumCast } from "../../../new_fields/Types"; +import { emptyFunction } from "../../../Utils"; import { ContextMenu } from "../ContextMenu"; +import { FieldView, FieldViewProps } from "../nodes/FieldView"; +import { CollectionBaseView, CollectionRenderProps, CollectionViewType } from "./CollectionBaseView"; +import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; import "./CollectionPDFView.scss"; import React = require("react"); -import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; -import { FieldView, FieldViewProps } from "../nodes/FieldView"; -import { CollectionRenderProps, CollectionBaseView, CollectionViewType } from "./CollectionBaseView"; -import { emptyFunction } from "../../../Utils"; -import { NumCast } from "../../../new_fields/Types"; -import { Id } from "../../../new_fields/FieldSymbols"; -import { HeightSym, WidthSym } from "../../../new_fields/Doc"; +import { PDFBox } from "../nodes/PDFBox"; @observer export class CollectionPDFView extends React.Component { + private _pdfBox?: PDFBox; private _reactionDisposer?: IReactionDisposer; private _buttonTray: React.RefObject; @@ -46,31 +48,12 @@ export class CollectionPDFView extends React.Component { } @observable _inThumb = false; - private set curPage(value: number) { this.props.Document.curPage = value; } + private set curPage(value: number) { this._pdfBox && this._pdfBox.GotoPage(value); } private get curPage() { return NumCast(this.props.Document.curPage, -1); } private get numPages() { return NumCast(this.props.Document.numPages); } - @action onPageBack = () => this.curPage > 1 ? (this.props.Document.curPage = this.curPage - 1) : -1; - @action onPageForward = () => this.curPage < this.numPages ? (this.props.Document.curPage = this.curPage + 1) : -1; + @action onPageBack = () => this._pdfBox && this._pdfBox.BackPage(); + @action onPageForward = () => this._pdfBox && this._pdfBox.ForwardPage(); - @action - onThumbDown = (e: React.PointerEvent) => { - document.addEventListener("pointermove", this.onThumbMove, false); - document.addEventListener("pointerup", this.onThumbUp, false); - e.stopPropagation(); - this._inThumb = true; - } - @action - onThumbMove = (e: PointerEvent) => { - let pso = (e.clientY - (e as any).target.parentElement.getBoundingClientRect().top) / (e as any).target.parentElement.getBoundingClientRect().height; - this.curPage = Math.trunc(Math.min(this.numPages, pso * this.numPages + 1)); - e.stopPropagation(); - } - @action - onThumbUp = (e: PointerEvent) => { - this._inThumb = false; - document.removeEventListener("pointermove", this.onThumbMove); - document.removeEventListener("pointerup", this.onThumbUp); - } nativeWidth = () => NumCast(this.props.Document.nativeWidth); nativeHeight = () => NumCast(this.props.Document.nativeHeight); private get uIButtons() { @@ -92,11 +75,14 @@ export class CollectionPDFView extends React.Component { } } + setPdfBox = (pdfBox: PDFBox) => { this._pdfBox = pdfBox; }; + + private subView = (_type: CollectionViewType, renderProps: CollectionRenderProps) => { let props = { ...this.props, ...renderProps }; return ( <> - + {renderProps.active() ? this.uIButtons : (null)} ); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 583fa3e1a..0d5df550a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -453,7 +453,6 @@ export class DocumentView extends DocComponent(Docu render() { var scaling = this.props.ContentScaling(); var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; - let ph = this.props.PanelHeight(); var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; return (
number; PanelHeight: () => number; setVideoBox?: (player: VideoBox) => void; + setPdfBox?: (player: PDFBox) => void; } @observer diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index b9ccd79e4..243982a3b 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -1,25 +1,22 @@ -import * as htmlToImage from "html-to-image"; -import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from 'mobx'; +import { action, IReactionDisposer, observable, reaction, trace, untracked } from 'mobx'; import { observer } from "mobx-react"; import 'react-image-lightbox/style.css'; -import Measure from "react-measure"; +import { WidthSym } from "../../../new_fields/Doc"; +import { makeInterface } from "../../../new_fields/Schema"; +import { Cast, NumCast } from "../../../new_fields/Types"; +import { PdfField } from "../../../new_fields/URLField"; //@ts-ignore // import { Document, Page } from "react-pdf"; // import 'react-pdf/dist/Page/AnnotationLayer.css'; import { RouteStore } from "../../../server/RouteStore"; import { DocComponent } from "../DocComponent"; import { InkingControl } from "../InkingControl"; +import { PDFViewer } from "../pdf/PDFViewer"; import { positionSchema } from "./DocumentView"; import { FieldView, FieldViewProps } from './FieldView'; import { pageSchema } from "./ImageBox"; import "./PDFBox.scss"; import React = require("react"); -import { NumCast, StrCast, Cast } from "../../../new_fields/Types"; -import { makeInterface } from "../../../new_fields/Schema"; -import { PDFViewer } from "../pdf/PDFViewer"; -import { PdfField } from "../../../new_fields/URLField"; -import { HeightSym, WidthSym } from "../../../new_fields/Doc"; -import { CollectionStackingView } from "../collections/CollectionStackingView"; type PdfDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>; const PdfDocument = makeInterface(positionSchema, pageSchema); @@ -31,19 +28,50 @@ export class PDFBox extends DocComponent(PdfDocumen @observable private _alt = false; @observable private _scrollY: number = 0; private _reactionDisposer?: IReactionDisposer; - _targetDiv: any = undefined; - componentDidMount: () => void = () => { + componentDidMount() { + if (this.props.setPdfBox) this.props.setPdfBox(this); + } + + public GetPage() { + return Math.floor(NumCast(this.props.Document.scrollY) / NumCast(this.Document.pdfHeight)) + 1; + } + public BackPage() { + let cp = Math.ceil(NumCast(this.props.Document.scrollY) / NumCast(this.Document.pdfHeight)) + 1; + cp = cp - 1; + if (cp > 0) { + this.props.Document.curPage = cp; + this.props.Document.scrollY = (cp - 1) * NumCast(this.Document.pdfHeight); + } + } + public GotoPage(p: number) { + if (p > 0 && p <= NumCast(this.props.Document.numPages)) { + this.props.Document.curPage = p; + this.props.Document.scrollY = (p - 1) * NumCast(this.Document.pdfHeight); + } + } + + public ForwardPage() { + let cp = this.GetPage() + 1; + if (cp <= NumCast(this.props.Document.numPages)) { + this.props.Document.curPage = cp; + this.props.Document.scrollY = (cp - 1) * NumCast(this.Document.pdfHeight); + } + } + + createRef = (ele: HTMLDivElement | null) => { if (this._reactionDisposer) this._reactionDisposer(); - this._reactionDisposer = reaction(() => this.props.Document.scrollY, () => - this._targetDiv && this._targetDiv.scrollTo({ top: NumCast(this.Document.scrollY), behavior: "smooth" }) - ); + this._reactionDisposer = reaction(() => this.props.Document.scrollY, () => { + ele && ele.scrollTo({ top: NumCast(this.Document.scrollY), behavior: "smooth" }); + }); } - loaded = (nw: number, nh: number) => { + loaded = (nw: number, nh: number, np: number) => { if (this.props.Document) { - if (this.props.Document.nativeWidth && this.props.Document.nativeHeight) return; let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document; + console.log("pages = " + np); + doc.numPages = np; + if (doc.nativeWidth && doc.nativeHeight) return; let oldaspect = NumCast(doc.nativeHeight) / NumCast(doc.nativeWidth, 1); doc.nativeWidth = nw; if (doc.nativeHeight) doc.nativeHeight = nw * oldaspect; @@ -59,7 +87,6 @@ export class PDFBox extends DocComponent(PdfDocumen @action onScroll = (e: React.UIEvent) => { if (e.currentTarget) { - this._targetDiv = e.currentTarget; this._scrollY = e.currentTarget.scrollTop; // e.currentTarget.scrollTo({ top: 1000, behavior: "smooth" }); let ccv = this.props.ContainingCollectionView; @@ -82,6 +109,7 @@ export class PDFBox extends DocComponent(PdfDocumen overflowY: "scroll", overflowX: "hidden", marginTop: `${NumCast(this.props.ContainingCollectionView!.props.Document.panY)}px` }} + ref={this.createRef} onWheel={(e: React.WheelEvent) => e.stopPropagation()} className={classname}> {/*
*/} diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 17f65c7a6..dee891ba6 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -23,7 +23,7 @@ import { Dictionary } from "typescript-collections"; interface IPDFViewerProps { url: string; - loaded: (nw: number, nh: number) => void; + loaded: (nw: number, nh: number, np: number) => void; scrollY: number; parent: PDFBox; } @@ -61,7 +61,7 @@ export class PDFViewer extends React.Component { interface IViewerProps { pdf: Opt; - loaded: (nw: number, nh: number) => void; + loaded: (nw: number, nh: number, np: number) => void; scrollY: number; parent: PDFBox; mainCont: React.RefObject; @@ -400,7 +400,7 @@ class Viewer extends React.Component { return; } let numPages = this.props.pdf ? this.props.pdf.numPages : 0; - this.props.loaded(page.width, page.height); + this.props.loaded(page.width, page.height, numPages); this._pageSizes[index - 1] = { width: page.width, height: page.height }; this._pagesLoaded++; if (this._pagesLoaded === numPages) { diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 44c502a04..e3dbeaebe 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -132,16 +132,14 @@ export default class Page extends React.Component { } } - highlight = (targetDoc: Doc | undefined) => { + highlight = (targetDoc?: Doc) => { // creates annotation documents for current highlights let annotationDoc = this.props.makeAnnotationDocuments(targetDoc); - let targetAnnotations = DocListCast(this.props.parent.Document.annotations); - if (targetAnnotations) { + let targetAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc)); + if (targetAnnotations === undefined) { + Doc.GetProto(this.props.parent.Document).annotations = new List([annotationDoc]); + } else { targetAnnotations.push(annotationDoc); - this.props.parent.Document.annotations = new List(targetAnnotations); - } - else { - this.props.parent.Document.annotations = new List([annotationDoc]); } return annotationDoc; } -- cgit v1.2.3-70-g09d2 From f7a567b27b5c124e8c3bfe1e866162e2911607c5 Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 17 Jun 2019 12:26:57 -0400 Subject: cleaned up minimization code, added minimization for stacking --- .../views/collections/CollectionSchemaView.tsx | 15 --- .../views/collections/CollectionStackingView.tsx | 21 +-- .../views/nodes/CollectionFreeFormDocumentView.tsx | 147 ++++----------------- src/client/views/nodes/DocumentView.tsx | 62 ++++++++- 4 files changed, 89 insertions(+), 156 deletions(-) (limited to 'src/client/views/nodes/DocumentView.tsx') diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index b9e5a5b65..7cc00ce07 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -416,20 +416,6 @@ export class CollectionSchemaPreview extends React.Component) => { this.props.setPreviewScript(e.currentTarget.value); } - @undoBatch - @action - public collapseToPoint = (scrpt: number[], expandedDocs: Doc[] | undefined): void => { - SelectionManager.DeselectAll(); - if (expandedDocs) { - let isMinimized: boolean | undefined; - expandedDocs.map(d => Doc.GetProto(d)).map(maximizedDoc => { - if (isMinimized === undefined) { - isMinimized = BoolCast(maximizedDoc.isMinimized, false); - } - maximizedDoc.isMinimized = !isMinimized; - }); - } - } render() { let input = this.props.previewScript === undefined ? (null) :
)} {input} diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 236bfe136..ef12545b8 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -1,17 +1,15 @@ import React = require("react"); -import { action, computed, IReactionDisposer, reaction, trace } from "mobx"; +import { action, computed, IReactionDisposer, reaction } from "mobx"; import { observer } from "mobx-react"; import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; import { BoolCast, NumCast } from "../../../new_fields/Types"; import { emptyFunction, returnOne, Utils } from "../../../Utils"; -import { SelectionManager } from "../../util/SelectionManager"; -import { undoBatch } from "../../util/UndoManager"; +import { ContextMenu } from "../ContextMenu"; import { DocumentView } from "../nodes/DocumentView"; import { CollectionSchemaPreview } from "./CollectionSchemaView"; import "./CollectionStackingView.scss"; import { CollectionSubView } from "./CollectionSubView"; -import { ContextMenu } from "../ContextMenu"; @observer export class CollectionStackingView extends CollectionSubView(doc => doc) { @@ -62,20 +60,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { this._masonryGridRef = ele; this.createDropTarget(ele!); } - @undoBatch - @action - public collapseToPoint = (scrpt: number[], expandedDocs: Doc[] | undefined): void => { - SelectionManager.DeselectAll(); - if (expandedDocs) { - let isMinimized: boolean | undefined; - expandedDocs.map(d => Doc.GetProto(d)).map(maximizedDoc => { - if (isMinimized === undefined) { - isMinimized = BoolCast(maximizedDoc.isMinimized, false); - } - maximizedDoc.isMinimized = !isMinimized; - }); - } - } @computed get singleColumnChildren() { @@ -144,7 +128,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { addDocTab={this.props.addDocTab} bringToFront={emptyFunction} whenActiveChanged={this.props.whenActiveChanged} - collapseToPoint={this.collapseToPoint} />
); }) diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 499b83c0f..f6b1c62ee 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -1,17 +1,12 @@ -import { computed, IReactionDisposer, reaction, action } from "mobx"; +import { computed } from "mobx"; import { observer } from "mobx-react"; -import { Doc } 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 } from "../../../Utils"; +import { createSchema, makeInterface } from "../../../new_fields/Schema"; +import { BoolCast, FieldValue, NumCast } from "../../../new_fields/Types"; import { Transform } from "../../util/Transform"; import { DocComponent } from "../DocComponent"; import { DocumentView, DocumentViewProps, positionSchema } from "./DocumentView"; import "./DocumentView.scss"; import React = require("react"); -import { UndoManager } from "../../util/UndoManager"; -import { SelectionManager } from "../../util/SelectionManager"; export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { } @@ -27,13 +22,7 @@ const FreeformDocument = makeInterface(schema, positionSchema); @observer export class CollectionFreeFormDocumentView extends DocComponent(FreeformDocument) { - private _mainCont = React.createRef(); - _bringToFrontDisposer?: IReactionDisposer; - - @computed get transform() { - return `scale(${this.props.ContentScaling()}, ${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) scale(${this.zoom}, ${this.zoom}) `; - } - + @computed get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) scale(${this.zoom}) `; } @computed get X() { return FieldValue(this.Document.x, 0); } @computed get Y() { return FieldValue(this.Document.y, 0); } @computed get zoom(): number { return 1 / FieldValue(this.Document.zoomBasis, 1); } @@ -59,91 +48,20 @@ export class CollectionFreeFormDocumentView extends DocComponent this.props.PanelHeight(); getTransform = (): Transform => this.props.ScreenToLocalTransform() .translate(-this.X, -this.Y) - .scale(1 / this.contentScaling()).scale(1 / this.zoom) - - @computed - get docView() { - return ; - } - - componentDidMount() { - this._bringToFrontDisposer = reaction(() => this.props.Document.isIconAnimating, (values) => { - this.props.bringToFront(this.props.Document); - if (values instanceof List) { - let scrpt = this.props.ScreenToLocalTransform().transformPoint(values[0], values[1]); - this.animateBetweenIcon(true, scrpt, [this.Document.x || 0, this.Document.y || 0], - this.Document.width || 0, this.Document.height || 0, values[2], values[3] ? true : false); - } - }, { fireImmediately: true }); - } - - componentWillUnmount() { - if (this._bringToFrontDisposer) this._bringToFrontDisposer(); - } - - static _undoBatch?: UndoManager.Batch = undefined; - @action - public collapseToPoint = async (scrpt: number[], expandedDocs: Doc[] | undefined): Promise => { - SelectionManager.DeselectAll(); - if (expandedDocs) { - if (!CollectionFreeFormDocumentView._undoBatch) { - CollectionFreeFormDocumentView._undoBatch = UndoManager.StartBatch("iconAnimating"); - } - let isMinimized: boolean | undefined; - expandedDocs.map(d => Doc.GetProto(d)).map(maximizedDoc => { - let iconAnimating = Cast(maximizedDoc.isIconAnimating, List); - if (!iconAnimating || (Date.now() - iconAnimating[2] > 1000)) { - if (isMinimized === undefined) { - isMinimized = BoolCast(maximizedDoc.isMinimized, false); - } - maximizedDoc.willMaximize = isMinimized; - maximizedDoc.isMinimized = false; - maximizedDoc.isIconAnimating = new List([scrpt[0], scrpt[1], Date.now(), isMinimized ? 1 : 0]); - } + .scale(1 / this.contentScaling()).scale(1 / this.zoom); + + animateBetweenIcon = (icon: number[], stime: number, maximizing: boolean) => { + this.props.bringToFront(this.props.Document); + let targetPos = [this.Document.x || 0, this.Document.y || 0]; + let iconPos = this.props.ScreenToLocalTransform().transformPoint(icon[0], icon[1]); + DocumentView.animateBetweenIconFunc(this.props.Document, + this.Document.width || 0, this.Document.height || 0, stime, maximizing, (progress: number) => { + let pval = maximizing ? + [iconPos[0] + (targetPos[0] - iconPos[0]) * progress, iconPos[1] + (targetPos[1] - iconPos[1]) * progress] : + [targetPos[0] + (iconPos[0] - targetPos[0]) * progress, targetPos[1] + (iconPos[1] - targetPos[1]) * progress]; + this.Document.x = progress === 1 ? targetPos[0] : pval[0]; + this.Document.y = progress === 1 ? targetPos[1] : pval[1]; }); - setTimeout(() => { - CollectionFreeFormDocumentView._undoBatch && CollectionFreeFormDocumentView._undoBatch.end(); - CollectionFreeFormDocumentView._undoBatch = undefined; - }, 500); - } - } - - animateBetweenIcon(first: boolean, icon: number[], targ: number[], width: number, height: number, stime: number, maximizing: boolean) { - - setTimeout(() => { - let now = Date.now(); - let progress = Math.min(1, (now - stime) / 200); - let pval = maximizing ? - [icon[0] + (targ[0] - icon[0]) * progress, icon[1] + (targ[1] - icon[1]) * progress] : - [targ[0] + (icon[0] - targ[0]) * progress, targ[1] + (icon[1] - targ[1]) * progress]; - this.props.Document.width = maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress; - this.props.Document.height = maximizing ? 25 + (height - 25) * progress : height + (25 - height) * progress; - this.props.Document.x = pval[0]; - this.props.Document.y = pval[1]; - if (first) { - this.props.Document.proto!.willMaximize = false; - } - if (now < stime + 200) { - this.animateBetweenIcon(false, icon, targ, width, height, stime, maximizing); - } - else { - if (!maximizing) { - this.props.Document.proto!.isMinimized = true; - this.props.Document.x = targ[0]; - this.props.Document.y = targ[1]; - this.props.Document.width = width; - this.props.Document.height = height; - } - this.props.Document.proto!.isIconAnimating = undefined; - } - }, - 2); } borderRounding = () => { @@ -155,34 +73,25 @@ export class CollectionFreeFormDocumentView extends DocComponent 800 ? Math.max(0, Math.min(1, 2 - 5 * (zoom < this.scale ? this.scale / zoom : zoom / this.scale))) : 1; - const screenWidth = Math.min(50 * NumCast(this.props.Document.nativeWidth, 0), 1800); - let fadeUp = .75 * screenWidth; - let fadeDown = (maximizedDoc ? .0075 : .075) * screenWidth; - // zoomFade = w < fadeDown /* || w > fadeUp */ ? Math.max(0.1, Math.min(1, 2 - (w < fadeDown ? Math.sqrt(Math.sqrt(fadeDown / w)) : w / fadeUp))) : 1; - return ( -
- {this.docView} +
); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 6fe01963a..a8dc13d1e 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -75,7 +75,7 @@ export interface DocumentViewProps { whenActiveChanged: (isActive: boolean) => void; bringToFront: (doc: Doc) => void; addDocTab: (doc: Doc, where: string) => void; - collapseToPoint?: (scrpt: number[], expandedDocs: Doc[] | undefined) => void; + animateBetweenIcon?: (iconPos: number[], startTime: number, maximizing: boolean) => void; } const schema = createSchema({ @@ -127,6 +127,7 @@ export class DocumentView extends DocComponent(Docu super(props); } + _animateToIconDisposer?: IReactionDisposer; _reactionDisposer?: IReactionDisposer; @action componentDidMount() { @@ -148,8 +149,35 @@ export class DocumentView extends DocComponent(Docu this.props.Document.proto!.title = "-" + sumDoc.title + ".expanded"; } }, { fireImmediately: true }); + this._animateToIconDisposer = reaction(() => this.props.Document.isIconAnimating, (values) => + (values instanceof List) && this.animateBetweenIcon(values, values[2], values[3] ? true : false) + , { fireImmediately: true }); DocumentManager.Instance.DocumentViews.push(this); } + + animateBetweenIcon = (iconPos: number[], startTime: number, maximizing: boolean) => { + this.props.animateBetweenIcon ? this.props.animateBetweenIcon(iconPos, startTime, maximizing) : + DocumentView.animateBetweenIconFunc(this.props.Document, this.Document[WidthSym](), this.Document[HeightSym](), startTime, maximizing); + Doc.GetProto(this.props.Document).willMaximize = false; + } + + public static animateBetweenIconFunc = (doc: Doc, width: number, height: number, stime: number, maximizing: boolean, cb?: (progress: number) => void) => { + setTimeout(() => { + let now = Date.now(); + let progress = now < stime + 200 ? Math.min(1, (now - stime) / 200) : 1 + doc.width = progress === 1 ? width : maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress + doc.height = progress === 1 ? height : maximizing ? 25 + (height - 25) * progress : height + (25 - height) * progress; + cb && cb(progress); + if (now < stime + 200) { + DocumentView.animateBetweenIconFunc(doc, width, height, stime, maximizing, cb); + } + else { + Doc.GetProto(doc).isMinimized = !maximizing; + Doc.GetProto(doc).isIconAnimating = undefined; + } + }, + 2); + } @action componentDidUpdate() { if (this._dropDisposer) { @@ -164,6 +192,7 @@ export class DocumentView extends DocComponent(Docu @action componentWillUnmount() { if (this._reactionDisposer) this._reactionDisposer(); + if (this._animateToIconDisposer) this._animateToIconDisposer(); if (this._dropDisposer) this._dropDisposer(); DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1); } @@ -195,7 +224,34 @@ export class DocumentView extends DocComponent(Docu if (minimizedDoc) { let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint( NumCast(minimizedDoc.x) - NumCast(this.Document.x), NumCast(minimizedDoc.y) - NumCast(this.Document.y)); - this.props.collapseToPoint && this.props.collapseToPoint(scrpt, await DocListCastAsync(minimizedDoc.maximizedDocs)); + this.collapseTargetsToPoint(scrpt, await DocListCastAsync(minimizedDoc.maximizedDocs)) + } + } + + static _undoBatch?: UndoManager.Batch = undefined; + @action + public collapseTargetsToPoint = async (scrpt: number[], expandedDocs: Doc[] | undefined): Promise => { + SelectionManager.DeselectAll(); + if (expandedDocs) { + if (!DocumentView._undoBatch) { + DocumentView._undoBatch = UndoManager.StartBatch("iconAnimating"); + } + let isMinimized: boolean | undefined; + expandedDocs.map(d => Doc.GetProto(d)).map(maximizedDoc => { + let iconAnimating = Cast(maximizedDoc.isIconAnimating, List); + if (!iconAnimating || (Date.now() - iconAnimating[2] > 1000)) { + if (isMinimized === undefined) { + isMinimized = BoolCast(maximizedDoc.isMinimized, false); + } + maximizedDoc.willMaximize = isMinimized; + maximizedDoc.isMinimized = false; + maximizedDoc.isIconAnimating = new List([scrpt[0], scrpt[1], Date.now(), isMinimized ? 1 : 0]); + } + }); + setTimeout(() => { + DocumentView._undoBatch && DocumentView._undoBatch.end(); + DocumentView._undoBatch = undefined; + }, 500); } } @@ -251,7 +307,7 @@ export class DocumentView extends DocComponent(Docu } } else { let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(NumCast(this.Document.width) / 2, NumCast(this.Document.height) / 2); - this.props.collapseToPoint && this.props.collapseToPoint(scrpt, expandedProtoDocs); + this.collapseTargetsToPoint(scrpt, expandedProtoDocs); } } else if (linkedToDocs.length || linkedFromDocs.length) { -- cgit v1.2.3-70-g09d2 From 589d2409cf00c3ff15eddbe88835a63a09785f2c Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 17 Jun 2019 12:36:15 -0400 Subject: from last --- src/client/views/nodes/DocumentView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/nodes/DocumentView.tsx') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index a8dc13d1e..856fdab7f 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -158,7 +158,6 @@ export class DocumentView extends DocComponent(Docu animateBetweenIcon = (iconPos: number[], startTime: number, maximizing: boolean) => { this.props.animateBetweenIcon ? this.props.animateBetweenIcon(iconPos, startTime, maximizing) : DocumentView.animateBetweenIconFunc(this.props.Document, this.Document[WidthSym](), this.Document[HeightSym](), startTime, maximizing); - Doc.GetProto(this.props.Document).willMaximize = false; } public static animateBetweenIconFunc = (doc: Doc, width: number, height: number, stime: number, maximizing: boolean, cb?: (progress: number) => void) => { @@ -175,6 +174,7 @@ export class DocumentView extends DocComponent(Docu Doc.GetProto(doc).isMinimized = !maximizing; Doc.GetProto(doc).isIconAnimating = undefined; } + Doc.GetProto(doc).willMaximize = false; }, 2); } -- cgit v1.2.3-70-g09d2 From f74e512e500252ad76d77935e7aacbf72cb0dd9c Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 17 Jun 2019 14:41:21 -0400 Subject: added dropping on text as colleciton. --- src/client/views/TemplateMenu.tsx | 2 +- .../views/collections/CollectionSchemaView.tsx | 30 ++++++++++------------ .../views/collections/CollectionStackingView.tsx | 1 + src/client/views/nodes/DocumentView.tsx | 4 +-- src/client/views/nodes/FormattedTextBox.tsx | 28 ++++++++++++++++++-- 5 files changed, 44 insertions(+), 21 deletions(-) (limited to 'src/client/views/nodes/DocumentView.tsx') diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index 3288abd90..a9bc4d3d2 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -45,7 +45,7 @@ export class TemplateMenu extends React.Component { if (template.Name === "Bullet") { let topDocView = this.props.docs[0]; topDocView.addTemplate(template); - topDocView.props.Document.subBulletDocs = new List(this.props.docs.filter(v => v !== topDocView).map(v => v.props.Document.proto!)); + topDocView.props.Document.subBulletDocs = new List(this.props.docs.filter(v => v !== topDocView).map(v => v.props.Document)); } else { this.props.docs.map(d => d.addTemplate(template)); } diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 7cc00ce07..4b46c73c1 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -2,36 +2,33 @@ import React = require("react"); import { library } from '@fortawesome/fontawesome-svg-core'; import { faCog, faPlus } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable, untracked, runInAction, trace } from "mobx"; +import { action, computed, observable, trace, untracked } from "mobx"; import { observer } from "mobx-react"; import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table"; -import { MAX_ROW_HEIGHT } from '../../views/globalCssVariables.scss'; import "react-table/react-table.css"; +import { Doc, DocListCast, DocListCastAsync, Field } from "../../../new_fields/Doc"; +import { Id } from "../../../new_fields/FieldSymbols"; +import { List } from "../../../new_fields/List"; +import { listSpec } from "../../../new_fields/Schema"; +import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types"; import { emptyFunction, returnFalse, returnZero } from "../../../Utils"; +import { Docs } from "../../documents/Documents"; +import { Gateway } from "../../northstar/manager/Gateway"; import { SetupDrag } from "../../util/DragManager"; import { CompileScript } from "../../util/Scripting"; import { Transform } from "../../util/Transform"; -import { COLLECTION_BORDER_WIDTH } from "../../views/globalCssVariables.scss"; +import { COLLECTION_BORDER_WIDTH, MAX_ROW_HEIGHT } from '../../views/globalCssVariables.scss'; +import { ContextMenu } from "../ContextMenu"; import { anchorPoints, Flyout } from "../DocumentDecorations"; import '../DocumentDecorations.scss'; import { EditableView } from "../EditableView"; import { DocumentView } from "../nodes/DocumentView"; import { FieldView, FieldViewProps } from "../nodes/FieldView"; +import { CollectionPDFView } from "./CollectionPDFView"; import "./CollectionSchemaView.scss"; import { CollectionSubView } from "./CollectionSubView"; -import { Opt, Field, Doc, DocListCastAsync, DocListCast } from "../../../new_fields/Doc"; -import { Cast, FieldValue, NumCast, StrCast, BoolCast } from "../../../new_fields/Types"; -import { listSpec } from "../../../new_fields/Schema"; -import { List } from "../../../new_fields/List"; -import { Id } from "../../../new_fields/FieldSymbols"; -import { Gateway } from "../../northstar/manager/Gateway"; -import { Docs } from "../../documents/Documents"; -import { ContextMenu } from "../ContextMenu"; -import { CollectionView } from "./CollectionView"; -import { CollectionPDFView } from "./CollectionPDFView"; import { CollectionVideoView } from "./CollectionVideoView"; -import { SelectionManager } from "../../util/SelectionManager"; -import { undoBatch } from "../../util/UndoManager"; +import { CollectionView } from "./CollectionView"; library.add(faCog); @@ -389,6 +386,7 @@ interface CollectionSchemaPreviewProps { CollectionView: CollectionView | CollectionPDFView | CollectionVideoView; getTransform: () => Transform; addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; + moveDocument: (document: Doc, target: Doc, addDoc: ((doc: Doc) => boolean)) => boolean; removeDocument: (document: Doc) => boolean; active: () => boolean; whenActiveChanged: (isActive: boolean) => void; @@ -424,7 +422,7 @@ export class CollectionSchemaPreview extends React.Component doc) { getTransform={dxf} CollectionView={this.props.CollectionView} addDocument={this.props.addDocument} + moveDocument={this.props.moveDocument} removeDocument={this.props.removeDocument} active={this.props.active} whenActiveChanged={this.props.whenActiveChanged} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 856fdab7f..8ece7d67f 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -285,8 +285,8 @@ export class DocumentView extends DocComponent(Docu let expandedProtoDocs = expandedDocs.map(doc => Doc.GetProto(doc)); let maxLocation = StrCast(this.props.Document.maximizeLocation, "inPlace"); let getDispDoc = (target: Doc) => Object.getOwnPropertyNames(target).indexOf("isPrototype") === -1 ? target : Doc.MakeDelegate(target); - if (altKey) { - maxLocation = this.props.Document.maximizeLocation = (maxLocation === "inPlace" || !maxLocation ? "inTab" : "inPlace"); + if (altKey || ctrlKey) { + maxLocation = this.props.Document.maximizeLocation = (ctrlKey ? maxLocation : (maxLocation === "inPlace" || !maxLocation ? "inTab" : "inPlace")); if (!maxLocation || maxLocation === "inPlace") { let hadView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedProtoDocs[0], this.props.ContainingCollectionView); let wasMinimized = !hadView && expandedDocs.reduce((min, d) => !min && !BoolCast(d.IsMinimized, false), false); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index e5a43c60a..7a9593a60 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -8,10 +8,10 @@ import { keymap } from "prosemirror-keymap"; import { NodeType } from 'prosemirror-model'; import { EditorState, Plugin, Transaction } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; -import { Doc, Opt } from "../../../new_fields/Doc"; +import { Doc, Opt, DocListCast } from "../../../new_fields/Doc"; import { Id } from '../../../new_fields/FieldSymbols'; import { RichTextField } from "../../../new_fields/RichTextField"; -import { createSchema, makeInterface } from "../../../new_fields/Schema"; +import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema"; import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { DocServer } from "../../DocServer"; import { Docs } from '../../documents/Documents'; @@ -30,6 +30,8 @@ import { InkingControl } from "../InkingControl"; import { FieldView, FieldViewProps } from "./FieldView"; import "./FormattedTextBox.scss"; import React = require("react"); +import { List } from '../../../new_fields/List'; +import { Templates } from '../Templates'; library.add(faEdit); library.add(faSmile); @@ -139,6 +141,28 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe let model: NodeType = (url.includes(".mov") || url.includes(".mp4")) ? schema.nodes.video : schema.nodes.image; this._editorView!.dispatch(this._editorView!.state.tr.insert(0, model.create({ src: url }))); e.stopPropagation(); + } else { + if (de.data instanceof DragManager.DocumentDragData) { + let ldocs = Cast(this.props.Document.subBulletDocs, listSpec(Doc)); + if (!ldocs) { + this.props.Document.subBulletDocs = new List([]); + } + ldocs = Cast(this.props.Document.subBulletDocs, listSpec(Doc)); + if (!ldocs) return; + if (!ldocs || !ldocs[0] || ldocs[0] instanceof Promise || StrCast((ldocs[0] as Doc).layout).indexOf("CollectionView") === -1) { + ldocs.splice(0, 0, Docs.StackingDocument([], { title: StrCast(this.props.Document.title) + "-subBullets", x: NumCast(this.props.Document.x), y: NumCast(this.props.Document.y) + NumCast(this.props.Document.height), width: 300, height: 300 })); + this.props.addDocument && this.props.addDocument(ldocs[0] as Doc); + this.props.Document.templates = new List([Templates.Bullet.Layout]); + this.props.Document.isBullet = true; + } + let stackDoc = (ldocs[0] as Doc); + if (de.data.moveDocument) { + de.data.moveDocument(de.data.draggedDocuments[0], stackDoc, (doc) => { + Cast(stackDoc.data, listSpec(Doc))!.push(doc); + return true; + }) + } + } } } -- cgit v1.2.3-70-g09d2 From 8c64ffd92e382050bc8727981cf9fb830e4f02a7 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Mon, 17 Jun 2019 23:04:07 -0400 Subject: Added share with user functionality --- src/client/views/nodes/DocumentView.tsx | 48 +++++++++++++++++++++++++-------- src/client/views/nodes/FieldView.tsx | 2 +- src/server/RouteStore.ts | 1 + src/server/database.ts | 4 +-- src/server/index.ts | 11 ++++++++ 5 files changed, 52 insertions(+), 14 deletions(-) (limited to 'src/client/views/nodes/DocumentView.tsx') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 9d0206e48..942228e71 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,11 +1,11 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faAlignCenter, faCaretSquareRight, faCompressArrowsAlt, faExpandArrowsAlt, faLayerGroup, faSquare, faTrash, faConciergeBell, faFolder, faMapPin, faLink, faFingerprint, faCrosshairs, faDesktop } from '@fortawesome/free-solid-svg-icons'; -import { action, computed, IReactionDisposer, reaction, trace, observable } from "mobx"; +import { action, computed, IReactionDisposer, reaction, trace, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocListCastAsync } from "../../../new_fields/Doc"; import { List } from "../../../new_fields/List"; import { ObjectField } from "../../../new_fields/ObjectField"; -import { createSchema, makeInterface } from "../../../new_fields/Schema"; +import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema"; import { BoolCast, Cast, FieldValue, StrCast, NumCast, PromiseValue } from "../../../new_fields/Types"; import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; import { emptyFunction, Utils } from "../../../Utils"; @@ -26,10 +26,12 @@ import { DocComponent } from "../DocComponent"; import { PresentationView } from "../PresentationView"; import { Template } from "./../Templates"; import { DocumentContentsView } from "./DocumentContentsView"; +import * as rp from "request-promise"; import "./DocumentView.scss"; import React = require("react"); import { Id, Copy } from '../../../new_fields/FieldSymbols'; import { ContextMenuProps } from '../ContextMenuItem'; +import { RouteStore } from '../../../server/RouteStore'; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? library.add(faTrash); @@ -452,7 +454,8 @@ export class DocumentView extends DocComponent(Docu } @action - onContextMenu = (e: React.MouseEvent): void => { + onContextMenu = async (e: React.MouseEvent): Promise => { + e.persist(); e.stopPropagation(); if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3 || e.isDefaultPrevented()) { @@ -483,14 +486,37 @@ export class DocumentView extends DocComponent(Docu cm.addItem({ description: "Copy URL", event: () => Utils.CopyText(DocServer.prepend("/doc/" + this.props.Document[Id])), icon: "link" }); cm.addItem({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document[Id]), icon: "fingerprint" }); cm.addItem({ description: "Delete", event: this.deleteClicked, icon: "trash" }); - if (!this.topMost) { - // DocumentViews should stop propagation of this event - e.stopPropagation(); - } - ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); - if (!SelectionManager.IsSelected(this)) { - SelectionManager.SelectDoc(this, false); - } + type User = { email: string, userDocumentId: string }; + const users: User[] = JSON.parse(await rp.get(DocServer.prepend(RouteStore.getUsers))); + let usersMenu: ContextMenuProps[] = users.filter(({ email }) => email !== CurrentUserUtils.email).map(({ email, userDocumentId }) => ({ + description: email, event: async () => { + const userDocument = await Cast(DocServer.GetRefField(userDocumentId), Doc); + if (!userDocument) { + throw new Error(`Couldn't get user document of user ${email}`); + } + const notifDoc = await Cast(userDocument.optionalRightCollection, Doc); + if (notifDoc instanceof Doc) { + const data = await Cast(notifDoc.data, listSpec(Doc)); + const sharedDoc = Doc.MakeAlias(this.props.Document); + if (data) { + data.push(sharedDoc); + } else { + notifDoc.data = new List([sharedDoc]); + } + } + } + })); + runInAction(() => { + cm.addItem({ description: "Share...", subitems: usersMenu }); + if (!this.topMost) { + // DocumentViews should stop propagation of this event + e.stopPropagation(); + } + ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); + if (!SelectionManager.IsSelected(this)) { + SelectionManager.SelectDoc(this, false); + } + }); } onPointerEnter = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = true; }; diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index cf6d2012f..4738e90d7 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -109,7 +109,7 @@ export class FieldView extends React.Component { } else if (field instanceof List) { return (
- {field.map(f => f instanceof Doc ? f.title : f.toString()).join(", ")} + {field.map(f => f instanceof Doc ? f.title : (f && f.toString && f.toString())).join(", ")}
); } // bcz: this belongs here, but it doesn't render well so taking it out for now diff --git a/src/server/RouteStore.ts b/src/server/RouteStore.ts index c4af5cdaa..5c13495ff 100644 --- a/src/server/RouteStore.ts +++ b/src/server/RouteStore.ts @@ -16,6 +16,7 @@ export enum RouteStore { // USER AND WORKSPACES getCurrUser = "/getCurrentUser", + getUsers = "/getUsers", getUserDocumentId = "/getUserDocumentId", updateCursor = "/updateCursor", diff --git a/src/server/database.ts b/src/server/database.ts index 70b3efced..d240bd909 100644 --- a/src/server/database.ts +++ b/src/server/database.ts @@ -120,9 +120,9 @@ export class Database { } } - public query(query: any): Promise { + public query(query: any, collectionName = "newDocuments"): Promise { if (this.db) { - return Promise.resolve(this.db.collection('newDocuments').find(query)); + return Promise.resolve(this.db.collection(collectionName).find(query)); } else { return new Promise(res => { this.onConnect.push(() => res(this.query(query))); diff --git a/src/server/index.ts b/src/server/index.ts index b91c91282..7ef542b01 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -203,6 +203,17 @@ addSecureRoute( RouteStore.root ); +addSecureRoute( + Method.GET, + async (_, res) => { + const cursor = await Database.Instance.query({}, "users"); + const results = await cursor.toArray(); + res.send(results.map(user => ({ email: user.email, userDocumentId: user.userDocumentId }))); + }, + undefined, + RouteStore.getUsers +); + addSecureRoute( Method.GET, (user, res, req) => { -- cgit v1.2.3-70-g09d2 From 749eef13af1338225b2bec4dbcd7a50a5650d285 Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 18 Jun 2019 11:31:46 -0400 Subject: fixed image drag drop when not selected. --- src/client/views/nodes/DocumentView.tsx | 7 +++++-- src/client/views/nodes/ImageBox.tsx | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'src/client/views/nodes/DocumentView.tsx') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 942228e71..de6e9f5fa 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,5 +1,5 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faAlignCenter, faCaretSquareRight, faCompressArrowsAlt, faExpandArrowsAlt, faLayerGroup, faSquare, faTrash, faConciergeBell, faFolder, faMapPin, faLink, faFingerprint, faCrosshairs, faDesktop } from '@fortawesome/free-solid-svg-icons'; +import { faAlignCenter, faCaretSquareRight, faCompressArrowsAlt, faUnlock, faLock, faExpandArrowsAlt, faLayerGroup, faSquare, faTrash, faConciergeBell, faFolder, faMapPin, faLink, faFingerprint, faCrosshairs, faDesktop } from '@fortawesome/free-solid-svg-icons'; import { action, computed, IReactionDisposer, reaction, trace, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocListCastAsync } from "../../../new_fields/Doc"; @@ -48,6 +48,8 @@ library.add(faLink); library.add(faFingerprint); library.add(faCrosshairs); library.add(faDesktop); +library.add(faUnlock); +library.add(faLock); const linkSchema = createSchema({ title: "string", @@ -354,7 +356,7 @@ export class DocumentView extends DocComponent(Docu if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) { document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); - if (!e.altKey && !this.topMost && e.buttons === 1) { + if (!e.altKey && !this.topMost && e.buttons === 1 && !BoolCast(this.props.Document.lockedPosition)) { this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined, this._hitExpander); } } @@ -475,6 +477,7 @@ export class DocumentView extends DocComponent(Docu cm.addItem({ description: "Open...", subitems: subitems }); cm.addItem({ description: BoolCast(this.props.Document.ignoreAspect, false) || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "edit" }); cm.addItem({ description: "Pin to Pres", event: () => PresentationView.Instance.PinDoc(this.props.Document), icon: "map-pin" }); + cm.addItem({ description: BoolCast(this.props.Document.lockedPosition) ? "Unlock Pos" : "Lock Pos", event: () => this.props.Document.lockedPosition = BoolCast(this.props.Document.lockedPosition) ? undefined : true, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); cm.addItem({ description: this.props.Document.isButton ? "Remove Button" : "Make Button", event: this.makeBtnClicked, icon: "concierge-bell" }); cm.addItem({ description: "Find aliases", event: async () => { diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 0d19508fa..6b8b64c5f 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -86,9 +86,9 @@ export class ImageBox extends DocComponent(ImageD } onPointerDown = (e: React.PointerEvent): void => { - if (e.shiftKey && e.ctrlKey) - - e.stopPropagation(); + if (e.shiftKey && e.ctrlKey) { + e.stopPropagation(); // allows default system drag drop of images with shift+ctrl only + } else e.preventDefault(); // if (Date.now() - this._lastTap < 300) { // if (e.buttons === 1) { // this._downX = e.clientX; -- cgit v1.2.3-70-g09d2 From 464fa03d6ebb2a7aaef1d7622afa3e1e7ee816a3 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Tue, 18 Jun 2019 20:11:31 -0400 Subject: Context menu improvements and error fixes --- src/client/views/ContextMenu.scss | 22 +++++----- src/client/views/ContextMenuItem.tsx | 47 +++++++++++++++++++--- .../views/collections/CollectionSchemaView.tsx | 1 + src/client/views/nodes/DocumentContentsView.tsx | 1 - src/client/views/nodes/DocumentView.tsx | 5 ++- 5 files changed, 57 insertions(+), 19 deletions(-) (limited to 'src/client/views/nodes/DocumentView.tsx') diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss index 7e066d53a..e363c5158 100644 --- a/src/client/views/ContextMenu.scss +++ b/src/client/views/ContextMenu.scss @@ -8,19 +8,19 @@ flex-direction: column; } -.contextMenu-item:first-child { - background: $intermediate-color; - color: $light-color; -} +// .contextMenu-item:first-child { +// background: $intermediate-color; +// color: $light-color; +// } -.contextMenu-item:first-child::placeholder { - color: $light-color; -} +// .contextMenu-item:first-child::placeholder { +// color: $light-color; +// } -.contextMenu-item:first-child:hover { - background: $intermediate-color; - color: $light-color; -} +// .contextMenu-item:first-child:hover { +// background: $intermediate-color; +// color: $light-color; +// } .contextMenu-subMenu-cont { position: absolute; diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx index fcda0db89..dc0751049 100644 --- a/src/client/views/ContextMenuItem.tsx +++ b/src/client/views/ContextMenuItem.tsx @@ -1,9 +1,12 @@ import React = require("react"); import { observable, action } from "mobx"; import { observer } from "mobx-react"; -import { IconProp } from '@fortawesome/fontawesome-svg-core'; +import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; +import { faAngleRight } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +library.add(faAngleRight); + export interface OriginalMenuProps { description: string; event: (e: React.MouseEvent) => void; @@ -14,6 +17,7 @@ export interface OriginalMenuProps { export interface SubmenuProps { description: string; subitems: ContextMenuProps[]; + icon?: IconProp; //maybe should be optional (icon?) closeMenu?: () => void; } @@ -41,13 +45,40 @@ export class ContextMenuItem extends React.Component { } } + currentTimeout?: any; + static readonly timeout = 300; + onPointerEnter = () => { + if (this.currentTimeout) { + clearTimeout(this.currentTimeout); + this.currentTimeout = undefined; + } + if (this.overItem) { + return; + } + this.currentTimeout = setTimeout(action(() => this.overItem = true), ContextMenuItem.timeout); + } + + onPointerLeave = () => { + if (this.currentTimeout) { + clearTimeout(this.currentTimeout); + this.currentTimeout = undefined; + } + if (!this.overItem) { + return; + } + this.currentTimeout = setTimeout(action(() => this.overItem = false), ContextMenuItem.timeout); + + } + render() { if ("event" in this.props) { return (
- - {this.props.icon ? : } - + {this.props.icon ? ( + + + + ) : null}
{this.props.description}
@@ -60,9 +91,15 @@ export class ContextMenuItem extends React.Component { {this._items.map(prop => )}
; return ( -
{ this.overItem = true; })} onMouseLeave={action(() => this.overItem = false)}> +
+ {this.props.icon ? ( + + + + ) : null}
{this.props.description} +
{submenu}
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 4b46c73c1..14a7d19d0 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -352,6 +352,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { height={this.previewHeight} getTransform={this.getPreviewTransform} CollectionView={this.props.CollectionView} + moveDocument={this.props.moveDocument} addDocument={this.props.addDocument} removeDocument={this.props.removeDocument} active={this.props.active} diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index c2caabb92..02396c3af 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -23,7 +23,6 @@ import { FieldViewProps } from "./FieldView"; import { Without, OmitKeys } from "../../../Utils"; import { Cast, StrCast, NumCast } from "../../../new_fields/Types"; import { List } from "../../../new_fields/List"; -import { PDFBox2 } from "../pdf/PDFBox2"; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? type BindingProps = Without; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index de6e9f5fa..7c058d91c 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,5 +1,5 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faAlignCenter, faCaretSquareRight, faCompressArrowsAlt, faUnlock, faLock, faExpandArrowsAlt, faLayerGroup, faSquare, faTrash, faConciergeBell, faFolder, faMapPin, faLink, faFingerprint, faCrosshairs, faDesktop } from '@fortawesome/free-solid-svg-icons'; +import { faAlignCenter, faCaretSquareRight, faCompressArrowsAlt, faUnlock, faLock, faExpandArrowsAlt, faLayerGroup, faSquare, faTrash, faConciergeBell, faFolder, faShare, faMapPin, faLink, faFingerprint, faCrosshairs, faDesktop } from '@fortawesome/free-solid-svg-icons'; import { action, computed, IReactionDisposer, reaction, trace, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocListCastAsync } from "../../../new_fields/Doc"; @@ -35,6 +35,7 @@ import { RouteStore } from '../../../server/RouteStore'; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? library.add(faTrash); +library.add(faShare); library.add(faExpandArrowsAlt); library.add(faCompressArrowsAlt); library.add(faLayerGroup); @@ -510,7 +511,7 @@ export class DocumentView extends DocComponent(Docu } })); runInAction(() => { - cm.addItem({ description: "Share...", subitems: usersMenu }); + cm.addItem({ description: "Share...", subitems: usersMenu, icon: "share" }); if (!this.topMost) { // DocumentViews should stop propagation of this event e.stopPropagation(); -- cgit v1.2.3-70-g09d2 From 13e301dea2f537b67b338cc6a98d3f3b5a8e1f36 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Tue, 18 Jun 2019 20:58:32 -0400 Subject: Fixed linter errors --- src/client/northstar/dash-nodes/HistogramBox.tsx | 3 +-- src/client/util/DocumentManager.ts | 6 +++--- src/client/util/DragManager.ts | 4 +++- src/client/util/RichTextSchema.tsx | 10 +++++----- src/client/util/TooltipTextMenu.tsx | 2 +- src/client/views/ContextMenu.tsx | 4 ++-- src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/MainOverlayTextBox.tsx | 2 +- src/client/views/MainView.tsx | 7 ++++--- src/client/views/PresentationView.tsx | 4 ++-- src/client/views/collections/CollectionBaseView.tsx | 2 +- src/client/views/collections/CollectionDockingView.tsx | 5 +++-- src/client/views/collections/CollectionPDFView.tsx | 2 +- src/client/views/collections/CollectionSchemaView.tsx | 11 ++++++----- src/client/views/collections/CollectionStackingView.tsx | 4 ++-- src/client/views/collections/CollectionTreeView.tsx | 10 +++++----- .../collectionFreeForm/CollectionFreeFormLinksView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- .../views/collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 12 ++++++------ src/client/views/nodes/FormattedTextBox.tsx | 2 +- src/client/views/nodes/ImageBox.tsx | 3 ++- src/client/views/pdf/PDFAnnotationLayer.tsx | 2 +- src/client/views/pdf/PDFMenu.tsx | 2 +- src/client/views/pdf/PDFViewer.tsx | 4 ++-- src/server/index.ts | 2 +- 27 files changed, 59 insertions(+), 54 deletions(-) (limited to 'src/client/views/nodes/DocumentView.tsx') diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx index d7732ee86..a60eaea85 100644 --- a/src/client/northstar/dash-nodes/HistogramBox.tsx +++ b/src/client/northstar/dash-nodes/HistogramBox.tsx @@ -125,8 +125,7 @@ export class HistogramBox extends React.Component { let mapped = brushingDocs.map((brush, i) => { brush.backgroundColor = StyleConstants.BRUSH_COLORS[i % StyleConstants.BRUSH_COLORS.length]; let brushed = DocListCast(brush.brushingDocs); - if (!brushed.length) - return null; + if (!brushed.length) return null; return { l: brush, b: brushed[0][Id] === proto[Id] ? brushed[1] : brushed[0] }; }); runInAction(() => this.HistoOp.BrushLinks.splice(0, this.HistoOp.BrushLinks.length, ...mapped.filter(m => m) as { l: Doc, b: Doc }[])); diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index ff0c1560b..862395d74 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -35,9 +35,9 @@ export class DocumentManager { let toReturn: DocumentView | null = null; let passes = preferredCollection ? [preferredCollection, undefined] : [undefined]; - for (let i = 0; i < passes.length; i++) { + for (let pass of passes) { DocumentManager.Instance.DocumentViews.map(view => { - if (view.props.Document[Id] === id && (!passes[i] || view.props.ContainingCollectionView === preferredCollection)) { + if (view.props.Document[Id] === id && (!pass || view.props.ContainingCollectionView === preferredCollection)) { toReturn = view; return; } @@ -45,7 +45,7 @@ export class DocumentManager { if (!toReturn) { DocumentManager.Instance.DocumentViews.map(view => { let doc = view.props.Document.proto; - if (doc && doc[Id] === id && (!passes[i] || view.props.ContainingCollectionView === preferredCollection)) { + if (doc && doc[Id] === id && (!pass || view.props.ContainingCollectionView === preferredCollection)) { toReturn = view; } }); diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 89566e777..c3c92daa5 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -282,11 +282,13 @@ export namespace DragManager { // } let set = dragElement.getElementsByTagName('*'); if (dragElement.hasAttribute("style")) (dragElement as any).style.pointerEvents = "none"; - for (let i = 0; i < set.length; i++) + // tslint:disable-next-line: prefer-for-of + for (let i = 0; i < set.length; i++) { if (set[i].hasAttribute("style")) { let s = set[i]; (s as any).style.pointerEvents = "none"; } + } dragDiv.appendChild(dragElement); diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index e1e595925..943cdb4d1 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -156,12 +156,12 @@ export const nodes: { [index: string]: NodeSpec } = { title: dom.getAttribute("title"), alt: dom.getAttribute("alt"), width: Math.min(100, Number(dom.getAttribute("width"))), - } + }; } }], toDOM(node) { - const attrs = { style: `width: ${node.attrs.width}` } - return ["video", { ...node.attrs, ...attrs }] + const attrs = { style: `width: ${node.attrs.width}` }; + return ["video", { ...node.attrs, ...attrs }]; } }, @@ -285,7 +285,7 @@ export const marks: { [index: string]: MarkSpec } = { toDOM() { return ['span', { style: 'color: blue' - }] + }]; } }, @@ -536,4 +536,4 @@ schema.nodeFromJSON = (json: any) => { node.attrs.oldtext = Slice.fromJSON(schema, node.attrs.oldtextslice); } return node; -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 0a61b7e7d..f2559db74 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -256,7 +256,7 @@ export class TooltipTextMenu { starButton.onclick = () => { let state = this.view.state; this.insertStar(state, this.view.dispatch); - } + }; this.tooltip.appendChild(starButton); } diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index da374455e..eb1937683 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -1,8 +1,8 @@ import React = require("react"); import { ContextMenuItem, ContextMenuProps } from "./ContextMenuItem"; import { observable, action } from "mobx"; -import { observer } from "mobx-react" -import "./ContextMenu.scss" +import { observer } from "mobx-react"; +import "./ContextMenu.scss"; import { library } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faSearch, faCircle } from '@fortawesome/free-solid-svg-icons'; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index ceca940b6..e60f8c86c 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -466,7 +466,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> SelectionManager.SelectedDocuments().forEach(element => { const rect = element.ContentDiv ? element.ContentDiv.getBoundingClientRect() : new DOMRect(); - if (rect.width !== 0 && (dX != 0 || dY != 0 || dW != 0 || dH != 0)) { + if (rect.width !== 0 && (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0)) { let doc = PositionDocument(element.props.Document); let nwidth = doc.nativeWidth || 0; let nheight = doc.nativeHeight || 0; diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx index 0de880175..b4ad5f4d7 100644 --- a/src/client/views/MainOverlayTextBox.tsx +++ b/src/client/views/MainOverlayTextBox.tsx @@ -40,7 +40,7 @@ export class MainOverlayTextBox extends React.Component this.TextDoc = box.props.Document; let sxf = Utils.GetScreenTransform(box ? box.CurrentDiv : undefined); let xf = () => { box.props.ScreenToLocalTransform(); return new Transform(-sxf.translateX, -sxf.translateY, 1 / sxf.scale); }; - this.setTextDoc(box.props.fieldKey, box.CurrentDiv, xf, BoolCast(box.props.Document.autoHeight, false) || box.props.height === "min-content") + this.setTextDoc(box.props.fieldKey, box.CurrentDiv, xf, BoolCast(box.props.Document.autoHeight, false) || box.props.height === "min-content"); } else { this.TextDoc = undefined; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index fd76cbbd3..7f979cd3b 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -237,10 +237,11 @@ export class MainView extends React.Component { } onColorClick = (e: React.MouseEvent) => { - let target = (e.nativeEvent! as any).path[0]; - let parent = (e.nativeEvent! as any).path[1]; - if (target.localName === "input" || parent.localName === "span") + let target = (e.nativeEvent as any).path[0]; + let parent = (e.nativeEvent as any).path[1]; + if (target.localName === "input" || parent.localName === "span") { e.stopPropagation(); + } } diff --git a/src/client/views/PresentationView.tsx b/src/client/views/PresentationView.tsx index d2d41a4ba..1dacbb663 100644 --- a/src/client/views/PresentationView.tsx +++ b/src/client/views/PresentationView.tsx @@ -40,8 +40,8 @@ class PresentationViewList extends React.Component { //this doc is selected className += " presentationView-selected"; } - let onEnter = (e: React.PointerEvent) => { document.libraryBrush = true; } - let onLeave = (e: React.PointerEvent) => { document.libraryBrush = false; } + let onEnter = (e: React.PointerEvent) => { document.libraryBrush = true; }; + let onLeave = (e: React.PointerEvent) => { document.libraryBrush = false; }; return (
{ @action.bound removeDocument(doc: Doc): boolean { - let docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView) + let docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView); docView && SelectionManager.DeselectDoc(docView); const props = this.props; //TODO This won't create the field if it doesn't already exist diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index f2b3528b8..5beb89315 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -347,7 +347,7 @@ export class CollectionDockingView extends React.Component CollectionDockingView.Instance.AddTab(stack, doc)} />, upDiv); tab.reactComponents = [upDiv]; tab.element.append(upDiv); @@ -432,8 +432,9 @@ export class DockedFrameRenderer extends React.Component { @observable private _document: Opt; get _stack(): any { let parent = (this.props as any).glContainer.parent.parent; - if (this._document && this._document.excludeFromLibrary && parent.parent && parent.parent.contentItems.length > 1) + if (this._document && this._document.excludeFromLibrary && parent.parent && parent.parent.contentItems.length > 1) { return parent.parent.contentItems[1]; + } return parent; } constructor(props: any) { diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx index b62d3f7bb..b2d016934 100644 --- a/src/client/views/collections/CollectionPDFView.tsx +++ b/src/client/views/collections/CollectionPDFView.tsx @@ -40,7 +40,7 @@ export class CollectionPDFView extends React.Component { // console.log(this.props.Document[HeightSym]()); }, { fireImmediately: true } - ) + ); } public static LayoutString(fieldKey: string = "data") { diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 14a7d19d0..faea8d44d 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -87,8 +87,9 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { let columnDocs = DocListCast(schemaDoc.data); if (columnDocs) { let ddoc = columnDocs.find(doc => doc.title === columnName); - if (ddoc) + if (ddoc) { return ddoc; + } } } return this.props.Document; @@ -285,7 +286,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { } getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate( - - this.borderWidth - this.DIVIDER_WIDTH - this.tableWidth, - this.borderWidth); + - this.borderWidth - this.DIVIDER_WIDTH - this.tableWidth, - this.borderWidth) get documentKeysCheckList() { @@ -334,7 +335,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { columns={this.tableColumns} column={{ ...ReactTableDefaults.column, Cell: this.renderCell, }} getTrProps={this.getTrProps} - /> + />; } @computed @@ -360,7 +361,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { addDocTab={this.props.addDocTab} setPreviewScript={this.setPreviewScript} previewScript={this.previewScript} - /> + />; } @action setPreviewScript = (script: string) => { @@ -409,7 +410,7 @@ export class CollectionSchemaPreview extends React.Component this.nativeWidth * this.contentScaling(); private PanelHeight = () => this.nativeHeight * this.contentScaling(); - private getTransform = () => this.props.getTransform().translate(-this.centeringOffset, 0).scale(1 / this.contentScaling()) + private getTransform = () => this.props.getTransform().translate(-this.centeringOffset, 0).scale(1 / this.contentScaling()); get centeringOffset() { return (this.props.width() - this.nativeWidth * this.contentScaling()) / 2; } @action onPreviewScriptChange = (e: React.ChangeEvent) => { diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index e1ac3505b..f5ad4ee95 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -101,7 +101,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { let childFocus = (doc: Doc) => { doc.libraryBrush = true; this.props.focus(this.props.Document); // just focus on this collection, not the underlying document because the API doesn't support adding an offset to focus on and we can't pan zoom our contents to be centered. - } + }; return (
doc) { whenActiveChanged={this.props.whenActiveChanged} />
); - }) + }); } onContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped() && this.props.Document[Id] !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index b13694e9d..3e495b734 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -67,8 +67,8 @@ class TreeView extends React.Component { @undoBatch delete = () => this.props.deleteDoc(this.props.document); @undoBatch openRight = async () => this.props.addDocTab(this.props.document, "openRight"); - @action onMouseEnter = () => { this._isOver = true; } - @action onMouseLeave = () => { this._isOver = false; } + @action onMouseEnter = () => { this._isOver = true; }; + @action onMouseLeave = () => { this._isOver = false; }; onPointerEnter = (e: React.PointerEvent): void => { this.props.active() && (this.props.document.libraryBrush = true); @@ -89,8 +89,8 @@ class TreeView extends React.Component { let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2); let before = x[1] < bounds[1]; let inside = x[0] > bounds[0] + 75 || (!before && this._bulletType === BulletType.Collapsible); - this._header!.current!.className = "treeViewItem-header" - if (inside && this._bulletType != BulletType.List) this._header!.current!.className += " treeViewItem-header-inside"; + this._header!.current!.className = "treeViewItem-header"; + if (inside && this._bulletType !== BulletType.List) this._header!.current!.className += " treeViewItem-header-inside"; else if (before) this._header!.current!.className += " treeViewItem-header-above"; else if (!before) this._header!.current!.className += " treeViewItem-header-below"; e.stopPropagation(); @@ -192,7 +192,7 @@ class TreeView extends React.Component { if (inside) { let docList = Cast(this.props.document.data, listSpec(Doc)); if (docList !== undefined) { - addDoc = (doc: Doc) => { docList && docList.push(doc); return true; } + addDoc = (doc: Doc) => { docList && docList.push(doc); return true; }; } } let added = false; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index c4dd534ed..be75c6c5c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -109,7 +109,7 @@ export class CollectionFreeFormLinksView extends React.Component [ , ...this.views - ]; + ] render() { const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`; const easing = () => this.props.Document.panTransformType === "Ease"; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index dedcc3172..05dc6f534 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -321,7 +321,7 @@ export class MarqueeView extends React.Component summary.imageSummary = imageSummary; this.props.addDocument(imageSummary, false); } - }) + }); newCollection.proto!.summaryDoc = summary; selected = [newCollection]; newCollection.x = bounds.left + bounds.width; diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index f6b1c62ee..858959d81 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -48,7 +48,7 @@ export class CollectionFreeFormDocumentView extends DocComponent this.props.PanelHeight(); getTransform = (): Transform => this.props.ScreenToLocalTransform() .translate(-this.X, -this.Y) - .scale(1 / this.contentScaling()).scale(1 / this.zoom); + .scale(1 / this.contentScaling()).scale(1 / this.zoom) animateBetweenIcon = (icon: number[], stime: number, maximizing: boolean) => { this.props.bringToFront(this.props.Document); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 7c058d91c..4992669df 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -168,8 +168,8 @@ export class DocumentView extends DocComponent(Docu public static animateBetweenIconFunc = (doc: Doc, width: number, height: number, stime: number, maximizing: boolean, cb?: (progress: number) => void) => { setTimeout(() => { let now = Date.now(); - let progress = now < stime + 200 ? Math.min(1, (now - stime) / 200) : 1 - doc.width = progress === 1 ? width : maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress + let progress = now < stime + 200 ? Math.min(1, (now - stime) / 200) : 1; + doc.width = progress === 1 ? width : maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress; doc.height = progress === 1 ? height : maximizing ? 25 + (height - 25) * progress : height + (25 - height) * progress; cb && cb(progress); if (now < stime + 200) { @@ -229,7 +229,7 @@ export class DocumentView extends DocComponent(Docu if (minimizedDoc) { let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint( NumCast(minimizedDoc.x) - NumCast(this.Document.x), NumCast(minimizedDoc.y) - NumCast(this.Document.y)); - this.collapseTargetsToPoint(scrpt, await DocListCastAsync(minimizedDoc.maximizedDocs)) + this.collapseTargetsToPoint(scrpt, await DocListCastAsync(minimizedDoc.maximizedDocs)); } } @@ -372,8 +372,8 @@ export class DocumentView extends DocComponent(Docu this._lastTap = Date.now(); } - deleteClicked = (): void => { this.props.removeDocument && this.props.removeDocument(this.props.Document); } - fieldsClicked = (): void => { this.props.addDocTab(Docs.KVPDocument(this.props.Document, { width: 300, height: 300 }), "onRight") }; + deleteClicked = (): void => { this.props.removeDocument && this.props.removeDocument(this.props.Document); }; + fieldsClicked = (): void => { this.props.addDocTab(Docs.KVPDocument(this.props.Document, { width: 300, height: 300 }), "onRight"); }; makeBtnClicked = (): void => { let doc = Doc.GetProto(this.props.Document); doc.isButton = !BoolCast(doc.isButton, false); @@ -527,7 +527,7 @@ export class DocumentView extends DocComponent(Docu onPointerLeave = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = false; }; isSelected = () => SelectionManager.IsSelected(this); - @action select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); } + @action select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); }; @computed get nativeWidth() { return this.Document.nativeWidth || 0; } @computed get nativeHeight() { return this.Document.nativeHeight || 0; } diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 36d902c4f..df12f261b 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -161,7 +161,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe de.data.moveDocument(de.data.draggedDocuments[0], stackDoc, (doc) => { Cast(stackDoc.data, listSpec(Doc))!.push(doc); return true; - }) + }); } } } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 6b8b64c5f..f208ce2ce 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -188,8 +188,9 @@ export class ImageBox extends DocComponent(ImageD } @action onError = () => { let timeout = this._curSuffix === "_s" ? this._smallRetryCount : this._curSuffix === "_m" ? this._mediumRetryCount : this._largeRetryCount; - if (timeout < 10) + if (timeout < 10) { setTimeout(this.retryPath, Math.min(10000, timeout * 5)); + } } _curSuffix = "_m"; render() { diff --git a/src/client/views/pdf/PDFAnnotationLayer.tsx b/src/client/views/pdf/PDFAnnotationLayer.tsx index e92dcacbf..1f49e0d2f 100644 --- a/src/client/views/pdf/PDFAnnotationLayer.tsx +++ b/src/client/views/pdf/PDFAnnotationLayer.tsx @@ -19,6 +19,6 @@ export class PDFAnnotationLayer extends React.Component {
- ) + ); } } \ No newline at end of file diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index 7817e8c26..2ed891131 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -162,6 +162,6 @@ export default class PDFMenu extends React.Component {
- ) + ); } } \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 69372f43b..8c0aaea00 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -198,7 +198,7 @@ class Viewer extends React.Component { { @action getPageImage = async (page: number) => { let handleError = () => this.getRenderedPage(page); - if (this._isPage[page] != "image") { + if (this._isPage[page] !== "image") { this._isPage[page] = "image"; const address = this.props.url; let res = JSON.parse(await rp.get(DocServer.prepend(`/thumbnail${address.substring("files/".length, address.length - ".pdf".length)}-${page + 1}.PNG`))); diff --git a/src/server/index.ts b/src/server/index.ts index 7ef542b01..2901f61ed 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -172,7 +172,7 @@ function LoadPage(file: string, pageNumber: number, res: Response) { canvasContext: canvasAndContext.context, viewport: viewport, canvasFactory: factory - } + }; console.log("read " + pageNumber); page.render(renderContext).promise -- cgit v1.2.3-70-g09d2 From 358437eeafe42e029ffe27702bde15a3fad54a3b Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 19 Jun 2019 16:17:18 -0400 Subject: working version of embedded tree view docs. --- .../views/collections/CollectionStackingView.tsx | 2 + .../views/collections/CollectionTreeView.scss | 15 ++- .../views/collections/CollectionTreeView.tsx | 132 +++++++++++++++------ src/client/views/nodes/DocumentView.tsx | 5 +- src/client/views/nodes/FormattedTextBox.tsx | 1 + 5 files changed, 109 insertions(+), 46 deletions(-) (limited to 'src/client/views/nodes/DocumentView.tsx') diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index f80ba0a4b..368e94a8c 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -10,6 +10,7 @@ import { DocumentView } from "../nodes/DocumentView"; import { CollectionSchemaPreview } from "./CollectionSchemaView"; import "./CollectionStackingView.scss"; import { CollectionSubView } from "./CollectionSubView"; +import { auto } from "async"; @observer export class CollectionStackingView extends CollectionSubView(doc => doc) { @@ -112,6 +113,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { style={{ width: width(), height: height(), + overflow: "auto", transformOrigin: "top left", gridRowEnd: `span ${rowSpan}`, gridColumnEnd: `span 1`, diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index 2d5092980..ec78fdb80 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -12,7 +12,7 @@ padding-right: 0px; background: $light-color-secondary; font-size: 13px; - overflow: scroll; + overflow: auto; ul { list-style: none; @@ -50,10 +50,12 @@ font-size: 24px; } - .collectionTreeView-keyHeader { - font-style: italic; - font-size: 8pt; - } +} +.collectionTreeView-keyHeader { + font-style: italic; + font-size: 8pt; + margin-left: 3px; + display:none; } .docContainer { @@ -74,6 +76,9 @@ } .treeViewItem-header:hover { + .collectionTreeView-keyHeader { + display:inherit; + } .treeViewItem-openRight { display: inline-block; height:13px; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index d43402e7d..1eab541b3 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -1,9 +1,9 @@ import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; import { faAngleRight, faCaretDown, faCaretRight, faTrashAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, observable, trace } from "mobx"; +import { action, observable, trace, computed } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast } from '../../../new_fields/Doc'; +import { Doc, DocListCast, WidthSym, HeightSym } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; import { Document, listSpec } from '../../../new_fields/Schema'; import { BoolCast, Cast, NumCast, StrCast, PromiseValue } from '../../../new_fields/Types'; @@ -16,14 +16,18 @@ import { EditableView } from "../EditableView"; import { MainView } from '../MainView'; import { CollectionViewType } from './CollectionBaseView'; import { CollectionDockingView } from './CollectionDockingView'; -import { CollectionSubView } from "./CollectionSubView"; +import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView"; import "./CollectionTreeView.scss"; import React = require("react"); import { Transform } from '../../util/Transform'; import { SelectionManager } from '../../util/SelectionManager'; -import { emptyFunction } from '../../../Utils'; +import { emptyFunction, returnFalse, Utils, returnOne, returnZero } from '../../../Utils'; import { List } from '../../../new_fields/List'; import { Templates } from '../Templates'; +import { DocumentView, DocumentViewProps } from '../nodes/DocumentView'; +import { number } from 'prop-types'; +import ReactTable from 'react-table'; +import { MainOverlayTextBox } from '../MainOverlayTextBox'; export interface TreeViewProps { @@ -32,9 +36,12 @@ export interface TreeViewProps { moveDocument: DragManager.MoveFunction; dropAction: "alias" | "copy" | undefined; addDocTab: (doc: Doc, where: string) => void; + panelWidth: () => number; + panelHeight: () => number; addDocument: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean; indentDocument?: () => void; ScreenToLocalTransform: () => Transform; + outerXf: () => number[]; treeViewId: string; parentKey: string; active: () => boolean; @@ -58,11 +65,13 @@ library.add(faCaretRight); class TreeView extends React.Component { private _header?: React.RefObject = React.createRef(); private treedropDisposer?: DragManager.DragDropDisposer; + private _mainEle?: HTMLDivElement; protected createTreeDropTarget = (ele: HTMLDivElement) => { this.treedropDisposer && this.treedropDisposer(); if (ele) { this.treedropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.treeDrop.bind(this) } }); } + this._mainEle = ele; } @observable _isOver: boolean = false; @@ -76,7 +85,7 @@ class TreeView extends React.Component { onPointerEnter = (e: React.PointerEvent): void => { this.props.active() && (this.props.document.libraryBrush = true); - if (e.buttons === 1) { + if (e.buttons === 1 && SelectionManager.GetIsDragging()) { this._header!.current!.className = "treeViewItem-header"; document.addEventListener("pointermove", this.onDragMove, true); } @@ -166,6 +175,28 @@ class TreeView extends React.Component { let reference = React.createRef(); let onItemDown = SetupDrag(reference, () => this.props.document, this.move, this.props.dropAction, this.props.treeViewId, true); + let keyList: string[] = []; + 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); + } + if (keys.indexOf("data") !== -1) { + keys.splice(keys.indexOf("data"), 1); + keys.splice(0, 0, "data"); + } + keys.map(key => { + let docList = Cast(this.props.document[key], listSpec(Doc)); + let doc = Cast(this.props.document[key], Doc); + if (doc instanceof Doc || (docList && (DocListCast(docList).length > 0 || key === "data"))) { + keyList.push(key); + } + }); + let headerElements = this._bulletType === BulletType.List ? (null) : [this._chosenKey].map(key => + { this._chosenKey = key; this.props.document.embed = !BoolCast(this.props.document.embed, false) })} + style={{ background: key === this._chosenKey ? "lightgray" : undefined }}> + {key} + ); let dataDocs = CollectionDockingView.Instance ? Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc), []) : []; let openRight = dataDocs && dataDocs.indexOf(this.props.document) !== -1 ? (null) : (
@@ -182,6 +213,7 @@ class TreeView extends React.Component { {this.editableView("title")} {/* {
} */}
+ {headerElements} {openRight} ; } @@ -244,34 +276,20 @@ class TreeView extends React.Component { return true; } + docTransform = () => { + let { scale, translateX, translateY } = Utils.GetScreenTransform(this._dref.current!); + let outerXf = this.props.outerXf(); + let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf[0] - translateX, outerXf[1] - translateY); + let finalXf = this.props.ScreenToLocalTransform().translate(offset[0], offset[1]); + return finalXf; + } @observable _chosenKey: string = "data"; _bulletType: BulletType = BulletType.List; + + _dref = React.createRef(); render() { let bulletType = BulletType.List; let contentElement: (JSX.Element | null)[] = []; - 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); - } - if (keys.indexOf("data") !== -1) { - keys.splice(keys.indexOf("data"), 1); - keys.splice(0, 0, "data"); - } - let keyList: string[] = []; - keys.map(key => { - let docList = Cast(this.props.document[key], listSpec(Doc)); - let doc = Cast(this.props.document[key], Doc); - if (doc instanceof Doc || (docList && (DocListCast(docList).length > 0 || key === "data"))) { - keyList.push(key); - } - }); - let headerElements =
{keyList.map(key => - this._chosenKey = key)} - style={{ display: "inline", marginRight: "3px", marginTop: "7px", background: key === this._chosenKey ? "lightgray" : undefined }}> - {key} - )} -
; [this._chosenKey].map(key => { let docList = Cast(this.props.document[key], listSpec(Doc)); let remDoc = (doc: Doc) => this.remove(doc, key); @@ -280,14 +298,31 @@ class TreeView extends React.Component { if (doc instanceof Doc || (docList && (DocListCast(docList).length > 0 || key === "data"))) { if (!this._collapsed) { bulletType = BulletType.Collapsible; - contentElement.push(
    - {headerElements} -
    - {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, key, addDoc, remDoc, this.move, - this.indent, - this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.active)} -
    -
); + if (this.props.document.embed) { + contentElement.push( +
+
); + } else + contentElement.push(
    +
    + {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, key, addDoc, remDoc, this.move, + this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth, this.props.panelHeight)} +
    +
); } else { bulletType = BulletType.Collapsed; } @@ -315,11 +350,13 @@ class TreeView extends React.Component { add: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean, remove: ((doc: Doc) => void), move: DragManager.MoveFunction, - indent: () => void, dropAction: dropActionType, addDocTab: (doc: Doc, where: string) => void, screenToLocalXf: () => Transform, - active: () => boolean + outerXf: () => number[], + active: () => boolean, + panelWidth: () => number, + panelHeight: () => number ) { let docList = docs.filter(child => !child.excludeFromLibrary && (key !== "data" || !child.isMinimized)); return docList.map((child, i) => { @@ -338,10 +375,13 @@ class TreeView extends React.Component { indentDocument={indent} deleteDoc={remove} addDocument={add} + panelWidth={panelWidth} + panelHeight={panelHeight} moveDocument={move} dropAction={dropAction} addDocTab={addDocTab} ScreenToLocalTransform={screenToLocalXf} + outerXf={outerXf} parentKey={key} active={active} />; }); @@ -351,6 +391,7 @@ class TreeView extends React.Component { @observer export class CollectionTreeView extends CollectionSubView(Document) { private treedropDisposer?: DragManager.DragDropDisposer; + private _mainEle?: HTMLDivElement; protected createTreeDropTarget = (ele: HTMLDivElement) => { if (this.treedropDisposer) { this.treedropDisposer(); @@ -358,6 +399,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { if (ele) { this.treedropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } }); } + this._mainEle = ele; } @action @@ -373,6 +415,10 @@ export class CollectionTreeView extends CollectionSubView(Document) { } } + outerXf = () => { + let outerXf = Utils.GetScreenTransform(this._mainEle!); + return [outerXf.translateX, outerXf.translateY]; + } onTreeDrop = (e: React.DragEvent) => { this.onDrop(e, {}); } @@ -384,7 +430,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { let addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => TreeView.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before); let moveDoc = (d: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc); let childElements = TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.fieldKey, addDoc, this.remove, - moveDoc, emptyFunction, dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.active); + moveDoc, dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.outerXf, this.props.active, this.props.PanelWidth, () => 25); return (
StrCast(this.props.Document.title)} + OnFillDown={(value: string) => { + let target = this.props.Document.proto ? this.props.Document.proto : this.props.Document; + target.title = value; + let doc = Docs.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25 }); + TreeView.loadId = doc[Id]; + doc.templates = new List([Templates.Title.Layout]); + this.props.addDocument(doc); + }} SetValue={(value: string) => { let target = this.props.Document.proto ? this.props.Document.proto : this.props.Document; target.title = value; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 4992669df..f7836d947 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -72,6 +72,7 @@ export interface DocumentViewProps { ScreenToLocalTransform: () => Transform; isTopMost: boolean; ContentScaling: () => number; + useActualDimensions?: boolean; PanelWidth: () => number; PanelHeight: () => number; focus: (doc: Doc) => void; @@ -538,8 +539,8 @@ export class DocumentView extends DocComponent(Docu render() { var scaling = this.props.ContentScaling(); - var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; - var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; + var nativeWidth = this.props.useActualDimensions ? NumCast(this.props.Document.width) : this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; + var nativeHeight = this.props.useActualDimensions ? NumCast(this.props.Document.height) : BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; return (
Date: Wed, 19 Jun 2019 17:36:52 -0400 Subject: Added basic keyboard controls to context menu --- src/client/views/ContextMenu.tsx | 40 ++++++++++++++++++++++++++++---- src/client/views/ContextMenuItem.tsx | 4 ++-- src/client/views/nodes/DocumentView.tsx | 41 +++++++++++++++++---------------- src/client/views/nodes/IconBox.tsx | 6 ++--- 4 files changed, 61 insertions(+), 30 deletions(-) (limited to 'src/client/views/nodes/DocumentView.tsx') diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index 33de57cfa..59a0de2a0 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -14,7 +14,7 @@ library.add(faCircle); export class ContextMenu extends React.Component { static Instance: ContextMenu; - @observable private _items: Array = [{ description: "test", event: (e: React.MouseEvent) => e.preventDefault(), icon: "smile" }]; + @observable private _items: Array = []; @observable private _pageX: number = 0; @observable private _pageY: number = 0; @observable private _display: boolean = false; @@ -109,6 +109,10 @@ export class ContextMenu extends React.Component { return flattenItems(this._items, name => [name]); } + @computed get flatItems(): OriginalMenuProps[] { + return this.filteredItems.filter(item => !Array.isArray(item)) as OriginalMenuProps[]; + } + @computed get filteredViews() { const createGroupHeader = (contents: any) => { return ( @@ -123,7 +127,7 @@ export class ContextMenu extends React.Component { if (Array.isArray(value)) { return createGroupHeader(value.join(" -> ")); } else { - return createItem(value, ++itemIndex === this.selectedIndex); + return createItem(value, itemIndex++ === this.selectedIndex); } }); } @@ -148,18 +152,44 @@ export class ContextMenu extends React.Component { - + {this.menuItems}
); } + @action + onKeyDown = (e: React.KeyboardEvent) => { + if (e.key === "ArrowDown") { + if (this.selectedIndex < this.flatItems.length - 1) { + this.selectedIndex++; + } + e.preventDefault(); + } else if (e.key === "ArrowUp") { + if (this.selectedIndex > 0) { + this.selectedIndex--; + } + e.preventDefault(); + } else if (e.key === "Enter") { + const item = this.flatItems[this.selectedIndex]; + item.event(); + this.closeMenu(); + } + } + @action onChange = (e: React.ChangeEvent) => { this._searchString = e.target.value; - if (this._searchString && this.selectedIndex === -1) { - this.selectedIndex = 0; + if (!this._searchString) { + this.selectedIndex = -1; + } + else { + if (this.selectedIndex === -1) { + this.selectedIndex = 0; + } else { + this.selectedIndex = Math.min(this.flatItems.length - 1, this.selectedIndex); + } } } } \ No newline at end of file diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx index ebcac7428..9bbb97d7e 100644 --- a/src/client/views/ContextMenuItem.tsx +++ b/src/client/views/ContextMenuItem.tsx @@ -9,7 +9,7 @@ library.add(faAngleRight); export interface OriginalMenuProps { description: string; - event: (e: React.MouseEvent) => void; + event: () => void; icon?: IconProp; //maybe should be optional (icon?) closeMenu?: () => void; } @@ -37,7 +37,7 @@ export class ContextMenuItem extends React.Component) => { if ("event" in this.props) { - this.props.event(e); + this.props.event(); this.props.closeMenu && this.props.closeMenu(); } } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index f7836d947..2062fe1b5 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,5 +1,5 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faAlignCenter, faCaretSquareRight, faCompressArrowsAlt, faUnlock, faLock, faExpandArrowsAlt, faLayerGroup, faSquare, faTrash, faConciergeBell, faFolder, faShare, faMapPin, faLink, faFingerprint, faCrosshairs, faDesktop } from '@fortawesome/free-solid-svg-icons'; +import * as fa from '@fortawesome/free-solid-svg-icons'; import { action, computed, IReactionDisposer, reaction, trace, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocListCastAsync } from "../../../new_fields/Doc"; @@ -34,23 +34,24 @@ import { ContextMenuProps } from '../ContextMenuItem'; import { RouteStore } from '../../../server/RouteStore'; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? -library.add(faTrash); -library.add(faShare); -library.add(faExpandArrowsAlt); -library.add(faCompressArrowsAlt); -library.add(faLayerGroup); -library.add(faAlignCenter); -library.add(faCaretSquareRight); -library.add(faSquare); -library.add(faConciergeBell); -library.add(faFolder); -library.add(faMapPin); -library.add(faLink); -library.add(faFingerprint); -library.add(faCrosshairs); -library.add(faDesktop); -library.add(faUnlock); -library.add(faLock); +library.add(fa.faTrash); +library.add(fa.faShare); +library.add(fa.faExpandArrowsAlt); +library.add(fa.faCompressArrowsAlt); +library.add(fa.faLayerGroup); +library.add(fa.faExternalLinkAlt); +library.add(fa.faAlignCenter); +library.add(fa.faCaretSquareRight); +library.add(fa.faSquare); +library.add(fa.faConciergeBell); +library.add(fa.faFolder); +library.add(fa.faMapPin); +library.add(fa.faLink); +library.add(fa.faFingerprint); +library.add(fa.faCrosshairs); +library.add(fa.faDesktop); +library.add(fa.faUnlock); +library.add(fa.faLock); const linkSchema = createSchema({ title: "string", @@ -447,7 +448,7 @@ export class DocumentView extends DocComponent(Docu this.templates = this.templates; } - freezeNativeDimensions = (e: React.MouseEvent): void => { + freezeNativeDimensions = (): void => { let proto = Doc.GetProto(this.props.Document); if (proto.ignoreAspect === undefined && !proto.nativeWidth) { proto.nativeWidth = this.props.PanelWidth(); @@ -476,7 +477,7 @@ export class DocumentView extends DocComponent(Docu subitems.push({ description: "Open Right", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, "onRight"), icon: "caret-square-right" }); subitems.push({ description: "Open Right Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), "onRight"), icon: "caret-square-right" }); subitems.push({ description: "Open Fields", event: this.fieldsClicked, icon: "layer-group" }); - cm.addItem({ description: "Open...", subitems: subitems }); + cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" }); cm.addItem({ description: BoolCast(this.props.Document.ignoreAspect, false) || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "edit" }); cm.addItem({ description: "Pin to Pres", event: () => PresentationView.Instance.PinDoc(this.props.Document), icon: "map-pin" }); cm.addItem({ description: BoolCast(this.props.Document.lockedPosition) ? "Unlock Pos" : "Lock Pos", event: () => this.props.Document.lockedPosition = BoolCast(this.props.Document.lockedPosition) ? undefined : true, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx index 00021bc78..d6ab2a34a 100644 --- a/src/client/views/nodes/IconBox.tsx +++ b/src/client/views/nodes/IconBox.tsx @@ -37,14 +37,14 @@ export class IconBox extends React.Component { return ; } - setLabelField = (e: React.MouseEvent): void => { + setLabelField = (): void => { this.props.Document.hideLabel = !BoolCast(this.props.Document.hideLabel); } - setUseOwnTitleField = (e: React.MouseEvent): void => { + setUseOwnTitleField = (): void => { this.props.Document.useOwnTitle = !BoolCast(this.props.Document.useTargetTitle); } - specificContextMenu = (e: React.MouseEvent): void => { + specificContextMenu = (): void => { ContextMenu.Instance.addItem({ description: BoolCast(this.props.Document.hideLabel) ? "Show label with icon" : "Remove label from icon", event: this.setLabelField -- cgit v1.2.3-70-g09d2 From 118ecb14ce519bcbade12b3d52e11b22fcc371b3 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 19 Jun 2019 22:40:54 -0400 Subject: cleaned up and enhanced tree view --- src/client/documents/Documents.ts | 1 - .../views/collections/CollectionSchemaView.tsx | 2 +- .../views/collections/CollectionTreeView.scss | 5 +- .../views/collections/CollectionTreeView.tsx | 347 +++++++++------------ src/client/views/nodes/DocumentView.tsx | 5 +- 5 files changed, 158 insertions(+), 202 deletions(-) (limited to 'src/client/views/nodes/DocumentView.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index fcd1010c6..de6c5bc6a 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -18,7 +18,6 @@ import { action } from "mobx"; import { ColumnAttributeModel } from "../northstar/core/attribute/AttributeModel"; import { AttributeTransformationModel } from "../northstar/core/attribute/AttributeTransformationModel"; import { AggregateFunction } from "../northstar/model/idea/idea"; -import { Template } from "../views/Templates"; import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss"; import { IconBox } from "../views/nodes/IconBox"; import { Field, Doc, Opt } from "../../new_fields/Doc"; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index faea8d44d..9cc8961e3 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -385,7 +385,7 @@ interface CollectionSchemaPreviewProps { Document?: Doc; width: () => number; height: () => number; - CollectionView: CollectionView | CollectionPDFView | CollectionVideoView; + CollectionView?: CollectionView | CollectionPDFView | CollectionVideoView; getTransform: () => Transform; addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; moveDocument: (document: Doc, target: Doc, addDoc: ((doc: Doc) => boolean)) => boolean; diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index ec78fdb80..a85604e58 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -4,10 +4,10 @@ border-width: $COLLECTION_BORDER_WIDTH; border-color: transparent; border-style: solid; - border-radius: $border-radius; + border-radius: inherit; box-sizing: border-box; height: 100%; - padding: 20px; + padding-top: 20px; padding-left: 10px; padding-right: 0px; background: $light-color-secondary; @@ -56,6 +56,7 @@ font-size: 8pt; margin-left: 3px; display:none; + background: lightgray; } .docContainer { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 1eab541b3..856430036 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -1,38 +1,35 @@ -import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; -import { faAngleRight, faCaretDown, faCaretRight, faTrashAlt } from '@fortawesome/free-solid-svg-icons'; +import { library } from '@fortawesome/fontawesome-svg-core'; +import { faAngleRight, faCaretDown, faCaretRight, faTrashAlt, faCaretSquareRight, faCaretSquareDown } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, observable, trace, computed } from "mobx"; +import { action, observable } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast, WidthSym, HeightSym } from '../../../new_fields/Doc'; +import { Doc, DocListCast, HeightSym, WidthSym } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; +import { List } from '../../../new_fields/List'; import { Document, listSpec } from '../../../new_fields/Schema'; -import { BoolCast, Cast, NumCast, StrCast, PromiseValue } from '../../../new_fields/Types'; +import { BoolCast, Cast, NumCast, StrCast } from '../../../new_fields/Types'; +import { emptyFunction, Utils } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager, dropActionType, SetupDrag } from "../../util/DragManager"; +import { SelectionManager } from '../../util/SelectionManager'; +import { Transform } from '../../util/Transform'; import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from '../ContextMenu'; import { EditableView } from "../EditableView"; import { MainView } from '../MainView'; +import { Templates } from '../Templates'; import { CollectionViewType } from './CollectionBaseView'; import { CollectionDockingView } from './CollectionDockingView'; -import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView"; +import { CollectionSchemaPreview } from './CollectionSchemaView'; +import { CollectionSubView } from "./CollectionSubView"; import "./CollectionTreeView.scss"; import React = require("react"); -import { Transform } from '../../util/Transform'; -import { SelectionManager } from '../../util/SelectionManager'; -import { emptyFunction, returnFalse, Utils, returnOne, returnZero } from '../../../Utils'; -import { List } from '../../../new_fields/List'; -import { Templates } from '../Templates'; -import { DocumentView, DocumentViewProps } from '../nodes/DocumentView'; -import { number } from 'prop-types'; -import ReactTable from 'react-table'; -import { MainOverlayTextBox } from '../MainOverlayTextBox'; export interface TreeViewProps { document: Doc; - deleteDoc: (doc: Doc) => void; + deleteDoc: (doc: Doc) => boolean; moveDocument: DragManager.MoveFunction; dropAction: "alias" | "copy" | undefined; addDocTab: (doc: Doc, where: string) => void; @@ -41,22 +38,18 @@ export interface TreeViewProps { addDocument: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean; indentDocument?: () => void; ScreenToLocalTransform: () => Transform; - outerXf: () => number[]; + outerXf: () => { translateX: number, translateY: number }; treeViewId: string; parentKey: string; active: () => boolean; } -export enum BulletType { - Collapsed, - Collapsible, - List -} - library.add(faTrashAlt); library.add(faAngleRight); library.add(faCaretDown); library.add(faCaretRight); +library.add(faCaretSquareDown); +library.add(faCaretSquareRight); @observer /** @@ -64,25 +57,22 @@ library.add(faCaretRight); */ class TreeView extends React.Component { private _header?: React.RefObject = React.createRef(); - private treedropDisposer?: DragManager.DragDropDisposer; - private _mainEle?: HTMLDivElement; + private _treedropDisposer?: DragManager.DragDropDisposer; + private _dref = React.createRef(); + @observable _chosenKey: string = "data"; + @observable _collapsed: boolean = true; + protected createTreeDropTarget = (ele: HTMLDivElement) => { - this.treedropDisposer && this.treedropDisposer(); + this._treedropDisposer && this._treedropDisposer(); if (ele) { - this.treedropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.treeDrop.bind(this) } }); + this._treedropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.treeDrop.bind(this) } }); } - this._mainEle = ele; } - @observable _isOver: boolean = false; - @observable _collapsed: boolean = true; - @undoBatch delete = () => this.props.deleteDoc(this.props.document); @undoBatch openRight = async () => this.props.addDocTab(this.props.document, "openRight"); - @action onMouseEnter = () => { this._isOver = true; }; - @action onMouseLeave = () => { this._isOver = false; }; - + onPointerDown = (e: React.PointerEvent) => e.stopPropagation() onPointerEnter = (e: React.PointerEvent): void => { this.props.active() && (this.props.document.libraryBrush = true); if (e.buttons === 1 && SelectionManager.GetIsDragging()) { @@ -101,80 +91,60 @@ class TreeView extends React.Component { let rect = this._header!.current!.getBoundingClientRect(); let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2); let before = x[1] < bounds[1]; - let inside = x[0] > bounds[0] + 75 || (!before && this._bulletType === BulletType.Collapsible); + let inside = x[0] > bounds[0] + 75 || (!before && !this._collapsed); this._header!.current!.className = "treeViewItem-header"; - if (inside && this._bulletType !== BulletType.List) this._header!.current!.className += " treeViewItem-header-inside"; + if (inside) this._header!.current!.className += " treeViewItem-header-inside"; else if (before) this._header!.current!.className += " treeViewItem-header-above"; else if (!before) this._header!.current!.className += " treeViewItem-header-below"; e.stopPropagation(); } - onPointerDown = (e: React.PointerEvent) => { - e.stopPropagation(); - } @action - remove = (document: Document, key: string) => { + remove = (document: Document, key: string): boolean => { let children = Cast(this.props.document[key], listSpec(Doc), []); - children.indexOf(document) !== -1 && children.splice(children.indexOf(document), 1); + if (children.indexOf(document) !== -1) { + children.splice(children.indexOf(document), 1); + return true; + } + return false; } @action - move: DragManager.MoveFunction = (document: Doc, target: Doc, addDoc) => { - if (this.props.document !== target) { - //TODO This should check if it was removed - this.props.deleteDoc(document); - return addDoc(document); - } - return true; + move: DragManager.MoveFunction = (doc: Doc, target: Doc, addDoc) => { + return this.props.document !== target && this.props.deleteDoc(doc) && addDoc(doc); } @action - indent = () => { - this.props.addDocument(this.props.document); - this.delete(); - } - + indent = () => this.props.addDocument(this.props.document) && this.delete(); - renderBullet(type: BulletType) { - let onClicked = action(() => this._collapsed = !this._collapsed); - let bullet: IconProp | undefined = undefined; - switch (type) { - case BulletType.Collapsed: bullet = "caret-right"; break; - case BulletType.Collapsible: bullet = "caret-down"; break; - } - return
{bullet ? : ""}
; + renderBullet() { + let docList = Cast(this.props.document["data"], listSpec(Doc)); + let doc = Cast(this.props.document["data"], Doc); + let isDoc = doc instanceof Doc || docList; + return
this._collapsed = !this._collapsed)}> + {} +
; } - static loadId = ""; - editableView = (key: string, style?: string) => - ( StrCast(this.props.document[key])} - OnFillDown={(value: string) => { - Doc.GetProto(this.props.document)[key] = value; - let doc = Docs.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25 }); - TreeView.loadId = doc[Id]; - doc.templates = new List([Templates.Title.Layout]); - this.props.addDocument(doc); - return true; - }} - OnTab={() => this.props.indentDocument && this.props.indentDocument()} - SetValue={(value: string) => { - Doc.GetProto(this.props.document)[key] = value; - return true; - }} - />) - /** - * Renders the EditableView title element for placement into the tree. - */ - renderTitle() { - let reference = React.createRef(); - let onItemDown = SetupDrag(reference, () => this.props.document, this.move, this.props.dropAction, this.props.treeViewId, true); + static loadId = ""; + editableView = (key: string, style?: string) => ( StrCast(this.props.document[key])} + SetValue={(value: string) => (Doc.GetProto(this.props.document)[key] = value) ? true : true} + OnFillDown={(value: string) => { + Doc.GetProto(this.props.document)[key] = value; + let doc = Docs.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List([Templates.Title.Layout]) }); + TreeView.loadId = doc[Id]; + return this.props.addDocument(doc); + }} + OnTab={() => this.props.indentDocument && this.props.indentDocument()} + />) + get keyList() { let keyList: string[] = []; let keys = Array.from(Object.keys(this.props.document)); if (this.props.document.proto instanceof Doc) { @@ -192,19 +162,26 @@ class TreeView extends React.Component { keyList.push(key); } }); - let headerElements = this._bulletType === BulletType.List ? (null) : [this._chosenKey].map(key => - { this._chosenKey = key; this.props.document.embed = !BoolCast(this.props.document.embed, false) })} - style={{ background: key === this._chosenKey ? "lightgray" : undefined }}> - {key} + return keyList; + } + /** + * Renders the EditableView title element for placement into the tree. + */ + renderTitle() { + let reference = React.createRef(); + let onItemDown = SetupDrag(reference, () => this.props.document, this.move, this.props.dropAction, this.props.treeViewId, true); + + let headerElements = ( + this.props.document.embed = !BoolCast(this.props.document.embed))} > + {this._chosenKey} ); let dataDocs = CollectionDockingView.Instance ? Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc), []) : []; let openRight = dataDocs && dataDocs.indexOf(this.props.document) !== -1 ? (null) : (
- {/* */}
); return <> -
{ let rect = this._header!.current!.getBoundingClientRect(); let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2); let before = x[1] < bounds[1]; - let inside = x[0] > bounds[0] + 75 || (!before && this._bulletType === BulletType.Collapsible); + let inside = x[0] > bounds[0] + 75 || (!before && !this._collapsed); if (de.data instanceof DragManager.DocumentDragData) { let addDoc = (doc: Doc) => this.props.addDocument(doc, this.props.document, before); if (inside) { @@ -250,18 +227,13 @@ class TreeView extends React.Component { addDoc = (doc: Doc) => { docList && docList.push(doc); return true; }; } } - let added = false; - if (de.data.dropAction || de.data.userDropAction) { - added = de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.props.document, before) || added, false); - } else if (de.data.moveDocument) { - let movedDocs = de.data.options === this.props.treeViewId ? de.data.draggedDocuments : de.data.droppedDocuments; - added = movedDocs.reduce((added: boolean, d) => - de.data.moveDocument(d, this.props.document, addDoc) || added, false); - } else { - added = de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.props.document, before), false); - } e.stopPropagation(); - return added; + let movedDocs = (de.data.options === this.props.treeViewId ? de.data.draggedDocuments : de.data.droppedDocuments); + return (de.data.dropAction || de.data.userDropAction) ? + de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.props.document, before) || added, false) + : (de.data.moveDocument) ? + movedDocs.reduce((added: boolean, d) => de.data.moveDocument(d, this.props.document, addDoc) || added, false) + : de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.props.document, before), false); } return false; } @@ -279,62 +251,47 @@ class TreeView extends React.Component { docTransform = () => { let { scale, translateX, translateY } = Utils.GetScreenTransform(this._dref.current!); let outerXf = this.props.outerXf(); - let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf[0] - translateX, outerXf[1] - translateY); + let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY); let finalXf = this.props.ScreenToLocalTransform().translate(offset[0], offset[1]); return finalXf; } - @observable _chosenKey: string = "data"; - _bulletType: BulletType = BulletType.List; - _dref = React.createRef(); render() { - let bulletType = BulletType.List; - let contentElement: (JSX.Element | null)[] = []; - [this._chosenKey].map(key => { - let docList = Cast(this.props.document[key], listSpec(Doc)); - let remDoc = (doc: Doc) => this.remove(doc, key); - let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => TreeView.AddDocToList(this.props.document, key, doc, addBefore, before); - let doc = Cast(this.props.document[key], Doc); - if (doc instanceof Doc || (docList && (DocListCast(docList).length > 0 || key === "data"))) { - if (!this._collapsed) { - bulletType = BulletType.Collapsible; - if (this.props.document.embed) { - contentElement.push( -
-
); - } else - contentElement.push(
    -
    - {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, key, addDoc, remDoc, this.move, - this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth, this.props.panelHeight)} -
    -
); - } else { - bulletType = BulletType.Collapsed; - } + let contentElement: (JSX.Element | null) = null; + let docList = Cast(this.props.document[this._chosenKey], listSpec(Doc)); + let remDoc = (doc: Doc) => this.remove(doc, this._chosenKey); + let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => TreeView.AddDocToList(this.props.document, this._chosenKey, doc, addBefore, before); + let doc = Cast(this.props.document[this._chosenKey], Doc); + let docWidth = () => NumCast(this.props.document.nativeWidth) ? Math.min(this.props.document[WidthSym](), this.props.panelWidth() - 5) : this.props.panelWidth() - 5; + if (!this._collapsed) { + if (!this.props.document.embed && (docList && (DocListCast(docList).length > 0 || this._chosenKey === "data"))) { + contentElement =
    + {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, this._chosenKey, addDoc, remDoc, this.move, + this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth)} +
; + } else { + contentElement =
+ + +
} - }); - this._bulletType = bulletType; - return
+ } + return
  • - {this.renderBullet(bulletType)} + {this.renderBullet()} {this.renderTitle()}
    @@ -348,17 +305,17 @@ class TreeView extends React.Component { treeViewId: string, key: string, add: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean, - remove: ((doc: Doc) => void), + remove: ((doc: Doc) => boolean), move: DragManager.MoveFunction, dropAction: dropActionType, addDocTab: (doc: Doc, where: string) => void, screenToLocalXf: () => Transform, - outerXf: () => number[], + outerXf: () => { translateX: number, translateY: number }, active: () => boolean, panelWidth: () => number, - panelHeight: () => number ) { let docList = docs.filter(child => !child.excludeFromLibrary && (key !== "data" || !child.isMinimized)); + let rowWidth = () => panelWidth() - 20; return docList.map((child, i) => { let indent = i === 0 ? undefined : () => { if (StrCast(docList[i - 1].layout).indexOf("CollectionView") !== -1) { @@ -368,15 +325,22 @@ class TreeView extends React.Component { remove(child); } } + let addDocument = (doc: Doc, relativeTo?: Doc, before?: boolean) => { + return add(doc, relativeTo ? relativeTo : docList[i], before !== undefined ? before : false); + } + let rowHeight = () => { + let aspect = NumCast(child.nativeWidth, 0) / NumCast(child.nativeHeight, 0); + return aspect ? Math.min(child[WidthSym](), rowWidth()) / aspect : child[HeightSym](); + } return { export class CollectionTreeView extends CollectionSubView(Document) { private treedropDisposer?: DragManager.DragDropDisposer; private _mainEle?: HTMLDivElement; + protected createTreeDropTarget = (ele: HTMLDivElement) => { - if (this.treedropDisposer) { - this.treedropDisposer(); - } - if (ele) { + this.treedropDisposer && this.treedropDisposer(); + if (this._mainEle = ele) { this.treedropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } }); } - this._mainEle = ele; + } + + componentWillUnmount() { + this.treedropDisposer && this.treedropDisposer(); } @action - remove = (document: Document) => { + remove = (document: Document): boolean => { let children = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []); - children.indexOf(document) !== -1 && children.splice(children.indexOf(document), 1); + if (children.indexOf(document) !== -1) { + children.splice(children.indexOf(document), 1); + return true; + } + return false; } onContextMenu = (e: React.MouseEvent): void => { // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout @@ -415,26 +385,16 @@ export class CollectionTreeView extends CollectionSubView(Document) { } } - outerXf = () => { - let outerXf = Utils.GetScreenTransform(this._mainEle!); - return [outerXf.translateX, outerXf.translateY]; - } - onTreeDrop = (e: React.DragEvent) => { - this.onDrop(e, {}); - } + outerXf = () => Utils.GetScreenTransform(this._mainEle!); + onTreeDrop = (e: React.DragEvent) => this.onDrop(e, {}); + render() { let dropAction = StrCast(this.props.Document.dropAction) as dropActionType; - if (!this.childDocs) { - return (null); - } let addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => TreeView.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before); let moveDoc = (d: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc); - let childElements = TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.fieldKey, addDoc, this.remove, - moveDoc, dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.outerXf, this.props.active, this.props.PanelWidth, () => 25); - return ( + return !this.childDocs ? (null) : (
    this.props.isSelected() && e.stopPropagation()} onDrop={this.onTreeDrop} @@ -445,22 +405,19 @@ export class CollectionTreeView extends CollectionSubView(Document) { display={"inline"} height={72} GetValue={() => StrCast(this.props.Document.title)} + SetValue={(value: string) => (Doc.GetProto(this.props.Document).title = value) ? true : true} OnFillDown={(value: string) => { - let target = this.props.Document.proto ? this.props.Document.proto : this.props.Document; - target.title = value; - let doc = Docs.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25 }); + Doc.GetProto(this.props.Document).title = value; + let doc = Docs.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List([Templates.Title.Layout]) }); TreeView.loadId = doc[Id]; - doc.templates = new List([Templates.Title.Layout]); - this.props.addDocument(doc); - }} - SetValue={(value: string) => { - let target = this.props.Document.proto ? this.props.Document.proto : this.props.Document; - target.title = value; - return true; + TreeView.AddDocToList(this.props.Document, this.props.fieldKey, doc, this.childDocs.length ? this.childDocs[0] : undefined, true); }} />
      - {childElements} + { + TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.fieldKey, addDoc, this.remove, + moveDoc, dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.outerXf, this.props.active, this.props.PanelWidth) + }
    ); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index f7836d947..4992669df 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -72,7 +72,6 @@ export interface DocumentViewProps { ScreenToLocalTransform: () => Transform; isTopMost: boolean; ContentScaling: () => number; - useActualDimensions?: boolean; PanelWidth: () => number; PanelHeight: () => number; focus: (doc: Doc) => void; @@ -539,8 +538,8 @@ export class DocumentView extends DocComponent(Docu render() { var scaling = this.props.ContentScaling(); - var nativeWidth = this.props.useActualDimensions ? NumCast(this.props.Document.width) : this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; - var nativeHeight = this.props.useActualDimensions ? NumCast(this.props.Document.height) : BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; + var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; + var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; return (