From db34b52b66311a72f986a0406acdb935369d264f Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 1 Jul 2019 12:38:51 -0400 Subject: added some context menu undo's --- src/client/views/nodes/DocumentView.tsx | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 9edde896f..a6f324a04 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -393,8 +393,13 @@ export class DocumentView extends DocComponent(Docu this._lastTap = Date.now(); } - deleteClicked = (): void => { this.props.removeDocument && this.props.removeDocument(this.props.Document); }; - fieldsClicked = (): void => { let kvp = Docs.KVPDocument(this.props.Document, { width: 300, height: 300 }); this.props.addDocTab(kvp, this.dataDoc, "onRight"); }; + @undoBatch + deleteClicked = (): void => { SelectionManager.DeselectAll(); this.props.removeDocument && this.props.removeDocument(this.props.Document); } + + @undoBatch + fieldsClicked = (): void => { let kvp = Docs.KVPDocument(this.props.Document, { width: 300, height: 300 }); this.props.addDocTab(kvp, this.dataDoc, "onRight"); } + + @undoBatch makeBtnClicked = (): void => { let doc = Doc.GetProto(this.props.Document); doc.isButton = !BoolCast(doc.isButton, false); @@ -407,6 +412,8 @@ export class DocumentView extends DocComponent(Docu doc.nativeWidth = doc.nativeHeight = undefined; } } + + @undoBatch public fullScreenClicked = (): void => { CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(this); SelectionManager.DeselectAll(); @@ -467,6 +474,8 @@ export class DocumentView extends DocComponent(Docu this.templates = this.templates; } + @undoBatch + @action freezeNativeDimensions = (): void => { let proto = Doc.GetProto(this.props.Document); if (proto.ignoreAspect === undefined && !proto.nativeWidth) { @@ -477,6 +486,12 @@ export class DocumentView extends DocComponent(Docu proto.ignoreAspect = !BoolCast(proto.ignoreAspect, false); } + @undoBatch + @action + toggleLockPosition = (): void => { + this.props.Document.lockedPosition = BoolCast(this.props.Document.lockedPosition) ? undefined : true; + } + @action onContextMenu = async (e: React.MouseEvent): Promise => { e.persist(); @@ -499,7 +514,7 @@ export class DocumentView extends DocComponent(Docu 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" }); + cm.addItem({ description: BoolCast(this.props.Document.lockedPosition) ? "Unlock Pos" : "Lock Pos", event: this.toggleLockPosition, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); cm.addItem({ description: this.props.Document.isButton ? "Remove Button" : "Make Button", event: this.makeBtnClicked, icon: "concierge-bell" }); cm.addItem({ description: "Find aliases", event: async () => { -- cgit v1.2.3-70-g09d2 From 9a222ede9fafd90e1d62ed40076da68e3aa299b7 Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 1 Jul 2019 13:09:36 -0400 Subject: fixed full screen problem mistaking template --- src/client/views/nodes/DocumentView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index a6f324a04..d5b329899 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -280,7 +280,7 @@ export class DocumentView extends DocComponent(Docu if (this._doubleTap && this.props.renderDepth) { let fullScreenAlias = Doc.MakeAlias(this.props.Document); fullScreenAlias.templates = new List(); - this.props.addDocTab(fullScreenAlias, this.dataDoc, "inTab"); + this.props.addDocTab(fullScreenAlias, this.props.Document === this.dataDoc ? undefined : this.dataDoc, "inTab"); SelectionManager.DeselectAll(); this.props.Document.libraryBrush = false; } -- cgit v1.2.3-70-g09d2 From 2b6e76fb60d4dafd1c5383f21f870542cc808aff Mon Sep 17 00:00:00 2001 From: yipstanley Date: Mon, 1 Jul 2019 13:12:10 -0400 Subject: small changes --- src/client/views/nodes/PDFBox.scss | 5 ++++- src/client/views/pdf/Annotation.tsx | 9 +++++++++ src/client/views/pdf/PDFMenu.tsx | 7 +++++-- src/client/views/pdf/Page.tsx | 7 +++++-- 4 files changed, 23 insertions(+), 5 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss index 9a38d6241..e7655d598 100644 --- a/src/client/views/nodes/PDFBox.scss +++ b/src/client/views/nodes/PDFBox.scss @@ -32,15 +32,18 @@ height: 100px; } -.pdfBox-cont, .pdfBox-cont-interactive { +.pdfBox-cont, +.pdfBox-cont-interactive { display: flex; flex-direction: row; height: 100%; overflow-y: scroll; overflow-x: hidden; } + .pdfBox-cont { pointer-events: none; + .textlayer { pointer-events: none; diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index ff77612d6..e88839edd 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -8,6 +8,7 @@ import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; import PDFMenu from "./PDFMenu"; import { DocumentManager } from "../../util/DocumentManager"; +import { PresentationView } from "../presentationview/PresentationView"; interface IAnnotationProps { anno: Doc; @@ -101,6 +102,13 @@ class RegionAnnotation extends React.Component { PDFMenu.Instance.fadeOut(true); } + pinToPres = () => { + let group = FieldValue(Cast(this.props.document.group, Doc)); + if (group) { + PresentationView.Instance.PinDoc(group); + } + } + @action onPointerDown = (e: React.PointerEvent) => { if (e.button === 0) { @@ -114,6 +122,7 @@ class RegionAnnotation extends React.Component { PDFMenu.Instance.Delete = this.deleteAnnotation.bind(this); PDFMenu.Instance.Pinned = false; PDFMenu.Instance.AddTag = this.addTag.bind(this); + PDFMenu.Instance.PinToPres = this.pinToPres; PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true); } } diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index aeed5213c..f93b2e59f 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -7,6 +7,7 @@ import { emptyFunction, returnZero, returnTrue, returnFalse } from "../../../Uti import { Doc } from "../../../new_fields/Doc"; import { DragManager } from "../../util/DragManager"; import { DocUtils } from "../../documents/Documents"; +import { PresentationView } from "../presentationview/PresentationView"; @observer export default class PDFMenu extends React.Component { @@ -24,6 +25,7 @@ export default class PDFMenu extends React.Component { Delete: () => void = emptyFunction; Snippet: (marquee: { left: number, top: number, width: number, height: number }) => void = emptyFunction; AddTag: (key: string, value: string) => boolean = returnFalse; + PinToPres: () => void = emptyFunction; @observable public Highlighting: boolean = false; @observable public Status: "pdf" | "annotation" | "snippet" | "" = ""; @@ -252,11 +254,12 @@ export default class PDFMenu extends React.Component { ] : [ , -
+ , +
, - , + , ]; return ( diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 782dbd261..e63ac23da 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -183,8 +183,11 @@ export default class Page extends React.Component { let doc = this.props.parent.Document; let view = Doc.MakeAlias(doc); let data = Doc.MakeDelegate(doc.proto!); + data.title = StrCast(data.title) + "_snippet"; view.proto = data; view.nativeHeight = marquee.height; + view.height = (doc[WidthSym]() / NumCast(doc.nativeWidth)) * marquee.height; + view.nativeWidth = doc.nativeWidth; view.startY = marquee.top + this.props.getScrollFromPage(this.props.page); view.width = doc[WidthSym](); let dragData = new DragManager.DocumentDragData([view], [undefined]); @@ -341,7 +344,7 @@ export default class Page extends React.Component { if (PDFMenu.Instance.Highlighting) { - this.highlight(undefined, "#f4f442"); + this.highlight(undefined, "goldenrod"); } else { PDFMenu.Instance.StartDrag = this.startDrag; @@ -402,7 +405,7 @@ export default class Page extends React.Component {
+ style={{ left: `${this._marqueeX}px`, top: `${this._marqueeY}px`, width: `${this._marqueeWidth}px`, height: `${this._marqueeHeight}px`, background: "red", border: "10px dashed black" }}> {/* */}
-- cgit v1.2.3-70-g09d2 From 6776a8d8d27c38adc5b232aab925c103cc1c59ba Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 1 Jul 2019 18:04:17 -0400 Subject: switched dataDoc stuff around for documentviews. --- src/client/views/nodes/DocumentView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index d5b329899..687c7f73d 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -217,7 +217,7 @@ export class DocumentView extends DocComponent(Docu e.stopPropagation(); } - get dataDoc() { return this.props.DataDoc ? this.props.DataDoc : this.props.Document; } + get dataDoc() { return this.props.DataDoc !== this.props.Document ? this.props.DataDoc : undefined; } startDragging(x: number, y: number, dropAction: dropActionType, dragSubBullets: boolean) { if (this._mainCont.current) { let allConnected = [this.props.Document, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])]; @@ -280,7 +280,7 @@ export class DocumentView extends DocComponent(Docu if (this._doubleTap && this.props.renderDepth) { let fullScreenAlias = Doc.MakeAlias(this.props.Document); fullScreenAlias.templates = new List(); - this.props.addDocTab(fullScreenAlias, this.props.Document === this.dataDoc ? undefined : this.dataDoc, "inTab"); + this.props.addDocTab(fullScreenAlias, this.dataDoc, "inTab"); SelectionManager.DeselectAll(); this.props.Document.libraryBrush = false; } -- cgit v1.2.3-70-g09d2 From 16822d88f981e7a86729d942a92b6ad8590680e4 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 1 Jul 2019 22:32:44 -0400 Subject: fixed title template view issue --- src/client/views/TemplateMenu.tsx | 8 ++++++++ src/client/views/Templates.tsx | 4 ++-- src/client/views/collections/CollectionDockingView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 5 +++++ 4 files changed, 16 insertions(+), 3 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index d63f45e5a..1b32f0ddd 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -64,6 +64,13 @@ export class TemplateMenu extends React.Component { } } + @undoBatch + @action + clearTemplates = (event: React.MouseEvent) => { + this.props.docs.map(d => d.clearTemplates()); + Array.from(this.props.templates.keys()).map(t => this.props.templates.set(t, false)); + } + @action componentWillReceiveProps(nextProps: TemplateMenuProps) { // this._templates = nextProps.templates; @@ -84,6 +91,7 @@ export class TemplateMenu extends React.Component {
this.toggleTemplateActivity()}>+
    {templateMenu} +
); diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx index 5bb8d454a..4843a70a6 100644 --- a/src/client/views/Templates.tsx +++ b/src/client/views/Templates.tsx @@ -50,7 +50,7 @@ export namespace Templates { export const Title = new Template("Title", TemplatePosition.InnerTop, `
- {props.DataDoc.title} + {props.Document.title}
{layout}
@@ -58,7 +58,7 @@ export namespace Templates {
` ); export const Header = new Template("Header", TemplatePosition.InnerTop, - `< div style = "display:flex; flex-direction:column; height:100%;" > + `
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index ad5472a4a..8724216f5 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -269,7 +269,7 @@ export class CollectionDockingView extends React.Component(Docu } this.templates = this.templates; } + @action + clearTemplates = () => { + this.templates.length = 0; + this.templates = this.templates; + } @undoBatch @action -- cgit v1.2.3-70-g09d2 From a8664face6f19cd2373bd928c2a2b3043bb6278d Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Tue, 2 Jul 2019 19:02:51 -0400 Subject: Added arrange script to freeform views --- src/client/views/MainView.tsx | 4 +- src/client/views/OverlayView.tsx | 46 ++++++++++++++++++++++ src/client/views/ScriptBox.tsx | 36 +++++++++++++++++ .../collectionFreeForm/CollectionFreeFormView.tsx | 41 ++++++++++++++++++- .../views/nodes/CollectionFreeFormDocumentView.tsx | 12 ++++-- src/client/views/nodes/KeyValueBox.tsx | 12 ++++-- 6 files changed, 141 insertions(+), 10 deletions(-) create mode 100644 src/client/views/OverlayView.tsx create mode 100644 src/client/views/ScriptBox.tsx (limited to 'src/client/views/nodes') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 04ecc47cf..cc39dc04c 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -38,6 +38,7 @@ import PDFMenu from './pdf/PDFMenu'; import { InkTool } from '../../new_fields/InkField'; import _ from "lodash"; import KeyManager from './GlobalKeyHandler'; +import { OverlayView } from './OverlayView'; @observer export class MainView extends React.Component { @@ -291,7 +292,7 @@ export class MainView extends React.Component { } else { CollectionDockingView.Instance.AddRightSplit(doc, dataDoc); } - } + }; let flyout = +
); } diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx new file mode 100644 index 000000000..72f1068ce --- /dev/null +++ b/src/client/views/OverlayView.tsx @@ -0,0 +1,46 @@ +import * as React from "react"; +import { observer } from "mobx-react"; +import { observable, action } from "mobx"; + +export type OverlayDisposer = () => void; + +export type OverlayElementOptions = { + x: number; + y: number; + width?: number; + height?: number; +}; + +@observer +export class OverlayView extends React.Component { + public static Instance: OverlayView; + @observable.shallow + private _elements: { ele: JSX.Element, options: OverlayElementOptions }[] = []; + + constructor(props: any) { + super(props); + if (!OverlayView.Instance) { + OverlayView.Instance = this; + } + } + + @action + addElement(ele: JSX.Element, options: OverlayElementOptions): OverlayDisposer { + const eleWithPosition = { ele, options }; + this._elements.push(eleWithPosition); + return action(() => { + const index = this._elements.indexOf(eleWithPosition); + if (index !== -1) this._elements.splice(index, 1); + }); + } + + render() { + return ( +
+ {this._elements.map(({ ele, options: { x, y, width, height } }) => ( +
{ele}
+ ))} +
+ ); + } +} \ No newline at end of file diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx new file mode 100644 index 000000000..aea9d52a4 --- /dev/null +++ b/src/client/views/ScriptBox.tsx @@ -0,0 +1,36 @@ +import * as React from "react"; +import { observer } from "mobx-react"; +import { observable, action } from "mobx"; + +export interface ScriptBoxProps { + onSave: (text: string, onError: (error: string) => void) => void; + onCancel?: () => void; +} + +@observer +export class ScriptBox extends React.Component { + @observable + private _scriptText: string = ""; + + @action + onChange = (e: React.ChangeEvent) => { + this._scriptText = e.target.value; + } + + @action + onError = (error: string) => { + console.log(error); + } + + render() { + return ( +
+
+ + +
+ +
+ ); + } +} \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 169dbe540..23c0fdd0d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -27,6 +27,10 @@ import "./CollectionFreeFormView.scss"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); import v5 = require("uuid/v5"); +import { ScriptField } from "../../../../new_fields/ScriptField"; +import { OverlayView } from "../../OverlayView"; +import { ScriptBox } from "../../ScriptBox"; +import { CompileScript } from "../../../util/Scripting"; export const panZoomSchema = createSchema({ @@ -388,6 +392,18 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { }; } + getCalculatedPositions(doc: Doc, index: number, collection: Doc): { x?: number, y?: number, width?: number, height?: number } | undefined { + const script = Cast(this.props.Document.arrangeScript, ScriptField); + if (!script) { + return undefined; + } + const result = script.script.run({ doc, index, collection }); + if (!result.success) { + return undefined; + } + return result.result; + } + @computed.struct get views() { let curPage = FieldValue(this.Document.curPage, -1); @@ -397,7 +413,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { if (Math.round(page) === Math.round(curPage) || page === -1) { let minim = BoolCast(doc.isMinimized, false); if (minim === undefined || !minim) { - prev.push(); + const pos = this.getCalculatedPositions(doc, prev.length, this.Document) || {}; + prev.push(); } } return prev; @@ -438,6 +455,28 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } } }); + ContextMenu.Instance.addItem({ + description: "Add freeform arrangement", + event: () => { + let overlayDisposer: () => void; + let scriptingBox = overlayDisposer()} onSave={(text, onError) => { + const script = CompileScript(text, { + params: { + doc: "Doc", index: "number", collection: "Doc" + }, + requiredType: "{x: number, y: number, width?: number, height?: number}", + typecheck: false + }); + if (!script.compiled) { + onError(script.errors.map(error => error.messageText).join("\n")); + return; + } + this.props.Document.arrangeScript = new ScriptField(script); + overlayDisposer(); + }} />; + overlayDisposer = OverlayView.Instance.addElement(scriptingBox, { x: 100, y: 100, width: 200, height: 200 }); + } + }); } private childViews = () => [ diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 858959d81..8556817cc 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -9,6 +9,10 @@ import "./DocumentView.scss"; import React = require("react"); export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { + x?: number; + y?: number; + width?: number; + height?: number; } const schema = createSchema({ @@ -23,13 +27,13 @@ const FreeformDocument = makeInterface(schema, positionSchema); @observer export class CollectionFreeFormDocumentView extends DocComponent(FreeformDocument) { @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 X() { return this.props.x !== undefined ? this.props.x : this.Document.x || 0; } + @computed get Y() { return this.props.y !== undefined ? this.props.y : this.Document.y || 0; } + @computed get width(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : this.props.width !== undefined ? this.props.width : this.Document.width || 0; } + @computed get height(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : this.props.height !== undefined ? this.props.height : this.Document.height || 0; } @computed get zoom(): number { return 1 / FieldValue(this.Document.zoomBasis, 1); } @computed get nativeWidth(): number { return FieldValue(this.Document.nativeWidth, 0); } @computed get nativeHeight(): number { return FieldValue(this.Document.nativeHeight, 0); } - @computed get width(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : FieldValue(this.Document.width, 0); } - @computed get height(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : FieldValue(this.Document.height, 0); } set width(w: number) { this.Document.width = w; diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 0e798d291..9407d742c 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -9,7 +9,7 @@ import { KeyValuePair } from "./KeyValuePair"; import React = require("react"); import { NumCast, Cast, FieldValue, StrCast } from "../../../new_fields/Types"; import { Doc, Field, FieldResult } from "../../../new_fields/Doc"; -import { ComputedField } from "../../../new_fields/ScriptField"; +import { ComputedField, ScriptField } from "../../../new_fields/ScriptField"; import { SetupDrag } from "../../util/DragManager"; import { Docs } from "../../documents/Documents"; import { RawDataOperationParameters } from "../../northstar/model/idea/idea"; @@ -50,7 +50,7 @@ export class KeyValueBox extends React.Component { let eq = value.startsWith("="); let target = eq ? doc : Doc.GetProto(doc); value = eq ? value.substr(1) : value; - let dubEq = value.startsWith(":="); + let dubEq = value.startsWith(":=") ? 1 : value.startsWith(";=") ? 2 : 0; value = dubEq ? value.substr(2) : value; let options: ScriptOptions = { addReturn: true, params: { this: "Doc" } }; if (dubEq) options.typecheck = false; @@ -58,8 +58,12 @@ export class KeyValueBox extends React.Component { if (!script.compiled) { return false; } - let field = new ComputedField(script); - if (!dubEq) { + let field: Field; + if (dubEq === 1) { + field = new ComputedField(script); + } else if (dubEq === 2) { + field = new ScriptField(script); + } else { let res = script.run({ this: target }); if (!res.success) return false; field = res.result; -- cgit v1.2.3-70-g09d2 From d059c64a7b3ce424b8c41c485c7c58c0d4bfbae8 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 2 Jul 2019 21:42:08 -0400 Subject: fixed doc decorations border for documents laid out with arrangeScripts --- src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 8556817cc..5171f7a04 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -52,7 +52,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 * NumCast(this.props.Document.width, this.width) / this.width) animateBetweenIcon = (icon: number[], stime: number, maximizing: boolean) => { this.props.bringToFront(this.props.Document); -- cgit v1.2.3-70-g09d2 From 5e35e1895bb54568bbf8cc3991480e20edd1de83 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 2 Jul 2019 23:28:57 -0400 Subject: fixed issue with editing applied template text boxes. --- src/client/views/MainOverlayTextBox.tsx | 7 +++++-- src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 3 ++- src/client/views/nodes/FormattedTextBox.tsx | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx index d31319429..7d15702a2 100644 --- a/src/client/views/MainOverlayTextBox.tsx +++ b/src/client/views/MainOverlayTextBox.tsx @@ -30,6 +30,7 @@ export class MainOverlayTextBox extends React.Component private _textBox: FormattedTextBox | undefined; private _tooltip?: HTMLElement; @observable public TextDoc?: Doc; + @observable public TextDataDoc?: Doc; updateTooltip = () => { this._outerdiv && this._tooltip && !this._outerdiv.contains(this._tooltip) && this._outerdiv.appendChild(this._tooltip); @@ -43,13 +44,15 @@ export class MainOverlayTextBox extends React.Component (box?: FormattedTextBox) => { this._textBox = box; if (box) { - this.TextDoc = box.props.DataDoc; + this.TextDoc = box.props.Document; + this.TextDataDoc = box.props.DataDoc; 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"); } else { this.TextDoc = undefined; + this.TextDataDoc = undefined; this.setTextDoc(); } }); @@ -114,7 +117,7 @@ export class MainOverlayTextBox extends React.Component } } render() { - this.TextDoc; + this.TextDoc; this.TextDataDoc; if (FormattedTextBox.InputBoxOverlay && this._textTargetDiv) { let textRect = this._textTargetDiv.getBoundingClientRect(); let s = this._textXf().Scale; diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 5171f7a04..3f9270597 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -47,12 +47,13 @@ export class CollectionFreeFormDocumentView extends DocComponent this.nativeWidth > 0 ? this.width / this.nativeWidth : 1; panelWidth = () => this.props.PanelWidth(); panelHeight = () => this.props.PanelHeight(); getTransform = (): Transform => this.props.ScreenToLocalTransform() .translate(-this.X, -this.Y) - .scale(1 / this.contentScaling()).scale(1 / this.zoom * NumCast(this.props.Document.width, this.width) / this.width) + .scale(1 / this.contentScaling()).scale(1 / this.zoom / this.scaleToOverridingWidth) animateBetweenIcon = (icon: number[], stime: number, maximizing: boolean) => { this.props.bringToFront(this.props.Document); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 07cd43ce3..1eeb04755 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -211,7 +211,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe const field = this.dataDoc ? Cast(this.dataDoc[this.props.fieldKey], RichTextField) : undefined; return field ? field.Data : `{"doc":{"type":"doc","content":[]},"selection":{"type":"text","anchor":0,"head":0}}`; }, - field => this._editorView && !this._applyingChange && this.props.Document[this.props.fieldKey] instanceof RichTextField && + field => this._editorView && !this._applyingChange && this._editorView.updateState(EditorState.fromJSON(config, JSON.parse(field))) ); this.setupEditor(config, this.dataDoc, this.props.fieldKey); -- cgit v1.2.3-70-g09d2 From c619bad0281ea6f248c48b8d418f324f67f530dd Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 3 Jul 2019 12:00:59 -0400 Subject: added to fitToBox to treeviews. fixed some fitToBox issues. moved notifications button to library tree view. --- src/client/northstar/dash-nodes/HistogramBox.tsx | 2 +- .../dash-nodes/HistogramBoxPrimitives.tsx | 2 - src/client/views/Main.scss | 4 +- src/client/views/MainView.tsx | 84 ++++++++-------------- .../views/collections/CollectionSchemaView.tsx | 4 +- .../views/collections/CollectionTreeView.tsx | 38 +++++++++- .../views/collections/CollectionVideoView.tsx | 1 - .../collectionFreeForm/CollectionFreeFormView.tsx | 6 +- src/client/views/nodes/DocumentView.tsx | 5 +- src/client/views/nodes/FieldView.tsx | 2 +- src/client/views/nodes/LinkEditor.tsx | 1 - src/client/views/search/SearchItem.tsx | 26 +++---- src/new_fields/Doc.ts | 12 ++++ 13 files changed, 98 insertions(+), 89 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx index a60eaea85..b81eafbee 100644 --- a/src/client/northstar/dash-nodes/HistogramBox.tsx +++ b/src/client/northstar/dash-nodes/HistogramBox.tsx @@ -36,7 +36,7 @@ export class HistogramBox extends React.Component { @computed public get HistogramResult(): HistogramResult { return this.HistoOp.Result as HistogramResult; } @observable public SizeConverter: SizeConverter = new SizeConverter(); - @computed get createOperationParamsCache() { trace(); return this.HistoOp.CreateOperationParameters(); } + @computed get createOperationParamsCache() { return this.HistoOp.CreateOperationParameters(); } @computed get BinRanges() { return this.HistogramResult ? this.HistogramResult.binRanges : undefined; } @computed get ChartType() { return !this.BinRanges ? ChartType.SinglePoint : this.BinRanges[0] instanceof AggregateBinRange ? diff --git a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx index 350987695..5a16b3782 100644 --- a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx +++ b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx @@ -62,7 +62,6 @@ export class HistogramBoxPrimitives extends React.Component); } render() { - trace(); return
{this.xaxislines} {this.yaxislines} diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss index 08f5cb8a5..44c1cb9fc 100644 --- a/src/client/views/Main.scss +++ b/src/client/views/Main.scss @@ -111,8 +111,8 @@ button:hover { //toolbar stuff #toolbar { position: absolute; - bottom: 62px; - left: 24px; + right: 8px; + top: 5px; .toolbar-button { display: block; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 19a04595a..1542fedef 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1,44 +1,43 @@ import { IconName, library } from '@fortawesome/fontawesome-svg-core'; -import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faArrowDown, faArrowUp, faCheck, faPenNib, faThumbtack, faRedoAlt, faTable, faTree, faUndoAlt, faBell, faCommentAlt, faCut, faExclamation } from '@fortawesome/free-solid-svg-icons'; +import { faArrowDown, faArrowUp, faBell, faCheck, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faThumbtack, faTree, faUndoAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, configure, observable, runInAction, trace } from 'mobx'; +import { action, computed, configure, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; -import { CirclePicker, SliderPicker, BlockPicker, TwitterPicker, SketchPicker } from 'react-color'; import "normalize.css"; import * as React from 'react'; +import { SketchPicker } from 'react-color'; import Measure from 'react-measure'; import * as request from 'request'; +import { Doc, DocListCast, Opt } from '../../new_fields/Doc'; +import { Id } from '../../new_fields/FieldSymbols'; +import { InkTool } from '../../new_fields/InkField'; +import { List } from '../../new_fields/List'; +import { listSpec } from '../../new_fields/Schema'; +import { Cast, FieldValue } from '../../new_fields/Types'; import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; import { RouteStore } from '../../server/RouteStore'; -import { emptyFunction, returnTrue, Utils, returnOne, returnZero } from '../../Utils'; -import { Docs, DocTypes } from '../documents/Documents'; -import { SetupDrag, DragManager } from '../util/DragManager'; +import { emptyFunction, returnOne, returnTrue } from '../../Utils'; +import { DocServer } from '../DocServer'; +import { Docs } from '../documents/Documents'; +import { SetupDrag } from '../util/DragManager'; +import { HistoryUtil } from '../util/History'; import { Transform } from '../util/Transform'; import { UndoManager } from '../util/UndoManager'; -import { PresentationView } from './presentationview/PresentationView'; +import { CollectionBaseView } from './collections/CollectionBaseView'; import { CollectionDockingView } from './collections/CollectionDockingView'; import { ContextMenu } from './ContextMenu'; import { DocumentDecorations } from './DocumentDecorations'; +import KeyManager from './GlobalKeyHandler'; import { InkingControl } from './InkingControl'; import "./Main.scss"; import { MainOverlayTextBox } from './MainOverlayTextBox'; import { DocumentView } from './nodes/DocumentView'; +import { OverlayView } from './OverlayView'; +import PDFMenu from './pdf/PDFMenu'; +import { PresentationView } from './presentationview/PresentationView'; import { PreviewCursor } from './PreviewCursor'; import { FilterBox } from './search/FilterBox'; -import { SelectionManager } from '../util/SelectionManager'; -import { FieldResult, Field, Doc, Opt, DocListCast } from '../../new_fields/Doc'; -import { Cast, FieldValue, StrCast, PromiseValue } from '../../new_fields/Types'; -import { DocServer } from '../DocServer'; -import { listSpec } from '../../new_fields/Schema'; -import { Id } from '../../new_fields/FieldSymbols'; -import { HistoryUtil } from '../util/History'; -import { CollectionBaseView } from './collections/CollectionBaseView'; -import { List } from '../../new_fields/List'; -import PDFMenu from './pdf/PDFMenu'; -import { InkTool } from '../../new_fields/InkField'; -import _ from "lodash"; -import KeyManager from './GlobalKeyHandler'; -import { OverlayView } from './OverlayView'; +import { CollectionTreeView } from './collections/CollectionTreeView'; @observer export class MainView extends React.Component { @@ -184,8 +183,6 @@ export class MainView extends React.Component { } } - @observable _notifsCol: Opt; - @action openWorkspace = async (doc: Doc, fromHistory = false) => { CurrentUserUtils.MainDocId = doc[Id]; @@ -197,18 +194,12 @@ export class MainView extends React.Component { if (col) { const l = Cast(col.data, listSpec(Doc)); if (l) { - runInAction(() => this._notifsCol = col); + runInAction(() => CollectionTreeView.NotifsCol = col); } } }, 100); } - openNotifsCol = () => { - if (this._notifsCol && CollectionDockingView.Instance) { - CollectionDockingView.Instance.AddRightSplit(this._notifsCol, undefined); - } - } - onDrop = (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); @@ -284,24 +275,25 @@ export class MainView extends React.Component { document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); } + flyoutWidthFunc = () => this.flyoutWidth; + addDocTabFunc = (doc: Doc) => { + if (doc.dockingConfig) { + this.openWorkspace(doc); + } else { + CollectionDockingView.Instance.AddRightSplit(doc, undefined); + } + }; @computed get flyout() { - let addDocTab = (doc: Doc, dataDoc: Doc | undefined, location: string) => { - if (doc.dockingConfig) { - this.openWorkspace(doc); - } else { - CollectionDockingView.Instance.AddRightSplit(doc, dataDoc); - } - }; return addDocTab(doc, undefined, "onRight")} + addDocTab={this.addDocTabFunc} removeDocument={undefined} ScreenToLocalTransform={Transform.Identity} ContentScaling={returnOne} - PanelWidth={this.getPWidth} + PanelWidth={this.flyoutWidthFunc} PanelHeight={this.getPHeight} renderDepth={0} selectOnLoad={false} @@ -403,23 +395,9 @@ export class MainView extends React.Component { /* @TODO this should really be moved into a moveable toolbar component, but for now let's put it here to meet the deadline */ @computed get miscButtons() { - const length = this._notifsCol ? DocListCast(this._notifsCol.data).length : 0; - const notifsRef = React.createRef(); - const dragNotifs = action(() => this._notifsCol!); let logoutRef = React.createRef(); return [ -
-
- -
0 ? { "display": "initial" } : { "display": "none" }}> - {length} -
-
-
, this.isSearchVisible ?
: null,
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index b0d46953c..d06d1dab6 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -334,7 +334,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { @computed get reactTable() { - trace(); let previewWidth = this.previewWidth() + 2 * this.borderWidth + this.DIVIDER_WIDTH + 1; return doc) { } render() { - trace(); return (
this.onDrop(e, {})} onContextMenu={this.onContextMenu} ref={this.createTarget}> @@ -404,6 +402,7 @@ interface CollectionSchemaPreviewProps { Document?: Doc; DataDocument?: Doc; childDocs?: Doc[]; + fitToBox?: () => number[]; renderDepth: number; width: () => number; height: () => number; @@ -472,6 +471,7 @@ export class CollectionSchemaPreview extends React.Component { } ContextMenu.Instance.displayMenu(e.pageX > 156 ? e.pageX - 156 : 0, e.pageY - 15); e.stopPropagation(); + e.preventDefault(); } } @@ -312,6 +314,11 @@ class TreeView extends React.Component { return ele; } + fitToBox = () => { + let layoutDoc = Doc.expandTemplateLayout(this.props.document, this.props.dataDoc); + let bounds = Doc.ComputeContentBounds(layoutDoc); + return [(bounds.x + bounds.r) / 2, (bounds.y + bounds.b) / 2, Math.min(this.props.panelHeight() / (bounds.b - bounds.y), this.props.panelWidth() / (bounds.r - bounds.x))]; + } render() { let contentElement: (JSX.Element | null) = null; let docList = Cast(this.resolvedDataDoc[this._chosenKey], listSpec(Doc)); @@ -333,6 +340,7 @@ class TreeView extends React.Component { Document={layoutDoc} DataDocument={this.resolvedDataDoc} renderDepth={this.props.renderDepth} + fitToBox={this.fitToBox} width={docWidth} height={layoutDoc[HeightSym]} getTransform={this.docTransform} @@ -456,6 +464,31 @@ export class CollectionTreeView extends CollectionSubView(Document) { outerXf = () => Utils.GetScreenTransform(this._mainEle!); onTreeDrop = (e: React.DragEvent) => this.onDrop(e, {}); + + @observable static NotifsCol: Opt; + + openNotifsCol = () => { + if (CollectionTreeView.NotifsCol && CollectionDockingView.Instance) { + CollectionDockingView.Instance.AddRightSplit(CollectionTreeView.NotifsCol, undefined); + } + } + @computed get notifsButton() { + const length = CollectionTreeView.NotifsCol ? DocListCast(CollectionTreeView.NotifsCol.data).length : 0; + const notifsRef = React.createRef(); + const dragNotifs = action(() => CollectionTreeView.NotifsCol!); + return
+
+ +
0 ? { "display": "initial" } : { "display": "none" }}> + {length} +
+
+
; + } + render() { let dropAction = StrCast(this.props.Document.dropAction) as dropActionType; let addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before); @@ -480,6 +513,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { TreeView.loadId = doc[Id]; Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, this.childDocs.length ? this.childDocs[0] : undefined, true); }} /> + {this.props.Document.excludeFromLibrary ? this.notifsButton : (null)}
    { TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.Document, this.props.DataDoc, this.props.fieldKey, addDoc, this.remove, diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx index c1a6ca44e..1984965ba 100644 --- a/src/client/views/collections/CollectionVideoView.tsx +++ b/src/client/views/collections/CollectionVideoView.tsx @@ -122,7 +122,6 @@ export class CollectionVideoView extends React.Component { } render() { - trace(); return ( {this.subView} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 23c0fdd0d..cb7be3f28 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -54,9 +54,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @computed get nativeHeight() { return this.Document.nativeHeight || 0; } public get isAnnotationOverlay() { return this.props.fieldKey === "annotations" || this.props.fieldExt === "annotations"; } private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; } - private panX = () => this.props.fitToBox ? this.props.fitToBox[0] : this.Document.panX || 0; - private panY = () => this.props.fitToBox ? this.props.fitToBox[1] : this.Document.panY || 0; - private zoomScaling = () => this.props.fitToBox ? this.props.fitToBox[2] : this.Document.scale || 1; + private panX = () => this.props.fitToBox ? this.props.fitToBox()[0] : this.Document.panX || 0; + private panY = () => this.props.fitToBox ? this.props.fitToBox()[1] : this.Document.panY || 0; + private zoomScaling = () => this.props.fitToBox ? this.props.fitToBox()[2] : this.Document.scale || 1; private centeringShiftX = () => !this.nativeWidth ? this._pwidth / 2 : 0; // shift so pan position is at center of window for non-overlay collections private centeringShiftY = () => !this.nativeHeight ? this._pheight / 2 : 0;// shift so pan position is at center of window for non-overlay collections private getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth + 1, -this.borderWidth + 1).translate(-this.centeringShiftX(), -this.centeringShiftY()).transform(this.getLocalTransform()); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 3673cb0e2..8b75d0d25 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -71,7 +71,7 @@ export interface DocumentViewProps { ContainingCollectionView: Opt; Document: Doc; DataDoc?: Doc; - fitToBox?: number[]; + fitToBox?: () => number[]; addDocument?: (doc: Doc, allowDuplicates?: boolean) => boolean; removeDocument?: (doc: Doc) => boolean; moveDocument?: (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; @@ -573,8 +573,7 @@ export class DocumentView extends DocComponent(Docu @computed get nativeWidth() { return this.Document.nativeWidth || 0; } @computed get nativeHeight() { return this.Document.nativeHeight || 0; } @computed get contents() { - return ( - ); + return (); } render() { diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 3f5a2e744..c5fc6c65a 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -31,7 +31,7 @@ export interface FieldViewProps { fieldKey: string; fieldExt: string; leaveNativeSize?: boolean; - fitToBox?: number[]; + fitToBox?: () => number[]; ContainingCollectionView: Opt; Document: Doc; DataDoc?: Doc; diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index 22da732cf..e6cc50620 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -271,7 +271,6 @@ export class LinkGroupEditor extends React.Component { ); } - trace(); return (
    diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx index 5a7bdd24d..129d71b3b 100644 --- a/src/client/views/search/SearchItem.tsx +++ b/src/client/views/search/SearchItem.tsx @@ -101,34 +101,23 @@ export class SearchItem extends React.Component { @observable _useIcons = true; @observable _displayDim = 50; + fitToBox = () => { + let bounds = Doc.ComputeContentBounds(this.props.doc); + return [(bounds.x + bounds.r) / 2, (bounds.y + bounds.b) / 2, Number(SEARCH_THUMBNAIL_SIZE) / Math.max((bounds.b - bounds.y), (bounds.r - bounds.x)), this._displayDim]; + } @computed public get DocumentIcon() { - let layoutresult = StrCast(this.props.doc.type); if (!this._useIcons) { - let renderDoc = this.props.doc; - let box: number[] = []; - if (layoutresult.indexOf(DocTypes.COL) !== -1) { - renderDoc = Doc.MakeDelegate(renderDoc); - let bounds = DocListCast(renderDoc.data).reduce((bounds, doc) => { - var [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)]; - let [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()]; - return { - x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y), - r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b) - }; - }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE }); - box = [(bounds.x + bounds.r) / 2, (bounds.y + bounds.b) / 2, Number(SEARCH_THUMBNAIL_SIZE) / (bounds.r - bounds.x), this._displayDim]; - } let returnXDimension = () => this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE); let returnYDimension = () => this._displayDim; - let scale = () => returnXDimension() / NumCast(renderDoc.nativeWidth, returnXDimension()); + let scale = () => returnXDimension() / NumCast(this.props.doc.nativeWidth, returnXDimension()); return
    { this._useIcons = !this._useIcons; this._displayDim = this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE); })} onPointerEnter={action(() => this._displayDim = this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE))} onPointerLeave={action(() => this._displayDim = 50)} > {
    ; } + let layoutresult = StrCast(this.props.doc.type); let button = layoutresult.indexOf(DocTypes.PDF) !== -1 ? faFilePdf : layoutresult.indexOf(DocTypes.IMG) !== -1 ? faImage : layoutresult.indexOf(DocTypes.TEXT) !== -1 ? faStickyNote : diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 734a90731..29d35e19f 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -249,6 +249,18 @@ export namespace Doc { return true; } + export function ComputeContentBounds(doc: Doc) { + let bounds = DocListCast(doc.data).reduce((bounds, doc) => { + var [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)]; + let [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()]; + return { + x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y), + r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b) + }; + }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE }); + return bounds; + } + // // Resolves a reference to a field by returning 'doc' if o field extension is specified, // otherwise, it returns the extension document stored in doc._ext. -- cgit v1.2.3-70-g09d2 From 6c5468eee0ec59d4ddaf116e67d067b567ccb87a Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 3 Jul 2019 12:12:08 -0400 Subject: pdf next/prev annotation fixes/improvements --- src/client/views/nodes/PDFBox.tsx | 2 +- src/client/views/pdf/Annotation.tsx | 2 +- src/client/views/pdf/PDFViewer.tsx | 26 ++++++++++++++++---------- 3 files changed, 18 insertions(+), 12 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 83dedb71d..cc02bb282 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -149,7 +149,7 @@ export class PDFBox extends DocComponent(PdfDocumen scrollTo(y: number) { if (this._mainCont.current) { - this._mainCont.current.scrollTo({ top: y }); + this._mainCont.current.scrollTo({ top: y, behavior: "auto" }); } } diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index 9718c1406..0a1661a1a 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -75,7 +75,7 @@ class RegionAnnotation extends React.Component { () => this.props.parent.Index, () => { if (this.props.parent.Index === this.props.index) { - this.props.parent.scrollTo(this.props.y - 50); + this.props.parent.scrollTo(this.props.y * scale - (NumCast(this.props.parent.props.parent.Document.pdfHeight) / 2)); } } ); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 35bf1c4d7..ad062a54d 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -166,6 +166,7 @@ export class Viewer extends React.Component { } }); } + this.Index = -1; }); } ); @@ -234,11 +235,15 @@ export class Viewer extends React.Component { mainAnnoDoc.title = "Annotation on " + StrCast(this.props.parent.Document.title); mainAnnoDoc.pdfDoc = this.props.parent.Document; + let minY = Number.MAX_VALUE; this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => { for (let anno of value) { let annoDoc = new Doc(); if (anno.style.left) annoDoc.x = parseInt(anno.style.left) / scale; - if (anno.style.top) annoDoc.y = parseInt(anno.style.top) / scale; + if (anno.style.top) { + annoDoc.y = parseInt(anno.style.top) / scale; + minY = Math.min(parseInt(anno.style.top), minY); + } if (anno.style.height) annoDoc.height = parseInt(anno.style.height) / scale; if (anno.style.width) annoDoc.width = parseInt(anno.style.width) / scale; annoDoc.page = key; @@ -251,12 +256,13 @@ export class Viewer extends React.Component { } }); - mainAnnoDoc.y = Math.max((NumCast(annoDocs[0].y) * scale) - 100, 0); + mainAnnoDoc.y = Math.max(minY, 0); mainAnnoDoc.annotations = new List(annoDocs); if (sourceDoc) { DocUtils.MakeLink(sourceDoc, mainAnnoDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); } this._savedAnnotations.clear(); + this.Index = -1; return mainAnnoDoc; } @@ -562,9 +568,10 @@ export class Viewer extends React.Component { prevAnnotation = (e: React.MouseEvent) => { e.stopPropagation(); - if (this.Index > 0) { - this.Index--; - } + // if (this.Index > 0) { + // this.Index--; + // } + this.Index = Math.max(this.Index - 1, 0); } @action @@ -572,7 +579,7 @@ export class Viewer extends React.Component { e.stopPropagation(); let compiled = this._script; - if (this.Index < this._annotations.filter(anno => { + let filtered = this._annotations.filter(anno => { if (compiled && compiled.compiled) { let run = compiled.run({ this: anno }); if (run.success) { @@ -580,9 +587,8 @@ export class Viewer extends React.Component { } } return true; - }).length - 1) { - this.Index++; - } + }); + this.Index = Math.min(this.Index + 1, filtered.length - 1) } nextResult = () => { @@ -630,7 +636,7 @@ export class Viewer extends React.Component { } } return true; - }).map((anno: Doc, index: number) => this.renderAnnotation(anno, index))} + }).sort((a: Doc, b: Doc) => NumCast(a.y) - NumCast(b.y)).map((anno: Doc, index: number) => this.renderAnnotation(anno, index))}
    e.stopPropagation()} -- cgit v1.2.3-70-g09d2 From c77dfd3fcb2df644fcf2c177db7e6e376ca4c3a9 Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 3 Jul 2019 14:29:53 -0400 Subject: quick and dirty arrange transition animations --- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 5 ++++- src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index cb7be3f28..6b0cb5728 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,6 +1,6 @@ import { action, computed } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCastAsync, HeightSym, WidthSym } from "../../../../new_fields/Doc"; +import { Doc, DocListCastAsync, HeightSym, WidthSym, DocListCast } from "../../../../new_fields/Doc"; import { Id } from "../../../../new_fields/FieldSymbols"; import { InkField, StrokeData } from "../../../../new_fields/InkField"; import { createSchema, makeInterface } from "../../../../new_fields/Schema"; @@ -458,6 +458,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { ContextMenu.Instance.addItem({ description: "Add freeform arrangement", event: () => { + const docs = DocListCast(this.Document[this.props.fieldKey]); + docs.map(d => d.transition = "transform 1s"); let overlayDisposer: () => void; let scriptingBox = overlayDisposer()} onSave={(text, onError) => { const script = CompileScript(text, { @@ -473,6 +475,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } this.props.Document.arrangeScript = new ScriptField(script); overlayDisposer(); + setTimeout(() => docs.map(d => d.transition = undefined), 1200); }} />; overlayDisposer = OverlayView.Instance.addElement(scriptingBox, { x: 100, y: 100, width: 200, height: 200 }); } diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 3f9270597..30cf74f3e 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -1,7 +1,7 @@ import { computed } from "mobx"; import { observer } from "mobx-react"; import { createSchema, makeInterface } from "../../../new_fields/Schema"; -import { BoolCast, FieldValue, NumCast } from "../../../new_fields/Types"; +import { BoolCast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types"; import { Transform } from "../../util/Transform"; import { DocComponent } from "../DocComponent"; import { DocumentView, DocumentViewProps, positionSchema } from "./DocumentView"; @@ -86,6 +86,7 @@ export class CollectionFreeFormDocumentView extends DocComponent Date: Wed, 3 Jul 2019 22:15:25 -0400 Subject: implemented directory import routine --- src/client/documents/Documents.ts | 57 ++++++++- src/client/util/Import & Export/ImageImporter.tsx | 67 +++++++++++ src/client/util/Import & Export/ImportBox.tsx | 134 +++++++++++++++++++++ src/client/util/SelectionManager.ts | 2 +- src/client/views/MainView.tsx | 3 + src/client/views/collections/CollectionSubView.tsx | 46 +------ src/client/views/nodes/DocumentContentsView.tsx | 3 +- 7 files changed, 265 insertions(+), 47 deletions(-) create mode 100644 src/client/util/Import & Export/ImageImporter.tsx create mode 100644 src/client/util/Import & Export/ImportBox.tsx (limited to 'src/client/views/nodes') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 7d7a1f02a..5d637dd3a 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -36,6 +36,7 @@ import { UndoManager } from "../util/UndoManager"; import { RouteStore } from "../../server/RouteStore"; import { LinkManager } from "../util/LinkManager"; import { DocumentManager } from "../util/DocumentManager"; +import ImportBox from "../util/Import & Export/ImportBox"; var requestImageSize = require('../util/request-image-size'); var path = require('path'); @@ -51,7 +52,8 @@ export enum DocTypes { KVP = "kvp", VID = "video", AUDIO = "audio", - LINK = "link" + LINK = "link", + IMPORT = "import" } export interface DocumentOptions { @@ -127,6 +129,7 @@ export namespace Docs { let audioProto: Doc; let pdfProto: Doc; let iconProto: Doc; + let importProto: Doc; // let linkProto: Doc; const textProtoId = "textProto"; const histoProtoId = "histoProto"; @@ -138,6 +141,7 @@ export namespace Docs { const videoProtoId = "videoProto"; const audioProtoId = "audioProto"; const iconProtoId = "iconProto"; + const importProtoId = "importProto"; // const linkProtoId = "linkProto"; export function initProtos(): Promise { @@ -152,6 +156,7 @@ export namespace Docs { audioProto = fields[audioProtoId] as Doc || CreateAudioPrototype(); pdfProto = fields[pdfProtoId] as Doc || CreatePdfPrototype(); iconProto = fields[iconProtoId] as Doc || CreateIconPrototype(); + importProto = fields[importProtoId] as Doc || CreateImportPrototype(); }); } @@ -174,6 +179,11 @@ export namespace Docs { return imageProto; } + function CreateImportPrototype(): Doc { + let importProto = setupPrototypeOptions(importProtoId, "IMPORT_PROTO", ImportBox.LayoutString(), { x: 0, y: 0, width: 600, height: 600, type: DocTypes.IMPORT }); + return importProto; + } + function CreateHistogramPrototype(): Doc { let histoProto = setupPrototypeOptions(histoProtoId, "HISTO PROTO", CollectionView.LayoutString("annotations"), { x: 0, y: 0, width: 300, height: 300, backgroundColor: "black", backgroundLayout: HistogramBox.LayoutString(), type: DocTypes.HIST }); @@ -261,6 +271,10 @@ export namespace Docs { return CreateInstance(audioProto, new AudioField(new URL(url)), options); } + export function DirectoryImportDocument(options: DocumentOptions = {}) { + return CreateInstance(importProto, "", options); + } + export function HistogramDocument(histoOp: HistogramOperation, options: DocumentOptions = {}) { return CreateInstance(histoProto, new HistogramField(histoOp), options); } @@ -333,6 +347,47 @@ export namespace Docs { return CreateInstance(collProto, new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }, id); } + export async function getDocumentFromType(type: string, path: string, options: DocumentOptions, addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean): Promise> { + let ctor: ((path: string, options: DocumentOptions) => (Doc | Promise)) | undefined = undefined; + if (type.indexOf("image") !== -1) { + ctor = Docs.ImageDocument; + } + if (type.indexOf("video") !== -1) { + ctor = Docs.VideoDocument; + } + if (type.indexOf("audio") !== -1) { + ctor = Docs.AudioDocument; + } + if (type.indexOf("pdf") !== -1) { + ctor = Docs.PdfDocument; + options.nativeWidth = 1200; + } + if (type.indexOf("excel") !== -1) { + ctor = Docs.DBDocument; + options.dropAction = "copy"; + } + if (type.indexOf("html") !== -1) { + if (path.includes(window.location.hostname)) { + let s = path.split('/'); + let id = s[s.length - 1]; + DocServer.GetRefField(id).then(field => { + if (field instanceof Doc) { + let alias = Doc.MakeAlias(field); + alias.x = options.x || 0; + alias.y = options.y || 0; + alias.width = options.width || 300; + alias.height = options.height || options.width || 300; + addDocument && addDocument(alias, false); + } + }); + return undefined; + } + ctor = Docs.WebDocument; + options = { height: options.width, ...options, title: path, nativeWidth: undefined }; + } + return ctor ? ctor(path, options) : undefined; + } + export function CaptionDocument(doc: Doc) { const captionDoc = Doc.MakeAlias(doc); captionDoc.overlayLayout = FixedCaption(); diff --git a/src/client/util/Import & Export/ImageImporter.tsx b/src/client/util/Import & Export/ImageImporter.tsx new file mode 100644 index 000000000..d664f6487 --- /dev/null +++ b/src/client/util/Import & Export/ImageImporter.tsx @@ -0,0 +1,67 @@ +import "fs"; +import React = require("react"); +import { Doc } from "../../../new_fields/Doc"; +import { DocServer } from "../../DocServer"; +import { RouteStore } from "../../../server/RouteStore"; +import { action } from "mobx"; +import { Docs } from "../../documents/Documents"; +import { FieldViewProps } from "../../views/nodes/FieldView"; + +interface ImageImporterProps { + addSchema: (imageDocs: Doc[]) => void; +} + +export default class BulkImporter extends React.Component { + private selector = React.createRef(); + + handleSelection = async (e: React.ChangeEvent) => { + let promises: Promise[] = []; + let docs: Doc[] = []; + + let files = e.target.files; + if (!files) return; + + for (let i = 0; i < files.length; i++) { + let target = files.item(i); + + if (target === null) { + continue; + } + + let type = target.type; + let formData = new FormData(); + formData.append('file', target); + let dropFileName = target ? target.name : "-empty-"; + + let prom = fetch(DocServer.prepend(RouteStore.upload), { + method: 'POST', + body: formData + }).then(async (res: Response) => { + (await res.json()).map(action((file: any) => { + let path = window.location.origin + file; + let docPromise = Docs.getDocumentFromType(type, path, { nativeWidth: 300, width: 300, title: dropFileName }); + docPromise.then(doc => doc && docs.push(doc)); + })); + }); + promises.push(prom); + } + + await Promise.all(promises); + + let parent = Docs.SchemaDocument(["title", "data"], docs, { width: 300, height: 300, title: "Bulk Import from Directory" }); + } + + componentDidMount() { + this.selector.current!.setAttribute("directory", "true"); + this.selector.current!.setAttribute("webkitdirectory", "true"); + } + + render() { + return ( +
    + +
    + ); + } + +} \ No newline at end of file diff --git a/src/client/util/Import & Export/ImportBox.tsx b/src/client/util/Import & Export/ImportBox.tsx new file mode 100644 index 000000000..630911710 --- /dev/null +++ b/src/client/util/Import & Export/ImportBox.tsx @@ -0,0 +1,134 @@ +import "fs"; +import React = require("react"); +import { Doc } from "../../../new_fields/Doc"; +import { DocServer } from "../../DocServer"; +import { RouteStore } from "../../../server/RouteStore"; +import { action, observable } from "mobx"; +import { FieldViewProps, FieldView } from "../../views/nodes/FieldView"; +import Measure, { ContentRect } from "react-measure"; +import { library } from '@fortawesome/fontawesome-svg-core'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faArrowUp } from '@fortawesome/free-solid-svg-icons'; +import { Docs, DocumentOptions } from "../../documents/Documents"; + +interface ImageImporterProps { + addSchema: (imageDocs: Doc[]) => void; +} + +export default class ImportBox extends React.Component { + @observable private top = 0; + @observable private left = 0; + private dimensions = 50; + + constructor(props: FieldViewProps) { + super(props); + library.add(faArrowUp); + } + + public static LayoutString() { return FieldView.LayoutString(ImportBox); } + + private selector = React.createRef(); + + handleSelection = async (e: React.ChangeEvent) => { + let promises: Promise[] = []; + let docs: Doc[] = []; + + let files = e.target.files; + if (!files || files.length === 0) return; + + let directory = (files.item(0) as any).webkitRelativePath.split("/", 1); + + for (let i = 0; i < files.length; i++) { + let uploaded_file = files.item(i); + + if (!uploaded_file) { + continue; + } + + let formData = new FormData(); + formData.append('file', uploaded_file); + let dropFileName = uploaded_file ? uploaded_file.name : "-empty-"; + let type = uploaded_file.type; + + let prom = fetch(DocServer.prepend(RouteStore.upload), { + method: 'POST', + body: formData + }).then(async (res: Response) => { + (await res.json()).map(action((file: any) => { + let path = DocServer.prepend(file); + let docPromise = Docs.getDocumentFromType(type, path, { nativeWidth: 300, width: 300, title: dropFileName }); + docPromise.then(doc => doc && docs.push(doc)); + })); + }); + promises.push(prom); + } + + await Promise.all(promises); + + let doc = this.props.Document; + let options: DocumentOptions = { title: `Import of ${directory}`, width: 500, height: 500, x: Doc.GetT(doc, "x", "number"), y: Doc.GetT(doc, "y", "number") }; + let parent = this.props.ContainingCollectionView; + if (parent) { + let importContainer = Docs.StackingDocument(docs, options); + Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer); + this.props.removeDocument && this.props.removeDocument(doc); + } + } + + componentDidMount() { + this.selector.current!.setAttribute("directory", "true"); + this.selector.current!.setAttribute("webkitdirectory", "true"); + } + + @action + preserveCentering = (rect: ContentRect) => { + let bounds = rect.offset!; + if (bounds.width === 0 || bounds.height === 0) { + return; + } + let offset = this.dimensions / 2; + this.left = bounds.width / 2 - offset; + this.top = bounds.height / 2 - offset; + } + + render() { + let dimensions = 50; + return ( + + {({ measureRef }) => +
    + +
    ; } - titleClicked = (e: React.MouseEvent) => { - if (this._collapsed) return false; - else { - this.props.document.embed = !BoolCast(this.props.document.embed); - return true; - } - } static loadId = ""; editableView = (key: string, style?: string) => ( StrCast(this.props.document[key])} @@ -236,18 +231,19 @@ class TreeView extends React.Component { onWorkspaceContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped()) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 - ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, this.props.dataDoc ? this.props.dataDoc : kvp, "onRight"); }, icon: "layer-group" }); + ContextMenu.Instance.addItem({ description: (BoolCast(this.props.document.embed) ? "Collapse" : "Expand") + " inline", event: () => this.props.document.embed = !BoolCast(this.props.document.embed), icon: "expand" }); if (NumCast(this.props.document.viewType) !== CollectionViewType.Docking) { ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.resolvedDataDoc, "inTab"), icon: "folder" }); ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.resolvedDataDoc, "onRight"), icon: "caret-square-right" }); if (DocumentManager.Instance.getDocumentViews(this.resolvedDataDoc).length) { - ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.resolvedDataDoc).map(view => view.props.focus(this.props.document, true)) }); + ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.resolvedDataDoc).map(view => view.props.focus(this.props.document, true)), icon: "camera" }); } - ContextMenu.Instance.addItem({ description: "Delete Item", event: undoBatch(() => this.props.deleteDoc(this.props.document)) }); + ContextMenu.Instance.addItem({ description: "Delete Item", event: undoBatch(() => this.props.deleteDoc(this.props.document)), icon: "trash-alt" }); } else { - ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.resolvedDataDoc)) }); - ContextMenu.Instance.addItem({ description: "Delete Workspace", event: undoBatch(() => this.props.deleteDoc(this.props.document)) }); + ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.resolvedDataDoc)), icon: "caret-square-right" }); + ContextMenu.Instance.addItem({ description: "Delete Workspace", event: undoBatch(() => this.props.deleteDoc(this.props.document)), icon: "trash-alt" }); } + ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, this.props.dataDoc ? this.props.dataDoc : kvp, "onRight"); }, icon: "layer-group" }); ContextMenu.Instance.displayMenu(e.pageX > 156 ? e.pageX - 156 : 0, e.pageY - 15); e.stopPropagation(); e.preventDefault(); @@ -268,6 +264,8 @@ class TreeView extends React.Component { e.stopPropagation(); } if (de.data instanceof DragManager.DocumentDragData) { + e.stopPropagation(); + if (de.data.draggedDocuments[0] === this.props.document) return true; let addDoc = (doc: Doc) => this.props.addDocument(doc, this.resolvedDataDoc, before); if (inside) { let docList = Cast(this.resolvedDataDoc.data, listSpec(Doc)); @@ -275,7 +273,6 @@ class TreeView extends React.Component { addDoc = (doc: Doc) => { docList && docList.push(doc); return true; }; } } - e.stopPropagation(); 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.resolvedDataDoc, before) || added, false) @@ -314,18 +311,37 @@ class TreeView extends React.Component { return ele; } - fitToBox = () => { + @computed get docBounds() { + if (StrCast(this.props.document.type).indexOf(DocTypes.COL) === -1) return undefined; let layoutDoc = Doc.expandTemplateLayout(this.props.document, this.props.dataDoc); - let bounds = Doc.ComputeContentBounds(layoutDoc); - return [(bounds.x + bounds.r) / 2, (bounds.y + bounds.b) / 2, Math.min(this.props.panelHeight() / (bounds.b - bounds.y), this.props.panelWidth() / (bounds.r - bounds.x))]; + return Doc.ComputeContentBounds(layoutDoc); + } + docWidth = () => { + let aspect = NumCast(this.props.document.nativeHeight) / NumCast(this.props.document.nativeWidth); + if (aspect) return Math.min(this.props.document[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.panelWidth() - 5)); + return NumCast(this.props.document.nativeWidth) ? Math.min(this.props.document[WidthSym](), this.props.panelWidth() - 5) : this.props.panelWidth() - 5; } + docHeight = () => { + let bounds = this.docBounds; + return Math.min(this.MAX_EMBED_HEIGHT, (() => { + let aspect = NumCast(this.props.document.nativeHeight) / NumCast(this.props.document.nativeWidth); + if (aspect) return this.docWidth() * aspect; + if (bounds) return this.docWidth() * (bounds.b - bounds.y) / (bounds.r - bounds.x); + return NumCast(this.props.document.height) ? NumCast(this.props.document.height) : 50; + })()); + } + fitToBox = () => { + let bounds = this.docBounds!; + return [(bounds.x + bounds.r) / 2, (bounds.y + bounds.b) / 2, Math.min(this.docHeight() / (bounds.b - bounds.y), this.docWidth() / (bounds.r - bounds.x))]; + } + render() { let contentElement: (JSX.Element | null) = null; let docList = Cast(this.resolvedDataDoc[this._chosenKey], listSpec(Doc)); let remDoc = (doc: Doc) => this.remove(doc, this._chosenKey); let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.resolvedDataDoc, this._chosenKey, doc, addBefore, before); let doc = Cast(this.resolvedDataDoc[this._chosenKey], Doc); - let 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) { contentElement =
      @@ -335,14 +351,14 @@ class TreeView extends React.Component {
    ; } else { let layoutDoc = Doc.expandTemplateLayout(this.props.document, this.props.dataDoc); - contentElement =
    + contentElement =
    this.cleanupInteractions(false); e.stopPropagation(); } - if (e.key === "c" || e.key === "s" || e.key === "S" || e.key === "e" || e.key === "p") { + if (e.key === "c" || e.key === "s" || e.key === "S" || e.key === "e") { this._commandExecuted = true; e.stopPropagation(); e.preventDefault(); @@ -277,7 +277,7 @@ export class MarqueeView extends React.Component width: bounds.width, height: bounds.height, ink: inkData ? new InkField(this.marqueeInkSelect(inkData)) : undefined, - title: e.key === "s" || e.key === "S" ? "-summary-" : e.key === "p" ? "-summary-" : "a nested collection", + title: e.key === "s" || e.key === "S" ? "-summary-" : "a nested collection", }); this.marqueeInkDelete(inkData); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 8b75d0d25..69dc31fec 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -589,8 +589,10 @@ export class DocumentView extends DocComponent(Docu style={{ outlineColor: "maroon", outlineStyle: "dashed", - outlineWidth: BoolCast(this.props.Document.libraryBrush) || BoolCast(this.props.Document.protoBrush) ? + outlineWidth: (BoolCast(this.props.Document.libraryBrush) || BoolCast(this.props.Document.protoBrush)) && !NumCast(this.props.Document.borderRounding) ? `${this.props.ScreenToLocalTransform().Scale}px` : "0px", + border: (BoolCast(this.props.Document.libraryBrush) || BoolCast(this.props.Document.protoBrush)) && NumCast(this.props.Document.borderRounding) ? + `dashed maroon ${this.props.ScreenToLocalTransform().Scale}px` : undefined, borderRadius: "inherit", background: backgroundColor, width: nativeWidth, -- cgit v1.2.3-70-g09d2 From 575ee72cd6ddd65d6c02c6f50a3266f632b1b858 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 4 Jul 2019 00:17:15 -0400 Subject: highlights current workspace in treeview --- src/client/views/collections/CollectionDockingView.tsx | 2 ++ src/client/views/collections/CollectionTreeView.tsx | 3 ++- src/client/views/nodes/DocumentView.tsx | 4 ++-- src/client/views/presentationview/PresentationElement.tsx | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 8b6f5b6a6..c5f8fb728 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -240,6 +240,7 @@ export class CollectionDockingView extends React.Component this.setupGoldenLayout(), 1); + this.props.Document.workspaceBrush = true; } this._ignoreStateChange = ""; }, { fireImmediately: true }); @@ -249,6 +250,7 @@ export class CollectionDockingView extends React.Component void = () => { try { + this.props.Document.workspaceBrush = false; this._goldenLayout.unbind('itemDropped', this.itemDropped); this._goldenLayout.unbind('tabCreated', this.tabCreated); this._goldenLayout.unbind('stackCreated', this.stackCreated); diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 622ba37d1..3db786043 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -217,7 +217,8 @@ class TreeView extends React.Component { return <>
    diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 69dc31fec..f751a45fe 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -589,9 +589,9 @@ export class DocumentView extends DocComponent(Docu style={{ outlineColor: "maroon", outlineStyle: "dashed", - outlineWidth: (BoolCast(this.props.Document.libraryBrush) || BoolCast(this.props.Document.protoBrush)) && !NumCast(this.props.Document.borderRounding) ? + outlineWidth: BoolCast(this.props.Document.libraryBrush) && !NumCast(this.props.Document.borderRounding) ? `${this.props.ScreenToLocalTransform().Scale}px` : "0px", - border: (BoolCast(this.props.Document.libraryBrush) || BoolCast(this.props.Document.protoBrush)) && NumCast(this.props.Document.borderRounding) ? + border: BoolCast(this.props.Document.libraryBrush) && NumCast(this.props.Document.borderRounding) ? `dashed maroon ${this.props.ScreenToLocalTransform().Scale}px` : undefined, borderRadius: "inherit", background: backgroundColor, diff --git a/src/client/views/presentationview/PresentationElement.tsx b/src/client/views/presentationview/PresentationElement.tsx index d63c0b066..6896ee452 100644 --- a/src/client/views/presentationview/PresentationElement.tsx +++ b/src/client/views/presentationview/PresentationElement.tsx @@ -377,7 +377,7 @@ export default class PresentationElement extends React.Component { p.gotoDocument(p.index, NumCast(this.props.mainDocument.selectedDoc)); e.stopPropagation(); }}> -- cgit v1.2.3-70-g09d2 From d479f5e6bcdb7d1a0edb2f8cf366549abe2a910b Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 4 Jul 2019 14:08:00 -0400 Subject: added text font color setting. adding interactive border radius. moved main toolbar. --- src/client/documents/Documents.ts | 4 +-- src/client/util/DragManager.ts | 1 + src/client/util/RichTextSchema.tsx | 11 ++++++++ src/client/util/TooltipTextMenu.tsx | 3 +- src/client/views/DocumentDecorations.scss | 22 +++++++++++++++ src/client/views/DocumentDecorations.tsx | 32 ++++++++++++++++++++++ src/client/views/InkingControl.tsx | 3 ++ src/client/views/Main.scss | 2 +- src/client/views/MainOverlayTextBox.tsx | 5 ++++ src/client/views/MainView.tsx | 2 +- .../collections/collectionFreeForm/MarqueeView.tsx | 16 ++++------- .../views/nodes/CollectionFreeFormDocumentView.tsx | 15 ++++++---- src/client/views/nodes/DocumentView.tsx | 4 +-- src/client/views/nodes/FormattedTextBox.tsx | 11 +++++++- 14 files changed, 106 insertions(+), 25 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 7a976e7d7..2bddf053a 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -77,7 +77,7 @@ export interface DocumentOptions { backgroundLayout?: string; curPage?: number; documentText?: string; - borderRounding?: number; + borderRounding?: string; schemaColumns?: List; dockingConfig?: string; dbDoc?: Doc; @@ -93,7 +93,7 @@ export namespace DocUtils { if (target === CurrentUserUtils.UserDocument) return; UndoManager.RunInBatch(() => { - let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 }); + let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: "100%" }); linkDoc.type = DocTypes.LINK; let linkDocProto = Doc.GetProto(linkDoc); diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 7dc48fb78..d4b1bc8f4 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -315,6 +315,7 @@ export namespace DragManager { scaleYs.push(scaleY); let dragElement = ele.cloneNode(true) as HTMLElement; dragElement.style.opacity = "0.7"; + dragElement.style.borderRadius = getComputedStyle(ele).borderRadius; dragElement.style.position = "absolute"; dragElement.style.margin = "0"; dragElement.style.top = "0"; diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 63c879d67..2a57180d3 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -348,6 +348,17 @@ export const marks: { [index: string]: MarkSpec } = { }] }, + pFontColor: { + attrs: { + color: { default: "yellow" } + }, + parseDOM: [{ style: 'background: #d9dbdd' }], + toDOM: (node) => { + return ['span', { + style: `color: ${node.attrs.color}` + }]; + } + }, /** FONT SIZES */ pFontSize: { diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 9f8d0b2f6..e3e26d1f4 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -111,7 +111,8 @@ export class TooltipTextMenu { this.fontSizeToNum.set(schema.marks.p32, 32); this.fontSizeToNum.set(schema.marks.p48, 48); this.fontSizeToNum.set(schema.marks.p72, 72); - //this.fontSizeToNum.set(schema.marks.pFontSize,schema.marks.pFontSize.) + this.fontSizeToNum.set(schema.marks.pFontSize, 10); + this.fontSizeToNum.set(schema.marks.pFontSize, 10); this.fontSizes = Array.from(this.fontSizeToNum.keys()); //list types diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index ba9f32d7d..2d430512b 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -26,6 +26,14 @@ $linkGap : 3px; opacity: 0.8; } + .documentDecorations-radius { + pointer-events: auto; + background: black; + opacity: 0.8; + transform: translate(10px, 10px); + grid-row: 4; + } + #documentDecorations-topLeftResizer, #documentDecorations-leftResizer, #documentDecorations-bottomLeftResizer { @@ -44,11 +52,25 @@ $linkGap : 3px; grid-column-start: 5; grid-column-end: 7; } + + #documentDecorations-borderRadius{ + grid-column-start: 5; + grid-column-end: 7; + border-radius: 100%; + .borderRadiusTooltip{ + width:10px; + height:10px; + position:absolute; + } + } #documentDecorations-topLeftResizer, #documentDecorations-bottomRightResizer { cursor: nwse-resize; } + #documentDecorations-bottomRightResizer { + grid-row:4; + } #documentDecorations-topRightResizer, #documentDecorations-bottomLeftResizer { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 3df520428..c7990647a 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -341,6 +341,37 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> iconDoc.y = where[1] + NumCast(selView.props.Document.y); } + _radiusDown = [0, 0]; + @action + onRadiusDown = (e: React.PointerEvent): void => { + e.stopPropagation(); + if (e.button === 0) { + this._radiusDown = [e.clientX, e.clientY]; + this._isPointerDown = true; + this._resizeUndo = UndoManager.StartBatch("DocDecs set radius"); + document.removeEventListener("pointermove", this.onRadiusMove); + document.removeEventListener("pointerup", this.onRadiusUp); + document.addEventListener("pointermove", this.onRadiusMove); + document.addEventListener("pointerup", this.onRadiusUp); + } + } + + onRadiusMove = (e: PointerEvent): void => { + let dist = Math.sqrt((e.clientX - this._radiusDown[0]) * (e.clientX - this._radiusDown[0]) + (e.clientY - this._radiusDown[1]) * (e.clientY - this._radiusDown[1])); + SelectionManager.SelectedDocuments().map(dv => Doc.GetProto(dv.props.Document).borderRounding = `${Math.min(100, dist)}%`); + e.stopPropagation(); + e.preventDefault(); + } + + onRadiusUp = (e: PointerEvent): void => { + e.stopPropagation(); + e.preventDefault(); + this._isPointerDown = false; + this._resizeUndo && this._resizeUndo.end(); + document.removeEventListener("pointermove", this.onRadiusMove); + document.removeEventListener("pointerup", this.onRadiusUp); + } + @action onPointerDown = (e: React.PointerEvent): void => { e.stopPropagation(); @@ -705,6 +736,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
    e.preventDefault()}>
    e.preventDefault()}>
    e.preventDefault()}>
    +
    e.preventDefault()}>
    {linkButton}
    diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index 0461d7299..c7f7bdb66 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -10,6 +10,8 @@ import { InkTool } from "../../new_fields/InkField"; import { Doc } from "../../new_fields/Doc"; import { undoBatch, UndoManager } from "../util/UndoManager"; import { StrCast } from "../../new_fields/Types"; +import { FormattedTextBox } from "./nodes/FormattedTextBox"; +import { MainOverlayTextBox } from "./MainOverlayTextBox"; library.add(faPen, faHighlighter, faEraser, faBan); @@ -42,6 +44,7 @@ export class InkingControl extends React.Component { switchColor = action((color: ColorResult): void => { this._selectedColor = color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff"); if (InkingControl.Instance.selectedTool === InkTool.None) { + if (MainOverlayTextBox.Instance.SetColor(color.hex)) return; let selected = SelectionManager.SelectedDocuments(); let oldColors = selected.map(view => { let targetDoc = view.props.Document.isTemplate ? view.props.Document : Doc.GetProto(view.props.Document); diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss index 44c1cb9fc..b85a8040a 100644 --- a/src/client/views/Main.scss +++ b/src/client/views/Main.scss @@ -144,7 +144,7 @@ button:hover { #add-nodes-menu { position: absolute; bottom: 22px; - left: 24px; + left: 250px; > label { background: $dark-color; diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx index 7d15702a2..d8aaea259 100644 --- a/src/client/views/MainOverlayTextBox.tsx +++ b/src/client/views/MainOverlayTextBox.tsx @@ -36,6 +36,11 @@ export class MainOverlayTextBox extends React.Component this._outerdiv && this._tooltip && !this._outerdiv.contains(this._tooltip) && this._outerdiv.appendChild(this._tooltip); } + public SetColor(color: string) { + return this._textBox && this._textBox.setFontColor(color); + } + + constructor(props: MainOverlayTextBoxProps) { super(props); this._textProxyDiv = React.createRef(); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 1542fedef..61ccf4c1d 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -354,7 +354,7 @@ export class MainView extends React.Component { [React.createRef(), "tree", "Add Tree", addTreeNode], ]; - return < div id="add-nodes-menu" > + return < div id="add-nodes-menu" style={{ left: this.flyoutWidth + 5 }} > diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 31c0fe499..4850c6218 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -44,14 +44,12 @@ export class MarqueeView extends React.Component _commandExecuted = false; @action - cleanupInteractions = (all: boolean = false, rem_keydown: boolean = true) => { + cleanupInteractions = (all: boolean = false) => { if (all) { document.removeEventListener("pointerup", this.onPointerUp, true); document.removeEventListener("pointermove", this.onPointerMove, true); } - if (rem_keydown) { - document.removeEventListener("keydown", this.marqueeCommand, true); - } + document.removeEventListener("keydown", this.marqueeCommand, true); this._visible = false; } @@ -191,12 +189,9 @@ export class MarqueeView extends React.Component SelectionManager.DeselectAll(mselect.length ? undefined : this.props.container.props.Document); } this.props.selectDocuments(mselect.length ? mselect : [this.props.container.props.Document]); - mselect.length ? this.cleanupInteractions(true, false) : this.cleanupInteractions(true); - } - else { - //console.log("invisible"); - this.cleanupInteractions(true); } + //console.log("invisible"); + this.cleanupInteractions(true); if (e.altKey) { e.preventDefault(); @@ -266,13 +261,12 @@ export class MarqueeView extends React.Component } let ink = Cast(this.props.container.props.Document.ink, InkField); let inkData = ink ? ink.inkData : undefined; - let zoomBasis = NumCast(this.props.container.props.Document.scale, 1); let newCollection = Docs.FreeformDocument(selected, { x: bounds.left, y: bounds.top, panX: 0, panY: 0, - borderRounding: e.key === "e" ? -1 : undefined, + borderRounding: e.key === "e" ? "100%" : undefined, backgroundColor: this.props.container.isAnnotationOverlay ? undefined : "white", width: bounds.width, height: bounds.height, diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 30cf74f3e..1c00687ed 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -70,11 +70,14 @@ export class CollectionFreeFormDocumentView extends DocComponent { - let br = NumCast(this.props.Document.borderRounding); - return br >= 0 ? br : - NumCast(this.props.Document.nativeWidth) === 0 ? - Math.min(this.props.PanelWidth(), this.props.PanelHeight()) - : Math.min(this.Document.nativeWidth || 0, this.Document.nativeHeight || 0); + let br = StrCast(this.props.Document.borderRounding); + if (br.endsWith("%")) { + let percent = Number(br.substr(0, br.length - 1)) / 100; + let nativeDim = Math.min(NumCast(this.props.Document.nativeWidth), NumCast(this.props.Document.nativeHeight)); + let minDim = percent * (nativeDim ? nativeDim : Math.min(this.props.PanelWidth(), this.props.PanelHeight())); + return minDim; + } + return undefined; } render() { @@ -84,7 +87,7 @@ export class CollectionFreeFormDocumentView extends DocComponent(Docu style={{ outlineColor: "maroon", outlineStyle: "dashed", - outlineWidth: BoolCast(this.props.Document.libraryBrush) && !NumCast(this.props.Document.borderRounding) ? + outlineWidth: BoolCast(this.props.Document.libraryBrush) && !StrCast(this.props.Document.borderRounding) ? `${this.props.ScreenToLocalTransform().Scale}px` : "0px", - border: BoolCast(this.props.Document.libraryBrush) && NumCast(this.props.Document.borderRounding) ? + border: BoolCast(this.props.Document.libraryBrush) && StrCast(this.props.Document.borderRounding) ? `dashed maroon ${this.props.ScreenToLocalTransform().Scale}px` : undefined, borderRadius: "inherit", background: backgroundColor, diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 1eeb04755..bf6f4c764 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -97,6 +97,15 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe return ""; } + @undoBatch + public setFontColor(color: string) { + if (this._editorView!.state.selection.from === this._editorView!.state.selection.to) return false; + let colorMark = this._editorView!.state.schema.mark(this._editorView!.state.schema.marks.pFontColor, { color: color }); + this._editorView!.dispatch(this._editorView!.state.tr.addMark(this._editorView!.state.selection.from, + this._editorView!.state.selection.to, colorMark)); + return true; + } + constructor(props: FieldViewProps) { super(props); if (this.props.outer_div) { @@ -418,7 +427,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } render() { let style = this.props.isOverlay ? "scroll" : "hidden"; - let rounded = NumCast(this.props.Document.borderRounding) < 0 ? "-rounded" : ""; + let rounded = StrCast(this.props.Document.borderRounding) === "100%" ? "-rounded" : ""; let interactive = InkingControl.Instance.selectedTool ? "" : "interactive"; return (
    Date: Thu, 4 Jul 2019 18:20:54 -0400 Subject: added dropping and reordering within Stacking views. needs GUI feedback. --- src/client/util/DragManager.ts | 2 +- .../views/collections/CollectionStackingView.tsx | 58 +++++++++++++++++++++- src/client/views/collections/CollectionSubView.tsx | 9 +++- src/client/views/nodes/ImageBox.tsx | 4 +- 4 files changed, 68 insertions(+), 5 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index d4b1bc8f4..ce1f9d890 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -448,7 +448,7 @@ export namespace DragManager { x: e.x, y: e.y, data: dragData, - mods: e.altKey ? "AltKey" : "" + mods: e.altKey ? "AltKey" : e.ctrlKey ? "CtrlKey" : "" } }) ); diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index b10907937..6b4eddec9 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -11,6 +11,8 @@ import { CollectionSchemaPreview } from "./CollectionSchemaView"; import "./CollectionStackingView.scss"; import { CollectionSubView } from "./CollectionSubView"; import { resolve } from "bluebird"; +import { undoBatch } from "../../util/UndoManager"; +import { DragManager } from "../../util/DragManager"; @observer export class CollectionStackingView extends CollectionSubView(doc => doc) { @@ -96,8 +98,10 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
    ; }); } + docXfs: any[] = [] @computed get children() { + this.docXfs.length = 0; return this.childDocs.filter(d => !d.isMinimized).map((d, i) => { let aspect = d.nativeHeight ? NumCast(d.nativeWidth) / NumCast(d.nativeHeight) : undefined; let dref = React.createRef(); @@ -105,6 +109,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { let width = () => d.nativeWidth ? Math.min(d[WidthSym](), this.columnWidth) : this.columnWidth; let height = () => aspect ? width() / aspect : d[HeightSym](); let rowSpan = Math.ceil((height() + this.gridGap) / (this._gridSize + this.gridGap)); + this.docXfs.push({ dxf: dxf, width: width, height: height }); return (
    doc) { }); } } + + @undoBatch + @action + drop = (e: Event, de: DragManager.DropEvent) => { + let targInd = -1; + let where = [de.x, de.y]; + if (de.data instanceof DragManager.DocumentDragData) { + this.docXfs.map((cd, i) => { + let pos = cd.dxf().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap); + let pos1 = cd.dxf().inverse().transformPoint(cd.width(), cd.height()); + if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && where[1] < pos1[1]) { + targInd = i; + } + }) + } + if (super.drop(e, de)) { + if (targInd !== -1) { + let newDoc = de.data.droppedDocuments[0]; + let docs = this.childDocList; + if (docs) { + let srcInd = docs.indexOf(newDoc); + docs.splice(srcInd, 1); + docs.splice(targInd > srcInd ? targInd - 1 : targInd, 0, newDoc); + } + } + } + return false; + } + @undoBatch + @action + onDrop = (e: React.DragEvent): void => { + let where = [e.clientX, e.clientY]; + let targInd = -1; + this.docXfs.map((cd, i) => { + let pos = cd.dxf().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap); + let pos1 = cd.dxf().inverse().transformPoint(cd.width(), cd.height()); + if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && where[1] < pos1[1]) { + targInd = i; + } + }) + super.onDrop(e, {}, () => { + if (targInd !== -1) { + let newDoc = this.childDocs[this.childDocs.length - 1]; + let docs = this.childDocList; + if (docs) { + docs.splice(docs.length - 1, 1); + docs.splice(targInd, 0, newDoc); + } + } + }); + } render() { let cols = this.singleColumn ? 1 : Math.max(1, Math.min(this.childDocs.filter(d => !d.isMinimized).length, Math.floor((this.props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap)))); let templatecols = ""; for (let i = 0; i < cols; i++) templatecols += `${this.columnWidth}px `; return ( -
    e.stopPropagation()} > +
    e.stopPropagation()} >
    (schemaCtor: (doc: Doc) => T) { //This linter error can't be fixed because of how js arguments work, so don't switch this to filter(FieldValue) return DocListCast((BoolCast(this.props.Document.isTemplate) ? this.extensionDoc : this.props.Document)[this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey]); } + get childDocList() { + //TODO tfs: This might not be what we want? + //This linter error can't be fixed because of how js arguments work, so don't switch this to filter(FieldValue) + return Cast((BoolCast(this.props.Document.isTemplate) ? this.extensionDoc : this.props.Document)[this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey], listSpec(Doc)); + } @action protected async setCursorPosition(position: [number, number]) { @@ -145,7 +150,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { @undoBatch @action - protected onDrop(e: React.DragEvent, options: DocumentOptions): void { + protected onDrop(e: React.DragEvent, options: DocumentOptions, completed?: () => void) { if (e.ctrlKey) { e.stopPropagation(); // bcz: this is a hack to stop propagation when dropping an image on a text document with shift+ctrl return; @@ -255,7 +260,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { } if (promises.length) { - Promise.all(promises).finally(() => batch.end()); + Promise.all(promises).finally(() => { completed && completed(); batch.end(); }); } else { batch.end(); } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 06bf65f73..dd4e4262d 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -73,7 +73,7 @@ export class ImageBox extends DocComponent(ImageD if (de.mods === "AltKey" && /*this.dataDoc !== this.props.Document &&*/ drop.data instanceof ImageField) { Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new ImageField(drop.data.url); e.stopPropagation(); - } else { + } else if (de.mods === "CtrlKey") { if (this.extensionDoc !== this.dataDoc) { let layout = StrCast(drop.backgroundLayout); if (layout.indexOf(ImageBox.name) !== -1) { @@ -205,6 +205,8 @@ export class ImageBox extends DocComponent(ImageD requestImageSize(window.origin + RouteStore.corsProxy + "/" + srcpath) .then((size: any) => { let aspect = size.height / size.width; + let rotation = NumCast(this.dataDoc.rotation) % 180; + if (rotation === 90 || rotation === 270) aspect = 1 / aspect; if (Math.abs(layoutdoc[HeightSym]() / layoutdoc[WidthSym]() - aspect) > 0.01) { setTimeout(action(() => { layoutdoc.height = layoutdoc[WidthSym]() * aspect; -- cgit v1.2.3-70-g09d2 From 1b02511185e22349e68bab1f6b8ce74b874a3ba5 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Thu, 4 Jul 2019 19:54:27 -0400 Subject: Made some changes to clean up some GoldenLayout stuff --- src/client/goldenLayout.js | 11 ++++ src/client/util/DragManager.ts | 1 + src/client/views/_nodeModuleOverrides.scss | 18 +++---- .../views/collections/CollectionDockingView.tsx | 58 ++++++++++------------ src/client/views/nodes/DocumentView.tsx | 20 ++++---- src/client/views/search/SearchItem.tsx | 2 +- 6 files changed, 57 insertions(+), 53 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js index 54c9c6068..28c009645 100644 --- a/src/client/goldenLayout.js +++ b/src/client/goldenLayout.js @@ -2271,6 +2271,17 @@ this._dragListener.on('dragStop', this._createDragListener, this); }, + destroy: function () { + this._dragListener.destroy(); + this._element = null; + this._itemConfig = null; + this._dragListener = null; + const index = this._layoutManager._dragSources.indexOf(this); + if (index > -1) { + this._layoutManager._dragSources.splice(index, 1); + } + }, + /** * Callback for the DragListener's dragStart event * diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index ce1f9d890..2fd83ae56 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -43,6 +43,7 @@ export function SetupDrag( e.stopPropagation(); e.preventDefault(); if (e.shiftKey && CollectionDockingView.Instance) { + e.persist(); CollectionDockingView.Instance.StartOtherDrag(e, [await docFunc()]); } else { document.addEventListener("pointermove", onRowMove); diff --git a/src/client/views/_nodeModuleOverrides.scss b/src/client/views/_nodeModuleOverrides.scss index 3594ac9f4..b8a7db034 100644 --- a/src/client/views/_nodeModuleOverrides.scss +++ b/src/client/views/_nodeModuleOverrides.scss @@ -2,21 +2,21 @@ // goldenlayout stuff div .lm_header { - background: $dark-color; + background: $dark-color; } .lm_tab { - margin-top: 0.6em !important; - padding-top: 0.5em !important; - min-height: 1.35em; - padding-bottom: 0px; - border-radius: 5px; - font-family: $sans-serif !important; + // margin-top: 0.6em !important; + // padding-top: 0.5em !important; + // min-height: 1.35em; + // padding-bottom: 0px; + // border-radius: 5px; + font-family: $sans-serif !important; } .lm_header .lm_controls { - right: 1em !important; + right: 1em !important; } // @TODO the ril__navgiation buttons in the img gallery are a lil messed up but I can't figure out -// why. Low priority for now but it's bugging me. --Julie +// why. Low priority for now but it's bugging me. --Julie \ No newline at end of file diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index c5f8fb728..f146ec62b 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -62,12 +62,30 @@ export class CollectionDockingView extends React.Component - this.AddRightSplit(dragDoc, dragDataDocs ? dragDataDocs[i] : undefined, true).contentItems[0].tab._dragListener. - onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 })); + public StartOtherDrag(e: any, dragDocs: Doc[], dragDataDocs: (Doc | undefined)[] = []) { + let config: any; + if (dragDocs.length === 1) { + config = CollectionDockingView.makeDocumentConfig(dragDocs[0], dragDataDocs[0]); + } else { + config = { + type: 'row', + content: dragDocs.map((doc, i) => { + CollectionDockingView.makeDocumentConfig(doc, dragDataDocs[i]); + }) + }; + } + const div = document.createElement("div"); + const dragSource = this._goldenLayout.createDragSource(div, config); + dragSource._dragListener.on("dragStop", () => { + dragSource.destroy(); + }); + dragSource._dragListener.onMouseDown(e); + // dragSource.destroy(); + // this.hack = true; + // this.undohack = UndoManager.StartBatch("goldenDrag"); + // dragDocs.map((dragDoc, i) => + // this.AddRightSplit(dragDoc, dragDataDocs[i], true).contentItems[0].tab._dragListener. + // onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 })); } @action @@ -299,33 +317,7 @@ export class CollectionDockingView extends React.Component) => (sourceDoc instanceof Doc) && DragLinksAsDocuments(tab, x, y, sourceDoc))); - } else - if ((className === "lm_title" || className === "lm_tab lm_active") && e.shiftKey) { - e.stopPropagation(); - e.preventDefault(); - let x = e.clientX; - let y = e.clientY; - let docid = (e.target as any).DashDocId; - let datadocid = (e.target as any).DashDataDocId; - let tab = (e.target as any).parentElement as HTMLElement; - let glTab = (e.target as any).Tab; - if (glTab && glTab.contentItem && glTab.contentItem.parent) { - glTab.contentItem.parent.setActiveContentItem(glTab.contentItem); - } - DocServer.GetRefField(docid).then(action(async (f: Opt) => { - if (f instanceof Doc) { - let dataDoc = (datadocid !== docid) ? await DocServer.GetRefField(datadocid) : f; - DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f], [dataDoc instanceof Doc ? dataDoc : f]), x, y, - { - handlers: { - dragComplete: emptyFunction, - }, - hideSource: false, - withoutShiftDrag: true - }); - } - })); - } + } if (className === "lm_drag_handle" || className === "lm_close" || className === "lm_maximise" || className === "lm_minimise" || className === "lm_close_tab") { this._flush = true; } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index a84cac37f..2610d0e6d 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -362,16 +362,16 @@ export class DocumentView extends DocComponent(Docu this._downX = e.clientX; this._downY = e.clientY; this._hitExpander = DocListCast(this.props.Document.subBulletDocs).length > 0; - if (e.shiftKey && e.buttons === 1 && CollectionDockingView.Instance) { - CollectionDockingView.Instance.StartOtherDrag(e, [Doc.MakeAlias(this.props.Document)], [this.dataDoc]); - e.stopPropagation(); - } else { - if (this.active) e.stopPropagation(); // events stop at the lowest document that is active. - document.removeEventListener("pointermove", this.onPointerMove); - document.addEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - document.addEventListener("pointerup", this.onPointerUp); - } + // if (e.shiftKey && e.buttons === 1 && CollectionDockingView.Instance) { + // CollectionDockingView.Instance.StartOtherDrag(e, [Doc.MakeAlias(this.props.Document)], [this.dataDoc]); + // e.stopPropagation(); + // } else { + if (this.active) e.stopPropagation(); // events stop at the lowest document that is active. + document.removeEventListener("pointermove", this.onPointerMove); + document.addEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointerup", this.onPointerUp); + // } } onPointerMove = (e: PointerEvent): void => { if (!e.cancelBubble && this.active) { diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx index 79a06c81a..606ac4e85 100644 --- a/src/client/views/search/SearchItem.tsx +++ b/src/client/views/search/SearchItem.tsx @@ -231,7 +231,7 @@ export class SearchItem extends React.Component { onPointerDown = (e: React.PointerEvent) => { e.stopPropagation(); - e.preventDefault(); + // e.preventDefault(); const doc = Doc.IsPrototype(this.props.doc) ? Doc.MakeDelegate(this.props.doc) : this.props.doc; DragManager.StartDocumentDrag([e.currentTarget], new DragManager.DocumentDragData([doc], []), e.clientX, e.clientY, { handlers: { dragComplete: emptyFunction }, -- cgit v1.2.3-70-g09d2 From ad87140f4a52984d754c06fcb55e66a0cc935ce2 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 4 Jul 2019 23:18:13 -0400 Subject: fixed shift-dragging of documents --- src/client/util/DragManager.ts | 1 - src/client/views/DocumentDecorations.tsx | 1 - src/client/views/collections/CollectionDockingView.tsx | 3 --- src/client/views/collections/CollectionSchemaView.tsx | 1 - src/client/views/nodes/ImageBox.tsx | 2 +- src/client/views/nodes/VideoBox.tsx | 1 - src/client/views/search/SearchItem.tsx | 1 - 7 files changed, 1 insertion(+), 9 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 2fd83ae56..f9492f452 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -41,7 +41,6 @@ export function SetupDrag( let onItemDown = async (e: React.PointerEvent) => { if (e.button === 0) { e.stopPropagation(); - e.preventDefault(); if (e.shiftKey && CollectionDockingView.Instance) { e.persist(); CollectionDockingView.Instance.StartOtherDrag(e, [await docFunc()]); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index c7990647a..48b89ff4e 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -172,7 +172,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> document.addEventListener("pointermove", this.onBackgroundMove); document.addEventListener("pointerup", this.onBackgroundUp); e.stopPropagation(); - e.preventDefault(); } @action diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index f146ec62b..19d07ecdc 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -321,9 +321,6 @@ export class CollectionDockingView extends React.Component doc) { onPointerDown = (e: React.PointerEvent): void => { if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey) { if (this.props.isSelected()) e.stopPropagation(); - else e.preventDefault(); } } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index dd4e4262d..5da52f4c2 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -95,7 +95,7 @@ export class ImageBox extends DocComponent(ImageD onPointerDown = (e: React.PointerEvent): void => { 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; diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 1239b498f..264d3c1f7 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -119,7 +119,6 @@ export class VideoBox extends DocComponent(VideoD }); } onPointerDown = (e: React.PointerEvent) => { - e.preventDefault(); e.stopPropagation(); } diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx index 606ac4e85..51f0ed607 100644 --- a/src/client/views/search/SearchItem.tsx +++ b/src/client/views/search/SearchItem.tsx @@ -231,7 +231,6 @@ export class SearchItem extends React.Component { onPointerDown = (e: React.PointerEvent) => { e.stopPropagation(); - // e.preventDefault(); const doc = Doc.IsPrototype(this.props.doc) ? Doc.MakeDelegate(this.props.doc) : this.props.doc; DragManager.StartDocumentDrag([e.currentTarget], new DragManager.DocumentDragData([doc], []), e.clientX, e.clientY, { handlers: { dragComplete: emptyFunction }, -- cgit v1.2.3-70-g09d2 From aedd283fb9f9eff4145e27658bc6647982256032 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Fri, 5 Jul 2019 00:34:19 -0400 Subject: beginning implementation of key value tags for all imported documents --- src/client/documents/Documents.ts | 12 +- .../util/Import & Export/DirectoryImportBox.scss | 0 .../util/Import & Export/DirectoryImportBox.tsx | 181 +++++++++++++++++++++ src/client/util/Import & Export/ImageImporter.tsx | 67 -------- src/client/util/Import & Export/ImportBox.tsx | 134 --------------- src/client/util/request-image-size.js | 6 +- src/client/views/MainView.tsx | 1 - src/client/views/collections/CollectionSubView.tsx | 4 +- src/client/views/nodes/DocumentContentsView.tsx | 4 +- src/client/views/nodes/ImageBox.tsx | 4 +- 10 files changed, 198 insertions(+), 215 deletions(-) create mode 100644 src/client/util/Import & Export/DirectoryImportBox.scss create mode 100644 src/client/util/Import & Export/DirectoryImportBox.tsx delete mode 100644 src/client/util/Import & Export/ImageImporter.tsx delete mode 100644 src/client/util/Import & Export/ImportBox.tsx (limited to 'src/client/views/nodes') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 5d637dd3a..26b5498a2 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -36,7 +36,7 @@ import { UndoManager } from "../util/UndoManager"; import { RouteStore } from "../../server/RouteStore"; import { LinkManager } from "../util/LinkManager"; import { DocumentManager } from "../util/DocumentManager"; -import ImportBox from "../util/Import & Export/ImportBox"; +import DirectoryImportBox from "../util/Import & Export/DirectoryImportBox"; var requestImageSize = require('../util/request-image-size'); var path = require('path'); @@ -180,7 +180,7 @@ export namespace Docs { } function CreateImportPrototype(): Doc { - let importProto = setupPrototypeOptions(importProtoId, "IMPORT_PROTO", ImportBox.LayoutString(), { x: 0, y: 0, width: 600, height: 600, type: DocTypes.IMPORT }); + let importProto = setupPrototypeOptions(importProtoId, "IMPORT_PROTO", DirectoryImportBox.LayoutString(), { x: 0, y: 0, width: 600, height: 600, type: DocTypes.IMPORT }); return importProto; } @@ -347,7 +347,7 @@ export namespace Docs { return CreateInstance(collProto, new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }, id); } - export async function getDocumentFromType(type: string, path: string, options: DocumentOptions, addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean): Promise> { + export async function getDocumentFromType(type: string, path: string, options: DocumentOptions): Promise> { let ctor: ((path: string, options: DocumentOptions) => (Doc | Promise)) | undefined = undefined; if (type.indexOf("image") !== -1) { ctor = Docs.ImageDocument; @@ -370,17 +370,17 @@ export namespace Docs { if (path.includes(window.location.hostname)) { let s = path.split('/'); let id = s[s.length - 1]; - DocServer.GetRefField(id).then(field => { + return DocServer.GetRefField(id).then(field => { if (field instanceof Doc) { let alias = Doc.MakeAlias(field); alias.x = options.x || 0; alias.y = options.y || 0; alias.width = options.width || 300; alias.height = options.height || options.width || 300; - addDocument && addDocument(alias, false); + return alias; } + return undefined; }); - return undefined; } ctor = Docs.WebDocument; options = { height: options.width, ...options, title: path, nativeWidth: undefined }; diff --git a/src/client/util/Import & Export/DirectoryImportBox.scss b/src/client/util/Import & Export/DirectoryImportBox.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx new file mode 100644 index 000000000..2d77f6ae6 --- /dev/null +++ b/src/client/util/Import & Export/DirectoryImportBox.tsx @@ -0,0 +1,181 @@ +import "fs"; +import React = require("react"); +import { Doc } from "../../../new_fields/Doc"; +import { DocServer } from "../../DocServer"; +import { RouteStore } from "../../../server/RouteStore"; +import { action, observable, runInAction } from "mobx"; +import { FieldViewProps, FieldView } from "../../views/nodes/FieldView"; +import Measure, { ContentRect } from "react-measure"; +import { library } from '@fortawesome/fontawesome-svg-core'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faArrowUp, faTag, faFileExcel } from '@fortawesome/free-solid-svg-icons'; +import { Docs, DocumentOptions } from "../../documents/Documents"; +import { EditableView } from "../../views/EditableView"; + +export default class DirectoryImportBox extends React.Component { + private selector = React.createRef(); + @observable private top = 0; + @observable private left = 0; + private dimensions = 50; + + @observable private key = "Key"; + @observable private value = "Value"; + + public static LayoutString() { return FieldView.LayoutString(DirectoryImportBox); } + + constructor(props: FieldViewProps) { + super(props); + library.add(faArrowUp, faTag); + } + + updateKey = (newKey: string) => { + runInAction(() => this.key = newKey); + console.log("KEY ", this.key); + return true; + } + + updateValue = (newValue: string) => { + runInAction(() => this.value = newValue); + console.log("VALUE ", this.value); + return true; + } + + handleSelection = async (e: React.ChangeEvent) => { + let promises: Promise[] = []; + let docs: Doc[] = []; + + let files = e.target.files; + if (!files || files.length === 0) return; + + let directory = (files.item(0) as any).webkitRelativePath.split("/", 1); + + for (let i = 0; i < files.length; i++) { + let uploaded_file = files.item(i); + + if (!uploaded_file) { + continue; + } + + let formData = new FormData(); + formData.append('file', uploaded_file); + let dropFileName = uploaded_file ? uploaded_file.name : "-empty-"; + let type = uploaded_file.type; + + let prom = fetch(DocServer.prepend(RouteStore.upload), { + method: 'POST', + body: formData + }).then(async (res: Response) => { + (await res.json()).map(action((file: any) => { + let path = DocServer.prepend(file); + console.log(path); + let docPromise = Docs.getDocumentFromType(type, path, { nativeWidth: 300, width: 300, title: dropFileName }); + docPromise.then(doc => doc && docs.push(doc)); + })); + }); + promises.push(prom); + } + + await Promise.all(promises); + + let doc = this.props.Document; + let options: DocumentOptions = { title: `Import of ${directory}`, width: 500, height: 500, x: Doc.GetT(doc, "x", "number"), y: Doc.GetT(doc, "y", "number") }; + let parent = this.props.ContainingCollectionView; + if (parent) { + let importContainer = Docs.StackingDocument(docs, options); + Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer); + this.props.removeDocument && this.props.removeDocument(doc); + } + } + + componentDidMount() { + this.selector.current!.setAttribute("directory", ""); + this.selector.current!.setAttribute("webkitdirectory", ""); + } + + @action + preserveCentering = (rect: ContentRect) => { + let bounds = rect.offset!; + if (bounds.width === 0 || bounds.height === 0) { + return; + } + let offset = this.dimensions / 2; + this.left = bounds.width / 2 - offset; + this.top = bounds.height / 2 - offset; + } + + render() { + let dimensions = 50; + let keyValueStyle = { paddingLeft: 5, width: "50%" }; + return ( + + {({ measureRef }) => +
    + +