diff options
Diffstat (limited to 'src/client')
33 files changed, 481 insertions, 522 deletions
diff --git a/src/client/Server.ts b/src/client/Server.ts index 3bbbebe72..66e9878d9 100644 --- a/src/client/Server.ts +++ b/src/client/Server.ts @@ -12,7 +12,6 @@ export class Server { static Socket: SocketIOClient.Socket = OpenSocket(`${window.location.protocol}//${window.location.hostname}:4321`); static GUID: string = Utils.GenerateGuid(); - // Retrieves the cached value of the field and sends a request to the server for the real value (if it's not cached). // Call this is from within a reaction and test whether the return value is FieldWaiting. public static GetField(fieldid: FieldId): Promise<Opt<Field>>; @@ -127,13 +126,6 @@ export class Server { } } - public static AddDocument(document: Document) { - SocketStub.SEND_ADD_DOCUMENT(document); - } - public static AddDocumentField(doc: Document, key: Key, value: Field) { - console.log("Add doc field " + doc.Title + " " + key.Name + " fid " + value.Id + " " + value); - SocketStub.SEND_ADD_DOCUMENT_FIELD(doc, key, value); - } public static DeleteDocumentField(doc: Document, key: Key) { SocketStub.SEND_DELETE_DOCUMENT_FIELD(doc, key); } @@ -161,18 +153,18 @@ export class Server { } @action - static updateField(field: { _id: string, data: any, type: Types }) { - if (Server.ClientFieldsCached.has(field._id)) { - var f = Server.ClientFieldsCached.get(field._id); + static updateField(field: { id: string, data: any, type: Types }) { + if (Server.ClientFieldsCached.has(field.id)) { + var f = Server.ClientFieldsCached.get(field.id); if (f) { - // console.log("Applying : " + field._id); + // console.log("Applying : " + field.id); f.UpdateFromServer(field.data); f.init(emptyFunction); } else { - // console.log("Not applying wa : " + field._id); + // console.log("Not applying wa : " + field.id); } } else { - // console.log("Not applying mi : " + field._id); + // console.log("Not applying mi : " + field.id); } } } diff --git a/src/client/SocketStub.ts b/src/client/SocketStub.ts index 5e2ca6a98..382a81f66 100644 --- a/src/client/SocketStub.ts +++ b/src/client/SocketStub.ts @@ -2,7 +2,7 @@ import { Key } from "../fields/Key"; import { Field, FieldId, Opt } from "../fields/Field"; import { ObservableMap } from "mobx"; import { Document } from "../fields/Document"; -import { MessageStore, DocumentTransfer } from "../server/Message"; +import { MessageStore, Transferable } from "../server/Message"; import { Utils } from "../Utils"; import { Server } from "./Server"; import { ServerUtils } from "../server/ServerUtil"; @@ -16,35 +16,12 @@ export interface FieldMap { export class SocketStub { static FieldStore: ObservableMap<FieldId, Field> = new ObservableMap(); - public static SEND_ADD_DOCUMENT(document: Document) { - - // Send a serialized version of the document to the server - // ...SOCKET(ADD_DOCUMENT, serialied document) - - // server stores each document field in its repository of stored fields - // document.fields.forEach((f, key) => this.FieldStore.set((f as Field).Id, f as Field)); - // let strippedDoc = new Document(document.Id); - // document.fields.forEach((f, key) => { - // if (f) { - // // let args: SetFieldArgs = new SetFieldArgs(f.Id, f.GetValue()) - // let args: Transferable = f.ToJson() - // Utils.Emit(Server.Socket, MessageStore.SetField, args) - // } - // }) - - // // server stores stripped down document (w/ only field id proxies) in the field store - // this.FieldStore.set(document.Id, new Document(document.Id)); - // document.fields.forEach((f, key) => (this.FieldStore.get(document.Id) as Document)._proxies.set(key.Id, (f as Field).Id)); - - console.log("sending " + document.Title); - Utils.Emit(Server.Socket, MessageStore.AddDocument, new DocumentTransfer(document.ToJson())); - } public static SEND_FIELD_REQUEST(fieldid: FieldId): Promise<Opt<Field>>; public static SEND_FIELD_REQUEST(fieldid: FieldId, callback: (field: Opt<Field>) => void): void; public static SEND_FIELD_REQUEST(fieldid: FieldId, callback?: (field: Opt<Field>) => void): Promise<Opt<Field>> | void { let fn = function (cb: (field: Opt<Field>) => void) { - Utils.EmitCallback(Server.Socket, MessageStore.GetField, fieldid, (field: any) => { + Utils.EmitCallback(Server.Socket, MessageStore.GetField, fieldid, (field: Transferable) => { if (field) { ServerUtils.FromJson(field).init(cb); } else { @@ -60,36 +37,15 @@ export class SocketStub { } public static SEND_FIELDS_REQUEST(fieldIds: FieldId[], callback: (fields: FieldMap) => any) { - Utils.EmitCallback(Server.Socket, MessageStore.GetFields, fieldIds, (fields: any[]) => { - let fieldMap: any = {}; - let proms: Promise<any>[] = []; - for (let field of fields) { - let f = ServerUtils.FromJson(field); - fieldMap[field._id] = f; - proms.push(new Promise(res => f.init(res))); - } + Utils.EmitCallback(Server.Socket, MessageStore.GetFields, fieldIds, (fields: Transferable[]) => { + let fieldMap: FieldMap = {}; + fields.map(field => fieldMap[field.id] = ServerUtils.FromJson(field)); + let proms = Object.values(fieldMap).map(val => + new Promise(resolve => val!.init(resolve))); Promise.all(proms).then(() => callback(fieldMap)); }); } - public static SEND_ADD_DOCUMENT_FIELD(doc: Document, key: Key, value: Field) { - - // Send a serialized version of the field to the server along with the - // associated info of the document id and key where it is used. - - // ...SOCKET(ADD_DOCUMENT_FIELD, document id, key id, serialized field) - - // server updates its document to hold a proxy mapping from key => fieldId - var document = this.FieldStore.get(doc.Id) as Document; - if (document) { - document._proxies.set(key.Id, value.Id); - } - - // server adds the field to its repository of fields - this.FieldStore.set(value.Id, value); - // Utils.Emit(Server.Socket, MessageStore.AddDocument, new DocumentTransfer(doc.ToJson())) - } - public static SEND_DELETE_DOCUMENT_FIELD(doc: Document, key: Key) { // Send a request to delete the field stored under the specified key from the document diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 3c36fe500..4febfa7eb 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -127,7 +127,7 @@ export namespace Documents { function CreateImagePrototype(): Document { let imageProto = setupPrototypeOptions(imageProtoId, "IMAGE_PROTO", CollectionView.LayoutString("AnnotationsKey"), - { x: 0, y: 0, nativeWidth: 300, width: 300, layoutKeys: [KeyStore.Data, KeyStore.Annotations, KeyStore.Caption] }); + { x: 0, y: 0, nativeWidth: 600, width: 300, layoutKeys: [KeyStore.Data, KeyStore.Annotations, KeyStore.Caption] }); imageProto.SetText(KeyStore.BackgroundLayout, ImageBox.LayoutString()); imageProto.SetNumber(KeyStore.CurPage, 0); return imageProto; diff --git a/src/client/northstar/dash-fields/HistogramField.ts b/src/client/northstar/dash-fields/HistogramField.ts index 6abde4677..82de51d56 100644 --- a/src/client/northstar/dash-fields/HistogramField.ts +++ b/src/client/northstar/dash-fields/HistogramField.ts @@ -35,12 +35,11 @@ export class HistogramField extends BasicField<HistogramOperation> { } - ToJson(): { type: Types, data: string, _id: string } { + ToJson() { return { type: Types.HistogramOp, - data: this.toString(), - _id: this.Id + id: this.Id }; } diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx index 2084fc346..3e94fed81 100644 --- a/src/client/northstar/dash-nodes/HistogramBox.tsx +++ b/src/client/northstar/dash-nodes/HistogramBox.tsx @@ -8,7 +8,7 @@ import { KeyStore } from "../../../fields/KeyStore"; import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; import { ChartType, VisualBinRange } from '../../northstar/model/binRanges/VisualBinRange'; import { VisualBinRangeHelper } from "../../northstar/model/binRanges/VisualBinRangeHelper"; -import { AggregateBinRange, AggregateFunction, BinRange, Catalog, DoubleValueAggregateResult, HistogramResult, Result } from "../../northstar/model/idea/idea"; +import { AggregateBinRange, AggregateFunction, BinRange, Catalog, DoubleValueAggregateResult, HistogramResult } from "../../northstar/model/idea/idea"; import { ModelHelpers } from "../../northstar/model/ModelHelpers"; import { HistogramOperation } from "../../northstar/operations/HistogramOperation"; import { SizeConverter } from "../../northstar/utils/SizeConverter"; diff --git a/src/client/northstar/manager/Gateway.ts b/src/client/northstar/manager/Gateway.ts index 207a9ad19..d26f2724f 100644 --- a/src/client/northstar/manager/Gateway.ts +++ b/src/client/northstar/manager/Gateway.ts @@ -36,7 +36,7 @@ export class Gateway { public async ClearCatalog(): Promise<void> { try { - const json = await this.MakePostJsonRequest("Datamart/ClearAllAugmentations", {}); + await this.MakePostJsonRequest("Datamart/ClearAllAugmentations", {}); } catch (error) { throw new Error("can not reach northstar's backend"); @@ -180,18 +180,18 @@ export class Gateway { public static ConstructUrl(appendix: string): string { - let base = Settings.Instance.ServerUrl; + let base = NorthstarSettings.Instance.ServerUrl; if (base.slice(-1) === "/") { base = base.slice(0, -1); } - let url = base + "/" + Settings.Instance.ServerApiPath + "/" + appendix; + let url = base + "/" + NorthstarSettings.Instance.ServerApiPath + "/" + appendix; return url; } } declare var ENV: any; -export class Settings { +export class NorthstarSettings { private _environment: any; @observable @@ -248,10 +248,10 @@ export class Settings { return window.location.origin + "/"; } - private static _instance: Settings; + private static _instance: NorthstarSettings; @action - public Update(environment: any): void { + public UpdateEnvironment(environment: any): void { /*let serverParam = new URL(document.URL).searchParams.get("serverUrl"); if (serverParam) { if (serverParam === "debug") { @@ -278,9 +278,9 @@ export class Settings { this.DegreeOfParallelism = environment.DEGREE_OF_PARALLISM; } - public static get Instance(): Settings { + public static get Instance(): NorthstarSettings { if (!this._instance) { - this._instance = new Settings(); + this._instance = new NorthstarSettings(); } return this._instance; } diff --git a/src/client/northstar/model/binRanges/VisualBinRangeHelper.ts b/src/client/northstar/model/binRanges/VisualBinRangeHelper.ts index 9671e55f8..a92412686 100644 --- a/src/client/northstar/model/binRanges/VisualBinRangeHelper.ts +++ b/src/client/northstar/model/binRanges/VisualBinRangeHelper.ts @@ -4,7 +4,7 @@ import { NominalVisualBinRange } from "./NominalVisualBinRange"; import { QuantitativeVisualBinRange } from "./QuantitativeVisualBinRange"; import { AlphabeticVisualBinRange } from "./AlphabeticVisualBinRange"; import { DateTimeVisualBinRange } from "./DateTimeVisualBinRange"; -import { Settings } from "../../manager/Gateway"; +import { NorthstarSettings } from "../../manager/Gateway"; import { ModelHelpers } from "../ModelHelpers"; import { AttributeTransformationModel } from "../../core/attribute/AttributeTransformationModel"; diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 3e093c8dc..3b5a5b470 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,11 +1,9 @@ -import React = require('react'); -import { observer } from 'mobx-react'; -import { observable, action, computed } from 'mobx'; +import { computed, observable } from 'mobx'; import { Document } from "../../fields/Document"; -import { DocumentView } from '../views/nodes/DocumentView'; -import { KeyStore } from '../../fields/KeyStore'; import { FieldWaiting } from '../../fields/Field'; +import { KeyStore } from '../../fields/KeyStore'; import { ListField } from '../../fields/ListField'; +import { DocumentView } from '../views/nodes/DocumentView'; export class DocumentManager { @@ -27,10 +25,6 @@ export class DocumentManager { // this.DocumentViews = new Array<DocumentView>(); } - public getAllDocumentViews(collection: Document) { - return this.DocumentViews.filter(dv => dv.props.ContainingCollectionView && dv.props.ContainingCollectionView.props.Document === collection); - } - public getDocumentView(toFind: Document): DocumentView | null { let toReturn: DocumentView | null; @@ -39,7 +33,6 @@ export class DocumentManager { //gets document view that is in a freeform canvas collection DocumentManager.Instance.DocumentViews.map(view => { let doc = view.props.Document; - // if (view.props.ContainingCollectionView instanceof CollectionFreeFormView) { if (doc === toFind) { toReturn = view; @@ -51,7 +44,7 @@ export class DocumentManager { } }); - return (toReturn); + return toReturn; } public getDocumentViews(toFind: Document): DocumentView[] { @@ -72,7 +65,7 @@ export class DocumentManager { } }); - return (toReturn); + return toReturn; } @computed @@ -84,9 +77,8 @@ export class DocumentManager { if (link instanceof Document) { let linkToDoc = link.GetT(KeyStore.LinkedToDocs, Document); if (linkToDoc && linkToDoc !== FieldWaiting) { - DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => { - pairs.push({ a: dv, b: docView1, l: link }); - }); + DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => + pairs.push({ a: dv, b: docView1, l: link })); } } return pairs; diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 6a7047725..4bd654e15 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -1,13 +1,12 @@ import { action } from "mobx"; import { Document } from "../../fields/Document"; +import { FieldWaiting } from "../../fields/Field"; +import { KeyStore } from "../../fields/KeyStore"; import { emptyFunction } from "../../Utils"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import { DocumentDecorations } from "../views/DocumentDecorations"; -import { Main } from "../views/Main"; -import { DocumentView } from "../views/nodes/DocumentView"; import * as globalCssVariables from "../views/globalCssVariables.scss"; -import { KeyStore } from "../../fields/KeyStore"; -import { FieldWaiting } from "../../fields/Field"; +import { MainOverlayTextBox } from "../views/MainOverlayTextBox"; export function SetupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document, moveFunc?: DragManager.MoveFunction, copyOnDrop: boolean = false) { let onRowMove = action((e: PointerEvent): void => { @@ -140,11 +139,13 @@ export namespace DragManager { constructor(dragDoc: Document[]) { this.draggedDocuments = dragDoc; this.droppedDocuments = dragDoc; + this.xOffset = 0; + this.yOffset = 0; } draggedDocuments: Document[]; droppedDocuments: Document[]; - xOffset?: number; - yOffset?: number; + xOffset: number; + yOffset: number; aliasOnDrop?: boolean; copyOnDrop?: boolean; moveDocument?: MoveFunction; @@ -175,7 +176,7 @@ export namespace DragManager { dragDiv.className = "dragManager-dragDiv"; DragManager.Root().appendChild(dragDiv); } - Main.Instance.SetTextDoc(); + MainOverlayTextBox.Instance.SetTextDoc(); let scaleXs: number[] = []; let scaleYs: number[] = []; diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 2fa45a086..c56f6a4ff 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -2,6 +2,7 @@ import { observable, action } from "mobx"; import { DocumentView } from "../views/nodes/DocumentView"; import { Document } from "../../fields/Document"; import { Main } from "../views/Main"; +import { MainOverlayTextBox } from "../views/MainOverlayTextBox"; export namespace SelectionManager { class Manager { @@ -25,7 +26,7 @@ export namespace SelectionManager { DeselectAll(): void { manager.SelectedDocuments.map(dv => dv.props.onActiveChanged(false)); manager.SelectedDocuments = []; - Main.Instance.SetTextDoc(); + MainOverlayTextBox.Instance.SetTextDoc(); } } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index a72f02f14..3dbb1cb0f 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -1,24 +1,19 @@ -import { action, computed, observable, trace, runInAction } from "mobx"; +import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import { Key } from "../../fields/Key"; -//import ContentEditable from 'react-contenteditable' import { KeyStore } from "../../fields/KeyStore"; import { ListField } from "../../fields/ListField"; import { NumberField } from "../../fields/NumberField"; -import { Document } from "../../fields/Document"; import { TextField } from "../../fields/TextField"; -import { DragManager, DragLinksAsDocuments } from "../util/DragManager"; +import { emptyFunction } from "../../Utils"; +import { DragLinksAsDocuments, DragManager } from "../util/DragManager"; import { SelectionManager } from "../util/SelectionManager"; -import { CollectionView } from "./collections/CollectionView"; +import { undoBatch } from "../util/UndoManager"; import './DocumentDecorations.scss'; +import { MainOverlayTextBox } from "./MainOverlayTextBox"; import { DocumentView } from "./nodes/DocumentView"; import { LinkMenu } from "./nodes/LinkMenu"; import React = require("react"); -import { FieldWaiting, Field } from "../../fields/Field"; -import { emptyFunction } from "../../Utils"; -import { Main } from "./Main"; -import { undo } from "prosemirror-history"; -import { undoBatch } from "../util/UndoManager"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -77,14 +72,12 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> if (this._documents.length > 0) { let field = this._documents[0].props.Document.Get(this._fieldKey); if (field instanceof TextField) { - this._documents.forEach(d => { - d.props.Document.Set(this._fieldKey, new TextField(this._title)); - }); + this._documents.forEach(d => + d.props.Document.Set(this._fieldKey, new TextField(this._title))); } else if (field instanceof NumberField) { - this._documents.forEach(d => { - d.props.Document.Set(this._fieldKey, new NumberField(+this._title)) - }); + this._documents.forEach(d => + d.props.Document.Set(this._fieldKey, new NumberField(+this._title))); } this._title = "changed"; } @@ -317,7 +310,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> break; } - Main.Instance.SetTextDoc(); + MainOverlayTextBox.Instance.SetTextDoc(); SelectionManager.SelectedDocuments().forEach(element => { const rect = element.screenRect(); if (rect.width !== 0) { diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss index 13cadb10d..4373534b2 100644 --- a/src/client/views/Main.scss +++ b/src/client/views/Main.scss @@ -168,24 +168,6 @@ button:hover { left:0; overflow: scroll; } -.mainDiv-textInput { - background-color: rgba(248, 6, 6, 0.001); - width: 200px; - height: 200px; - position:absolute; - overflow: visible; - top: 0; - left: 0; - z-index: $mainTextInput-zindex; - .formattedTextBox-cont { - background-color: rgba(248, 6, 6, 0.001); - width: 100%; - height: 100%; - position:absolute; - top: 0; - left: 0; - } -} #mainContent-div { width:100%; height:100%; diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index ed61aa5a7..d26586216 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -1,7 +1,7 @@ import { IconName, library } from '@fortawesome/fontawesome-svg-core'; import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faTree, faUndoAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, configure, observable, runInAction, trace } from 'mobx'; +import { action, computed, configure, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import "normalize.css"; import * as React from 'react'; @@ -9,26 +9,25 @@ import * as ReactDOM from 'react-dom'; import Measure from 'react-measure'; import * as request from 'request'; import { Document } from '../../fields/Document'; -import { Field, FieldWaiting, Opt } from '../../fields/Field'; +import { Field, FieldWaiting, Opt, FIELD_WAITING } from '../../fields/Field'; import { KeyStore } from '../../fields/KeyStore'; import { ListField } from '../../fields/ListField'; import { WorkspacesMenu } from '../../server/authentication/controllers/WorkspacesMenu'; import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; import { MessageStore } from '../../server/Message'; -import { Utils, returnTrue, emptyFunction, emptyDocFunction } from '../../Utils'; -import * as rp from 'request-promise'; import { RouteStore } from '../../server/RouteStore'; import { ServerUtils } from '../../server/ServerUtil'; +import { emptyDocFunction, emptyFunction, returnTrue, Utils } from '../../Utils'; import { Documents } from '../documents/Documents'; import { ColumnAttributeModel } from '../northstar/core/attribute/AttributeModel'; import { AttributeTransformationModel } from '../northstar/core/attribute/AttributeTransformationModel'; -import { Gateway, Settings } from '../northstar/manager/Gateway'; -import { AggregateFunction, Catalog, Point } from '../northstar/model/idea/idea'; +import { Gateway, NorthstarSettings } from '../northstar/manager/Gateway'; +import { AggregateFunction, Catalog } from '../northstar/model/idea/idea'; import '../northstar/model/ModelExtensions'; import { HistogramOperation } from '../northstar/operations/HistogramOperation'; import '../northstar/utils/Extensions'; import { Server } from '../Server'; -import { SetupDrag, DragManager } from '../util/DragManager'; +import { SetupDrag } from '../util/DragManager'; import { Transform } from '../util/Transform'; import { UndoManager } from '../util/UndoManager'; import { CollectionDockingView } from './collections/CollectionDockingView'; @@ -36,41 +35,27 @@ import { ContextMenu } from './ContextMenu'; import { DocumentDecorations } from './DocumentDecorations'; import { InkingControl } from './InkingControl'; import "./Main.scss"; +import { MainOverlayTextBox } from './MainOverlayTextBox'; import { DocumentView } from './nodes/DocumentView'; -import { FormattedTextBox } from './nodes/FormattedTextBox'; -import { REPLCommand } from 'repl'; -import { Key } from '../../fields/Key'; import { PreviewCursor } from './PreviewCursor'; @observer export class Main extends React.Component { - // dummy initializations keep the compiler happy - @observable private mainfreeform?: Document; + public static Instance: Main; + @observable private _workspacesShown: boolean = false; @observable public pwidth: number = 0; @observable public pheight: number = 0; - private _northstarSchemas: Document[] = []; - - @computed private get mainContainer(): Document | undefined { - let doc = this.userDocument.GetT(KeyStore.ActiveWorkspace, Document); - return doc === FieldWaiting ? undefined : doc; - } - private set mainContainer(doc: Document | undefined) { - if (doc) { - this.userDocument.Set(KeyStore.ActiveWorkspace, doc); - } + @computed private get mainContainer(): Document | undefined | FIELD_WAITING { + return CurrentUserUtils.UserDocument.GetT(KeyStore.ActiveWorkspace, Document); } - - private get userDocument(): Document { - return CurrentUserUtils.UserDocument; + private set mainContainer(doc: Document | undefined | FIELD_WAITING) { + doc && CurrentUserUtils.UserDocument.Set(KeyStore.ActiveWorkspace, doc); } - public static Instance: Main; - constructor(props: Readonly<{}>) { super(props); - this._textProxyDiv = React.createRef(); Main.Instance = this; // causes errors to be generated when modifying an observable outside of an action configure({ enforceActions: "observed" }); @@ -102,6 +87,10 @@ export class Main extends React.Component { this.initializeNorthstar(); } + componentDidMount() { window.onpopstate = this.onHistory; } + + componentWillUnmount() { window.onpopstate = null; } + onHistory = () => { if (window.location.pathname !== RouteStore.home) { let pathname = window.location.pathname.split("/"); @@ -114,14 +103,6 @@ export class Main extends React.Component { } } - componentDidMount() { - window.onpopstate = this.onHistory; - } - - componentWillUnmount() { - window.onpopstate = null; - } - initEventListeners = () => { // window.addEventListener("pointermove", (e) => this.reportLocation(e)) window.addEventListener("drop", (e) => e.preventDefault(), false); // drop event handler @@ -137,7 +118,7 @@ export class Main extends React.Component { initAuthenticationRouters = () => { // Load the user's active workspace, or create a new one if initial session after signup if (!CurrentUserUtils.MainDocId) { - this.userDocument.GetTAsync(KeyStore.ActiveWorkspace, Document).then(doc => { + CurrentUserUtils.UserDocument.GetTAsync(KeyStore.ActiveWorkspace, Document).then(doc => { if (doc) { CurrentUserUtils.MainDocId = doc.Id; this.openWorkspace(doc); @@ -146,19 +127,15 @@ export class Main extends React.Component { } }); } else { - Server.GetField(CurrentUserUtils.MainDocId).then(field => { - if (field instanceof Document) { - this.openWorkspace(field); - } else { - this.createNewWorkspace(CurrentUserUtils.MainDocId); - } - }); + Server.GetField(CurrentUserUtils.MainDocId).then(field => + field instanceof Document ? this.openWorkspace(field) : + this.createNewWorkspace(CurrentUserUtils.MainDocId)); } } @action createNewWorkspace = (id?: string): void => { - this.userDocument.GetTAsync<ListField<Document>>(KeyStore.Workspaces, ListField).then(action((list: Opt<ListField<Document>>) => { + CurrentUserUtils.UserDocument.GetTAsync<ListField<Document>>(KeyStore.Workspaces, ListField).then(action((list: Opt<ListField<Document>>) => { if (list) { let freeformDoc = Documents.FreeformDocument([], { x: 0, y: 400, title: "mini collection" }); var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc)] }] }; @@ -179,139 +156,49 @@ export class Main extends React.Component { openWorkspace = (doc: Document, fromHistory = false): void => { this.mainContainer = doc; fromHistory || window.history.pushState(null, doc.Title, "/doc/" + doc.Id); - this.userDocument.GetTAsync(KeyStore.OptionalRightCollection, Document).then(col => { + CurrentUserUtils.UserDocument.GetTAsync(KeyStore.OptionalRightCollection, Document).then(col => // if there is a pending doc, and it has new data, show it (syip: we use a timeout to prevent collection docking view from being uninitialized) - setTimeout(() => { - if (col) { - col.GetTAsync<ListField<Document>>(KeyStore.Data, ListField, (f: Opt<ListField<Document>>) => { - if (f && f.Data.length > 0) { - CollectionDockingView.Instance.AddRightSplit(col); - } - }); - } - }, 100); - }); - } - - @observable - workspacesShown: boolean = false; - - areWorkspacesShown = () => this.workspacesShown; - @action - toggleWorkspaces = () => { - this.workspacesShown = !this.workspacesShown; - } - - pwidthFunc = () => this.pwidth; - pheightFunc = () => this.pheight; - noScaling = () => 1; - - @observable _textDoc?: Document = undefined; - _textRect: any; - _textXf: Transform = Transform.Identity(); - _textScroll: number = 0; - _textFieldKey: Key = KeyStore.Data; - _textColor: string | null = null; - _textTargetDiv: HTMLDivElement | undefined; - _textProxyDiv: React.RefObject<HTMLDivElement>; - @action - SetTextDoc(textDoc?: Document, textFieldKey?: Key, div?: HTMLDivElement, tx?: Transform) { - if (this._textTargetDiv) { - this._textTargetDiv.style.color = this._textColor; - } - - this._textDoc = undefined; - this._textDoc = textDoc; - this._textFieldKey = textFieldKey!; - this._textXf = tx ? tx : Transform.Identity(); - this._textTargetDiv = div; - if (div) { - this._textColor = div.style.color; - div.style.color = "transparent"; - this._textRect = div.getBoundingClientRect(); - this._textScroll = div.scrollTop; - } - } - - @action - textScroll = (e: React.UIEvent) => { - if (this._textProxyDiv.current && this._textTargetDiv) { - this._textTargetDiv.scrollTop = this._textScroll = this._textProxyDiv.current.children[0].scrollTop; - } - } - - textBoxDown = (e: React.PointerEvent) => { - if (e.button !== 0 || e.metaKey || e.altKey) { - document.addEventListener("pointermove", this.textBoxMove); - document.addEventListener('pointerup', this.textBoxUp); - } - } - textBoxMove = (e: PointerEvent) => { - if (e.movementX > 1 || e.movementY > 1) { - document.removeEventListener("pointermove", this.textBoxMove); - document.removeEventListener('pointerup', this.textBoxUp); - let dragData = new DragManager.DocumentDragData([this._textDoc!]); - const [left, top] = this._textXf - .inverse() - .transformPoint(0, 0); - dragData.xOffset = e.clientX - left; - dragData.yOffset = e.clientY - top; - DragManager.StartDocumentDrag([this._textTargetDiv!], dragData, e.clientX, e.clientY, { - handlers: { - dragComplete: action(emptyFunction), - }, - hideSource: false - }); - } - } - textBoxUp = (e: PointerEvent) => { - document.removeEventListener("pointermove", this.textBoxMove); - document.removeEventListener('pointerup', this.textBoxUp); - } - - @computed - get activeTextBox() { - if (this._textDoc) { - let x: number = this._textRect.x; - let y: number = this._textRect.y; - let w: number = this._textRect.width; - let h: number = this._textRect.height; - let t = this._textXf.transformPoint(0, 0); - let s = this._textXf.transformPoint(1, 0); - s[0] = Math.sqrt((s[0] - t[0]) * (s[0] - t[0]) + (s[1] - t[1]) * (s[1] - t[1])); - return <div className="mainDiv-textInput" style={{ pointerEvents: "none", transform: `translate(${x}px, ${y}px) scale(${1 / s[0]},${1 / s[0]})`, width: "auto", height: "auto" }} > - <div className="mainDiv-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll} style={{ pointerEvents: "none", transform: `scale(${1}, ${1})`, width: `${w * s[0]}px`, height: `${h * s[0]}px` }}> - <FormattedTextBox fieldKey={this._textFieldKey!} isOverlay={true} Document={this._textDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true} - selectOnLoad={true} onActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={() => this._textXf} focus={emptyDocFunction} /> - </div> - </ div>; - } - else return (null); + setTimeout(() => + col && col.GetTAsync<ListField<Document>>(KeyStore.Data, ListField, (f: Opt<ListField<Document>>) => + f && f.Data.length > 0 && CollectionDockingView.Instance.AddRightSplit(col)) + , 100) + ); } @computed get mainContent() { - return !this.mainContainer ? (null) : - <DocumentView Document={this.mainContainer} - addDocument={undefined} - removeDocument={undefined} - ScreenToLocalTransform={Transform.Identity} - ContentScaling={this.noScaling} - PanelWidth={this.pwidthFunc} - PanelHeight={this.pheightFunc} - isTopMost={true} - selectOnLoad={false} - focus={emptyDocFunction} - parentActive={returnTrue} - onActiveChanged={emptyFunction} - ContainingCollectionView={undefined} />; + let pwidthFunc = () => this.pwidth; + let pheightFunc = () => this.pheight; + let noScaling = () => 1; + let mainCont = this.mainContainer; + return <Measure onResize={action((r: any) => { this.pwidth = r.entry.width; this.pheight = r.entry.height; })}> + {({ measureRef }) => + <div ref={measureRef} id="mainContent-div"> + {!mainCont ? (null) : + <DocumentView Document={mainCont} + addDocument={undefined} + removeDocument={undefined} + opacity={1} + ScreenToLocalTransform={Transform.Identity} + ContentScaling={noScaling} + PanelWidth={pwidthFunc} + PanelHeight={pheightFunc} + isTopMost={true} + selectOnLoad={false} + focus={emptyDocFunction} + parentActive={returnTrue} + onActiveChanged={emptyFunction} + ContainingCollectionView={undefined} />} + </div> + } + </Measure>; } /* for the expandable add nodes menu. Not included with the miscbuttons because once it expands it expands the whole div with it, making canvas interactions limited. */ @computed get nodesMenu() { let imgurl = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg"; - let pdfurl = "http://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf"; + let pdfurl = "http://www.adobe.com/support/products/enterprise/knowledgecenter/media/c27211_sample_explain.pdf"; let weburl = "https://cs.brown.edu/courses/cs166/"; let audiourl = "http://techslides.com/demos/samples/sample.mp3"; let videourl = "http://techslides.com/demos/sample-videos/small.mp4"; @@ -360,6 +247,7 @@ export class Main extends React.Component { get miscButtons() { let workspacesRef = React.createRef<HTMLDivElement>(); let logoutRef = React.createRef<HTMLDivElement>(); + let toggleWorkspaces = () => runInAction(() => this._workspacesShown = !this._workspacesShown); let clearDatabase = action(() => Utils.Emit(Server.Socket, MessageStore.DeleteAll, {})); return [ @@ -370,55 +258,50 @@ export class Main extends React.Component { <button className="toolbar-button round-button" title="Ink" onClick={() => InkingControl.Instance.toggleDisplay()}><FontAwesomeIcon icon="pen-nib" size="sm" /></button> </div >, <div className="main-buttonDiv" key="workspaces" style={{ top: '34px', left: '2px', position: 'absolute' }} ref={workspacesRef}> - <button onClick={this.toggleWorkspaces}>Workspaces</button></div>, + <button onClick={toggleWorkspaces}>Workspaces</button></div>, <div className="main-buttonDiv" key="logout" style={{ top: '34px', right: '1px', position: 'absolute' }} ref={logoutRef}> <button onClick={() => request.get(ServerUtils.prepend(RouteStore.logout), emptyFunction)}>Log Out</button></div> ]; } + @computed + get workspaceMenu() { + let areWorkspacesShown = () => this._workspacesShown; + let toggleWorkspaces = () => runInAction(() => this._workspacesShown = !this._workspacesShown); + let workspaces = CurrentUserUtils.UserDocument.GetT<ListField<Document>>(KeyStore.Workspaces, ListField); + return (!workspaces || workspaces === FieldWaiting || this.mainContainer === FieldWaiting) ? (null) : + <WorkspacesMenu active={this.mainContainer} open={this.openWorkspace} + new={this.createNewWorkspace} allWorkspaces={workspaces.Data} + isShown={areWorkspacesShown} toggle={toggleWorkspaces} />; + } + render() { - let workspaceMenu: any = null; - let workspaces = this.userDocument.GetT<ListField<Document>>(KeyStore.Workspaces, ListField); - if (workspaces && workspaces !== FieldWaiting) { - workspaceMenu = <WorkspacesMenu active={this.mainContainer} open={this.openWorkspace} new={this.createNewWorkspace} allWorkspaces={workspaces.Data} - isShown={this.areWorkspacesShown} toggle={this.toggleWorkspaces} />; - } return ( - <> - <div id="main-div"> - <DocumentDecorations /> - <Measure onResize={(r: any) => runInAction(() => { - this.pwidth = r.entry.width; - this.pheight = r.entry.height; - })}> - {({ measureRef }) => - <div ref={measureRef} id="mainContent-div"> - {this.mainContent} - <PreviewCursor /> - </div> - } - </Measure> - <ContextMenu /> - {this.nodesMenu} - {this.miscButtons} - {workspaceMenu} - <InkingControl /> - </div> - {this.activeTextBox} - </> + <div id="main-div"> + <DocumentDecorations /> + {this.mainContent} + <PreviewCursor /> + <ContextMenu /> + {this.nodesMenu} + {this.miscButtons} + {this.workspaceMenu} + <InkingControl /> + <MainOverlayTextBox /> + </div> ); } // --------------- Northstar hooks ------------- / + private _northstarSchemas: Document[] = []; - @action AddToNorthstarCatalog(ctlog: Catalog) { - CurrentUserUtils.NorthstarDBCatalog = CurrentUserUtils.NorthstarDBCatalog ? CurrentUserUtils.NorthstarDBCatalog : ctlog; + @action SetNorthstarCatalog(ctlog: Catalog) { + CurrentUserUtils.NorthstarDBCatalog = ctlog; if (ctlog && ctlog.schemas) { ctlog.schemas.map(schema => { - let promises: Promise<void>[] = []; let schemaDocuments: Document[] = []; - CurrentUserUtils.GetAllNorthstarColumnAttributes(schema).map(attr => { - let prom = Server.GetField(attr.displayName! + ".alias").then(action((field: Opt<Field>) => { + let attributesToBecomeDocs = CurrentUserUtils.GetAllNorthstarColumnAttributes(schema); + Promise.all(attributesToBecomeDocs.reduce((promises, attr) => { + promises.push(Server.GetField(attr.displayName! + ".alias").then(action((field: Opt<Field>) => { if (field instanceof Document) { schemaDocuments.push(field); } else { @@ -429,32 +312,17 @@ export class Main extends React.Component { new AttributeTransformationModel(atmod, AggregateFunction.Count)); schemaDocuments.push(Documents.HistogramDocument(histoOp, { width: 200, height: 200, title: attr.displayName! }, undefined, attr.displayName! + ".alias")); } - })); - promises.push(prom); - }); - Promise.all(promises).finally(() => { - let schemaDoc = Documents.TreeDocument(schemaDocuments, { width: 50, height: 100, title: schema.displayName! }); - this._northstarSchemas.push(schemaDoc); - }); + }))); + return promises; + }, [] as Promise<void>[])).finally(() => + this._northstarSchemas.push(Documents.TreeDocument(schemaDocuments, { width: 50, height: 100, title: schema.displayName! }))); }); } } async initializeNorthstar(): Promise<void> { - let envPath = "/assets/env.json"; - const response = await fetch(envPath, { - redirect: "follow", - method: "GET", - credentials: "include" - }); - const env = await response.json(); - Settings.Instance.Update(env); - let cat = Gateway.Instance.ClearCatalog(); - cat.then(async () => { - this.AddToNorthstarCatalog(await Gateway.Instance.GetCatalog()); - // if (!CurrentUserUtils.GetNorthstarSchema("Book1")) - // this.AddToNorthstarCatalog(await Gateway.Instance.GetSchema("http://www.cs.brown.edu/~bcz/Book1.csv", "Book1")); - }); - + const getEnvironment = await fetch("/assets/env.json", { redirect: "follow", method: "GET", credentials: "include" }); + NorthstarSettings.Instance.UpdateEnvironment(await getEnvironment.json()); + Gateway.Instance.ClearCatalog().then(async () => this.SetNorthstarCatalog(await Gateway.Instance.GetCatalog())); } } diff --git a/src/client/views/MainOverlayTextBox.scss b/src/client/views/MainOverlayTextBox.scss new file mode 100644 index 000000000..697d68c8c --- /dev/null +++ b/src/client/views/MainOverlayTextBox.scss @@ -0,0 +1,19 @@ +@import "globalCssVariables"; +.mainOverlayTextBox-textInput { + background-color: rgba(248, 6, 6, 0.001); + width: 200px; + height: 200px; + position:absolute; + overflow: visible; + top: 0; + left: 0; + z-index: $mainTextInput-zindex; + .formattedTextBox-cont { + background-color: rgba(248, 6, 6, 0.001); + width: 100%; + height: 100%; + position:absolute; + top: 0; + left: 0; + } +}
\ No newline at end of file diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx new file mode 100644 index 000000000..df1addbc7 --- /dev/null +++ b/src/client/views/MainOverlayTextBox.tsx @@ -0,0 +1,110 @@ +import { action, observable, trace } from 'mobx'; +import { observer } from 'mobx-react'; +import "normalize.css"; +import * as React from 'react'; +import { Document } from '../../fields/Document'; +import { Key } from '../../fields/Key'; +import { KeyStore } from '../../fields/KeyStore'; +import { emptyDocFunction, emptyFunction, returnTrue } from '../../Utils'; +import '../northstar/model/ModelExtensions'; +import '../northstar/utils/Extensions'; +import { DragManager } from '../util/DragManager'; +import { Transform } from '../util/Transform'; +import "./MainOverlayTextBox.scss"; +import { FormattedTextBox } from './nodes/FormattedTextBox'; + +interface MainOverlayTextBoxProps { +} + +@observer +export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps> { + public static Instance: MainOverlayTextBox; + @observable public TextDoc?: Document = undefined; + public TextScroll: number = 0; + private _textRect: any; + private _textXf: Transform = Transform.Identity(); + private _textFieldKey: Key = KeyStore.Data; + private _textColor: string | null = null; + private _textTargetDiv: HTMLDivElement | undefined; + private _textProxyDiv: React.RefObject<HTMLDivElement>; + + constructor(props: MainOverlayTextBoxProps) { + super(props); + this._textProxyDiv = React.createRef(); + MainOverlayTextBox.Instance = this; + } + + @action + SetTextDoc(textDoc?: Document, textFieldKey?: Key, div?: HTMLDivElement, tx?: Transform) { + if (this._textTargetDiv) { + this._textTargetDiv.style.color = this._textColor; + } + + this.TextDoc = undefined; + this.TextDoc = textDoc; + this._textFieldKey = textFieldKey!; + this._textXf = tx ? tx : Transform.Identity(); + this._textTargetDiv = div; + if (div) { + this._textColor = div.style.color; + div.style.color = "transparent"; + this._textRect = div.getBoundingClientRect(); + this.TextScroll = div.scrollTop; + } + } + + @action + textScroll = (e: React.UIEvent) => { + if (this._textProxyDiv.current && this._textTargetDiv) { + this._textTargetDiv.scrollTop = this.TextScroll = this._textProxyDiv.current.children[0].scrollTop; + } + } + + textBoxDown = (e: React.PointerEvent) => { + if (e.button !== 0 || e.metaKey || e.altKey) { + document.addEventListener("pointermove", this.textBoxMove); + document.addEventListener('pointerup', this.textBoxUp); + } + } + textBoxMove = (e: PointerEvent) => { + if (e.movementX > 1 || e.movementY > 1) { + document.removeEventListener("pointermove", this.textBoxMove); + document.removeEventListener('pointerup', this.textBoxUp); + let dragData = new DragManager.DocumentDragData([this.TextDoc!]); + const [left, top] = this._textXf + .inverse() + .transformPoint(0, 0); + dragData.xOffset = e.clientX - left; + dragData.yOffset = e.clientY - top; + DragManager.StartDocumentDrag([this._textTargetDiv!], dragData, e.clientX, e.clientY, { + handlers: { + dragComplete: action(emptyFunction), + }, + hideSource: false + }); + } + } + textBoxUp = (e: PointerEvent) => { + document.removeEventListener("pointermove", this.textBoxMove); + document.removeEventListener('pointerup', this.textBoxUp); + } + + render() { + if (this.TextDoc) { + let x: number = this._textRect.x; + let y: number = this._textRect.y; + let w: number = this._textRect.width; + let h: number = this._textRect.height; + let t = this._textXf.transformPoint(0, 0); + let s = this._textXf.transformPoint(1, 0); + s[0] = Math.sqrt((s[0] - t[0]) * (s[0] - t[0]) + (s[1] - t[1]) * (s[1] - t[1])); + return <div className="mainOverlayTextBox-textInput" style={{ pointerEvents: "none", transform: `translate(${x}px, ${y}px) scale(${1 / s[0]},${1 / s[0]})`, width: "auto", height: "auto" }} > + <div className="mainOverlayTextBox-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll} style={{ pointerEvents: "none", transform: `scale(${1}, ${1})`, width: `${w * s[0]}px`, height: `${h * s[0]}px` }}> + <FormattedTextBox fieldKey={this._textFieldKey!} isOverlay={true} Document={this.TextDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true} + selectOnLoad={true} ContainingCollectionView={undefined} onActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={() => this._textXf} focus={emptyDocFunction} /> + </div> + </ div>; + } + else return (null); + } +}
\ No newline at end of file diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index b5eaab349..bff56e8c3 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -1,4 +1,4 @@ -import { action } from 'mobx'; +import { action, computed } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Document } from '../../../fields/Document'; @@ -80,12 +80,16 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> { } return false; } + @computed get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey.Id === KeyStore.Annotations.Id; } // bcz: ? Why do we need to compare Id's? @action.bound addDocument(doc: Document, allowDuplicates: boolean = false): boolean { let props = this.props; var curPage = props.Document.GetNumber(KeyStore.CurPage, -1); doc.SetOnPrototype(KeyStore.Page, new NumberField(curPage)); + if (this.isAnnotationOverlay) { + doc.SetOnPrototype(KeyStore.Zoom, new NumberField(this.props.Document.GetNumber(KeyStore.Scale, 1))); + } if (curPage >= 0) { doc.SetOnPrototype(KeyStore.AnnotationOn, props.Document); } diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index eb1cd1c09..4f7d4fc0d 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -172,7 +172,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp } catch (e) { } - this._goldenLayout.destroy(); + if (this._goldenLayout) this._goldenLayout.destroy(); this._goldenLayout = null; window.removeEventListener('resize', this.onResize); } @@ -343,6 +343,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> { <DocumentView key={this._document.Id} Document={this._document} addDocument={undefined} removeDocument={undefined} + opacity={1} ContentScaling={this._contentScaling} PanelWidth={this._nativeWidth} PanelHeight={this._nativeHeight} diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx index 6cbe59012..229bc4059 100644 --- a/src/client/views/collections/CollectionPDFView.tsx +++ b/src/client/views/collections/CollectionPDFView.tsx @@ -1,4 +1,4 @@ -import { action, computed, observable } from "mobx"; +import { action } from "mobx"; import { observer } from "mobx-react"; import { KeyStore } from "../../../fields/KeyStore"; import { ContextMenu } from "../ContextMenu"; @@ -42,7 +42,7 @@ export class CollectionPDFView extends React.Component<FieldViewProps> { let props = { ...this.props, ...renderProps }; return ( <> - <CollectionFreeFormView {...props} /> + <CollectionFreeFormView {...props} CollectionView={this} /> {this.props.isSelected() ? this.uIButtons : (null)} </> ); diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index fdb82690a..2c99c9c67 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -75,6 +75,7 @@ export class CollectionSchemaView extends CollectionSubView { let props: FieldViewProps = { Document: rowProps.value[0], fieldKey: rowProps.value[1], + ContainingCollectionView: this.props.CollectionView, isSelected: returnFalse, select: emptyFunction, isTopMost: false, @@ -307,12 +308,13 @@ export class CollectionSchemaView extends CollectionSubView { <DocumentView Document={doc} addDocument={this.props.addDocument} removeDocument={this.props.removeDocument} isTopMost={false} + opacity={1} selectOnLoad={false} ScreenToLocalTransform={this.getPreviewTransform} ContentScaling={this.getContentScaling} PanelWidth={this.getPanelWidth} PanelHeight={this.getPanelHeight} - ContainingCollectionView={undefined} + ContainingCollectionView={this.props.CollectionView} focus={emptyDocFunction} parentActive={this.props.active} onActiveChanged={this.props.onActiveChanged} /> : null} diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index d91db68bb..d3d69b1af 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -16,6 +16,9 @@ import { Server } from "../../Server"; import { FieldViewProps } from "../nodes/FieldView"; import * as rp from 'request-promise'; import { emptyFunction } from "../../../Utils"; +import { CollectionView } from "./CollectionView"; +import { CollectionPDFView } from "./CollectionPDFView"; +import { CollectionVideoView } from "./CollectionVideoView"; export interface CollectionViewProps extends FieldViewProps { addDocument: (document: Document, allowDuplicates?: boolean) => boolean; @@ -24,6 +27,7 @@ export interface CollectionViewProps extends FieldViewProps { } export interface SubCollectionViewProps extends CollectionViewProps { + CollectionView: CollectionView | CollectionPDFView | CollectionVideoView; } export type CursorEntry = TupleField<[string, string], [number, number]>; @@ -193,7 +197,7 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> { }).then(async (res: Response) => { (await res.json()).map(action((file: any) => { let path = window.location.origin + file; - let docPromise = this.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300, title: dropFileName }); + let docPromise = this.getDocumentFromType(type, path, { ...options, nativeWidth: 600, width: 300, title: dropFileName }); docPromise.then(action((doc?: Document) => { let docs = this.props.Document.GetT(KeyStore.Data, ListField); diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx index 6c9780adb..29fb342c6 100644 --- a/src/client/views/collections/CollectionVideoView.tsx +++ b/src/client/views/collections/CollectionVideoView.tsx @@ -109,7 +109,7 @@ export class CollectionVideoView extends React.Component<FieldViewProps> { let props = { ...this.props, ...renderProps }; return ( <> - <CollectionFreeFormView {...props} /> + <CollectionFreeFormView {...props} CollectionView={this} /> {this.props.isSelected() ? this.uIButtons : (null)} </> ); diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 8abd0a02d..1f69a69e8 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -18,12 +18,12 @@ export class CollectionView extends React.Component<FieldViewProps> { private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => { let props = { ...this.props, ...renderProps }; switch (type) { - case CollectionViewType.Schema: return (<CollectionSchemaView {...props} />); - case CollectionViewType.Docking: return (<CollectionDockingView {...props} />); - case CollectionViewType.Tree: return (<CollectionTreeView {...props} />); + case CollectionViewType.Schema: return (<CollectionSchemaView {...props} CollectionView={this} />); + case CollectionViewType.Docking: return (<CollectionDockingView {...props} CollectionView={this} />); + case CollectionViewType.Tree: return (<CollectionTreeView {...props} CollectionView={this} />); case CollectionViewType.Freeform: default: - return (<CollectionFreeFormView {...props} />); + return (<CollectionFreeFormView {...props} CollectionView={this} />); } return (null); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index 647c83d4d..2f684a54e 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -1,7 +1,6 @@ -import { computed, reaction, trace, IReactionDisposer } from "mobx"; +import { computed, IReactionDisposer, reaction } from "mobx"; import { observer } from "mobx-react"; import { Document } from "../../../../fields/Document"; -import { FieldWaiting } from "../../../../fields/Field"; import { KeyStore } from "../../../../fields/KeyStore"; import { ListField } from "../../../../fields/ListField"; import { Utils } from "../../../../Utils"; @@ -85,19 +84,17 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP let targetViews = this.documentAnchors(connection.b); let possiblePairs: { a: Document, b: Document, }[] = []; srcViews.map(sv => targetViews.map(tv => possiblePairs.push({ a: sv.props.Document, b: tv.props.Document }))); - possiblePairs.map(possiblePair => { - if (!drawnPairs.reduce((found, drawnPair) => { + possiblePairs.map(possiblePair => + drawnPairs.reduce((found, drawnPair) => { let match = (possiblePair.a === drawnPair.a && possiblePair.b === drawnPair.b); - if (match) { - if (!drawnPair.l.reduce((found, link) => found || link.Id === connection.l.Id, false)) { - drawnPair.l.push(connection.l); - } + if (match && !drawnPair.l.reduce((found, link) => found || link.Id === connection.l.Id, false)) { + drawnPair.l.push(connection.l); } return match || found; - }, false)) { - drawnPairs.push({ a: possiblePair.a, b: possiblePair.b, l: [connection.l] }); - } - }); + }, false) + || + drawnPairs.push({ a: possiblePair.a, b: possiblePair.b, l: [connection.l] }) + ); return drawnPairs; }, [] as { a: Document, b: Document, l: Document[] }[]); return connections.map(c => <CollectionFreeFormLinkView key={Utils.GenerateGuid()} A={c.a} B={c.b} LinkDocs={c.l} />); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index e19dc98fa..cb4e8fb2e 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,10 +1,9 @@ -import { action, computed, observable, runInAction } from "mobx"; +import { action, computed, observable, runInAction, untracked } from "mobx"; import { observer } from "mobx-react"; import Measure from "react-measure"; import { Document } from "../../../../fields/Document"; import { FieldWaiting } from "../../../../fields/Field"; import { KeyStore } from "../../../../fields/KeyStore"; -import { NumberField } from "../../../../fields/NumberField"; import { TextField } from "../../../../fields/TextField"; import { emptyFunction, returnFalse } from "../../../../Utils"; import { DocumentManager } from "../../../util/DocumentManager"; @@ -18,13 +17,14 @@ import { Main } from "../../Main"; import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView"; import { DocumentContentsView } from "../../nodes/DocumentContentsView"; import { DocumentViewProps } from "../../nodes/DocumentView"; -import { CollectionSubView } from "../CollectionSubView"; +import { CollectionSubView, SubCollectionViewProps } from "../CollectionSubView"; import { CollectionFreeFormLinksView } from "./CollectionFreeFormLinksView"; import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors"; import "./CollectionFreeFormView.scss"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); import v5 = require("uuid/v5"); +import { MainOverlayTextBox } from "../../MainOverlayTextBox"; @observer export class CollectionFreeFormView extends CollectionSubView { @@ -38,19 +38,16 @@ export class CollectionFreeFormView extends CollectionSubView { } public addDocument = (newBox: Document, allowDuplicates: boolean) => { - let added = this.props.addDocument(newBox, false); - this.bringToFront(newBox); - return added; + if (this.isAnnotationOverlay) { + newBox.SetNumber(KeyStore.Zoom, this.props.Document.GetNumber(KeyStore.Scale, 1)); + } + return this.props.addDocument(this.bringToFront(newBox), false); } public selectDocuments = (docs: Document[]) => { SelectionManager.DeselectAll; - docs.map(doc => { - const dv = DocumentManager.Instance.getDocumentView(doc); - if (dv) { - SelectionManager.SelectDoc(dv, true); - } - }); + docs.map(doc => DocumentManager.Instance.getDocumentView(doc)).filter(dv => dv).map(dv => + SelectionManager.SelectDoc(dv!, true)); } public getActiveDocuments = () => { @@ -84,41 +81,28 @@ export class CollectionFreeFormView extends CollectionSubView { @undoBatch @action drop = (e: Event, de: DragManager.DropEvent) => { - if (super.drop(e, de)) { - if (de.data instanceof DragManager.DocumentDragData) { - let droppedDocs = de.data.droppedDocuments; - let xoff = de.data.xOffset as number || 0; - let yoff = de.data.yOffset as number || 0; - if (droppedDocs.length) { - let screenX = de.x - xoff; - let screenY = de.y - yoff; - const [x, y] = this.getTransform().transformPoint(screenX, screenY); - let dragDoc = droppedDocs[0]; - let dragX = dragDoc.GetNumber(KeyStore.X, 0); - let dragY = dragDoc.GetNumber(KeyStore.Y, 0); - droppedDocs.map(async d => { - let docX = d.GetNumber(KeyStore.X, 0); - let docY = d.GetNumber(KeyStore.Y, 0); - d.SetNumber(KeyStore.X, x + (docX - dragX)); - d.SetNumber(KeyStore.Y, y + (docY - dragY)); - let docW = await d.GetTAsync(KeyStore.Width, NumberField); - let docH = await d.GetTAsync(KeyStore.Height, NumberField); - if (!docW) { - d.SetNumber(KeyStore.Width, 300); - } - if (!docH) { - d.SetNumber(KeyStore.Height, 300); - } - this.bringToFront(d); - }); - } + if (super.drop(e, de) && de.data instanceof DragManager.DocumentDragData) { + const [x, y] = this.getTransform().transformPoint(de.x - de.data.xOffset, de.y - de.data.yOffset); + if (de.data.droppedDocuments.length) { + let dropX = de.data.droppedDocuments[0].GetNumber(KeyStore.X, 0); + let dropY = de.data.droppedDocuments[0].GetNumber(KeyStore.Y, 0); + de.data.droppedDocuments.map(d => { + d.SetNumber(KeyStore.X, x + (d.GetNumber(KeyStore.X, 0) - dropX)); + d.SetNumber(KeyStore.Y, y + (d.GetNumber(KeyStore.Y, 0) - dropY)); + if (!d.GetNumber(KeyStore.Width, 0)) { + d.SetNumber(KeyStore.Width, 300); + } + if (!d.GetNumber(KeyStore.Height, 0)) { + d.SetNumber(KeyStore.Height, 300); + } + this.bringToFront(d); + }); } return true; } return false; } - @action cleanupInteractions = () => { document.removeEventListener("pointermove", this.onPointerMove); @@ -150,7 +134,28 @@ export class CollectionFreeFormView extends CollectionSubView { if ((!this.isAnnotationOverlay || this.zoomScaling !== 1) && !e.shiftKey) { let x = this.props.Document.GetNumber(KeyStore.PanX, 0); let y = this.props.Document.GetNumber(KeyStore.PanY, 0); + let docs = this.props.Document.GetList(this.props.fieldKey, [] as Document[]); let [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY); + if (!this.isAnnotationOverlay) { + let minx = docs.length ? docs[0].GetNumber(KeyStore.X, 0) : 0; + let maxx = docs.length ? docs[0].GetNumber(KeyStore.Width, 0) + minx : minx; + let miny = docs.length ? docs[0].GetNumber(KeyStore.Y, 0) : 0; + let maxy = docs.length ? docs[0].GetNumber(KeyStore.Height, 0) + miny : miny; + let ranges = docs.filter(doc => doc).reduce((range, doc) => { + let x = doc.GetNumber(KeyStore.X, 0); + let xe = x + doc.GetNumber(KeyStore.Width, 0); + let y = doc.GetNumber(KeyStore.Y, 0); + let ye = y + doc.GetNumber(KeyStore.Height, 0); + return [[range[0][0] > x ? x : range[0][0], range[0][1] < xe ? xe : range[0][1]], + [range[1][0] > y ? y : range[1][0], range[1][1] < ye ? ye : range[1][1]]]; + }, [[minx, maxx], [miny, maxy]]); + let panelwidth = this._pwidth / this.scale / 2; + let panelheight = this._pheight / this.scale / 2; + if (x - dx < ranges[0][0] - panelwidth) x = ranges[0][1] + panelwidth + dx; + if (x - dx > ranges[0][1] + panelwidth) x = ranges[0][0] - panelwidth + dx; + if (y - dy < ranges[1][0] - panelheight) y = ranges[1][1] + panelheight + dy; + if (y - dy > ranges[1][1] + panelheight) y = ranges[1][0] - panelheight + dy; + } this.SetPan(x - dx, y - dy); this._lastX = e.pageX; this._lastY = e.pageY; @@ -162,7 +167,9 @@ export class CollectionFreeFormView extends CollectionSubView { @action onPointerWheel = (e: React.WheelEvent): void => { - this.props.select(false); + // if (!this.props.active()) { + // return; + // } e.stopPropagation(); let coefficient = 1000; @@ -192,12 +199,14 @@ export class CollectionFreeFormView extends CollectionSubView { this.props.Document.SetNumber(KeyStore.Scale, localTransform.Scale); this.SetPan(-localTransform.TranslateX / localTransform.Scale, -localTransform.TranslateY / localTransform.Scale); + e.stopPropagation(); + e.preventDefault(); } } @action private SetPan(panX: number, panY: number) { - Main.Instance.SetTextDoc(); + MainOverlayTextBox.Instance.SetTextDoc(); var x1 = this.getLocalTransform().inverse().Scale; const newPanX = Math.min((1 - 1 / x1) * this.nativeWidth, Math.max(0, panX)); const newPanY = Math.min((1 - 1 / x1) * this.nativeHeight, Math.max(0, panY)); @@ -227,9 +236,9 @@ export class CollectionFreeFormView extends CollectionSubView { return -1; } return doc1.GetNumber(KeyStore.ZIndex, 0) - doc2.GetNumber(KeyStore.ZIndex, 0); - }).map((doc, index) => { - doc.SetNumber(KeyStore.ZIndex, index + 1); - }); + }).map((doc, index) => + doc.SetNumber(KeyStore.ZIndex, index + 1)); + return doc; } @computed get backgroundLayout(): string | undefined { @@ -252,9 +261,10 @@ export class CollectionFreeFormView extends CollectionSubView { this.props.focus(this.props.Document); } - getDocumentViewProps(document: Document): DocumentViewProps { + getDocumentViewProps(document: Document, opacity: number): DocumentViewProps { return { Document: document, + opacity: opacity, addDocument: this.props.addDocument, removeDocument: this.props.removeDocument, moveDocument: this.props.moveDocument, @@ -264,7 +274,7 @@ export class CollectionFreeFormView extends CollectionSubView { PanelWidth: document.Width, PanelHeight: document.Height, ContentScaling: this.noScaling, - ContainingCollectionView: undefined, + ContainingCollectionView: this.props.CollectionView, focus: this.focusDocument, parentActive: this.props.active, onActiveChanged: this.props.active, @@ -274,25 +284,34 @@ export class CollectionFreeFormView extends CollectionSubView { @computed get views() { var curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1); - return this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc).reduce((prev, doc) => { + let docviews = this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc).reduce((prev, doc) => { var page = doc.GetNumber(KeyStore.Page, -1); - if (page === curPage || page === -1) { - prev.push(<CollectionFreeFormDocumentView key={doc.Id} {...this.getDocumentViewProps(doc)} />); + var zoom = doc.GetNumber(KeyStore.Zoom, 1); + var dv = DocumentManager.Instance.getDocumentView(doc); + let opacity = this.isAnnotationOverlay && (!dv || !SelectionManager.IsSelected(dv)) ? 1 - Math.abs(zoom - this.scale) : 1; + if ((page === curPage || page === -1)) { + prev.push(<CollectionFreeFormDocumentView key={doc.Id} {...this.getDocumentViewProps(doc, opacity)} />); } return prev; }, [] as JSX.Element[]); + + setTimeout(() => { // bcz: surely there must be a better way .... + this._selectOnLoaded = ""; + }, 600); + + return docviews; } @computed get backgroundView() { return !this.backgroundLayout ? (null) : - (<DocumentContentsView {...this.getDocumentViewProps(this.props.Document)} + (<DocumentContentsView {...this.getDocumentViewProps(this.props.Document, 1)} layoutKey={KeyStore.BackgroundLayout} isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />); } @computed get overlayView() { return !this.overlayLayout ? (null) : - (<DocumentContentsView {...this.getDocumentViewProps(this.props.Document)} + (<DocumentContentsView {...this.getDocumentViewProps(this.props.Document, 1)} layoutKey={KeyStore.OverlayLayout} isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />); } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.scss b/src/client/views/collections/collectionFreeForm/MarqueeView.scss index e5ffcec76..ae0a9fd48 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.scss +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.scss @@ -13,4 +13,14 @@ border-width: 1px; border-color: black; pointer-events: none; + .marquee-legend { + bottom:-18px; + left:0; + position: absolute; + font-size: 9; + white-space:nowrap; + } + .marquee-legend::after { + content: "Press: C (collection), or Delete" + } }
\ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index a4722a6f8..dbab790d4 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -209,10 +209,11 @@ export class MarqueeView extends React.Component<MarqueeViewProps> let selRect = this.Bounds; let selection: Document[] = []; this.props.activeDocuments().map(doc => { + var z = doc.GetNumber(KeyStore.Zoom, 1); var x = doc.GetNumber(KeyStore.X, 0); var y = doc.GetNumber(KeyStore.Y, 0); - var w = doc.GetNumber(KeyStore.Width, 0); - var h = doc.GetNumber(KeyStore.Height, 0); + var w = doc.GetNumber(KeyStore.Width, 0) / z; + var h = doc.GetNumber(KeyStore.Height, 0) / z; if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) { selection.push(doc); } @@ -224,7 +225,9 @@ export class MarqueeView extends React.Component<MarqueeViewProps> get marqueeDiv() { let p = this.props.getContainerTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY); let v = this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY); - return <div className="marquee" style={{ transform: `translate(${p[0]}px, ${p[1]}px)`, width: `${Math.abs(v[0])}`, height: `${Math.abs(v[1])}` }} />; + return <div className="marquee" style={{ transform: `translate(${p[0]}px, ${p[1]}px)`, width: `${Math.abs(v[0])}`, height: `${Math.abs(v[1])}` }} > + <span className="marquee-legend" /> + </div>; } render() { diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 77f41105f..0aa152209 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -6,7 +6,6 @@ import { Transform } from "../../util/Transform"; import { DocumentView, DocumentViewProps } from "./DocumentView"; import "./DocumentView.scss"; import React = require("react"); -import { thisExpression } from "babel-types"; @observer @@ -16,18 +15,13 @@ export class CollectionFreeFormDocumentView extends React.Component<DocumentView constructor(props: DocumentViewProps) { super(props); } - get screenRect(): ClientRect | DOMRect { - if (this._mainCont.current) { - return this._mainCont.current.getBoundingClientRect(); - } - return new DOMRect(); - } @computed get transform(): string { - return `scale(${this.props.ContentScaling()}, ${this.props.ContentScaling()}) translate(${this.props.Document.GetNumber(KeyStore.X, 0)}px, ${this.props.Document.GetNumber(KeyStore.Y, 0)}px)`; + return `scale(${this.props.ContentScaling()}, ${this.props.ContentScaling()}) translate(${this.props.Document.GetNumber(KeyStore.X, 0)}px, ${this.props.Document.GetNumber(KeyStore.Y, 0)}px) scale(${this.zoom}, ${this.zoom}) `; } + @computed get zoom(): number { return 1 / this.props.Document.GetNumber(KeyStore.Zoom, 1); } @computed get zIndex(): number { return this.props.Document.GetNumber(KeyStore.ZIndex, 0); } @computed get width(): number { return this.props.Document.Width(); } @computed get height(): number { return this.props.Document.Height(); } @@ -57,7 +51,7 @@ export class CollectionFreeFormDocumentView extends React.Component<DocumentView getTransform = (): Transform => this.props.ScreenToLocalTransform() .translate(-this.props.Document.GetNumber(KeyStore.X, 0), -this.props.Document.GetNumber(KeyStore.Y, 0)) - .scale(1 / this.contentScaling()) + .scale(1 / this.contentScaling()).scale(1 / this.zoom) @computed get docView() { @@ -74,6 +68,7 @@ export class CollectionFreeFormDocumentView extends React.Component<DocumentView render() { return ( <div className="collectionFreeFormDocumentView-container" ref={this._mainCont} style={{ + opacity: this.props.opacity, transformOrigin: "left top", transform: this.transform, pointerEvents: "all", diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 5836da396..88900c4b1 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -44,19 +44,19 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & { CreateBindings(): JsxBindings { - let - { - Document, - isSelected, - select, - isTopMost, - selectOnLoad, - ScreenToLocalTransform, - addDocument, - removeDocument, - onActiveChanged, - parentActive: active, - } = this.props; + let { + Document, + isSelected, + select, + isTopMost, + selectOnLoad, + ScreenToLocalTransform, + ContainingCollectionView, + addDocument, + removeDocument, + onActiveChanged, + parentActive: active, + } = this.props; let bindings: JsxBindings = { props: { Document, @@ -65,6 +65,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & { isTopMost, selectOnLoad, ScreenToLocalTransform, + ContainingCollectionView, active, onActiveChanged, addDocument, diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 4eef07a2f..e74b609bb 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,31 +1,34 @@ -import { action, computed, IReactionDisposer, reaction, runInAction, trace } from "mobx"; +import { action, computed, runInAction } from "mobx"; import { observer } from "mobx-react"; +import { BooleanField } from "../../../fields/BooleanField"; import { Document } from "../../../fields/Document"; import { Field, FieldWaiting, Opt } from "../../../fields/Field"; import { Key } from "../../../fields/Key"; import { KeyStore } from "../../../fields/KeyStore"; import { ListField } from "../../../fields/ListField"; -import { BooleanField } from "../../../fields/BooleanField"; import { TextField } from "../../../fields/TextField"; import { ServerUtils } from "../../../server/ServerUtil"; -import { Utils, emptyFunction } from "../../../Utils"; +import { emptyFunction, Utils } from "../../../Utils"; import { Documents } from "../../documents/Documents"; import { DocumentManager } from "../../util/DocumentManager"; import { DragManager } from "../../util/DragManager"; import { SelectionManager } from "../../util/SelectionManager"; import { Transform } from "../../util/Transform"; +import { undoBatch, UndoManager } from "../../util/UndoManager"; import { CollectionDockingView } from "../collections/CollectionDockingView"; +import { CollectionPDFView } from "../collections/CollectionPDFView"; +import { CollectionVideoView } from "../collections/CollectionVideoView"; import { CollectionView } from "../collections/CollectionView"; import { ContextMenu } from "../ContextMenu"; import { DocumentContentsView } from "./DocumentContentsView"; import "./DocumentView.scss"; import React = require("react"); -import { undoBatch, UndoManager } from "../../util/UndoManager"; export interface DocumentViewProps { - ContainingCollectionView: Opt<CollectionView>; + ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>; Document: Document; + opacity: number; addDocument?: (doc: Document, allowDuplicates?: boolean) => boolean; removeDocument?: (doc: Document) => boolean; moveDocument?: (doc: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean; diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 0037d7b28..562de4827 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -1,7 +1,7 @@ import React = require("react"); import { observer } from "mobx-react"; import { computed } from "mobx"; -import { Field, FieldWaiting, FieldValue } from "../../../fields/Field"; +import { Field, FieldWaiting, FieldValue, Opt } from "../../../fields/Field"; import { Document } from "../../../fields/Document"; import { TextField } from "../../../fields/TextField"; import { NumberField } from "../../../fields/NumberField"; @@ -20,6 +20,9 @@ import { DocumentContentsView } from "./DocumentContentsView"; import { Transform } from "../../util/Transform"; import { KeyStore } from "../../../fields/KeyStore"; import { returnFalse, emptyDocFunction } from "../../../Utils"; +import { CollectionView } from "../collections/CollectionView"; +import { CollectionPDFView } from "../collections/CollectionPDFView"; +import { CollectionVideoView } from "../collections/CollectionVideoView"; // @@ -29,6 +32,7 @@ import { returnFalse, emptyDocFunction } from "../../../Utils"; // export interface FieldViewProps { fieldKey: Key; + ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>; Document: Document; isSelected: () => boolean; select: (isCtrlPressed: boolean) => void; @@ -79,6 +83,7 @@ export class FieldView extends React.Component<FieldViewProps> { <DocumentContentsView Document={field} addDocument={undefined} removeDocument={undefined} + opacity={1} ScreenToLocalTransform={Transform.Identity} ContentScaling={() => 1} PanelWidth={() => 100} @@ -89,7 +94,7 @@ export class FieldView extends React.Component<FieldViewProps> { isSelected={returnFalse} select={returnFalse} layoutKey={KeyStore.Layout} - ContainingCollectionView={undefined} + ContainingCollectionView={this.props.ContainingCollectionView} parentActive={this.props.active} onActiveChanged={this.props.onActiveChanged} /> ); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 8ea747b1c..87c1bcb1e 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -14,6 +14,9 @@ import { Main } from "../Main"; import { FieldView, FieldViewProps } from "./FieldView"; import "./FormattedTextBox.scss"; import React = require("react"); +import { TextField } from "../../../fields/TextField"; +import { KeyStore } from "../../../fields/KeyStore"; +import { MainOverlayTextBox } from "../MainOverlayTextBox"; const { buildMenuItems } = require("prosemirror-example-setup"); const { menuBar } = require("prosemirror-menu"); @@ -55,15 +58,20 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte this.onChange = this.onChange.bind(this); } + _applyingChange: boolean = false; + dispatchTransaction = (tx: Transaction) => { if (this._editorView) { const state = this._editorView.state.apply(tx); this._editorView.updateState(state); + this._applyingChange = true; this.props.Document.SetDataOnPrototype( this.props.fieldKey, JSON.stringify(state.toJSON()), RichTextField ); + this.props.Document.SetDataOnPrototype(KeyStore.DocumentText, state.doc.textBetween(0, state.doc.content.size, "\n\n"), TextField); + this._applyingChange = false; // doc.SetData(fieldKey, JSON.stringify(state.toJSON()), RichTextField); } } @@ -85,7 +93,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte }; if (this.props.isOverlay) { - this._inputReactionDisposer = reaction(() => Main.Instance._textDoc && Main.Instance._textDoc.Id, + this._inputReactionDisposer = reaction(() => MainOverlayTextBox.Instance.TextDoc && MainOverlayTextBox.Instance.TextDoc.Id, () => { if (this._editorView) { this._editorView.destroy(); @@ -96,7 +104,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte ); } else { this._proxyReactionDisposer = reaction(() => this.props.isSelected(), - () => this.props.isSelected() && Main.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform())); + () => this.props.isSelected() && MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform())); } this._reactionDisposer = reaction( @@ -105,7 +113,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte return field && field !== FieldWaiting ? field.Data : undefined; }, field => { - if (field && this._editorView) { + if (field && this._editorView && !this._applyingChange) { this._editorView.updateState( EditorState.fromJSON(config, JSON.parse(field)) ); @@ -177,10 +185,10 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte onFocused = (e: React.FocusEvent): void => { if (!this.props.isOverlay) { - Main.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform()); + MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform()); } else { if (this._ref.current) { - this._ref.current.scrollTop = Main.Instance._textScroll; + this._ref.current.scrollTop = MainOverlayTextBox.Instance.TextScroll; } } } @@ -209,7 +217,9 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte } onPointerWheel = (e: React.WheelEvent): void => { - e.stopPropagation(); + if (this.props.isSelected()) { + e.stopPropagation(); + } } tooltipMenuPlugin() { @@ -229,6 +239,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte render() { return ( <div + style={{ overflowY: this.props.isSelected() || this.props.isOverlay ? "scroll" : "hidden" }} className={`formattedTextBox-cont`} onKeyDown={this.onKeyPress} onKeyPress={this.onKeyPress} diff --git a/src/client/views/nodes/KeyValuePair.scss b/src/client/views/nodes/KeyValuePair.scss index 04d002c7b..01701e02c 100644 --- a/src/client/views/nodes/KeyValuePair.scss +++ b/src/client/views/nodes/KeyValuePair.scss @@ -1,30 +1,28 @@ @import "../globalCssVariables"; -.container{ - width:100%; - height:100%; - display: flex; - flex-direction: row; - flex-wrap: nowrap; - justify-content: space-between; -} .keyValuePair-td-key { display:inline-block; - width: 50%; + .keyValuePair-td-key-container{ + width:100%; + height:100%; + display: flex; + flex-direction: row; + flex-wrap: nowrap; + justify-content: space-between; + .keyValuePair-td-key-delete{ + position: relative; + background-color: transparent; + color:red; + } + .keyValuePair-keyField { + width:100%; + text-align: center; + position: relative; + overflow: auto; + } + } } .keyValuePair-td-value { display:inline-block; - width: 50%; -} -.keyValuePair-keyField { - width:100%; - text-align: center; - position: relative; - overflow: auto; -} -.delete{ - position: relative; - background-color: transparent; - color:red; }
\ No newline at end of file diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index 3e0b61c3d..5d69f23b2 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -1,18 +1,18 @@ -import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app -import "./KeyValueBox.scss"; -import "./KeyValuePair.scss"; -import React = require("react"); -import { FieldViewProps, FieldView } from './FieldView'; -import { Opt, Field } from '../../../fields/Field'; +import { action, observable } from 'mobx'; import { observer } from "mobx-react"; -import { observable, action } from 'mobx'; +import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app import { Document } from '../../../fields/Document'; +import { Field, Opt } from '../../../fields/Field'; import { Key } from '../../../fields/Key'; +import { emptyDocFunction, emptyFunction, returnFalse } from '../../../Utils'; import { Server } from "../../Server"; -import { EditableView } from "../EditableView"; import { CompileScript, ToField } from "../../util/Scripting"; import { Transform } from '../../util/Transform'; -import { returnFalse, emptyFunction, emptyDocFunction } from '../../../Utils'; +import { EditableView } from "../EditableView"; +import { FieldView, FieldViewProps } from './FieldView'; +import "./KeyValueBox.scss"; +import "./KeyValuePair.scss"; +import React = require("react"); // Represents one row in a key value plane @@ -25,28 +25,23 @@ export interface KeyValuePairProps { @observer export class KeyValuePair extends React.Component<KeyValuePairProps> { - @observable - private key: Opt<Key>; + @observable private key: Opt<Key>; constructor(props: KeyValuePairProps) { super(props); Server.GetField(this.props.fieldId, - action((field: Opt<Field>) => { - if (field) { - this.key = field as Key; - } - })); + action((field: Opt<Field>) => field instanceof Key && (this.key = field))); } render() { if (!this.key) { - return <tr><td>error</td><td></td></tr>; - + return <tr><td>error</td><td /></tr>; } let props: FieldViewProps = { Document: this.props.doc, + ContainingCollectionView: undefined, fieldKey: this.key, isSelected: returnFalse, select: emptyFunction, @@ -57,19 +52,17 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> { ScreenToLocalTransform: Transform.Identity, focus: emptyDocFunction, }; - let contents = ( - <FieldView {...props} /> - ); + let contents = <FieldView {...props} />; return ( <tr className={this.props.rowStyle}> <td className="keyValuePair-td-key" style={{ width: `${this.props.keyWidth}%` }}> - <div className="container"> - <button className="delete" onClick={() => { + <div className="keyValuePair-td-key-container"> + <button className="keyValuePair-td-key-delete" onClick={() => { let field = props.Document.Get(props.fieldKey); - if (field && field instanceof Field) { - props.Document.Set(props.fieldKey, undefined); - } - }}>X</button> + field && field instanceof Field && props.Document.Set(props.fieldKey, undefined); + }}> + X + </button> <div className="keyValuePair-keyField">{this.key.Name}</div> </div> </td> |
