From 81c0a8373fd5cb051531762243e200f11f8c7297 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Sat, 9 Mar 2019 23:00:41 -0500 Subject: editable workspace titles now supported, added 'display' prop to EditableView --- src/client/views/collections/CollectionTreeView.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 8b06d9ac4..9c31bdae2 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -69,7 +69,9 @@ class TreeView extends React.Component { return
; } - return
{ let title = this.props.document.GetT(KeyStore.Title, TextField); if (title && title !== "") @@ -159,6 +161,7 @@ export class CollectionTreeView extends CollectionViewBase {
this.onDrop(e, {})} ref={this.createDropTarget} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }}>

{ return this.props.Document.Title; }} SetValue={(value: string) => { -- cgit v1.2.3-70-g09d2 From 772182d40f180121ed14384bc9c6f623dd77c415 Mon Sep 17 00:00:00 2001 From: Jude Date: Sun, 10 Mar 2019 21:46:30 -0400 Subject: treeview improvements --- .../views/collections/CollectionTreeView.scss | 26 ++++++++++++++++++++-- .../views/collections/CollectionTreeView.tsx | 25 ++++++++++++++------- src/client/views/nodes/AudioBox.tsx | 12 +++++----- src/client/views/nodes/DocumentView.tsx | 5 +++-- 4 files changed, 50 insertions(+), 18 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index 4b28ded45..c2b376a34 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -3,16 +3,24 @@ #body { padding: 20px; background: $light-color-secondary; + font-size: 13px; + overflow: scroll; } ul { list-style: none; + padding-left: 20px; } li { margin: 5px 0; } +.collection-child { + margin-top: 10px; + margin-bottom: 10px; +} + .no-indent { padding-left: 0; } @@ -20,10 +28,16 @@ li { .bullet { width: 1.5em; display: inline-block; + color: $intermediate-color; +} + +.coll-title { + font-size: 24px; + margin-bottom: 20px; } .collectionTreeView-dropTarget { - border: 0px solid $intermediate-color; + border: 0px solid transparent; border-radius: $border-radius; box-sizing: border-box; height: 100%; @@ -33,8 +47,16 @@ li { display: inline-table; } +.docContainer:hover { + .delete-button { + display: inline; + } +} + .delete-button { color: $intermediate-color; float: right; - margin-left: 1em; + margin-left: 15px; + margin-top: 3px; + display: none; } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 8b06d9ac4..80fc89712 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -12,6 +12,10 @@ import { setupDrag } from "../../util/DragManager"; import { FieldWaiting } from "../../../fields/Field"; import { COLLECTION_BORDER_WIDTH } from "./CollectionView"; +import { library } from '@fortawesome/fontawesome-svg-core'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faTrashAlt, faCaretRight, faCaretDown } from '@fortawesome/free-solid-svg-icons'; + export interface TreeViewProps { document: Document; deleteDoc: (doc: Document) => void; @@ -23,6 +27,10 @@ export enum BulletType { List } +library.add(faTrashAlt); +library.add(faCaretDown); +library.add(faCaretRight); + @observer /** * Component that takes in a document prop and a boolean whether it's collapsed or not. @@ -50,11 +58,11 @@ class TreeView extends React.Component { switch (type) { case BulletType.Collapsed: - return
+ return
case BulletType.Collapsible: - return
+ return
case BulletType.List: - return
+ return
} } @@ -79,7 +87,7 @@ class TreeView extends React.Component { this.props.document.SetData(KeyStore.Title, value, TextField); return true; }} /> -
x
+

} @@ -101,7 +109,7 @@ class TreeView extends React.Component { ) ) subView = -
  • +
  • {this.renderBullet(BulletType.Collapsible)} {titleElement}
      @@ -109,7 +117,7 @@ class TreeView extends React.Component {
  • } else { - subView =
  • + subView =
  • {this.renderBullet(BulletType.Collapsed)} {titleElement}
  • @@ -157,7 +165,7 @@ export class CollectionTreeView extends CollectionViewBase { return (
    this.onDrop(e, {})} ref={this.createDropTarget} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }}> -

    +
    { return this.props.Document.Title; @@ -165,7 +173,8 @@ export class CollectionTreeView extends CollectionViewBase { this.props.Document.SetData(KeyStore.Title, value, TextField); return true; }} /> -

    +
    +
      {childrenElement}
    diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index f7d89843d..6daf15f5f 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -18,7 +18,7 @@ export class AudioBox extends React.Component { super(props); } - + componentDidMount() { } @@ -26,16 +26,16 @@ export class AudioBox extends React.Component { componentWillUnmount() { } - + render() { let field = this.props.doc.Get(this.props.fieldKey) - let path = field == FieldWaiting ? "http://techslides.com/demos/samples/sample.mp3": + let path = field == FieldWaiting ? "http://techslides.com/demos/samples/sample.mp3" : field instanceof AudioField ? field.Data.href : "http://techslides.com/demos/samples/sample.mp3"; - + return (
    -
    diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 263bb31d7..9a0c1e6a4 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -16,8 +16,8 @@ import { CollectionPDFView } from "../collections/CollectionPDFView"; import { ContextMenu } from "../ContextMenu"; import { FormattedTextBox } from "../nodes/FormattedTextBox"; import { ImageBox } from "../nodes/ImageBox"; -import { VideoBox } from "../nodes/VideoBox"; -import { AudioBox } from "../nodes/AudioBox"; +import { VideoBox } from "../nodes/VideoBox"; +import { AudioBox } from "../nodes/AudioBox"; import { Documents } from "../../documents/Documents" import { KeyValueBox } from "./KeyValueBox" import { WebBox } from "../nodes/WebBox"; @@ -97,6 +97,7 @@ export class DocumentView extends React.Component { onPointerDown = (e: React.PointerEvent): void => { this._downX = e.clientX; this._downY = e.clientY; + if (e.shiftKey && e.buttons === 1) { CollectionDockingView.Instance.StartOtherDrag(this.props.Document, e); e.stopPropagation(); -- cgit v1.2.3-70-g09d2 From b639d1fdfc8dfe9b1ea300665b4b4d74929f8af7 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 20 Mar 2019 20:21:12 -0400 Subject: added drag from tree view --- .../views/collections/CollectionTreeView.tsx | 23 +++++++++++----------- 1 file changed, 11 insertions(+), 12 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index f9da759fd..19dd44c97 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -10,7 +10,7 @@ import "./CollectionTreeView.scss"; import { EditableView } from "../EditableView"; import { setupDrag } from "../../util/DragManager"; import { FieldWaiting } from "../../../fields/Field"; -import { COLLECTION_BORDER_WIDTH } from "./CollectionView"; +import { COLLECTION_BORDER_WIDTH, CollectionView } from "./CollectionView"; import { library } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; @@ -70,16 +70,12 @@ class TreeView extends React.Component { * Renders the EditableView title element for placement into the tree. */ renderTitle() { + let reference = React.createRef(); + let onItemDown = setupDrag(reference, () => this.props.document, (containingCollection: CollectionView) => this.props.deleteDoc(this.props.document)); let title = this.props.document.GetT(KeyStore.Title, TextField); - - // if the title hasn't loaded, immediately return the div - if (!title || title === "") { - return
    ; - } - - return
    ( { let title = this.props.document.GetT(KeyStore.Title, TextField); if (title && title !== "") @@ -88,9 +84,12 @@ class TreeView extends React.Component { }} SetValue={(value: string) => { this.props.document.SetData(KeyStore.Title, value, TextField); return true; - }} /> -
    -
    + }} />); + return ( +
    + {(!title || title === "") ? (null) : editableView(title.Data)} +
    +
    ) } render() { -- cgit v1.2.3-70-g09d2 From d882d4013dc2df03a55c3f4afc954b7d9a38b4e4 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 20 Mar 2019 22:08:32 -0400 Subject: tree view cleanup --- src/client/Server.ts | 2 +- src/client/views/EditableView.tsx | 4 +- src/client/views/InkingCanvas.tsx | 2 +- .../views/collections/CollectionFreeFormView.tsx | 4 +- .../views/collections/CollectionTreeView.scss | 5 +- .../views/collections/CollectionTreeView.tsx | 143 ++++++++------------- src/client/views/collections/CollectionView.tsx | 4 +- src/client/views/nodes/DocumentView.tsx | 4 +- 8 files changed, 66 insertions(+), 102 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/Server.ts b/src/client/Server.ts index bbdc27397..7d882c76d 100644 --- a/src/client/Server.ts +++ b/src/client/Server.ts @@ -39,7 +39,7 @@ export class Server { reaction(() => { return this.ClientFieldsCached.get(fieldid); }, (field, reaction) => { - if (field !== "") { + if (field !== FieldWaiting) { reaction.dispose() callback(field) } diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index 98a6ed1ba..579d6e6ad 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -1,6 +1,6 @@ import React = require('react') import { observer } from 'mobx-react'; -import { observable, action } from 'mobx'; +import { observable, action, trace } from 'mobx'; import "./EditableView.scss" export interface EditableProps { @@ -52,7 +52,7 @@ export class EditableView extends React.Component { } else { return (
    this.editing = true)}> + onClick={action(() => this.editing = true)} > {this.props.contents}
    ) diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx index 8d0121035..36a8834a0 100644 --- a/src/client/views/InkingCanvas.tsx +++ b/src/client/views/InkingCanvas.tsx @@ -137,7 +137,7 @@ export class InkingCanvas extends React.Component { // get data from server // let inkField = this.props.Document.GetT(KeyStore.Ink, InkField); - // if (!inkField || inkField == "") { + // if (!inkField || inkField == FieldWaiting) { // return (
    // diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx index 8bf4a7539..da9f7b392 100644 --- a/src/client/views/collections/CollectionFreeFormView.tsx +++ b/src/client/views/collections/CollectionFreeFormView.tsx @@ -219,13 +219,13 @@ export class CollectionFreeFormView extends CollectionViewBase { @computed get backgroundLayout(): string | undefined { let field = this.props.Document.GetT(KeyStore.BackgroundLayout, TextField); - if (field && field !== "") { + if (field && field !== FieldWaiting) { return field.Data; } } @computed get overlayLayout(): string | undefined { let field = this.props.Document.GetT(KeyStore.OverlayLayout, TextField); - if (field && field !== "") { + if (field && field !== FieldWaiting) { return field.Data; } } diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index fa0f1c761..5a14aa54d 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -1,6 +1,8 @@ @import "../global_variables"; #body { padding: 20px; + padding-left: 10px; + padding-right: 0px; background: $light-color-secondary; font-size: 13px; overflow: scroll; @@ -49,6 +51,7 @@ li { .docContainer:hover { .delete-button { display: inline; + width: auto; } } @@ -57,5 +60,5 @@ li { float: right; margin-left: 15px; margin-top: 3px; - display: none; + display: inline; } \ No newline at end of file diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 19dd44c97..6edd90215 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -1,20 +1,19 @@ +import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; +import { faCaretDown, faCaretRight, faTrashAlt } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { action, observable } from "mobx"; import { observer } from "mobx-react"; -import { CollectionViewBase } from "./CollectionViewBase"; import { Document } from "../../../fields/Document"; +import { FieldWaiting } from "../../../fields/Field"; import { KeyStore } from "../../../fields/KeyStore"; import { ListField } from "../../../fields/ListField"; -import React = require("react") -import { TextField } from "../../../fields/TextField"; -import { observable, action } from "mobx"; -import "./CollectionTreeView.scss"; -import { EditableView } from "../EditableView"; import { setupDrag } from "../../util/DragManager"; -import { FieldWaiting } from "../../../fields/Field"; -import { COLLECTION_BORDER_WIDTH, CollectionView } from "./CollectionView"; +import { EditableView } from "../EditableView"; +import "./CollectionTreeView.scss"; +import { CollectionView, COLLECTION_BORDER_WIDTH } from "./CollectionView"; +import { CollectionViewBase } from "./CollectionViewBase"; +import React = require("react") -import { library } from '@fortawesome/fontawesome-svg-core'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faTrashAlt, faCaretRight, faCaretDown } from '@fortawesome/free-solid-svg-icons'; export interface TreeViewProps { document: Document; @@ -37,13 +36,9 @@ library.add(faCaretRight); */ class TreeView extends React.Component { - @observable - collapsed: boolean = false; - - delete = () => { - this.props.deleteDoc(this.props.document); - } + @observable _collapsed: boolean = true; + delete = () => this.props.deleteDoc(this.props.document); @action remove = (document: Document) => { @@ -54,16 +49,13 @@ class TreeView extends React.Component { } renderBullet(type: BulletType) { - let onClicked = action(() => this.collapsed = !this.collapsed); - + let onClicked = action(() => this._collapsed = !this._collapsed); + let bullet: IconProp | undefined = undefined; switch (type) { - case BulletType.Collapsed: - return
    - case BulletType.Collapsible: - return
    - case BulletType.List: - return
    + case BulletType.Collapsed: bullet = "caret-right"; break; + case BulletType.Collapsible: bullet = "caret-down"; break; } + return
    {bullet ? : ""}
    } /** @@ -72,74 +64,48 @@ class TreeView extends React.Component { renderTitle() { let reference = React.createRef(); let onItemDown = setupDrag(reference, () => this.props.document, (containingCollection: CollectionView) => this.props.deleteDoc(this.props.document)); - let title = this.props.document.GetT(KeyStore.Title, TextField); - let editableView = (titleString: string) => ( { - let title = this.props.document.GetT(KeyStore.Title, TextField); - if (title && title !== "") - return title.Data; - return ""; - }} SetValue={(value: string) => { - this.props.document.SetData(KeyStore.Title, value, TextField); - return true; - }} />); + let editableView = (titleString: string) => + ( this.props.document.Title} + SetValue={(value: string) => { + this.props.document.SetText(KeyStore.Title, value); + return true; + }} + />); return (
    - {(!title || title === "") ? (null) : editableView(title.Data)} + {editableView(this.props.document.Title)}
    ) } render() { - var children = this.props.document.GetT>(KeyStore.Data, ListField); - - let reference = React.createRef(); - let onItemDown = setupDrag(reference, () => this.props.document); - let titleElement = this.renderTitle(); + let bulletType = BulletType.List; + let childElements: JSX.Element | undefined = undefined; - // check if this document is a collection - if (children && children !== FieldWaiting) { - let subView; - - // if uncollapsed, then add the children elements - if (!this.collapsed) { - // render all children elements - let childrenElement = (children.Data.map(value => - ) - ) - subView = -
  • - {this.renderBullet(BulletType.Collapsible)} - {titleElement} -
      - {childrenElement} -
    -
  • - } else { - subView =
  • - {this.renderBullet(BulletType.Collapsed)} - {titleElement} -
  • + var children = this.props.document.GetT>(KeyStore.Data, ListField); + if (children && children !== FieldWaiting) { // add children for a collection + if (!this._collapsed) { + bulletType = BulletType.Collapsible; + childElements =
      + {children.Data.map(value => )} +
    } - - return
    - {subView} -
    - } - - // otherwise this is a normal leaf node - else { - return
  • - {this.renderBullet(BulletType.List)} - {titleElement} -
  • ; + else bulletType = BulletType.Collapsed; } + return
    +
  • + {this.renderBullet(bulletType)} + {this.renderTitle()} + {childElements ? childElements : (null)} +
  • +
    } } - @observer export class CollectionTreeView extends CollectionViewBase { @@ -152,12 +118,6 @@ export class CollectionTreeView extends CollectionViewBase { } render() { - let titleStr = ""; - let title = this.props.Document.GetT(KeyStore.Title, TextField); - if (title && title !== FieldWaiting) { - titleStr = title.Data; - } - var children = this.props.Document.GetT>(KeyStore.Data, ListField); let childrenElement = !children || children === FieldWaiting ? (null) : (children.Data.map(value => @@ -167,12 +127,13 @@ export class CollectionTreeView extends CollectionViewBase { return (
    this.onDrop(e, {})} ref={this.createDropTarget} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }}>
    - { - return this.props.Document.Title; - }} SetValue={(value: string) => { - this.props.Document.SetData(KeyStore.Title, value, TextField); + height={72} + GetValue={() => this.props.Document.Title} + SetValue={(value: string) => { + this.props.Document.SetText(KeyStore.Title, value); return true; }} />
    diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index d4afc69cf..7e1d31018 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -12,7 +12,7 @@ import { CollectionDockingView } from "./CollectionDockingView"; import { CollectionSchemaView } from "./CollectionSchemaView"; import { CollectionViewProps } from "./CollectionViewBase"; import { CollectionTreeView } from "./CollectionTreeView"; -import { Field, FieldId } from "../../../fields/Field"; +import { Field, FieldId, FieldWaiting } from "../../../fields/Field"; import { Main } from "../Main"; export enum CollectionViewType { @@ -86,7 +86,7 @@ export class CollectionView extends React.Component { get collectionViewType(): CollectionViewType { let Document = this.props.Document; let viewField = Document.GetT(KeyStore.ViewType, NumberField); - if (viewField === "") { + if (viewField === FieldWaiting) { return CollectionViewType.Invalid; } else if (viewField) { return viewField.Data; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 9e34b2b60..fec451b09 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,7 +1,7 @@ import { action, computed, IReactionDisposer, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import { Document } from "../../../fields/Document"; -import { Field, Opt } from "../../../fields/Field"; +import { Field, Opt, FieldWaiting } from "../../../fields/Field"; import { Key } from "../../../fields/Key"; import { KeyStore } from "../../../fields/KeyStore"; import { ListField } from "../../../fields/ListField"; @@ -315,7 +315,7 @@ export class DocumentView extends React.Component { return (null); } let lkeys = this.props.Document.GetT(KeyStore.LayoutKeys, ListField); - if (!lkeys || lkeys === "") { + if (!lkeys || lkeys === FieldWaiting) { return

    Error loading layout keys

    ; } var scaling = this.props.ContentScaling(); -- cgit v1.2.3-70-g09d2 From 79846f5e85c04435082106724d49b03eea31657f Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 21 Mar 2019 00:57:02 -0400 Subject: minor fix to schema dragger and treeview --- src/client/views/collections/CollectionSchemaView.tsx | 2 +- src/client/views/collections/CollectionTreeView.tsx | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index c2645ea73..d3ade7ebb 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -189,7 +189,7 @@ export class CollectionSchemaView extends CollectionViewBase { @action onDividerMove = (e: PointerEvent): void => { let nativeWidth = this._mainCont.current!.getBoundingClientRect(); - this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, 100 - Math.round((e.clientX - nativeWidth.left) / nativeWidth.width * 100)); + this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, Math.max(0, 100 - Math.round((e.clientX - nativeWidth.left) / nativeWidth.width * 100))); } @action onDividerUp = (e: PointerEvent): void => { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 6edd90215..ec1bf5d0e 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -1,7 +1,7 @@ import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; import { faCaretDown, faCaretRight, faTrashAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, observable } from "mobx"; +import { action, observable, trace } from "mobx"; import { observer } from "mobx-react"; import { Document } from "../../../fields/Document"; import { FieldWaiting } from "../../../fields/Field"; @@ -90,14 +90,14 @@ class TreeView extends React.Component { if (children && children !== FieldWaiting) { // add children for a collection if (!this._collapsed) { bulletType = BulletType.Collapsible; - childElements =
      + childElements =
        {children.Data.map(value => )}
      } else bulletType = BulletType.Collapsed; } return
      -
    • +
    • {this.renderBullet(bulletType)} {this.renderTitle()} {childElements ? childElements : (null)} -- cgit v1.2.3-70-g09d2 From 08c35582dc71623c2b7300458238b6ba25ff98d7 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 21 Mar 2019 15:30:15 -0400 Subject: fixed performance problem with loading schema with a lot of documents. --- .../views/collections/CollectionSchemaView.tsx | 25 +++++++++++++++++----- .../views/collections/CollectionTreeView.tsx | 2 +- 2 files changed, 21 insertions(+), 6 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index b986f394c..34b019244 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -2,7 +2,7 @@ import React = require("react") import { library } from '@fortawesome/fontawesome-svg-core'; import { faCog } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable } from "mobx"; +import { action, computed, observable, trace, untracked } from "mobx"; import { observer } from "mobx-react"; import Measure from "react-measure"; import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table"; @@ -141,6 +141,7 @@ export class CollectionSchemaView extends CollectionViewBase { }; } + @computed get columns() { return this.props.Document.GetList(KeyStore.ColumnsKey, []); } @@ -167,10 +168,19 @@ export class CollectionSchemaView extends CollectionViewBase { this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage == 0 ? 33 : 0); } } - findAllDocumentKeys = (): { [id: string]: boolean } => { + + @computed + get findAllDocumentKeys(): { [id: string]: boolean } { const docs = this.props.Document.GetList(this.props.fieldKey, []); let keys: { [id: string]: boolean } = {} - docs.map(doc => doc.GetAllPrototypes().map(proto => proto._proxies.forEach((val: any, key: string) => keys[key] = false))); + if (this._optionsActivated > -1) { + // bcz: ugh. this is untracked since otherwise a large collection of documents will blast the server for all their fields. + // then as each document's fields come back, we update the documents _proxies. Each time we do this, the whole schema will be + // invalidated and re-rendered. This workaround will inquire all of the document fields before the options button is clicked. + // then by the time the options button is clicked, all of the fields should be in place. If a new field is added while this menu + // is displayed (unlikely) it won't show up until something else changes. + untracked(() => docs.map(doc => doc.GetAllPrototypes().map(proto => proto._proxies.forEach((val: any, key: string) => keys[key] = false)))); + } this.columns.forEach(key => keys[key.Id] = true) return keys; } @@ -228,13 +238,18 @@ export class CollectionSchemaView extends CollectionViewBase { } } + @observable _optionsActivated: number = 0; + @action + OptionsMenuDown = (e: React.PointerEvent) => { + this._optionsActivated++; + } render() { library.add(faCog); const columns = this.columns; const children = this.props.Document.GetList(this.props.fieldKey, []); const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined; //all the keys/columns that will be displayed in the schema - const allKeys = this.findAllDocumentKeys(); + const allKeys = this.findAllDocumentKeys; let content = this._selectedIndex == -1 || !selected ? (null) : ( {({ measureRef }) => @@ -274,7 +289,7 @@ export class CollectionSchemaView extends CollectionViewBase {
    }> - + ); return ( diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index ec1bf5d0e..6cc14ebcb 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -76,7 +76,7 @@ class TreeView extends React.Component { }} />); return ( -
    +
    {editableView(this.props.document.Title)}
    ) -- cgit v1.2.3-70-g09d2 From 9f585c4f0a8287951f142f99dcc54861cb348728 Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 26 Mar 2019 16:09:27 -0400 Subject: treeview scrolling. --- src/client/views/collections/CollectionTreeView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 6cc14ebcb..70790af18 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -125,7 +125,7 @@ export class CollectionTreeView extends CollectionViewBase { ) return ( -
    this.onDrop(e, {})} ref={this.createDropTarget} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }}> +
    e.stopPropagation()} onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }}>
    Date: Wed, 3 Apr 2019 01:38:29 -0400 Subject: Almost compiles --- src/Utils.ts | 10 ++++++ src/client/Server.ts | 2 +- src/client/documents/Documents.ts | 3 +- src/client/northstar/dash-nodes/HistogramBox.tsx | 14 ++++---- src/client/util/DragManager.ts | 2 +- src/client/util/Transform.ts | 2 +- src/client/util/type_decls.d | 2 +- src/client/views/Main.tsx | 10 +++--- .../views/collections/CollectionBaseView.tsx | 28 +++++++++++++--- .../views/collections/CollectionDockingView.tsx | 9 ++--- src/client/views/collections/CollectionPDFView.tsx | 36 ++++++++++---------- .../views/collections/CollectionSchemaView.tsx | 12 +++++-- .../views/collections/CollectionTreeView.tsx | 3 +- .../views/collections/CollectionVideoView.tsx | 39 +++++++++++----------- src/client/views/collections/CollectionView.tsx | 4 +-- .../views/collections/CollectionViewBase.tsx | 7 ++-- .../CollectionFreeFormRemoteCursors.tsx | 17 ++-------- .../collectionFreeForm/CollectionFreeFormView.tsx | 30 +++++++++++------ src/client/views/nodes/DocumentContentsView.tsx | 18 +++++++++- src/client/views/nodes/DocumentView.tsx | 37 +++++++++----------- src/client/views/nodes/FieldView.tsx | 17 ++++++---- src/client/views/nodes/KeyValuePair.tsx | 4 +++ src/fields/Field.ts | 4 +-- 23 files changed, 181 insertions(+), 129 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/Utils.ts b/src/Utils.ts index a5d9bd0ca..3eb192eb8 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -86,4 +86,14 @@ export class Utils { } } +export function returnTrue() { + return true; +} + +export function returnFalse() { + return false; +} + +export function emptyFunction() { } + export type Without = Pick>; \ No newline at end of file diff --git a/src/client/Server.ts b/src/client/Server.ts index 37e3c2c0d..c301b04d3 100644 --- a/src/client/Server.ts +++ b/src/client/Server.ts @@ -42,7 +42,7 @@ export class Server { reaction(() => { return this.ClientFieldsCached.get(fieldid); }, (field, reaction) => { - if (field !== "") { + if (field !== FieldWaiting) { reaction.dispose() cb(field) } diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 0bf275df8..15f980895 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -17,7 +17,8 @@ import { HistogramOperation } from "../northstar/operations/HistogramOperation"; import { Server } from "../Server"; import { CollectionPDFView } from "../views/collections/CollectionPDFView"; import { CollectionVideoView } from "../views/collections/CollectionVideoView"; -import { CollectionView, CollectionViewType } from "../views/collections/CollectionView"; +import { CollectionView } from "../views/collections/CollectionView"; +import { CollectionViewType } from "../views/collections/CollectionBaseView"; import { AudioBox } from "../views/nodes/AudioBox"; import { FormattedTextBox } from "../views/nodes/FormattedTextBox"; import { ImageBox } from "../views/nodes/ImageBox"; diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx index a968def96..4457c9502 100644 --- a/src/client/northstar/dash-nodes/HistogramBox.tsx +++ b/src/client/northstar/dash-nodes/HistogramBox.tsx @@ -117,14 +117,14 @@ export class HistogramBox extends React.Component { activateHistogramOperation(catalog?: Catalog) { if (catalog) { - this.props.doc.GetTAsync(this.props.fieldKey, HistogramField).then((histoOp: Opt) => runInAction(() => { + this.props.Document.GetTAsync(this.props.fieldKey, HistogramField).then((histoOp: Opt) => runInAction(() => { this.HistoOp = histoOp ? histoOp.Data : HistogramOperation.Empty; if (this.HistoOp != HistogramOperation.Empty) { - reaction(() => this.props.doc.GetList(KeyStore.LinkedFromDocs, []), (docs: Document[]) => this.HistoOp.Links.splice(0, this.HistoOp.Links.length, ...docs), { fireImmediately: true }); - reaction(() => this.props.doc.GetList(KeyStore.BrushingDocs, []).length, + reaction(() => this.props.Document.GetList(KeyStore.LinkedFromDocs, []), (docs: Document[]) => this.HistoOp.Links.splice(0, this.HistoOp.Links.length, ...docs), { fireImmediately: true }); + reaction(() => this.props.Document.GetList(KeyStore.BrushingDocs, []).length, () => { - let brushingDocs = this.props.doc.GetList(KeyStore.BrushingDocs, [] as Document[]); - let proto = this.props.doc.GetPrototype() as Document; + let brushingDocs = this.props.Document.GetList(KeyStore.BrushingDocs, [] as Document[]); + let proto = this.props.Document.GetPrototype() as Document; this.HistoOp.BrushLinks.splice(0, this.HistoOp.BrushLinks.length, ...brushingDocs.map((brush, i) => { brush.SetNumber(KeyStore.BackgroundColor, StyleConstants.BRUSH_COLORS[i % StyleConstants.BRUSH_COLORS.length]); let brushed = brush.GetList(KeyStore.BrushingDocs, [] as Document[]); @@ -139,8 +139,8 @@ export class HistogramBox extends React.Component { render() { let labelY = this.HistoOp && this.HistoOp.Y ? this.HistoOp.Y.PresentedName : "<...>"; let labelX = this.HistoOp && this.HistoOp.X ? this.HistoOp.X.PresentedName : "<...>"; - var h = this.props.isTopMost ? this.PanelHeight : this.props.doc.GetNumber(KeyStore.Height, 0); - var w = this.props.isTopMost ? this.PanelWidth : this.props.doc.GetNumber(KeyStore.Width, 0); + var h = this.props.isTopMost ? this.PanelHeight : this.props.Document.GetNumber(KeyStore.Height, 0); + var w = this.props.isTopMost ? this.PanelWidth : this.props.Document.GetNumber(KeyStore.Width, 0); let loff = this.SizeConverter.LeftOffset; let toff = this.SizeConverter.TopOffset; let roff = this.SizeConverter.RightOffset; diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 70b1c9829..53bbae18d 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -110,7 +110,7 @@ export namespace DragManager { xOffset?: number; yOffset?: number; aliasOnDrop?: boolean; - removeDocument?: (collectionDrop: CollectionView) => void; + moveDocument?: (targetCollection: Document, addDocument: (document: Document) => void) => void; [id: string]: any; } export function StartDocumentDrag(ele: HTMLElement, dragData: DocumentDragData, options?: DragOptions) { diff --git a/src/client/util/Transform.ts b/src/client/util/Transform.ts index 3e1039166..889134e3e 100644 --- a/src/client/util/Transform.ts +++ b/src/client/util/Transform.ts @@ -3,7 +3,7 @@ export class Transform { private _translateY: number = 0; private _scale: number = 1; - static get Identity(): Transform { + static Identity(): Transform { return new Transform(0, 0, 1); } diff --git a/src/client/util/type_decls.d b/src/client/util/type_decls.d index 4f69053b1..47c3481b2 100644 --- a/src/client/util/type_decls.d +++ b/src/client/util/type_decls.d @@ -181,7 +181,7 @@ declare class Key extends Field { Copy(): Field; ToScriptString(): string; } -declare type FIELD_WAITING = ""; +declare type FIELD_WAITING = null; declare type Opt = T | undefined; declare type FieldValue = Opt | FIELD_WAITING; // @ts-ignore diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 82fd1e7c9..bcbe6b987 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -6,7 +6,7 @@ import { Document } from '../../fields/Document'; import { KeyStore } from '../../fields/KeyStore'; import "./Main.scss"; import { MessageStore } from '../../server/Message'; -import { Utils } from '../../Utils'; +import { Utils, returnTrue } from '../../Utils'; import * as request from 'request' import * as rp from 'request-promise' import { Documents } from '../documents/Documents'; @@ -212,7 +212,6 @@ export class Main extends React.Component { this.workspacesShown = !this.workspacesShown; } - screenToLocalTransform = () => Transform.Identity pwidthFunc = () => this.pwidth; pheightFunc = () => this.pheight; focusDocument = (doc: Document) => { } @@ -221,15 +220,16 @@ export class Main extends React.Component { get content() { return !this.mainContainer ? (null) : } diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index c8c840085..c9f640c4a 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -19,16 +19,21 @@ export enum CollectionViewType { } export interface CollectionRenderProps { - addDocument: (document: Document, allowDuplicates: boolean) => void; + addDocument: (document: Document, allowDuplicates?: boolean) => boolean; removeDocument: (document: Document) => boolean; + moveDocument: (document: Document, targetCollection: Document, addDocument: (document: Document) => void) => boolean; active: () => boolean; } export interface CollectionViewProps extends FieldViewProps { onContextMenu?: (e: React.MouseEvent) => void; - children: (type: CollectionViewType, props: CollectionRenderProps) => JSX.Element | null; + children: (type: CollectionViewType, props: CollectionRenderProps) => JSX.Element | JSX.Element[] | null; + className?: string; + contentRef?: React.Ref; } +export const COLLECTION_BORDER_WIDTH = 1; + export class CollectionBaseView extends React.Component { get collectionViewType(): CollectionViewType { let Document = this.props.Document; @@ -44,7 +49,7 @@ export class CollectionBaseView extends React.Component { active = (): boolean => { var isSelected = this.props.isSelected(); - var childSelected = SelectionManager.SelectedDocuments().some(view => view.props.ContainingCollectionView == self); + var childSelected = SelectionManager.SelectedDocuments().some(view => view.props.ContainingCollectionView == this); var topMost = this.props.isTopMost; return isSelected || childSelected || topMost; } @@ -68,7 +73,7 @@ export class CollectionBaseView extends React.Component { } @action.bound - addDocument(doc: Document, allowDuplicates: boolean): boolean { + 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)); @@ -123,14 +128,27 @@ export class CollectionBaseView extends React.Component { return false } + @action.bound + moveDocument(doc: Document, targetCollection: Document, addDocument: (doc: Document) => void): boolean { + if (this.props.Document === targetCollection) { + return false; + } + if (this.removeDocument(doc)) { + addDocument(doc); + return true; + } + return false; + } + render() { const props: CollectionRenderProps = { addDocument: this.addDocument, removeDocument: this.removeDocument, + moveDocument: this.moveDocument, active: this.active } return ( -
    +
    {this.props.children(this.collectionViewType, props)}
    ) diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index c67354139..bb34340fb 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -8,12 +8,12 @@ import { Document } from "../../../fields/Document"; import { KeyStore } from "../../../fields/KeyStore"; import Measure from "react-measure"; import { FieldId, Opt, Field } from "../../../fields/Field"; -import { Utils } from "../../../Utils"; +import { Utils, returnTrue } from "../../../Utils"; import { Server } from "../../Server"; import { undoBatch } from "../../util/UndoManager"; import { DocumentView } from "../nodes/DocumentView"; import "./CollectionDockingView.scss"; -import { COLLECTION_BORDER_WIDTH } from "./CollectionView"; +import { COLLECTION_BORDER_WIDTH } from "./CollectionBaseView"; import React = require("react"); import { SubCollectionViewProps } from "./CollectionViewBase"; import { ServerUtils } from "../../../server/ServerUtil"; @@ -310,14 +310,15 @@ export class DockedFrameRenderer extends React.Component { var content =
    { }} ContainingCollectionView={undefined} />
    diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx index 6bb3bacc7..14ed70b8c 100644 --- a/src/client/views/collections/CollectionPDFView.tsx +++ b/src/client/views/collections/CollectionPDFView.tsx @@ -1,23 +1,20 @@ import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; -import { Document } from "../../../fields/Document"; import { KeyStore } from "../../../fields/KeyStore"; import { ContextMenu } from "../ContextMenu"; -import { CollectionView, CollectionViewType } from "./CollectionView"; import { CollectionViewProps } from "./CollectionViewBase"; import "./CollectionPDFView.scss" import React = require("react"); -import { FieldId } from "../../../fields/Field"; import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; +import { FieldView } from "../nodes/FieldView"; +import { CollectionRenderProps, CollectionBaseView, CollectionViewType } from "./CollectionBaseView"; @observer export class CollectionPDFView extends React.Component { public static LayoutString(fieldKey: string = "DataKey") { - return `<${CollectionPDFView.name} Document={Document} - ScreenToLocalTransform={ScreenToLocalTransform} fieldKey={${fieldKey}} panelWidth={PanelWidth} panelHeight={PanelHeight} isSelected={isSelected} select={select} bindings={bindings} - isTopMost={isTopMost} SelectOnLoad={selectOnLoad} BackgroundView={BackgroundView} focus={focus}/>`; + return FieldView.LayoutString(CollectionPDFView, fieldKey); } private get curPage() { return this.props.Document.GetNumber(KeyStore.CurPage, -1); } @@ -35,26 +32,27 @@ export class CollectionPDFView extends React.Component { ); } - // "inherited" CollectionView API starts here... - @observable - public SelectedDocs: FieldId[] = [] - public active: () => boolean = () => CollectionView.Active(this); - - addDocument = (doc: Document, allowDuplicates: boolean): boolean => { return CollectionView.AddDocument(this.props, doc, allowDuplicates); } - removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); } - - specificContextMenu = (e: React.MouseEvent): void => { + onContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped() && this.props.Document.Id != "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 ContextMenu.Instance.addItem({ description: "PDFOptions", event: () => { } }); } } - render() { + private subView = (_type: CollectionViewType, renderProps: CollectionRenderProps) => { + let props = { ...renderProps, ...this.props }; return ( -
    - + <> + {this.props.isSelected() ? this.uIButtons : (null)} -
    + + ) + } + + render() { + return ( + + {this.subView} + ) } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 0c7e28691..0eacd38b0 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -22,9 +22,11 @@ import { EditableView } from "../EditableView"; import { DocumentView } from "../nodes/DocumentView"; import { FieldView, FieldViewProps } from "../nodes/FieldView"; import "./CollectionSchemaView.scss"; -import { CollectionView, COLLECTION_BORDER_WIDTH } from "./CollectionView"; +import { CollectionView } from "./CollectionView"; import { CollectionViewBase } from "./CollectionViewBase"; import { TextField } from "../../../fields/TextField"; +import { COLLECTION_BORDER_WIDTH } from "./CollectionBaseView"; +import { emptyFunction, returnFalse } from "../../../Utils"; // bcz: need to add drag and drop of rows and columns. This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657 @@ -79,6 +81,9 @@ export class CollectionSchemaView extends CollectionViewBase { select: () => { }, isTopMost: false, selectOnLoad: false, + ScreenToLocalTransform: Transform.Identity, + focus: emptyFunction, + active: returnFalse } let contents = ( @@ -295,15 +300,16 @@ export class CollectionSchemaView extends CollectionViewBase { {({ measureRef }) =>
    {doc instanceof Document ? : null} diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 70790af18..6f9ba40d2 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -10,9 +10,10 @@ import { ListField } from "../../../fields/ListField"; import { setupDrag } from "../../util/DragManager"; import { EditableView } from "../EditableView"; import "./CollectionTreeView.scss"; -import { CollectionView, COLLECTION_BORDER_WIDTH } from "./CollectionView"; +import { CollectionView } from "./CollectionView"; import { CollectionViewBase } from "./CollectionViewBase"; import React = require("react") +import { COLLECTION_BORDER_WIDTH } from './CollectionBaseView'; export interface TreeViewProps { diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx index 1f0143d87..7cb461b4d 100644 --- a/src/client/views/collections/CollectionVideoView.tsx +++ b/src/client/views/collections/CollectionVideoView.tsx @@ -3,12 +3,14 @@ import { observer } from "mobx-react"; import { Document } from "../../../fields/Document"; import { KeyStore } from "../../../fields/KeyStore"; import { ContextMenu } from "../ContextMenu"; -import { CollectionView, CollectionViewType } from "./CollectionView"; +import { CollectionView } from "./CollectionView"; +import { CollectionViewType, CollectionBaseView, CollectionRenderProps } from "./CollectionBaseView"; import { CollectionViewProps } from "./CollectionViewBase"; import React = require("react"); import { FieldId } from "../../../fields/Field"; import "./CollectionVideoView.scss" -import { CollectionFreeFormView } from "./CollectionFreeFormView"; +import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; +import { FieldView } from "../nodes/FieldView"; @observer @@ -20,9 +22,7 @@ export class CollectionVideoView extends React.Component { @observable _isPlaying: boolean = false; public static LayoutString(fieldKey: string = "DataKey") { - return `<${CollectionVideoView.name} Document={Document} - ScreenToLocalTransform={ScreenToLocalTransform} fieldKey={${fieldKey}} panelWidth={PanelWidth} panelHeight={PanelHeight} isSelected={isSelected} select={select} bindings={bindings} - isTopMost={isTopMost} SelectOnLoad={selectOnLoad} BackgroundView={BackgroundView} focus={focus}/>`; + return FieldView.LayoutString(CollectionVideoView, fieldKey); } private get uIButtons() { let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().transformDirection(1, 1)[0]); @@ -102,26 +102,27 @@ export class CollectionVideoView extends React.Component { } - // "inherited" CollectionView API starts here... - - @observable - public SelectedDocs: FieldId[] = [] - public active: () => boolean = () => CollectionView.Active(this); - - addDocument = (doc: Document, allowDuplicates: boolean): boolean => { return CollectionView.AddDocument(this.props, doc, allowDuplicates); } - removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); } - - specificContextMenu = (e: React.MouseEvent): void => { + onContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped() && this.props.Document.Id != "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 ContextMenu.Instance.addItem({ description: "VideoOptions", event: () => { } }); } } + private subView = (_type: CollectionViewType, renderProps: CollectionRenderProps) => { + let props = { ...renderProps, ...this.props }; + return ( + <> + + {this.props.isSelected() ? this.uIButtons : (null)} + + ) + } + render() { trace(); - return (
    - - {this.props.isSelected() ? this.uIButtons : (null)} -
    ) + return ( + + {this.subView} + ) } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 51cc99595..d440dcff9 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -12,7 +12,7 @@ export class CollectionView extends React.Component { public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(CollectionView, fieldStr) } private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => { - let props = { ...renderProps, ...this.props }; + let props = { ...this.props, ...renderProps }; switch (type) { case CollectionViewType.Freeform: return () case CollectionViewType.Schema: return () @@ -34,7 +34,7 @@ export class CollectionView extends React.Component { return ( {this.SubView} - > + ) } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx index ca4f3b12f..4a0b2312e 100644 --- a/src/client/views/collections/CollectionViewBase.tsx +++ b/src/client/views/collections/CollectionViewBase.tsx @@ -20,6 +20,9 @@ import { Server } from "../../Server"; import { FieldViewProps } from "../nodes/FieldView"; export interface CollectionViewProps extends FieldViewProps { + addDocument: (document: Document, allowDuplicates?: boolean) => boolean; + removeDocument: (document: Document) => boolean; + moveDocument: (document: Document, targetCollection: Document, addDocument: (document: Document) => void) => boolean; } export interface SubCollectionViewProps extends CollectionViewProps { @@ -72,8 +75,8 @@ export class CollectionViewBase extends React.Component de.data.draggedDocument.GetTAsync(key, NumberField, (f: Opt) => f ? de.data.droppedDocument.SetNumber(key, f.Data) : null)); } let added = this.props.addDocument(de.data.droppedDocument, false); - if (added && de.data.removeDocument && !de.data.aliasOnDrop) { - de.data.removeDocument(this.props.CollectionView); + if (added && de.data.moveDocument && !de.data.aliasOnDrop) { + de.data.moveDocument(this.props.Document, this.props.addDocument); } e.stopPropagation(); return added; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx index 19382e66f..f664f4e65 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx @@ -1,21 +1,8 @@ -import { action, computed, observable } from "mobx"; +import { computed } from "mobx"; import { observer } from "mobx-react"; -import { Document } from "../../../../fields/Document"; -import { FieldWaiting } from "../../../../fields/Field"; import { KeyStore } from "../../../../fields/KeyStore"; -import { TextField } from "../../../../fields/TextField"; -import { DragManager } from "../../../util/DragManager"; -import { Transform } from "../../../util/Transform"; -import { undoBatch } from "../../../util/UndoManager"; -import { InkingCanvas } from "../../InkingCanvas"; -import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView"; -import { DocumentContentsView } from "../../nodes/DocumentContentsView"; -import { DocumentViewProps } from "../../nodes/DocumentView"; -import { COLLECTION_BORDER_WIDTH } from "../CollectionView"; -import { CollectionViewBase, CollectionViewProps, CursorEntry } from "../CollectionViewBase"; -import { CollectionFreeFormLinksView } from "./CollectionFreeFormLinksView"; +import { CollectionViewProps, CursorEntry } from "../CollectionViewBase"; import "./CollectionFreeFormView.scss"; -import { MarqueeView } from "./MarqueeView"; import React = require("react"); import v5 = require("uuid/v5"); import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils"; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 51ab85b71..144d121db 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -11,7 +11,7 @@ import { InkingCanvas } from "../../InkingCanvas"; import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView"; import { DocumentContentsView } from "../../nodes/DocumentContentsView"; import { DocumentViewProps } from "../../nodes/DocumentView"; -import { COLLECTION_BORDER_WIDTH } from "../CollectionView"; +import { COLLECTION_BORDER_WIDTH } from "../CollectionBaseView"; import { CollectionViewBase } from "../CollectionViewBase"; import { CollectionFreeFormLinksView } from "./CollectionFreeFormLinksView"; import "./CollectionFreeFormView.scss"; @@ -20,6 +20,8 @@ import React = require("react"); import v5 = require("uuid/v5"); import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors"; import { PreviewCursor } from "./PreviewCursor"; +import { DocumentManager } from "../../../util/DocumentManager"; +import { SelectionManager } from "../../../util/SelectionManager"; @observer export class CollectionFreeFormView extends CollectionViewBase { @@ -39,8 +41,13 @@ export class CollectionFreeFormView extends CollectionViewBase { } public selectDocuments = (docs: Document[]) => { - this.props.CollectionView.SelectedDocs.length = 0; - docs.map(d => this.props.CollectionView.SelectedDocs.push(d.Id)); + SelectionManager.DeselectAll; + docs.map(doc => { + const dv = DocumentManager.Instance.getDocumentView(doc); + if (dv) { + SelectionManager.SelectDoc(dv, true); + } + }) } public getActiveDocuments = () => { @@ -59,6 +66,8 @@ export class CollectionFreeFormView extends CollectionViewBase { @observable private _lastX: number = 0; @observable private _lastY: number = 0; + private outerElement?: HTMLDivElement; + @computed get panX(): number { return this.props.Document.GetNumber(KeyStore.PanX, 0) } @computed get panY(): number { return this.props.Document.GetNumber(KeyStore.PanY, 0) } @computed get scale(): number { return this.props.Document.GetNumber(KeyStore.Scale, 1); } @@ -66,8 +75,8 @@ export class CollectionFreeFormView extends CollectionViewBase { @computed get nativeWidth() { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); } @computed get nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); } @computed get zoomScaling() { return this.props.Document.GetNumber(KeyStore.Scale, 1); } - @computed get centeringShiftX() { return !this.props.Document.GetNumber(KeyStore.NativeWidth, 0) ? this.props.panelWidth() / 2 : 0; } // shift so pan position is at center of window for non-overlay collections - @computed get centeringShiftY() { return !this.props.Document.GetNumber(KeyStore.NativeHeight, 0) ? this.props.panelHeight() / 2 : 0; }// shift so pan position is at center of window for non-overlay collections + @computed get centeringShiftX() { return !this.props.Document.GetNumber(KeyStore.NativeWidth, 0) && this.outerElement ? this.outerElement.clientWidth / 2 : 0; } // shift so pan position is at center of window for non-overlay collections + @computed get centeringShiftY() { return !this.props.Document.GetNumber(KeyStore.NativeHeight, 0) && this.outerElement ? this.outerElement.clientHeight / 2 : 0; }// shift so pan position is at center of window for non-overlay collections @undoBatch @action @@ -227,16 +236,17 @@ export class CollectionFreeFormView extends CollectionViewBase { getDocumentViewProps(document: Document): DocumentViewProps { return { Document: document, - AddDocument: this.props.addDocument, - RemoveDocument: this.props.removeDocument, + addDocument: this.props.addDocument, + removeDocument: this.props.removeDocument, ScreenToLocalTransform: this.getTransform, isTopMost: false, selectOnLoad: document.Id == this._selectOnLoaded, PanelWidth: document.Width, PanelHeight: document.Height, ContentScaling: this.noScaling, - ContainingCollectionView: this.props.CollectionView, - focus: this.focusDocument + ContainingCollectionView: undefined, + focus: this.focusDocument, + parentActive: this.props.active, } } @@ -266,7 +276,7 @@ export class CollectionFreeFormView extends CollectionViewBase { getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH).translate(-this.centeringShiftX, -this.centeringShiftY).transform(this.getLocalTransform()) getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH) - getLocalTransform = (): Transform => Transform.Identity.scale(1 / this.scale).translate(this.panX, this.panY); + getLocalTransform = (): Transform => Transform.Identity().scale(1 / this.scale).translate(this.panX, this.panY); noScaling = () => 1; childViews = () => this.views; diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index a5e4b1a17..2574b374a 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -44,7 +44,18 @@ export class DocumentContentsView extends React.Component; Document: Document; - AddDocument?: (doc: Document, allowDuplicates: boolean) => boolean; - RemoveDocument?: (doc: Document) => boolean; + addDocument?: (doc: Document, allowDuplicates?: boolean) => boolean; + removeDocument?: (doc: Document) => boolean; + moveDocument?: (doc: Document, targetCollection: Document, addDocument: (document: Document) => void) => boolean; ScreenToLocalTransform: () => Transform; isTopMost: boolean; ContentScaling: () => number; @@ -33,6 +35,7 @@ export interface DocumentViewProps { PanelHeight: () => number; focus: (doc: Document) => void; selectOnLoad: boolean; + parentActive: () => boolean; } export interface JsxArgs extends DocumentViewProps { Keys: { [name: string]: Key } @@ -83,9 +86,8 @@ export class DocumentView extends React.Component { private _mainCont = React.createRef(); private _downX: number = 0; private _downY: number = 0; - private _reactionDisposer: Opt; - @computed get active(): boolean { return SelectionManager.IsSelected(this) || !this.props.ContainingCollectionView || this.props.ContainingCollectionView.active(); } - @computed get topMost(): boolean { return !this.props.ContainingCollectionView || this.props.ContainingCollectionView.collectionViewType == CollectionViewType.Docking; } + @computed get active(): boolean { return SelectionManager.IsSelected(this) || this.props.parentActive(); } + @computed get topMost(): boolean { return this.props.isTopMost; } @computed get layout(): string { return this.props.Document.GetText(KeyStore.Layout, "

    Error loading layout data

    "); } @computed get layoutKeys(): Key[] { return this.props.Document.GetData(KeyStore.LayoutKeys, ListField, new Array()); } @computed get layoutFields(): Key[] { return this.props.Document.GetData(KeyStore.LayoutFields, ListField, new Array()); } @@ -119,12 +121,6 @@ export class DocumentView extends React.Component { runInAction(() => { DocumentManager.Instance.DocumentViews.push(this); }) - this._reactionDisposer = reaction( - () => this.props.ContainingCollectionView && this.props.ContainingCollectionView.SelectedDocs.slice(), - () => { - if (this.props.ContainingCollectionView && this.props.ContainingCollectionView.SelectedDocs.indexOf(this.props.Document.Id) != -1) - SelectionManager.SelectDoc(this, true); - }); } componentDidUpdate() { @@ -144,9 +140,6 @@ export class DocumentView extends React.Component { DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1); }) - if (this._reactionDisposer) { - this._reactionDisposer(); - } } startDragging(x: number, y: number, dropAliasOfDraggedDoc: boolean) { @@ -156,9 +149,9 @@ export class DocumentView extends React.Component { dragData.aliasOnDrop = dropAliasOfDraggedDoc; dragData.xOffset = x - left; dragData.yOffset = y - top; - dragData.removeDocument = (dropCollectionView: CollectionView) => { - if (this.props.RemoveDocument && this.props.ContainingCollectionView !== dropCollectionView) { - this.props.RemoveDocument(this.props.Document); + dragData.moveDocument = (targetCollection: Document, addDocument: (document: Document) => void) => { + if (this.props.moveDocument) { + this.props.moveDocument(this.props.Document, targetCollection, addDocument) } } DragManager.StartDocumentDrag(this._mainCont.current, dragData, { @@ -197,14 +190,14 @@ export class DocumentView extends React.Component { } deleteClicked = (): void => { - if (this.props.RemoveDocument) { - this.props.RemoveDocument(this.props.Document); + if (this.props.removeDocument) { + this.props.removeDocument(this.props.Document); } } fieldsClicked = (e: React.MouseEvent): void => { - if (this.props.AddDocument) { - this.props.AddDocument(Documents.KVPDocument(this.props.Document, { width: 300, height: 300 }), false); + if (this.props.addDocument) { + this.props.addDocument(Documents.KVPDocument(this.props.Document, { width: 300, height: 300 }), false); } } fullScreenClicked = (e: React.MouseEvent): void => { diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 2a7d3175f..57af55f9f 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -19,6 +19,7 @@ import { ListField } from "../../../fields/ListField"; import { DocumentContentsView } from "./DocumentContentsView"; import { Transform } from "../../util/Transform"; import { KeyStore } from "../../../fields/KeyStore"; +import { returnFalse } from "../../../Utils"; // @@ -33,8 +34,9 @@ export interface FieldViewProps { select: (isCtrlPressed: boolean) => void; isTopMost: boolean; selectOnLoad: boolean; - addDocument: (document: Document, allowDuplicates: boolean) => boolean; - removeDocument: (document: Document) => boolean; + addDocument?: (document: Document, allowDuplicates?: boolean) => boolean; + removeDocument?: (document: Document) => boolean; + moveDocument?: (document: Document, targetCollection: Document, addDocument: (document: Document) => void) => boolean; ScreenToLocalTransform: () => Transform; active: () => boolean; focus: (doc: Document) => void; @@ -73,19 +75,20 @@ export class FieldView extends React.Component { } else if (field instanceof Document) { return ( Transform.Identity} + addDocument={undefined} + removeDocument={undefined} + ScreenToLocalTransform={Transform.Identity} ContentScaling={() => 1} PanelWidth={() => 100} PanelHeight={() => 100} - isTopMost={true} + isTopMost={true} //TODO Why is this top most? selectOnLoad={false} focus={() => { }} isSelected={() => false} select={() => false} layoutKey={KeyStore.Layout} - ContainingCollectionView={undefined} />) + ContainingCollectionView={undefined} + parentActive={this.props.active} />) } else if (field instanceof ListField) { return (
    diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index 2fc11328f..e178cbc8d 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -11,6 +11,7 @@ import { Key } from '../../../fields/Key'; import { Server } from "../../Server" import { EditableView } from "../EditableView"; import { CompileScript, ToField } from "../../util/Scripting"; +import { Transform } from '../../util/Transform'; // Represents one row in a key value plane @@ -49,6 +50,9 @@ export class KeyValuePair extends React.Component { select: () => { }, isTopMost: false, selectOnLoad: false, + active: () => false, + ScreenToLocalTransform: Transform.Identity, + focus: () => { }, } let contents = ( diff --git a/src/fields/Field.ts b/src/fields/Field.ts index d48509a47..0d0e56f77 100644 --- a/src/fields/Field.ts +++ b/src/fields/Field.ts @@ -12,8 +12,8 @@ export function Cast(field: FieldValue, ctor: { new(): T return undefined; } -export const FieldWaiting: FIELD_WAITING = ""; -export type FIELD_WAITING = ""; +export const FieldWaiting: FIELD_WAITING = null; +export type FIELD_WAITING = null; export type FieldId = string; export type Opt = T | undefined; export type FieldValue = Opt | FIELD_WAITING; -- cgit v1.2.3-70-g09d2 From 318dacd846ab06358e26ca49e711e06434287d16 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Thu, 4 Apr 2019 19:37:10 -0400 Subject: Most stuff seems to be working --- src/client/util/DragManager.ts | 9 +++++---- src/client/views/collections/CollectionBaseView.tsx | 16 +++++++++------- src/client/views/collections/CollectionSchemaView.tsx | 18 +++++++++--------- src/client/views/collections/CollectionTreeView.tsx | 19 +++++++++++++++---- src/client/views/collections/CollectionView.tsx | 7 ++++++- src/client/views/collections/CollectionViewBase.tsx | 2 ++ .../collectionFreeForm/CollectionFreeFormView.tsx | 1 + src/client/views/nodes/DocumentView.tsx | 2 +- 8 files changed, 48 insertions(+), 26 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index ff778302b..d8e70a8b4 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -6,9 +6,9 @@ import { ImageField } from "../../fields/ImageField"; import { KeyStore } from "../../fields/KeyStore"; import { CollectionView } from "../views/collections/CollectionView"; import { DocumentView } from "../views/nodes/DocumentView"; -import { emptyFunction } from "../../Utils"; +import { returnFalse } from "../../Utils"; -export function setupDrag(_reference: React.RefObject, docFunc: () => Document, removeFunc: (containingCollection: CollectionView) => void = () => { }) { +export function setupDrag(_reference: React.RefObject, docFunc: () => Document, moveFunc?: DragManager.MoveFunction) { let onRowMove = action((e: PointerEvent): void => { e.stopPropagation(); e.preventDefault(); @@ -16,7 +16,7 @@ export function setupDrag(_reference: React.RefObject, docFunc: document.removeEventListener("pointermove", onRowMove); document.removeEventListener('pointerup', onRowUp); var dragData = new DragManager.DocumentDragData([docFunc()]); - dragData.removeDocument = removeFunc; + dragData.moveDocument = moveFunc; DragManager.StartDocumentDrag([_reference.current!], dragData); }); let onRowUp = action((e: PointerEvent): void => { @@ -101,6 +101,7 @@ export namespace DragManager { }; } + export type MoveFunction = (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean; export class DocumentDragData { constructor(dragDoc: Document[]) { this.draggedDocuments = dragDoc; @@ -111,7 +112,7 @@ export namespace DragManager { xOffset?: number; yOffset?: number; aliasOnDrop?: boolean; - moveDocument?: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean; + moveDocument?: MoveFunction; [id: string]: any; } diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index 9b68ee06c..ee752a2ca 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -9,6 +9,7 @@ import { Document } from '../../../fields/Document'; import { ListField } from '../../../fields/ListField'; import { action } from 'mobx'; import { Transform } from '../../util/Transform'; +import { observer } from 'mobx-react'; export enum CollectionViewType { Invalid, @@ -35,6 +36,7 @@ export interface CollectionViewProps extends FieldViewProps { export const COLLECTION_BORDER_WIDTH = 1; +@observer export class CollectionBaseView extends React.Component { get collectionViewType(): CollectionViewType { let Document = this.props.Document; @@ -72,8 +74,8 @@ export class CollectionBaseView extends React.Component { if (this.createsCycle(annots[i], containerDocument)) return true; } - for (let containerProto: FieldValue = containerDocument; containerProto && containerProto != FieldWaiting; containerProto = containerProto.GetPrototype()) { - if (containerProto.Id == documentToAdd.Id) + for (let containerProto: FieldValue = containerDocument; containerProto && containerProto !== FieldWaiting; containerProto = containerProto.GetPrototype()) { + if (containerProto.Id === documentToAdd.Id) return true; } return false; @@ -91,14 +93,14 @@ export class CollectionBaseView extends React.Component { //TODO This won't create the field if it doesn't already exist const value = props.Document.GetData(props.fieldKey, ListField, new Array()) if (!this.createsCycle(doc, props.Document)) { - if (!value.some(v => v.Id == doc.Id) || allowDuplicates) + if (!value.some(v => v.Id === doc.Id) || allowDuplicates) value.push(doc); } else return false; } else { let proto = props.Document.GetPrototype(); - if (!proto || proto == FieldWaiting || !this.createsCycle(proto, doc)) { + if (!proto || proto === FieldWaiting || !this.createsCycle(proto, doc)) { props.Document.SetOnPrototype(props.fieldKey, new ListField([doc])); } else @@ -114,13 +116,13 @@ export class CollectionBaseView extends React.Component { const value = props.Document.GetData(props.fieldKey, ListField, new Array()) let index = -1; for (let i = 0; i < value.length; i++) { - if (value[i].Id == doc.Id) { + if (value[i].Id === doc.Id) { index = i; break; } } doc.GetTAsync(KeyStore.AnnotationOn, Document).then((annotationOn) => { - if (annotationOn == props.Document) { + if (annotationOn === props.Document) { doc.Set(KeyStore.AnnotationOn, undefined, true); } }) @@ -138,7 +140,7 @@ export class CollectionBaseView extends React.Component { @action.bound moveDocument(doc: Document, targetCollection: Document, addDocument: (doc: Document) => boolean): boolean { if (this.props.Document === targetCollection) { - return false; + return true; } if (this.removeDocument(doc)) { return addDocument(doc); diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index ced46cc25..9dade94f1 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -90,7 +90,7 @@ export class CollectionSchemaView extends CollectionViewBase { ) let reference = React.createRef(); - let onItemDown = setupDrag(reference, () => props.Document, (containingCollection: CollectionView) => this.props.removeDocument(props.Document)); + let onItemDown = setupDrag(reference, () => props.Document, this.props.moveDocument); let applyToDoc = (doc: Document, value: string) => { let script = CompileScript(value, { this: doc }, true); if (!script.compiled) { @@ -150,7 +150,7 @@ export class CollectionSchemaView extends CollectionViewBase { } }), style: { - background: rowInfo.index == this._selectedIndex ? "lightGray" : "white", + background: rowInfo.index === this._selectedIndex ? "lightGray" : "white", //color: rowInfo.index == this._selectedIndex ? "white" : "black" } }; @@ -179,8 +179,8 @@ export class CollectionSchemaView extends CollectionViewBase { @action toggleExpander = (event: React.ChangeEvent) => { this._startSplitPercent = this.splitPercentage; - if (this._startSplitPercent == this.splitPercentage) { - this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage == 0 ? 33 : 0); + if (this._startSplitPercent === this.splitPercentage) { + this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage === 0 ? 33 : 0); } } @@ -209,8 +209,8 @@ export class CollectionSchemaView extends CollectionViewBase { onDividerUp = (e: PointerEvent): void => { document.removeEventListener("pointermove", this.onDividerMove); document.removeEventListener('pointerup', this.onDividerUp); - if (this._startSplitPercent == this.splitPercentage) { - this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage == 0 ? 33 : 0); + if (this._startSplitPercent === this.splitPercentage) { + this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage === 0 ? 33 : 0); } } onDividerDown = (e: React.PointerEvent) => { @@ -290,7 +290,7 @@ export class CollectionSchemaView extends CollectionViewBase { let doc: any = selected ? selected.Get(new Key(this.previewScript)) : undefined; // let doc = CompileScript(this.previewScript, { this: selected }, true)(); - let content = this._selectedIndex == -1 || !selected ? (null) : ( + let content = this._selectedIndex === -1 || !selected ? (null) : ( {({ measureRef }) =>
    @@ -313,7 +313,7 @@ export class CollectionSchemaView extends CollectionViewBase { } ) - let dividerDragger = this.splitPercentage == 0 ? (null) : + let dividerDragger = this.splitPercentage === 0 ? (null) :
    //options button and menu @@ -323,7 +323,7 @@ export class CollectionSchemaView extends CollectionViewBase {
    Options
    Preview Window
    -
    Show Preview
    +
    Show Preview
    Displayed Columns
      {Array.from(Object.keys(allKeys)).map(item => diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 6f9ba40d2..32d74164d 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -7,7 +7,7 @@ import { Document } from "../../../fields/Document"; import { FieldWaiting } from "../../../fields/Field"; import { KeyStore } from "../../../fields/KeyStore"; import { ListField } from "../../../fields/ListField"; -import { setupDrag } from "../../util/DragManager"; +import { setupDrag, DragManager } from "../../util/DragManager"; import { EditableView } from "../EditableView"; import "./CollectionTreeView.scss"; import { CollectionView } from "./CollectionView"; @@ -19,6 +19,7 @@ import { COLLECTION_BORDER_WIDTH } from './CollectionBaseView'; export interface TreeViewProps { document: Document; deleteDoc: (doc: Document) => void; + moveDocument: DragManager.MoveFunction; } export enum BulletType { @@ -49,6 +50,16 @@ class TreeView extends React.Component { } } + @action + move: DragManager.MoveFunction = (document, target, addDoc) => { + if (this.props.document === target) { + return true; + } + //TODO This should check if it was removed + this.remove(document) + return addDoc(document); + } + renderBullet(type: BulletType) { let onClicked = action(() => this._collapsed = !this._collapsed); let bullet: IconProp | undefined = undefined; @@ -64,7 +75,7 @@ class TreeView extends React.Component { */ renderTitle() { let reference = React.createRef(); - let onItemDown = setupDrag(reference, () => this.props.document, (containingCollection: CollectionView) => this.props.deleteDoc(this.props.document)); + let onItemDown = setupDrag(reference, () => this.props.document, this.props.moveDocument); let editableView = (titleString: string) => ( { if (!this._collapsed) { bulletType = BulletType.Collapsible; childElements =
        - {children.Data.map(value => )} + {children.Data.map(value => )}
      } else bulletType = BulletType.Collapsed; @@ -122,7 +133,7 @@ export class CollectionTreeView extends CollectionViewBase { var children = this.props.Document.GetT>(KeyStore.Data, ListField); let childrenElement = !children || children === FieldWaiting ? (null) : (children.Data.map(value => - ) + ) ) return ( diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index d440dcff9..5b4caf58d 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -8,16 +8,21 @@ import { CollectionTreeView } from './CollectionTreeView'; import { ContextMenu } from '../ContextMenu'; import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils'; import { KeyStore } from '../../../fields/KeyStore'; +import { observer } from 'mobx-react'; + +@observer export class CollectionView extends React.Component { public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(CollectionView, fieldStr) } private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => { let props = { ...this.props, ...renderProps }; switch (type) { - case CollectionViewType.Freeform: return () case CollectionViewType.Schema: return () case CollectionViewType.Docking: return () case CollectionViewType.Tree: return () + case CollectionViewType.Freeform: + default: + return () } return (null); } diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx index 2b858789f..147e5fd6b 100644 --- a/src/client/views/collections/CollectionViewBase.tsx +++ b/src/client/views/collections/CollectionViewBase.tsx @@ -80,6 +80,8 @@ export class CollectionViewBase extends React.Component } else if (de.data.moveDocument) { const move = de.data.moveDocument; added = de.data.droppedDocuments.reduce((added, d) => added || move(d, this.props.Document, this.props.addDocument), false) + } else { + added = de.data.droppedDocuments.reduce((added, d) => added || this.props.addDocument(d), false) } e.stopPropagation(); return added; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index f03205739..4aad06607 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -245,6 +245,7 @@ export class CollectionFreeFormView extends CollectionViewBase { Document: document, addDocument: this.props.addDocument, removeDocument: this.props.removeDocument, + moveDocument: this.props.moveDocument, ScreenToLocalTransform: this.getTransform, isTopMost: false, selectOnLoad: document.Id === this._selectOnLoaded, diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 3b146654c..2a9b3b9ac 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -27,7 +27,7 @@ export interface DocumentViewProps { Document: Document; addDocument?: (doc: Document, allowDuplicates?: boolean) => boolean; removeDocument?: (doc: Document) => boolean; - moveDocument?: (doc: Document, targetCollection: Document, addDocument: (document: Document) => void) => boolean; + moveDocument?: (doc: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean; ScreenToLocalTransform: () => Transform; isTopMost: boolean; ContentScaling: () => number; -- cgit v1.2.3-70-g09d2 From 3a9f6df918ad45e55b0c6a540cb566aff4940288 Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 8 Apr 2019 12:49:39 -0400 Subject: added copyOnDrag stuff. --- src/client/documents/Documents.ts | 2 + src/client/northstar/dash-fields/HistogramField.ts | 2 +- .../northstar/operations/HistogramOperation.ts | 4 + src/client/util/DragManager.ts | 14 +-- src/client/views/Main.tsx | 64 +++++-------- .../views/collections/CollectionTreeView.tsx | 11 ++- .../views/collections/CollectionViewBase.tsx | 4 +- src/fields/Document.ts | 30 +++++- src/fields/KeyStore.ts | 105 ++++++++++++--------- 9 files changed, 133 insertions(+), 103 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 1f0744782..7d2f9cde1 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -43,6 +43,7 @@ export interface DocumentOptions { layoutKeys?: Key[]; viewType?: number; backgroundColor?: string; + copyDraggedItems?: boolean; } export namespace Documents { @@ -86,6 +87,7 @@ export namespace Documents { if (options.ink !== undefined) { doc.Set(KeyStore.Ink, new InkField(options.ink)); } if (options.layout !== undefined) { doc.SetText(KeyStore.Layout, options.layout); } if (options.layoutKeys !== undefined) { doc.Set(KeyStore.LayoutKeys, new ListField(options.layoutKeys)); } + if (options.copyDraggedItems !== undefined) { doc.SetBoolean(KeyStore.CopyDraggedItems, options.copyDraggedItems); } return doc; } diff --git a/src/client/northstar/dash-fields/HistogramField.ts b/src/client/northstar/dash-fields/HistogramField.ts index 90be70b80..48731f52d 100644 --- a/src/client/northstar/dash-fields/HistogramField.ts +++ b/src/client/northstar/dash-fields/HistogramField.ts @@ -27,7 +27,7 @@ export class HistogramField extends BasicField { } Copy(): Field { - return new HistogramField(this.Data); + return new HistogramField(this.Data.Copy()); } ToScriptString(): string { diff --git a/src/client/northstar/operations/HistogramOperation.ts b/src/client/northstar/operations/HistogramOperation.ts index e63de1632..840520235 100644 --- a/src/client/northstar/operations/HistogramOperation.ts +++ b/src/client/northstar/operations/HistogramOperation.ts @@ -42,6 +42,10 @@ export class HistogramOperation extends BaseOperation implements IBaseFilterCons this.SchemaName = schemaName; } + Copy(): HistogramOperation { + return new HistogramOperation(this.SchemaName, this.X, this.Y, this.V, this.Normalization); + } + Equals(other: Object): boolean { throw new Error("Method not implemented."); } diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 043932de5..c1919093e 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -10,7 +10,8 @@ import { DocumentView } from "../views/nodes/DocumentView"; export function setupDrag( _reference: React.RefObject, docFunc: () => Document, - removeFunc: (containingCollection: CollectionView) => void = () => { } + removeFunc: (containingCollection: CollectionView) => void = () => { }, + copyOnDrop: boolean = false ) { let onRowMove = action( (e: PointerEvent): void => { @@ -20,6 +21,7 @@ export function setupDrag( document.removeEventListener("pointermove", onRowMove); document.removeEventListener("pointerup", onRowUp); var dragData = new DragManager.DocumentDragData([docFunc()]); + dragData.copyOnDrop = copyOnDrop; dragData.removeDocument = removeFunc; DragManager.StartDocumentDrag([_reference.current!], dragData, e.x, e.y); } @@ -125,6 +127,7 @@ export namespace DragManager { xOffset?: number; yOffset?: number; aliasOnDrop?: boolean; + copyOnDrop?: boolean; removeDocument?: (collectionDrop: CollectionView) => void; [id: string]: any; } @@ -136,15 +139,12 @@ export namespace DragManager { downY: number, options?: DragOptions ) { - StartDrag( - eles, - dragData, - downX, downY, - options, + StartDrag(eles, dragData, downX, downY, options, (dropData: { [id: string]: any }) => (dropData.droppedDocuments = dragData.aliasOnDrop ? dragData.draggedDocuments.map(d => d.CreateAlias()) - : dragData.draggedDocuments) + : dragData.copyOnDrop ? dragData.draggedDocuments.map(d => d.Copy(true) as Document) : + dragData.draggedDocuments) ); } diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 237eb3b6e..b4ab9e21d 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -1,57 +1,41 @@ -import { action, configure, observable, runInAction, trace, computed, reaction } from 'mobx'; +import { IconName, library } from '@fortawesome/fontawesome-svg-core'; +import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faTree, faUndoAlt } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { action, computed, configure, observable, runInAction } from 'mobx'; +import { observer } from 'mobx-react'; import "normalize.css"; import * as React from 'react'; 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 { KeyStore } from '../../fields/KeyStore'; -import "./Main.scss"; +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 { RouteStore } from '../../server/RouteStore'; +import { ServerUtils } from '../../server/ServerUtil'; import { Utils } from '../../Utils'; -import * as request from 'request' -import * as rp from 'request-promise' 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 } 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 } from '../util/DragManager'; import { Transform } from '../util/Transform'; -import { UndoManager, undoBatch } from '../util/UndoManager'; -import { WorkspacesMenu } from '../../server/authentication/controllers/WorkspacesMenu'; +import { UndoManager } from '../util/UndoManager'; import { CollectionDockingView } from './collections/CollectionDockingView'; import { ContextMenu } from './ContextMenu'; import { DocumentDecorations } from './DocumentDecorations'; -import { DocumentView } from './nodes/DocumentView'; -import "./Main.scss"; -import { observer } from 'mobx-react'; import { InkingControl } from './InkingControl'; -import { RouteStore } from '../../server/RouteStore'; -import { library, IconName } from '@fortawesome/fontawesome-svg-core'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faFont } from '@fortawesome/free-solid-svg-icons'; -import { faImage } from '@fortawesome/free-solid-svg-icons'; -import { faFilePdf } from '@fortawesome/free-solid-svg-icons'; -import { faObjectGroup } from '@fortawesome/free-solid-svg-icons'; -import { faTable } from '@fortawesome/free-solid-svg-icons'; -import { faGlobeAsia } from '@fortawesome/free-solid-svg-icons'; -import { faUndoAlt } from '@fortawesome/free-solid-svg-icons'; -import { faRedoAlt } from '@fortawesome/free-solid-svg-icons'; -import { faPenNib } from '@fortawesome/free-solid-svg-icons'; -import { faFilm } from '@fortawesome/free-solid-svg-icons'; -import { faMusic } from '@fortawesome/free-solid-svg-icons'; -import { faTree } from '@fortawesome/free-solid-svg-icons'; -import Measure from 'react-measure'; -import { DashUserModel } from '../../server/authentication/models/user_model'; -import { ServerUtils } from '../../server/ServerUtil'; -import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; -import { Field, Opt, FieldWaiting } from '../../fields/Field'; -import { ListField } from '../../fields/ListField'; -import { Gateway, Settings } from '../northstar/manager/Gateway'; -import { Catalog, Schema, Attribute, AttributeGroup, AggregateFunction } from '../northstar/model/idea/idea'; -import { ArrayUtil } from '../northstar/utils/ArrayUtil'; -import '../northstar/model/ModelExtensions' -import '../northstar/utils/Extensions' -import { HistogramOperation } from '../northstar/operations/HistogramOperation'; -import { AttributeTransformationModel } from '../northstar/core/attribute/AttributeTransformationModel'; -import { ColumnAttributeModel } from '../northstar/core/attribute/AttributeModel'; -import { CollectionView } from './collections/CollectionView'; +import "./Main.scss"; +import { DocumentView } from './nodes/DocumentView'; @observer export class Main extends React.Component { @@ -247,7 +231,7 @@ export class Main extends React.Component { let addTextNode = action(() => Documents.TextDocument({ width: 200, height: 200, title: "a text note" })) let addColNode = action(() => Documents.FreeformDocument([], { width: 200, height: 200, title: "a freeform collection" })); let addSchemaNode = action(() => Documents.SchemaDocument([], { width: 200, height: 200, title: "a schema collection" })); - let addTreeNode = action(() => Documents.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas" })); + let addTreeNode = action(() => Documents.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", copyDraggedItems: true })); let addVideoNode = action(() => Documents.VideoDocument(videourl, { width: 200, height: 200, title: "video node" })); let addPDFNode = action(() => Documents.PdfDocument(pdfurl, { width: 200, height: 200, title: "a schema collection" })); let addImageNode = action(() => Documents.ImageDocument(imgurl, { width: 200, height: 200, title: "an image of a cat" })); diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 70790af18..0b12f11fd 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -18,6 +18,7 @@ import React = require("react") export interface TreeViewProps { document: Document; deleteDoc: (doc: Document) => void; + copyOnDrag: boolean; } export enum BulletType { @@ -63,7 +64,7 @@ class TreeView extends React.Component { */ renderTitle() { let reference = React.createRef(); - let onItemDown = setupDrag(reference, () => this.props.document, (containingCollection: CollectionView) => this.props.deleteDoc(this.props.document)); + let onItemDown = setupDrag(reference, () => this.props.document, (containingCollection: CollectionView) => this.props.deleteDoc(this.props.document), this.props.copyOnDrag); let editableView = (titleString: string) => ( { render() { let bulletType = BulletType.List; let childElements: JSX.Element | undefined = undefined; - var children = this.props.document.GetT>(KeyStore.Data, ListField); if (children && children !== FieldWaiting) { // add children for a collection if (!this._collapsed) { bulletType = BulletType.Collapsible; childElements =
        - {children.Data.map(value => )} + {children.Data.map(value => )}
      } else bulletType = BulletType.Collapsed; @@ -118,10 +118,11 @@ export class CollectionTreeView extends CollectionViewBase { } render() { - var children = this.props.Document.GetT>(KeyStore.Data, ListField); + let children = this.props.Document.GetT>(KeyStore.Data, ListField); + let copyOnDrag = this.props.Document.GetBoolean(KeyStore.CopyDraggedItems, false); let childrenElement = !children || children === FieldWaiting ? (null) : (children.Data.map(value => - ) + ) ) return ( diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx index 458bae7ab..7cf49e215 100644 --- a/src/client/views/collections/CollectionViewBase.tsx +++ b/src/client/views/collections/CollectionViewBase.tsx @@ -82,13 +82,13 @@ export class CollectionViewBase extends React.Component @action protected drop(e: Event, de: DragManager.DropEvent): boolean { if (de.data instanceof DragManager.DocumentDragData) { - if (de.data.aliasOnDrop) { + if (de.data.aliasOnDrop || de.data.copyOnDrop) { [KeyStore.Width, KeyStore.Height, KeyStore.CurPage].map(key => de.data.draggedDocuments.map((draggedDocument: Document, i: number) => draggedDocument.GetTAsync(key, NumberField, (f: Opt) => f ? de.data.droppedDocuments[i].SetNumber(key, f.Data) : null))); } let added = de.data.droppedDocuments.reduce((added, d) => this.props.addDocument(d, false), true); - if (added && de.data.removeDocument && !de.data.aliasOnDrop) { + if (added && de.data.removeDocument && !de.data.aliasOnDrop && !de.data.copyOnDrop) { de.data.removeDocument(this.props.CollectionView); } e.stopPropagation(); diff --git a/src/fields/Document.ts b/src/fields/Document.ts index 538d4ada9..02004678d 100644 --- a/src/fields/Document.ts +++ b/src/fields/Document.ts @@ -10,6 +10,9 @@ import { Types } from "../server/Message"; import { UndoManager } from "../client/util/UndoManager"; import { HtmlField } from "./HtmlField"; import { BooleanField } from "./BooleanField"; +import { allLimit } from "async"; +import { prototype } from "nodemailer/lib/smtp-pool"; +import { HistogramField } from "../client/northstar/dash-fields/HistogramField"; export class Document extends Field { //TODO tfs: We should probably store FieldWaiting in fields when we request it from the server so that we don't set up multiple server gets for the same document and field @@ -343,7 +346,10 @@ export class Document extends Field { SetText(key: Key, value: string, replaceWrongType = true) { this.SetData(key, value, TextField, replaceWrongType); } - + @action + SetBoolean(key: Key, value: boolean, replaceWrongType = true) { + this.SetData(key, value, BooleanField, replaceWrongType); + } @action SetNumber(key: Key, value: number, replaceWrongType = true) { this.SetData(key, value, NumberField, replaceWrongType); @@ -393,8 +399,26 @@ export class Document extends Field { return title; //throw new Error("Method not implemented."); } - Copy(): Field { - throw new Error("Method not implemented."); + Copy(copyProto?: boolean, id?: string): Field { + let copy = new Document(); + this._proxies.forEach((fieldid, keyid) => { // copy each prototype field + let key = KeyStore.KeyLookup(keyid); + if (key) { + this.GetAsync(key, (field: Opt) => { + if (key === KeyStore.Prototype && copyProto) { // handle prototype field specially + if (field instanceof Document) { + copy.Set(key, field.Copy(false)); // only copying one level of prototypes for now... + } + } + else + if (field instanceof Document) // ... TODO bcz: should we copy documents or reference them + copy.Set(key!, field) + else if (field) + copy.Set(key!, field.Copy()) + }) + } + }); + return copy; } ToJson(): { type: Types; data: [string, string][]; _id: string } { diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts index 6ed3b1604..42dc34c51 100644 --- a/src/fields/KeyStore.ts +++ b/src/fields/KeyStore.ts @@ -2,49 +2,64 @@ import { Key } from "./Key"; import { KeyTransfer } from "../server/Message"; export namespace KeyStore { - export const Prototype = new Key("Prototype"); - export const X = new Key("X"); - export const Y = new Key("Y"); - export const Page = new Key("Page"); - export const Title = new Key("Title"); - export const Author = new Key("Author"); - export const PanX = new Key("PanX"); - export const PanY = new Key("PanY"); - export const Scale = new Key("Scale"); - export const NativeWidth = new Key("NativeWidth"); - export const NativeHeight = new Key("NativeHeight"); - export const Width = new Key("Width"); - export const Height = new Key("Height"); - export const ZIndex = new Key("ZIndex"); - export const Data = new Key("Data"); - export const Annotations = new Key("Annotations"); - export const ViewType = new Key("ViewType"); - export const Layout = new Key("Layout"); - export const BackgroundColor = new Key("BackgroundColor"); - export const BackgroundLayout = new Key("BackgroundLayout"); - export const OverlayLayout = new Key("OverlayLayout"); - export const LayoutKeys = new Key("LayoutKeys"); - export const LayoutFields = new Key("LayoutFields"); - export const ColumnsKey = new Key("SchemaColumns"); - export const SchemaSplitPercentage = new Key("SchemaSplitPercentage"); - export const Caption = new Key("Caption"); - export const ActiveWorkspace = new Key("ActiveWorkspace"); - export const DocumentText = new Key("DocumentText"); - export const BrushingDocs = new Key("BrushingDocs"); - export const LinkedToDocs = new Key("LinkedToDocs"); - export const LinkedFromDocs = new Key("LinkedFromDocs"); - export const LinkDescription = new Key("LinkDescription"); - export const LinkTags = new Key("LinkTag"); - export const Thumbnail = new Key("Thumbnail"); - export const ThumbnailPage = new Key("ThumbnailPage"); - export const CurPage = new Key("CurPage"); - export const AnnotationOn = new Key("AnnotationOn"); - export const NumPages = new Key("NumPages"); - export const Ink = new Key("Ink"); - export const Cursors = new Key("Cursors"); - export const OptionalRightCollection = new Key("OptionalRightCollection"); - export const Archives = new Key("Archives"); - export const Updated = new Key("Updated"); - export const Workspaces = new Key("Workspaces"); - export const Minimized = new Key("Minimized"); + export const Prototype = new Key("Prototype"); + export const X = new Key("X"); + export const Y = new Key("Y"); + export const Page = new Key("Page"); + export const Title = new Key("Title"); + export const Author = new Key("Author"); + export const PanX = new Key("PanX"); + export const PanY = new Key("PanY"); + export const Scale = new Key("Scale"); + export const NativeWidth = new Key("NativeWidth"); + export const NativeHeight = new Key("NativeHeight"); + export const Width = new Key("Width"); + export const Height = new Key("Height"); + export const ZIndex = new Key("ZIndex"); + export const Data = new Key("Data"); + export const Annotations = new Key("Annotations"); + export const ViewType = new Key("ViewType"); + export const Layout = new Key("Layout"); + export const BackgroundColor = new Key("BackgroundColor"); + export const BackgroundLayout = new Key("BackgroundLayout"); + export const OverlayLayout = new Key("OverlayLayout"); + export const LayoutKeys = new Key("LayoutKeys"); + export const LayoutFields = new Key("LayoutFields"); + export const ColumnsKey = new Key("SchemaColumns"); + export const SchemaSplitPercentage = new Key("SchemaSplitPercentage"); + export const Caption = new Key("Caption"); + export const ActiveWorkspace = new Key("ActiveWorkspace"); + export const DocumentText = new Key("DocumentText"); + export const BrushingDocs = new Key("BrushingDocs"); + export const LinkedToDocs = new Key("LinkedToDocs"); + export const LinkedFromDocs = new Key("LinkedFromDocs"); + export const LinkDescription = new Key("LinkDescription"); + export const LinkTags = new Key("LinkTag"); + export const Thumbnail = new Key("Thumbnail"); + export const ThumbnailPage = new Key("ThumbnailPage"); + export const CurPage = new Key("CurPage"); + export const AnnotationOn = new Key("AnnotationOn"); + export const NumPages = new Key("NumPages"); + export const Ink = new Key("Ink"); + export const Cursors = new Key("Cursors"); + export const OptionalRightCollection = new Key("OptionalRightCollection"); + export const Archives = new Key("Archives"); + export const Workspaces = new Key("Workspaces"); + export const Minimized = new Key("Minimized"); + export const CopyDraggedItems = new Key("CopyDraggedItems"); + + export const KeyList: Key[] = [Prototype, X, Y, Page, Title, Author, PanX, PanY, Scale, NativeWidth, NativeHeight, + Width, Height, ZIndex, Data, Annotations, ViewType, Layout, BackgroundColor, BackgroundLayout, OverlayLayout, LayoutKeys, + LayoutFields, ColumnsKey, SchemaSplitPercentage, Caption, ActiveWorkspace, DocumentText, BrushingDocs, LinkedToDocs, LinkedFromDocs, + LinkDescription, LinkTags, Thumbnail, ThumbnailPage, CurPage, AnnotationOn, NumPages, Ink, Cursors, OptionalRightCollection, + Archives, Workspaces, Minimized, CopyDraggedItems + ]; + export function KeyLookup(keyid: string) { + for (let i = 0; i < KeyList.length; i++) { + let keylistid = KeyList[i].Id; + if (keylistid === keyid) + return KeyList[i]; + } + return null; + } } -- cgit v1.2.3-70-g09d2 From cecbafdd8e42b1fa021a54fe624c3cf68e8645bd Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Mon, 8 Apr 2019 22:22:50 -0400 Subject: Renamed CollectionViewBase to CollectionSubView Fixed bug with adding documents to PDF and videos --- .../views/collections/CollectionDockingView.tsx | 2 +- src/client/views/collections/CollectionPDFView.tsx | 9 +- .../views/collections/CollectionSchemaView.tsx | 4 +- src/client/views/collections/CollectionSubView.tsx | 231 +++++++++++++++++++++ .../views/collections/CollectionTreeView.tsx | 4 +- .../views/collections/CollectionVideoView.tsx | 14 +- .../views/collections/CollectionViewBase.tsx | 231 --------------------- .../CollectionFreeFormLinksView.tsx | 2 +- .../CollectionFreeFormRemoteCursors.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 +- 10 files changed, 249 insertions(+), 254 deletions(-) create mode 100644 src/client/views/collections/CollectionSubView.tsx delete mode 100644 src/client/views/collections/CollectionViewBase.tsx (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index c5e1e7f4b..c3757a377 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -15,7 +15,7 @@ import { DocumentView } from "../nodes/DocumentView"; import "./CollectionDockingView.scss"; import { COLLECTION_BORDER_WIDTH } from "./CollectionBaseView"; import React = require("react"); -import { SubCollectionViewProps } from "./CollectionViewBase"; +import { SubCollectionViewProps } from "./CollectionSubView"; import { ServerUtils } from "../../../server/ServerUtil"; import { DragManager } from "../../util/DragManager"; import { TextField } from "../../../fields/TextField"; diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx index 1f2e71f6e..32f50afc0 100644 --- a/src/client/views/collections/CollectionPDFView.tsx +++ b/src/client/views/collections/CollectionPDFView.tsx @@ -2,16 +2,15 @@ import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import { KeyStore } from "../../../fields/KeyStore"; import { ContextMenu } from "../ContextMenu"; -import { CollectionViewProps } from "./CollectionViewBase"; import "./CollectionPDFView.scss" import React = require("react"); import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; -import { FieldView } from "../nodes/FieldView"; +import { FieldView, FieldViewProps } from "../nodes/FieldView"; import { CollectionRenderProps, CollectionBaseView, CollectionViewType } from "./CollectionBaseView"; @observer -export class CollectionPDFView extends React.Component { +export class CollectionPDFView extends React.Component { public static LayoutString(fieldKey: string = "DataKey") { return FieldView.LayoutString(CollectionPDFView, fieldKey); @@ -23,7 +22,7 @@ export class CollectionPDFView extends React.Component { @action onPageForward = () => this.curPage < this.numPages ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage + 1) : -1; private get uIButtons() { - let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().transformDirection(1, 1)[0]); + let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().Scale); return (
      @@ -39,7 +38,7 @@ export class CollectionPDFView extends React.Component { } private subView = (_type: CollectionViewType, renderProps: CollectionRenderProps) => { - let props = { ...renderProps, ...this.props }; + let props = { ...this.props, ...renderProps }; return ( <> diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 4604945d1..9b780f29b 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -23,7 +23,7 @@ import { DocumentView } from "../nodes/DocumentView"; import { FieldView, FieldViewProps } from "../nodes/FieldView"; import "./CollectionSchemaView.scss"; import { CollectionView } from "./CollectionView"; -import { CollectionViewBase } from "./CollectionViewBase"; +import { CollectionSubView } from "./CollectionSubView"; import { TextField } from "../../../fields/TextField"; import { COLLECTION_BORDER_WIDTH } from "./CollectionBaseView"; import { emptyFunction, returnFalse } from "../../../Utils"; @@ -56,7 +56,7 @@ class KeyToggle extends React.Component<{ keyId: string, checked: boolean, toggl } @observer -export class CollectionSchemaView extends CollectionViewBase { +export class CollectionSchemaView extends CollectionSubView { private _mainCont = React.createRef(); private _startSplitPercent = 0; private DIVIDER_WIDTH = 4; diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx new file mode 100644 index 000000000..59b89e119 --- /dev/null +++ b/src/client/views/collections/CollectionSubView.tsx @@ -0,0 +1,231 @@ +import { action, runInAction } from "mobx"; +import { Document } from "../../../fields/Document"; +import { ListField } from "../../../fields/ListField"; +import React = require("react"); +import { KeyStore } from "../../../fields/KeyStore"; +import { FieldWaiting, Opt } from "../../../fields/Field"; +import { undoBatch, UndoManager } from "../../util/UndoManager"; +import { DragManager } from "../../util/DragManager"; +import { Documents, DocumentOptions } from "../../documents/Documents"; +import { RouteStore } from "../../../server/RouteStore"; +import { TupleField } from "../../../fields/TupleField"; +import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; +import { NumberField } from "../../../fields/NumberField"; +import { ServerUtils } from "../../../server/ServerUtil"; +import { Server } from "../../Server"; +import { FieldViewProps } from "../nodes/FieldView"; +import * as rp from 'request-promise' + +export interface CollectionViewProps extends FieldViewProps { + addDocument: (document: Document, allowDuplicates?: boolean) => boolean; + removeDocument: (document: Document) => boolean; + moveDocument: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean; +} + +export interface SubCollectionViewProps extends CollectionViewProps { +} + +export type CursorEntry = TupleField<[string, string], [number, number]>; + +export class CollectionSubView extends React.Component { + private dropDisposer?: DragManager.DragDropDisposer; + protected createDropTarget = (ele: HTMLDivElement) => { + if (this.dropDisposer) { + this.dropDisposer(); + } + if (ele) { + this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } }); + } + } + + @action + protected setCursorPosition(position: [number, number]) { + let ind; + let doc = this.props.Document; + let id = CurrentUserUtils.id; + let email = CurrentUserUtils.email; + if (id && email) { + let textInfo: [string, string] = [id, email]; + doc.GetTAsync(KeyStore.Prototype, Document).then(proto => { + if (!proto) { + return; + } + proto.GetOrCreateAsync>(KeyStore.Cursors, ListField, action((field: ListField) => { + let cursors = field.Data; + if (cursors.length > 0 && (ind = cursors.findIndex(entry => entry.Data[0][0] === id)) > -1) { + cursors[ind].Data[1] = position; + } else { + let entry = new TupleField<[string, string], [number, number]>([textInfo, position]); + cursors.push(entry); + } + })) + }) + } + } + + @undoBatch + @action + protected drop(e: Event, de: DragManager.DropEvent): boolean { + if (de.data instanceof DragManager.DocumentDragData) { + if (de.data.aliasOnDrop || de.data.copyOnDrop) { + [KeyStore.Width, KeyStore.Height, KeyStore.CurPage].map(key => + de.data.draggedDocuments.map((draggedDocument: Document, i: number) => + draggedDocument.GetTAsync(key, NumberField, (f: Opt) => f ? de.data.droppedDocuments[i].SetNumber(key, f.Data) : null))); + } + let added = false; + if (de.data.aliasOnDrop) { + added = de.data.droppedDocuments.reduce((added: boolean, d) => added || this.props.addDocument(d), false); + } else if (de.data.moveDocument) { + const move = de.data.moveDocument; + added = de.data.droppedDocuments.reduce((added: boolean, d) => added || move(d, this.props.Document, this.props.addDocument), false) + } else { + added = de.data.droppedDocuments.reduce((added: boolean, d) => added || this.props.addDocument(d), false) + } + e.stopPropagation(); + return added; + } + if (de.data instanceof DragManager.LinkDragData) { + let sourceDoc: Document = de.data.linkSourceDocumentView.props.Document; + if (sourceDoc) runInAction(() => { + let srcTarg = sourceDoc.GetT(KeyStore.Prototype, Document) + if (srcTarg && srcTarg !== FieldWaiting) { + let linkDocs = srcTarg.GetList(KeyStore.LinkedToDocs, [] as Document[]); + linkDocs.map(linkDoc => { + let targDoc = linkDoc.GetT(KeyStore.LinkedToDocs, Document); + if (targDoc && targDoc !== FieldWaiting) { + let dropdoc = targDoc.MakeDelegate(); + de.data.droppedDocuments.push(dropdoc); + this.props.addDocument(dropdoc, false); + } + }) + } + }) + return true; + } + return false; + } + + protected getDocumentFromType(type: string, path: string, options: DocumentOptions): Opt { + let ctor: ((path: string, options: DocumentOptions) => Document) | undefined; + if (type.indexOf("image") !== -1) { + ctor = Documents.ImageDocument; + } + if (type.indexOf("video") !== -1) { + ctor = Documents.VideoDocument; + } + if (type.indexOf("audio") !== -1) { + ctor = Documents.AudioDocument; + } + if (type.indexOf("pdf") !== -1) { + ctor = Documents.PdfDocument; + options.nativeWidth = 1200; + } + if (type.indexOf("html") !== -1) { + if (path.includes('localhost')) { + let s = path.split('/'); + let id = s[s.length - 1]; + Server.GetField(id).then(field => { + if (field instanceof Document) { + let alias = field.CreateAlias(); + alias.SetNumber(KeyStore.X, options.x || 0); + alias.SetNumber(KeyStore.Y, options.y || 0); + alias.SetNumber(KeyStore.Width, options.width || 300); + alias.SetNumber(KeyStore.Height, options.height || options.width || 300); + this.props.addDocument(alias, false); + } + }) + return undefined; + } + ctor = Documents.WebDocument; + options = { height: options.width, ...options, title: path }; + } + return ctor ? ctor(path, options) : undefined; + } + + @undoBatch + @action + protected onDrop(e: React.DragEvent, options: DocumentOptions): void { + let html = e.dataTransfer.getData("text/html"); + let text = e.dataTransfer.getData("text/plain"); + + if (text && text.startsWith("[] = []; + // tslint:disable-next-line:prefer-for-of + for (let i = 0; i < e.dataTransfer.items.length; i++) { + const upload = window.location.origin + RouteStore.upload; + let item = e.dataTransfer.items[i]; + if (item.kind === "string" && item.type.indexOf("uri") !== -1) { + let str: string; + let prom = new Promise(res => + e.dataTransfer.items[i].getAsString(res)).then(action((s: string) => { + str = s; + return rp.head(ServerUtils.prepend(RouteStore.corsProxy + "/" + s)) + })).then(res => { + let type = res.headers["content-type"]; + if (type) { + let doc = this.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 }) + if (doc) { + this.props.addDocument(doc, false); + } + } + }); + promises.push(prom); + // this.props.addDocument(Documents.WebDocument(s, { ...options, width: 300, height: 300 }), false) + } + let type = item.type + if (item.kind === "file") { + let file = item.getAsFile(); + let formData = new FormData() + + if (file) { + formData.append('file', file) + } + + let prom = fetch(upload, { + method: 'POST', + body: formData + }).then(async (res: Response) => { + const json = await res.json(); + json.map((file: any) => { + let path = window.location.origin + file + runInAction(() => { + let doc = this.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300 }) + + let docs = this.props.Document.GetT(KeyStore.Data, ListField); + if (docs !== FieldWaiting) { + if (!docs) { + docs = new ListField(); + this.props.Document.Set(KeyStore.Data, docs) + } + if (doc) { + docs.Data.push(doc); + } + } + }); + }); + }); + promises.push(prom); + } + } + + if (promises.length) { + Promise.all(promises).catch(() => { }).then(() => batch.end()); + } else { + batch.end(); + } + } +} diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index dce35868e..2bdf0baa6 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -11,7 +11,7 @@ import { setupDrag, DragManager } from "../../util/DragManager"; import { EditableView } from "../EditableView"; import "./CollectionTreeView.scss"; import { CollectionView } from "./CollectionView"; -import { CollectionViewBase } from "./CollectionViewBase"; +import { CollectionSubView } from "./CollectionSubView"; import React = require("react") import { COLLECTION_BORDER_WIDTH } from './CollectionBaseView'; import { props } from 'bluebird'; @@ -120,7 +120,7 @@ class TreeView extends React.Component { } @observer -export class CollectionTreeView extends CollectionViewBase { +export class CollectionTreeView extends CollectionSubView { @action remove = (document: Document) => { diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx index afadfeefb..1acc76c85 100644 --- a/src/client/views/collections/CollectionVideoView.tsx +++ b/src/client/views/collections/CollectionVideoView.tsx @@ -1,20 +1,16 @@ -import { action, computed, observable, trace } from "mobx"; +import { action, observable, trace } from "mobx"; import { observer } from "mobx-react"; -import { Document } from "../../../fields/Document"; import { KeyStore } from "../../../fields/KeyStore"; import { ContextMenu } from "../ContextMenu"; -import { CollectionView } from "./CollectionView"; import { CollectionViewType, CollectionBaseView, CollectionRenderProps } from "./CollectionBaseView"; -import { CollectionViewProps } from "./CollectionViewBase"; import React = require("react"); -import { FieldId } from "../../../fields/Field"; import "./CollectionVideoView.scss" import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; -import { FieldView } from "../nodes/FieldView"; +import { FieldView, FieldViewProps } from "../nodes/FieldView"; @observer -export class CollectionVideoView extends React.Component { +export class CollectionVideoView extends React.Component { private _intervalTimer: any = undefined; private _player: HTMLVideoElement | undefined = undefined; @@ -25,7 +21,7 @@ export class CollectionVideoView extends React.Component { return FieldView.LayoutString(CollectionVideoView, fieldKey); } private get uIButtons() { - let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().transformDirection(1, 1)[0]); + let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().Scale); return ([
      {"" + Math.round(this._currentTimecode)} @@ -109,7 +105,7 @@ export class CollectionVideoView extends React.Component { } private subView = (_type: CollectionViewType, renderProps: CollectionRenderProps) => { - let props = { ...renderProps, ...this.props }; + let props = { ...this.props, ...renderProps }; return ( <> diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx deleted file mode 100644 index ad1294f76..000000000 --- a/src/client/views/collections/CollectionViewBase.tsx +++ /dev/null @@ -1,231 +0,0 @@ -import { action, runInAction } from "mobx"; -import { Document } from "../../../fields/Document"; -import { ListField } from "../../../fields/ListField"; -import React = require("react"); -import { KeyStore } from "../../../fields/KeyStore"; -import { FieldWaiting, Opt } from "../../../fields/Field"; -import { undoBatch, UndoManager } from "../../util/UndoManager"; -import { DragManager } from "../../util/DragManager"; -import { Documents, DocumentOptions } from "../../documents/Documents"; -import { RouteStore } from "../../../server/RouteStore"; -import { TupleField } from "../../../fields/TupleField"; -import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; -import { NumberField } from "../../../fields/NumberField"; -import { ServerUtils } from "../../../server/ServerUtil"; -import { Server } from "../../Server"; -import { FieldViewProps } from "../nodes/FieldView"; -import * as rp from 'request-promise' - -export interface CollectionViewProps extends FieldViewProps { - addDocument: (document: Document, allowDuplicates?: boolean) => boolean; - removeDocument: (document: Document) => boolean; - moveDocument: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean; -} - -export interface SubCollectionViewProps extends CollectionViewProps { -} - -export type CursorEntry = TupleField<[string, string], [number, number]>; - -export class CollectionViewBase extends React.Component { - private dropDisposer?: DragManager.DragDropDisposer; - protected createDropTarget = (ele: HTMLDivElement) => { - if (this.dropDisposer) { - this.dropDisposer(); - } - if (ele) { - this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } }); - } - } - - @action - protected setCursorPosition(position: [number, number]) { - let ind; - let doc = this.props.Document; - let id = CurrentUserUtils.id; - let email = CurrentUserUtils.email; - if (id && email) { - let textInfo: [string, string] = [id, email]; - doc.GetTAsync(KeyStore.Prototype, Document).then(proto => { - if (!proto) { - return; - } - proto.GetOrCreateAsync>(KeyStore.Cursors, ListField, action((field: ListField) => { - let cursors = field.Data; - if (cursors.length > 0 && (ind = cursors.findIndex(entry => entry.Data[0][0] === id)) > -1) { - cursors[ind].Data[1] = position; - } else { - let entry = new TupleField<[string, string], [number, number]>([textInfo, position]); - cursors.push(entry); - } - })) - }) - } - } - - @undoBatch - @action - protected drop(e: Event, de: DragManager.DropEvent): boolean { - if (de.data instanceof DragManager.DocumentDragData) { - if (de.data.aliasOnDrop || de.data.copyOnDrop) { - [KeyStore.Width, KeyStore.Height, KeyStore.CurPage].map(key => - de.data.draggedDocuments.map((draggedDocument: Document, i: number) => - draggedDocument.GetTAsync(key, NumberField, (f: Opt) => f ? de.data.droppedDocuments[i].SetNumber(key, f.Data) : null))); - } - let added = false; - if (de.data.aliasOnDrop) { - added = de.data.droppedDocuments.reduce((added: boolean, d) => added || this.props.addDocument(d), false); - } else if (de.data.moveDocument) { - const move = de.data.moveDocument; - added = de.data.droppedDocuments.reduce((added: boolean, d) => added || move(d, this.props.Document, this.props.addDocument), false) - } else { - added = de.data.droppedDocuments.reduce((added: boolean, d) => added || this.props.addDocument(d), false) - } - e.stopPropagation(); - return added; - } - if (de.data instanceof DragManager.LinkDragData) { - let sourceDoc: Document = de.data.linkSourceDocumentView.props.Document; - if (sourceDoc) runInAction(() => { - let srcTarg = sourceDoc.GetT(KeyStore.Prototype, Document) - if (srcTarg && srcTarg !== FieldWaiting) { - let linkDocs = srcTarg.GetList(KeyStore.LinkedToDocs, [] as Document[]); - linkDocs.map(linkDoc => { - let targDoc = linkDoc.GetT(KeyStore.LinkedToDocs, Document); - if (targDoc && targDoc !== FieldWaiting) { - let dropdoc = targDoc.MakeDelegate(); - de.data.droppedDocuments.push(dropdoc); - this.props.addDocument(dropdoc, false); - } - }) - } - }) - return true; - } - return false; - } - - protected getDocumentFromType(type: string, path: string, options: DocumentOptions): Opt { - let ctor: ((path: string, options: DocumentOptions) => Document) | undefined; - if (type.indexOf("image") !== -1) { - ctor = Documents.ImageDocument; - } - if (type.indexOf("video") !== -1) { - ctor = Documents.VideoDocument; - } - if (type.indexOf("audio") !== -1) { - ctor = Documents.AudioDocument; - } - if (type.indexOf("pdf") !== -1) { - ctor = Documents.PdfDocument; - options.nativeWidth = 1200; - } - if (type.indexOf("html") !== -1) { - if (path.includes('localhost')) { - let s = path.split('/'); - let id = s[s.length - 1]; - Server.GetField(id).then(field => { - if (field instanceof Document) { - let alias = field.CreateAlias(); - alias.SetNumber(KeyStore.X, options.x || 0); - alias.SetNumber(KeyStore.Y, options.y || 0); - alias.SetNumber(KeyStore.Width, options.width || 300); - alias.SetNumber(KeyStore.Height, options.height || options.width || 300); - this.props.addDocument(alias, false); - } - }) - return undefined; - } - ctor = Documents.WebDocument; - options = { height: options.width, ...options, title: path }; - } - return ctor ? ctor(path, options) : undefined; - } - - @undoBatch - @action - protected onDrop(e: React.DragEvent, options: DocumentOptions): void { - let html = e.dataTransfer.getData("text/html"); - let text = e.dataTransfer.getData("text/plain"); - - if (text && text.startsWith("[] = []; - // tslint:disable-next-line:prefer-for-of - for (let i = 0; i < e.dataTransfer.items.length; i++) { - const upload = window.location.origin + RouteStore.upload; - let item = e.dataTransfer.items[i]; - if (item.kind === "string" && item.type.indexOf("uri") !== -1) { - let str: string; - let prom = new Promise(res => - e.dataTransfer.items[i].getAsString(res)).then(action((s: string) => { - str = s; - return rp.head(ServerUtils.prepend(RouteStore.corsProxy + "/" + s)) - })).then(res => { - let type = res.headers["content-type"]; - if (type) { - let doc = this.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 }) - if (doc) { - this.props.addDocument(doc, false); - } - } - }); - promises.push(prom); - // this.props.addDocument(Documents.WebDocument(s, { ...options, width: 300, height: 300 }), false) - } - let type = item.type - if (item.kind === "file") { - let file = item.getAsFile(); - let formData = new FormData() - - if (file) { - formData.append('file', file) - } - - let prom = fetch(upload, { - method: 'POST', - body: formData - }).then(async (res: Response) => { - const json = await res.json(); - json.map((file: any) => { - let path = window.location.origin + file - runInAction(() => { - let doc = this.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300 }) - - let docs = this.props.Document.GetT(KeyStore.Data, ListField); - if (docs !== FieldWaiting) { - if (!docs) { - docs = new ListField(); - this.props.Document.Set(KeyStore.Data, docs) - } - if (doc) { - docs.Data.push(doc); - } - } - }); - }); - }); - promises.push(prom); - } - } - - if (promises.length) { - Promise.all(promises).catch(() => { }).then(() => batch.end()); - } else { - batch.end(); - } - } -} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index abc6cecff..c288e7abf 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -7,7 +7,7 @@ import { ListField } from "../../../../fields/ListField"; import { Utils } from "../../../../Utils"; import { DocumentManager } from "../../../util/DocumentManager"; import { DocumentView } from "../../nodes/DocumentView"; -import { CollectionViewProps } from "../CollectionViewBase"; +import { CollectionViewProps } from "../CollectionSubView"; import "./CollectionFreeFormLinksView.scss"; import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView"; import React = require("react"); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx index f664f4e65..17656e4ae 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx @@ -1,7 +1,7 @@ import { computed } from "mobx"; import { observer } from "mobx-react"; import { KeyStore } from "../../../../fields/KeyStore"; -import { CollectionViewProps, CursorEntry } from "../CollectionViewBase"; +import { CollectionViewProps, CursorEntry } from "../CollectionSubView"; import "./CollectionFreeFormView.scss"; import React = require("react"); import v5 = require("uuid/v5"); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 731ba3332..1c3899b24 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -12,7 +12,7 @@ import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDo import { DocumentContentsView } from "../../nodes/DocumentContentsView"; import { DocumentViewProps } from "../../nodes/DocumentView"; import { COLLECTION_BORDER_WIDTH } from "../CollectionBaseView"; -import { CollectionViewBase } from "../CollectionViewBase"; +import { CollectionSubView } from "../CollectionSubView"; import { CollectionFreeFormLinksView } from "./CollectionFreeFormLinksView"; import "./CollectionFreeFormView.scss"; import { MarqueeView } from "./MarqueeView"; @@ -26,7 +26,7 @@ import { NumberField } from "../../../../fields/NumberField"; import { Main } from "../../Main"; @observer -export class CollectionFreeFormView extends CollectionViewBase { +export class CollectionFreeFormView extends CollectionSubView { public _canvasRef = React.createRef(); private _selectOnLoaded: string = ""; // id of document that should be selected once it's loaded (used for click-to-type) -- cgit v1.2.3-70-g09d2 From 5e086920bf97297a02bcd38faea56454c2220279 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Mon, 8 Apr 2019 22:45:22 -0400 Subject: Enabled semi-colon and braces linter rule --- src/Utils.ts | 12 +-- src/client/Server.ts | 35 ++++---- src/client/SocketStub.ts | 24 +++--- src/client/documents/Documents.ts | 38 +++++---- src/client/northstar/core/BaseObject.ts | 4 +- .../northstar/core/attribute/AttributeModel.ts | 6 +- .../core/attribute/AttributeTransformationModel.ts | 17 ++-- .../core/attribute/CalculatedAttributeModel.ts | 2 +- .../northstar/core/brusher/IBaseBrushable.ts | 4 +- src/client/northstar/core/brusher/IBaseBrusher.ts | 2 +- src/client/northstar/core/filter/FilterModel.ts | 2 +- .../northstar/core/filter/IBaseFilterConsumer.ts | 4 +- .../northstar/core/filter/IBaseFilterProvider.ts | 2 +- .../northstar/core/filter/ValueComparision.ts | 16 ++-- src/client/northstar/dash-fields/HistogramField.ts | 2 +- .../dash-nodes/HistogramBinPrimitiveCollection.ts | 12 +-- src/client/northstar/dash-nodes/HistogramBox.tsx | 24 +++--- .../dash-nodes/HistogramBoxPrimitives.tsx | 40 +++++---- .../dash-nodes/HistogramLabelPrimitives.tsx | 15 ++-- src/client/northstar/manager/Gateway.ts | 2 +- src/client/northstar/model/ModelExtensions.ts | 18 ++-- src/client/northstar/model/ModelHelpers.ts | 8 +- .../model/binRanges/AlphabeticVisualBinRange.ts | 4 +- .../model/binRanges/DateTimeVisualBinRange.ts | 4 +- .../model/binRanges/NominalVisualBinRange.ts | 4 +- .../model/binRanges/QuantitativeVisualBinRange.ts | 2 +- .../northstar/model/binRanges/VisualBinRange.ts | 2 +- .../model/binRanges/VisualBinRangeHelper.ts | 14 +-- src/client/northstar/operations/BaseOperation.ts | 9 +- .../northstar/operations/HistogramOperation.ts | 2 +- src/client/northstar/utils/ArrayUtil.ts | 2 +- src/client/northstar/utils/Extensions.ts | 6 +- src/client/northstar/utils/GeometryUtil.ts | 34 ++++---- src/client/northstar/utils/MathUtil.ts | 38 +++++---- src/client/northstar/utils/SizeConverter.ts | 16 ++-- src/client/northstar/utils/StyleContants.ts | 2 +- src/client/northstar/utils/Utils.ts | 8 +- src/client/util/DocumentManager.ts | 12 +-- src/client/util/DragManager.ts | 5 +- src/client/util/RichTextSchema.tsx | 42 ++++----- src/client/util/Scripting.ts | 10 +-- src/client/util/ScrollBox.tsx | 4 +- src/client/util/SelectionManager.ts | 4 +- src/client/util/TooltipTextMenu.tsx | 38 ++++----- src/client/util/Transform.ts | 20 ++--- src/client/util/TypedEvent.ts | 2 +- src/client/util/UndoManager.ts | 24 +++--- src/client/views/ContextMenu.tsx | 14 +-- src/client/views/ContextMenuItem.tsx | 4 +- src/client/views/DocumentDecorations.tsx | 99 +++++++++++----------- src/client/views/EditableView.tsx | 10 +-- src/client/views/InkingCanvas.tsx | 15 ++-- src/client/views/InkingControl.tsx | 12 +-- src/client/views/InkingStroke.tsx | 6 +- src/client/views/Main.tsx | 55 ++++++------ .../views/collections/CollectionBaseView.tsx | 34 +++++--- .../views/collections/CollectionDockingView.tsx | 37 ++++---- src/client/views/collections/CollectionPDFView.tsx | 6 +- .../views/collections/CollectionSchemaView.tsx | 39 ++++----- src/client/views/collections/CollectionSubView.tsx | 41 ++++----- .../views/collections/CollectionTreeView.tsx | 14 +-- .../views/collections/CollectionVideoView.tsx | 6 +- src/client/views/collections/CollectionView.tsx | 20 ++--- .../CollectionFreeFormLinkView.tsx | 2 +- .../CollectionFreeFormLinksView.tsx | 18 ++-- .../CollectionFreeFormRemoteCursors.tsx | 6 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 33 ++++---- .../collections/collectionFreeForm/MarqueeView.tsx | 15 ++-- .../collectionFreeForm/PreviewCursor.tsx | 5 +- src/client/views/nodes/Annotation.tsx | 22 ++--- src/client/views/nodes/AudioBox.tsx | 12 +-- .../views/nodes/CollectionFreeFormDocumentView.tsx | 12 +-- src/client/views/nodes/DocumentContentsView.tsx | 6 +- src/client/views/nodes/DocumentView.tsx | 35 ++++---- src/client/views/nodes/FieldView.tsx | 29 ++++--- src/client/views/nodes/FormattedTextBox.tsx | 17 ++-- src/client/views/nodes/ImageBox.tsx | 12 +-- src/client/views/nodes/KeyValueBox.tsx | 18 ++-- src/client/views/nodes/KeyValuePair.tsx | 14 +-- src/client/views/nodes/LinkBox.tsx | 17 ++-- src/client/views/nodes/LinkEditor.tsx | 6 +- src/client/views/nodes/LinkMenu.tsx | 10 +-- src/client/views/nodes/PDFBox.tsx | 56 ++++++------ src/client/views/nodes/Sticky.tsx | 6 +- src/client/views/nodes/VideoBox.tsx | 10 +-- src/client/views/nodes/WebBox.tsx | 6 +- src/debug/Test.tsx | 8 +- src/debug/Viewer.tsx | 48 +++++------ src/fields/AudioField.ts | 2 +- src/fields/BasicField.ts | 6 +- src/fields/Document.ts | 25 +++--- src/fields/DocumentReference.ts | 2 +- src/fields/Field.ts | 2 +- src/fields/HtmlField.ts | 2 +- src/fields/ImageField.ts | 2 +- src/fields/InkField.ts | 2 +- src/fields/Key.ts | 6 +- src/fields/KeyStore.ts | 3 +- src/fields/ListField.ts | 31 ++++--- src/fields/NumberField.ts | 4 +- src/fields/PDFField.ts | 4 +- src/fields/RichTextField.ts | 2 +- src/fields/TextField.ts | 4 +- src/fields/TupleField.ts | 6 +- src/fields/VideoField.ts | 2 +- src/fields/WebField.ts | 2 +- src/mobile/ImageUpload.tsx | 14 +-- src/server/Client.ts | 4 +- src/server/authentication/config/passport.ts | 6 +- .../authentication/controllers/WorkspacesMenu.tsx | 2 +- .../authentication/controllers/user_controller.ts | 20 ++--- .../authentication/models/current_user_utils.ts | 8 +- src/server/authentication/models/user_model.ts | 2 +- src/server/database.ts | 34 ++++---- src/server/index.ts | 76 ++++++++--------- test/test.ts | 6 +- tslint.json | 16 ++-- 117 files changed, 901 insertions(+), 826 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/Utils.ts b/src/Utils.ts index 8bd7f2f5c..b0e66787e 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -6,16 +6,16 @@ import { Message, Types } from './server/Message'; export class Utils { public static GenerateGuid(): string { - return v4() + return v4(); } public static GenerateDeterministicGuid(seed: string): string { - return v5(seed, v5.URL) + return v5(seed, v5.URL); } public static GetScreenTransform(ele: HTMLElement): { scale: number, translateX: number, translateY: number } { if (!ele) { - return { scale: 1, translateX: 1, translateY: 1 } + return { scale: 1, translateX: 1, translateY: 1 }; } const rect = ele.getBoundingClientRect(); const scale = ele.offsetWidth === 0 && rect.width === 0 ? 1 : rect.width / ele.offsetWidth; @@ -55,8 +55,8 @@ export class Utils { return (args: any) => { this.log(prefix, messageName, args, true); func(args); - } - }; + }; + } public static Emit(socket: Socket | SocketIOClient.Socket, message: Message, args: T) { this.log("Emit", message.Name, args, false); @@ -81,7 +81,7 @@ export class Utils { public static AddServerHandlerCallback(socket: Socket, message: Message, handler: (args: [T, (res: any) => any]) => any) { socket.on(message.Message, (arg: T, fn: (res: any) => any) => { this.log('S receiving', message.Name, arg, true); - handler([arg, this.loggingCallback('S sending', fn, message.Name)]) + handler([arg, this.loggingCallback('S sending', fn, message.Name)]); }); } } diff --git a/src/client/Server.ts b/src/client/Server.ts index 45c7144ca..857101a33 100644 --- a/src/client/Server.ts +++ b/src/client/Server.ts @@ -1,7 +1,7 @@ -import { Key } from "../fields/Key" +import { Key } from "../fields/Key"; import { ObservableMap, action, reaction } from "mobx"; -import { Field, FieldWaiting, FIELD_WAITING, Opt, FieldId } from "../fields/Field" -import { Document } from "../fields/Document" +import { Field, FieldWaiting, FIELD_WAITING, Opt, FieldId } from "../fields/Field"; +import { Document } from "../fields/Document"; import { SocketStub, FieldMap } from "./SocketStub"; import * as OpenSocket from 'socket.io-client'; import { Utils } from "./../Utils"; @@ -10,7 +10,7 @@ import { MessageStore, Types } from "./../server/Message"; export class Server { public static ClientFieldsCached: ObservableMap = new ObservableMap(); static Socket: SocketIOClient.Socket = OpenSocket(`${window.location.protocol}//${window.location.hostname}:4321`); - static GUID: string = Utils.GenerateGuid() + 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). @@ -25,15 +25,16 @@ export class Server { this.ClientFieldsCached.set(fieldid, FieldWaiting); SocketStub.SEND_FIELD_REQUEST(fieldid, action((field: Field | undefined) => { let cached = this.ClientFieldsCached.get(fieldid); - if (cached !== FieldWaiting) + if (cached !== FieldWaiting) { cb(cached); + } else { if (field) { this.ClientFieldsCached.set(fieldid, field); } else { - this.ClientFieldsCached.delete(fieldid) + this.ClientFieldsCached.delete(fieldid); } - cb(field) + cb(field); } })); } else if (cached !== FieldWaiting) { @@ -42,12 +43,12 @@ export class Server { reaction(() => this.ClientFieldsCached.get(fieldid), (field, reaction) => { if (field !== FieldWaiting) { - reaction.dispose() - cb(field) + reaction.dispose(); + cb(field); } - }) + }); } - } + }; if (callback) { fn(callback); } else { @@ -79,15 +80,15 @@ export class Server { let field = fields[id]; if (field) { if (!(this.ClientFieldsCached.get(field.Id) instanceof Field)) { - this.ClientFieldsCached.set(field.Id, field) + this.ClientFieldsCached.set(field.Id, field); } else { - throw new Error("we shouldn't be trying to replace things that are already in the cache") + throw new Error("we shouldn't be trying to replace things that are already in the cache"); } } else { if (this.ClientFieldsCached.get(id) === FieldWaiting) { this.ClientFieldsCached.delete(id); } else { - throw new Error("we shouldn't be trying to replace things that are already in the cache") + throw new Error("we shouldn't be trying to replace things that are already in the cache"); } } } @@ -99,9 +100,9 @@ export class Server { let realField = field as Field; existingFields[realField.Id] = realField; } - cb({ ...fields, ...existingFields }) + cb({ ...fields, ...existingFields }); } - }, { fireImmediately: true }) + }, { fireImmediately: true }); })); }; if (callback) { @@ -139,7 +140,7 @@ export class Server { public static UpdateField(field: Field) { if (!this.ClientFieldsCached.has(field.Id)) { - this.ClientFieldsCached.set(field.Id, field) + this.ClientFieldsCached.set(field.Id, field); } SocketStub.SEND_SET_FIELD(field); } diff --git a/src/client/SocketStub.ts b/src/client/SocketStub.ts index c3cd8bee6..257973e3d 100644 --- a/src/client/SocketStub.ts +++ b/src/client/SocketStub.ts @@ -1,7 +1,7 @@ -import { Key } from "../fields/Key" -import { Field, FieldId, Opt } from "../fields/Field" +import { Key } from "../fields/Key"; +import { Field, FieldId, Opt } from "../fields/Field"; import { ObservableMap } from "mobx"; -import { Document } from "../fields/Document" +import { Document } from "../fields/Document"; import { MessageStore, DocumentTransfer } from "../server/Message"; import { Utils } from "../Utils"; import { Server } from "./Server"; @@ -37,7 +37,7 @@ export class SocketStub { // 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())) + Utils.Emit(Server.Socket, MessageStore.AddDocument, new DocumentTransfer(document.ToJson())); } public static SEND_FIELD_REQUEST(fieldid: FieldId): Promise>; @@ -50,12 +50,12 @@ export class SocketStub { } else { cb(undefined); } - }) - } + }); + }; if (callback) { fn(callback); } else { - return new Promise(fn) + return new Promise(fn); } } @@ -65,7 +65,7 @@ export class SocketStub { for (let field of fields) { fieldMap[field._id] = ServerUtils.FromJson(field); } - callback(fieldMap) + callback(fieldMap); }); } @@ -78,8 +78,9 @@ export class SocketStub { // server updates its document to hold a proxy mapping from key => fieldId var document = this.FieldStore.get(doc.Id) as Document; - if (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); @@ -93,8 +94,9 @@ export class SocketStub { // Server removes the field id from the document's list of field proxies var document = this.FieldStore.get(doc.Id) as Document; - if (document) + if (document) { document._proxies.delete(key.Id); + } } public static SEND_SET_FIELD(field: Field) { @@ -103,6 +105,6 @@ export class SocketStub { // ...SOCKET(SET_FIELD, field id, serialized field value) // Server updates the value of the field in its fieldstore - Utils.Emit(Server.Socket, MessageStore.SetField, field.ToJson()) + Utils.Emit(Server.Socket, MessageStore.SetField, field.ToJson()); } } diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 613b94abd..72e6e57ab 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -64,7 +64,7 @@ export namespace Documents { const webProtoId = "webProto"; const collProtoId = "collectionProto"; const kvpProtoId = "kvpProto"; - const videoProtoId = "videoProto" + const videoProtoId = "videoProto"; const audioProtoId = "audioProto"; export function initProtos(): Promise { @@ -102,7 +102,7 @@ export namespace Documents { if (options.height !== undefined) { doc.SetNumber(KeyStore.Height, options.height); } if (options.panx !== undefined) { doc.SetNumber(KeyStore.PanX, options.panx); } if (options.pany !== undefined) { doc.SetNumber(KeyStore.PanY, options.pany); } - return doc + return doc; } function setupPrototypeOptions(protoId: string, title: string, layout: string, options: DocumentOptions): Document { @@ -110,10 +110,12 @@ export namespace Documents { } function SetInstanceOptions(doc: Document, options: DocumentOptions, value: [T, { new(): U }] | Document, id?: string) { var deleg = doc.MakeDelegate(id); - if (value instanceof Document) - deleg.Set(KeyStore.Data, value) - else + if (value instanceof Document) { + deleg.Set(KeyStore.Data, value); + } + else { deleg.SetData(KeyStore.Data, value[0], value[1]); + } return assignOptions(deleg, options); } @@ -134,7 +136,7 @@ export namespace Documents { function CreateTextPrototype(): Document { let textProto = setupPrototypeOptions(textProtoId, "TEXT_PROTO", FormattedTextBox.LayoutString(), { x: 0, y: 0, width: 300, height: 150, layoutKeys: [KeyStore.Data] }); - return textProto + return textProto; } function CreatePdfPrototype(): Document { let pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", CollectionPDFView.LayoutString("AnnotationsKey"), @@ -168,7 +170,7 @@ export namespace Documents { } function CreateAudioPrototype(): Document { let audioProto = setupPrototypeOptions(audioProtoId, "AUDIO_PROTO", AudioBox.LayoutString(), - { x: 0, y: 0, width: 300, height: 150, layoutKeys: [KeyStore.Data] }) + { x: 0, y: 0, width: 300, height: 150, layoutKeys: [KeyStore.Data] }); return audioProto; } @@ -205,22 +207,22 @@ export namespace Documents { return assignToDelegate(SetInstanceOptions(webProto, options, [html, HtmlField]).MakeDelegate(), options); } export function KVPDocument(document: Document, options: DocumentOptions = {}, id?: string) { - return assignToDelegate(SetInstanceOptions(kvpProto, options, document, id), options) + return assignToDelegate(SetInstanceOptions(kvpProto, options, document, id), options); } export function FreeformDocument(documents: Array, options: DocumentOptions, id?: string, makePrototype: boolean = true) { if (!makePrototype) { - return SetInstanceOptions(collProto, { ...options, viewType: CollectionViewType.Freeform }, [documents, ListField], id) + return SetInstanceOptions(collProto, { ...options, viewType: CollectionViewType.Freeform }, [documents, ListField], id); } - return assignToDelegate(SetInstanceOptions(collProto, { ...options, viewType: CollectionViewType.Freeform }, [documents, ListField], id).MakeDelegate(), options) + return assignToDelegate(SetInstanceOptions(collProto, { ...options, viewType: CollectionViewType.Freeform }, [documents, ListField], id).MakeDelegate(), options); } export function SchemaDocument(documents: Array, options: DocumentOptions, id?: string) { - return assignToDelegate(SetInstanceOptions(collProto, { ...options, viewType: CollectionViewType.Schema }, [documents, ListField], id), options) + return assignToDelegate(SetInstanceOptions(collProto, { ...options, viewType: CollectionViewType.Schema }, [documents, ListField], id), options); } export function TreeDocument(documents: Array, options: DocumentOptions, id?: string) { - return assignToDelegate(SetInstanceOptions(collProto, { ...options, viewType: CollectionViewType.Tree }, [documents, ListField], id), options) + return assignToDelegate(SetInstanceOptions(collProto, { ...options, viewType: CollectionViewType.Tree }, [documents, ListField], id), options); } export function DockDocument(config: string, options: DocumentOptions, id?: string) { - return assignToDelegate(SetInstanceOptions(collProto, { ...options, viewType: CollectionViewType.Docking }, [config, TextField], id), options) + return assignToDelegate(SetInstanceOptions(collProto, { ...options, viewType: CollectionViewType.Docking }, [config, TextField], id), options); } export function CaptionDocument(doc: Document) { @@ -240,13 +242,13 @@ export namespace Documents {
      ` + FormattedTextBox.LayoutString("CaptionKey") + `
      -
      ` }; +
      `; } export function FixedCaption(fieldName: string = "Caption") { return `
      ` + FormattedTextBox.LayoutString(fieldName + "Key") + `
      -
      ` }; +
    `; } function OuterCaption() { return (` @@ -258,7 +260,7 @@ export namespace Documents {
    - `) + `); } function InnerCaption() { return (` @@ -270,7 +272,7 @@ export namespace Documents {
    - `) + `); } /* @@ -293,6 +295,6 @@ export namespace Documents {
    - `) + `); } } \ No newline at end of file diff --git a/src/client/northstar/core/BaseObject.ts b/src/client/northstar/core/BaseObject.ts index f1761e643..ed3818071 100644 --- a/src/client/northstar/core/BaseObject.ts +++ b/src/client/northstar/core/BaseObject.ts @@ -1,5 +1,5 @@ -import { IEquatable } from '../utils/IEquatable' -import { IDisposable } from '../utils/IDisposable' +import { IEquatable } from '../utils/IEquatable'; +import { IDisposable } from '../utils/IDisposable'; export class BaseObject implements IEquatable, IDisposable { diff --git a/src/client/northstar/core/attribute/AttributeModel.ts b/src/client/northstar/core/attribute/AttributeModel.ts index 230bfecc8..c89b1617c 100644 --- a/src/client/northstar/core/attribute/AttributeModel.ts +++ b/src/client/northstar/core/attribute/AttributeModel.ts @@ -1,5 +1,5 @@ -import { Attribute, DataType, VisualizationHint } from '../../model/idea/idea' -import { BaseObject } from '../BaseObject' +import { Attribute, DataType, VisualizationHint } from '../../model/idea/idea'; +import { BaseObject } from '../BaseObject'; import { observable } from "mobx"; export abstract class AttributeModel extends BaseObject { @@ -93,7 +93,7 @@ export class BackendAttributeModel extends AttributeModel { } public get DisplayName(): string { - return this._displayName.ReplaceAll("_", " ");; + return this._displayName.ReplaceAll("_", " "); } public get CodeName(): string { diff --git a/src/client/northstar/core/attribute/AttributeTransformationModel.ts b/src/client/northstar/core/attribute/AttributeTransformationModel.ts index f50b78d51..66485183b 100644 --- a/src/client/northstar/core/attribute/AttributeTransformationModel.ts +++ b/src/client/northstar/core/attribute/AttributeTransformationModel.ts @@ -1,4 +1,4 @@ -; + import { computed, observable } from "mobx"; import { AggregateFunction } from "../../model/idea/idea"; import { AttributeModel } from "./AttributeModel"; @@ -20,16 +20,21 @@ export class AttributeTransformationModel implements IEquatable { if (this.AggregateFunction === AggregateFunction.Count) { return "count"; } - if (this.AggregateFunction === AggregateFunction.Avg) + if (this.AggregateFunction === AggregateFunction.Avg) { displayName = "avg(" + displayName + ")"; - else if (this.AggregateFunction === AggregateFunction.Max) + } + else if (this.AggregateFunction === AggregateFunction.Max) { displayName = "max(" + displayName + ")"; - else if (this.AggregateFunction === AggregateFunction.Min) + } + else if (this.AggregateFunction === AggregateFunction.Min) { displayName = "min(" + displayName + ")"; - else if (this.AggregateFunction === AggregateFunction.Sum) + } + else if (this.AggregateFunction === AggregateFunction.Sum) { displayName = "sum(" + displayName + ")"; - else if (this.AggregateFunction === AggregateFunction.SumE) + } + else if (this.AggregateFunction === AggregateFunction.SumE) { displayName = "sumE(" + displayName + ")"; + } return displayName; } diff --git a/src/client/northstar/core/attribute/CalculatedAttributeModel.ts b/src/client/northstar/core/attribute/CalculatedAttributeModel.ts index 0b8e0d12c..a197c1305 100644 --- a/src/client/northstar/core/attribute/CalculatedAttributeModel.ts +++ b/src/client/northstar/core/attribute/CalculatedAttributeModel.ts @@ -1,5 +1,5 @@ import { BackendAttributeModel, AttributeModel, CodeAttributeModel } from "./AttributeModel"; -import { DataType, VisualizationHint } from '../../model/idea/idea' +import { DataType, VisualizationHint } from '../../model/idea/idea'; export class CalculatedAttributeManager { public static AllCalculatedAttributes: Array = new Array(); diff --git a/src/client/northstar/core/brusher/IBaseBrushable.ts b/src/client/northstar/core/brusher/IBaseBrushable.ts index 99a36636f..c46db4d22 100644 --- a/src/client/northstar/core/brusher/IBaseBrushable.ts +++ b/src/client/northstar/core/brusher/IBaseBrushable.ts @@ -1,6 +1,6 @@ -import { PIXIPoint } from '../../utils/MathUtil' +import { PIXIPoint } from '../../utils/MathUtil'; import { IEquatable } from '../../utils/IEquatable'; -import { Document } from '../../../../fields/Document' +import { Document } from '../../../../fields/Document'; export interface IBaseBrushable extends IEquatable { BrusherModels: Array; diff --git a/src/client/northstar/core/brusher/IBaseBrusher.ts b/src/client/northstar/core/brusher/IBaseBrusher.ts index d7ae65464..d2de6ed62 100644 --- a/src/client/northstar/core/brusher/IBaseBrusher.ts +++ b/src/client/northstar/core/brusher/IBaseBrusher.ts @@ -1,4 +1,4 @@ -import { PIXIPoint } from '../../utils/MathUtil' +import { PIXIPoint } from '../../utils/MathUtil'; import { IEquatable } from '../../utils/IEquatable'; diff --git a/src/client/northstar/core/filter/FilterModel.ts b/src/client/northstar/core/filter/FilterModel.ts index 20fca77f5..e2ba3f652 100644 --- a/src/client/northstar/core/filter/FilterModel.ts +++ b/src/client/northstar/core/filter/FilterModel.ts @@ -15,7 +15,7 @@ export class FilterModel { public Equals(other: FilterModel): boolean { if (!Utils.EqualityHelper(this, other)) return false; - if (!this.isSame(this.ValueComparisons, (other as FilterModel).ValueComparisons)) return false; + if (!this.isSame(this.ValueComparisons, (other).ValueComparisons)) return false; return true; } diff --git a/src/client/northstar/core/filter/IBaseFilterConsumer.ts b/src/client/northstar/core/filter/IBaseFilterConsumer.ts index 93f66a154..59d7adf4c 100644 --- a/src/client/northstar/core/filter/IBaseFilterConsumer.ts +++ b/src/client/northstar/core/filter/IBaseFilterConsumer.ts @@ -1,5 +1,5 @@ -import { FilterOperand } from '../filter/FilterOperand' -import { IEquatable } from '../../utils/IEquatable' +import { FilterOperand } from '../filter/FilterOperand'; +import { IEquatable } from '../../utils/IEquatable'; import { Document } from "../../../../fields/Document"; export interface IBaseFilterConsumer extends IEquatable { diff --git a/src/client/northstar/core/filter/IBaseFilterProvider.ts b/src/client/northstar/core/filter/IBaseFilterProvider.ts index d082bfe12..fc3301b11 100644 --- a/src/client/northstar/core/filter/IBaseFilterProvider.ts +++ b/src/client/northstar/core/filter/IBaseFilterProvider.ts @@ -1,4 +1,4 @@ -import { FilterModel } from '../filter/FilterModel' +import { FilterModel } from '../filter/FilterModel'; export interface IBaseFilterProvider { FilterModels: Array; diff --git a/src/client/northstar/core/filter/ValueComparision.ts b/src/client/northstar/core/filter/ValueComparision.ts index 1a3e461f5..80b1242a9 100644 --- a/src/client/northstar/core/filter/ValueComparision.ts +++ b/src/client/northstar/core/filter/ValueComparision.ts @@ -1,5 +1,5 @@ -import { Predicate } from '../../model/idea/idea' -import { Utils } from '../../utils/Utils' +import { Predicate } from '../../model/idea/idea'; +import { Utils } from '../../utils/Utils'; import { AttributeModel } from '../attribute/AttributeModel'; export class ValueComparison { @@ -15,15 +15,19 @@ export class ValueComparison { } public Equals(other: Object): boolean { - if (!Utils.EqualityHelper(this, other)) + if (!Utils.EqualityHelper(this, other)) { return false; - if (this.Predicate !== (other as ValueComparison).Predicate) + } + if (this.Predicate !== (other as ValueComparison).Predicate) { return false; + } let isComplex = (typeof this.Value === "object"); - if (!isComplex && this.Value !== (other as ValueComparison).Value) + if (!isComplex && this.Value !== (other as ValueComparison).Value) { return false; - if (isComplex && !this.Value.Equals((other as ValueComparison).Value)) + } + if (isComplex && !this.Value.Equals((other as ValueComparison).Value)) { return false; + } return true; } diff --git a/src/client/northstar/dash-fields/HistogramField.ts b/src/client/northstar/dash-fields/HistogramField.ts index fa2a9c008..6abde4677 100644 --- a/src/client/northstar/dash-fields/HistogramField.ts +++ b/src/client/northstar/dash-fields/HistogramField.ts @@ -41,7 +41,7 @@ export class HistogramField extends BasicField { data: this.toString(), _id: this.Id - } + }; } @action diff --git a/src/client/northstar/dash-nodes/HistogramBinPrimitiveCollection.ts b/src/client/northstar/dash-nodes/HistogramBinPrimitiveCollection.ts index cdae90c8b..6291ec1fc 100644 --- a/src/client/northstar/dash-nodes/HistogramBinPrimitiveCollection.ts +++ b/src/client/northstar/dash-nodes/HistogramBinPrimitiveCollection.ts @@ -1,4 +1,4 @@ -import React = require("react") +import React = require("react"); import { AttributeTransformationModel } from "../../northstar/core/attribute/AttributeTransformationModel"; import { ChartType } from '../../northstar/model/binRanges/VisualBinRange'; import { AggregateFunction, Bin, Brush, DoubleValueAggregateResult, HistogramResult, MarginAggregateParameters, MarginAggregateResult } from "../../northstar/model/idea/idea"; @@ -29,7 +29,7 @@ export class HistogramBinPrimitiveCollection { private _histoBox: HistogramBox; private get histoOp() { return this._histoBox.HistoOp; } private get histoResult() { return this.histoOp.Result as HistogramResult; } - private get sizeConverter() { return this._histoBox.SizeConverter!; } + private get sizeConverter() { return this._histoBox.SizeConverter; } public BinPrimitives: Array = new Array(); public HitGeom: PIXIRectangle = PIXIRectangle.EMPTY; @@ -99,13 +99,14 @@ export class HistogramBinPrimitiveCollection { private createHeatmapBinPrimitives(bin: Bin, brush: Brush, brushFactorSum: number): number { let unNormalizedValue = this.getBinValue(2, bin, brush.brushIndex!); - if (unNormalizedValue === undefined) + if (unNormalizedValue === undefined) { return brushFactorSum; + } var normalizedValue = (unNormalizedValue - this._histoBox.ValueRange[0]) / (Math.abs((this._histoBox.ValueRange[1] - this._histoBox.ValueRange[0])) < HistogramBinPrimitiveCollection.TOLERANCE ? unNormalizedValue : this._histoBox.ValueRange[1] - this._histoBox.ValueRange[0]); - let allUnNormalizedValue = this.getBinValue(2, bin, ModelHelpers.AllBrushIndex(this.histoResult)) + let allUnNormalizedValue = this.getBinValue(2, bin, ModelHelpers.AllBrushIndex(this.histoResult)); // bcz: are these calls needed? let [xFrom, xTo] = this.sizeConverter.DataToScreenXAxisRange(this._histoBox.VisualBinRanges, 0, bin); @@ -145,8 +146,9 @@ export class HistogramBinPrimitiveCollection { let [xFrom, xTo] = this.sizeConverter.DataToScreenPointRange(0, bin, ModelHelpers.CreateAggregateKey(this.histoOp.Schema!.distinctAttributeParameters, this.histoOp.X, this.histoResult, brush.brushIndex!)); let [yFrom, yTo] = this.sizeConverter.DataToScreenPointRange(1, bin, ModelHelpers.CreateAggregateKey(this.histoOp.Schema!.distinctAttributeParameters, this.histoOp.Y, this.histoResult, brush.brushIndex!)); - if (xFrom !== undefined && yFrom !== undefined && xTo !== undefined && yTo !== undefined) + if (xFrom !== undefined && yFrom !== undefined && xTo !== undefined && yTo !== undefined) { this.createBinPrimitive(-1, brush, PIXIRectangle.EMPTY, 0, xFrom, xTo, yFrom, yTo, this.baseColorFromBrush(brush), 1, unNormalizedValue); + } } return 0; } diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx index ec9413649..7df59ef07 100644 --- a/src/client/northstar/dash-nodes/HistogramBox.tsx +++ b/src/client/northstar/dash-nodes/HistogramBox.tsx @@ -1,4 +1,4 @@ -import React = require("react") +import React = require("react"); import { action, computed, observable, reaction, runInAction, trace } from "mobx"; import { observer } from "mobx-react"; import Measure from "react-measure"; @@ -25,7 +25,7 @@ import { StyleConstants } from "../utils/StyleContants"; @observer export class HistogramBox extends React.Component { - public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(HistogramBox, fieldStr) } + public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(HistogramBox, fieldStr); } private _dropXRef = React.createRef(); private _dropYRef = React.createRef(); private _dropXDisposer?: DragManager.DragDropDisposer; @@ -92,15 +92,15 @@ export class HistogramBox extends React.Component { } reaction(() => CurrentUserUtils.NorthstarDBCatalog, (catalog?: Catalog) => this.activateHistogramOperation(catalog), { fireImmediately: true }); reaction(() => [this.VisualBinRanges && this.VisualBinRanges.slice()], () => this.SizeConverter.SetVisualBinRanges(this.VisualBinRanges)); - reaction(() => [this.PanelHeight, this.PanelWidth], () => this.SizeConverter.SetIsSmall(this.PanelWidth < 40 && this.PanelHeight < 40)) + reaction(() => [this.PanelHeight, this.PanelWidth], () => this.SizeConverter.SetIsSmall(this.PanelWidth < 40 && this.PanelHeight < 40)); reaction(() => this.HistogramResult ? this.HistogramResult.binRanges : undefined, (binRanges: BinRange[] | undefined) => { if (binRanges) { this.VisualBinRanges.splice(0, this.VisualBinRanges.length, ...binRanges.map((br, ind) => - VisualBinRangeHelper.GetVisualBinRange(this.HistoOp.Schema!.distinctAttributeParameters, br, this.HistogramResult!, ind ? this.HistoOp.Y : this.HistoOp.X, this.ChartType))); + VisualBinRangeHelper.GetVisualBinRange(this.HistoOp.Schema!.distinctAttributeParameters, br, this.HistogramResult, ind ? this.HistoOp.Y : this.HistoOp.X, this.ChartType))); - let valueAggregateKey = ModelHelpers.CreateAggregateKey(this.HistoOp.Schema!.distinctAttributeParameters, this.HistoOp.V, this.HistogramResult!, ModelHelpers.AllBrushIndex(this.HistogramResult!)); - this.ValueRange = Object.values(this.HistogramResult!.bins!).reduce((prev, cur) => { + let valueAggregateKey = ModelHelpers.CreateAggregateKey(this.HistoOp.Schema!.distinctAttributeParameters, this.HistoOp.V, this.HistogramResult, ModelHelpers.AllBrushIndex(this.HistogramResult)); + this.ValueRange = Object.values(this.HistogramResult.bins!).reduce((prev, cur) => { let value = ModelHelpers.GetAggregateResult(cur, valueAggregateKey) as DoubleValueAggregateResult; return value && value.hasResult ? [Math.min(prev[0], value.result!), Math.max(prev[1], value.result!)] : prev; }, [Number.MAX_VALUE, Number.MIN_VALUE]); @@ -109,10 +109,12 @@ export class HistogramBox extends React.Component { } componentWillUnmount() { - if (this._dropXDisposer) + if (this._dropXDisposer) { this._dropXDisposer(); - if (this._dropYDisposer) + } + if (this._dropYDisposer) { this._dropYDisposer(); + } } activateHistogramOperation(catalog?: Catalog) { @@ -128,7 +130,7 @@ export class HistogramBox extends React.Component { this.HistoOp.BrushLinks.splice(0, this.HistoOp.BrushLinks.length, ...brushingDocs.map((brush, i) => { brush.SetNumber(KeyStore.BackgroundColor, StyleConstants.BRUSH_COLORS[i % StyleConstants.BRUSH_COLORS.length]); let brushed = brush.GetList(KeyStore.BrushingDocs, [] as Document[]); - return { l: brush, b: brushed[0].Id === proto.Id ? brushed[1] : brushed[0] } + return { l: brush, b: brushed[0].Id === proto.Id ? brushed[1] : brushed[0] }; })); }, { fireImmediately: true }); reaction(() => this.createOperationParamsCache, () => this.HistoOp.Update(), { fireImmediately: true }); @@ -146,7 +148,7 @@ export class HistogramBox extends React.Component { let roff = this.SizeConverter.RightOffset; let boff = this.SizeConverter.BottomOffset; return ( - runInAction(() => { this.PanelWidth = r.entry.width; this.PanelHeight = r.entry.height })}> + runInAction(() => { this.PanelWidth = r.entry.width; this.PanelHeight = r.entry.height; })}> {({ measureRef }) =>
    @@ -168,7 +170,7 @@ export class HistogramBox extends React.Component {
    } - ) + ); } } diff --git a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx index a78703247..4c5bdb14b 100644 --- a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx +++ b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx @@ -1,4 +1,4 @@ -import React = require("react") +import React = require("react"); import { computed, observable, reaction, runInAction, trace, action } from "mobx"; import { observer } from "mobx-react"; import { Utils as DashUtils } from '../../../Utils'; @@ -28,17 +28,18 @@ export class HistogramBoxPrimitives extends React.Component this.drawRect(bp.Rect, bp.BarAxis, undefined, "border")); } @computed get barPrimitives() { let histoResult = this.props.HistoBox.HistogramResult; - if (!histoResult || !histoResult.bins || !this.props.HistoBox.VisualBinRanges.length) + if (!histoResult || !histoResult.bins || !this.props.HistoBox.VisualBinRanges.length) { return (null); + } let allBrushIndex = ModelHelpers.AllBrushIndex(histoResult); return Object.keys(histoResult.bins).reduce((prims: JSX.Element[], key: string) => { - let drawPrims = new HistogramBinPrimitiveCollection(histoResult!.bins![key], this.props.HistoBox); + let drawPrims = new HistogramBinPrimitiveCollection(histoResult.bins![key], this.props.HistoBox); let toggle = this.getSelectionToggle(drawPrims.BinPrimitives, allBrushIndex, - ModelHelpers.GetBinFilterModel(histoResult!.bins![key], allBrushIndex, histoResult!, this.histoOp.X, this.histoOp.Y)); + ModelHelpers.GetBinFilterModel(histoResult.bins![key], allBrushIndex, histoResult, this.histoOp.X, this.histoOp.Y)); drawPrims.BinPrimitives.filter(bp => bp.DataValue && bp.BrushIndex !== allBrushIndex).map(bp => prims.push(...[{ r: bp.Rect, c: bp.Color }, { r: bp.MarginRect, c: StyleConstants.MARGIN_BARS_COLOR }].map(pair => this.drawRect(pair.r, bp.BarAxis, pair.c, "bar", toggle)))); return prims; - }, [] as JSX.Element[]) + }, [] as JSX.Element[]); } componentDidMount() { @@ -47,8 +48,9 @@ export class HistogramBoxPrimitives extends React.Component bp.BrushIndex === allBrushIndex); - if (!rawAllBrushPrim) - return () => { } + if (!rawAllBrushPrim) { + return () => { }; + } let allBrushPrim = rawAllBrushPrim; return () => runInAction(() => { if (ArrayUtil.Contains(this.histoOp.FilterModels, filterModel)) { @@ -59,23 +61,25 @@ export class HistogramBoxPrimitives extends React.Component {labels.reduce((prims, binLabel, i) => { let r = this.props.HistoBox.SizeConverter.DataToScreenRange(binLabel.minValue!, binLabel.maxValue!, axis); prims.push(this.drawLine(r.xFrom, r.yFrom, axis === 0 ? 0 : r.xTo - r.xFrom, axis === 0 ? r.yTo - r.yFrom : 0)); - if (i === labels.length - 1) + if (i === labels.length - 1) { prims.push(this.drawLine(axis === 0 ? r.xTo : r.xFrom, axis === 0 ? r.yFrom : r.yTo, axis === 0 ? 0 : r.xTo - r.xFrom, axis === 0 ? r.yTo - r.yFrom : 0)); + } return prims; }, [] as JSX.Element[])} - + ; } drawLine(xFrom: number, yFrom: number, width: number, height: number) { @@ -89,9 +93,9 @@ export class HistogramBoxPrimitives extends React.Component + let trans1Xpercent = `${xFrom / this.renderDimension * 100}%`; + let trans1Ypercent = `${yFrom / this.renderDimension * 100}%`; + return ; } drawRect(r: PIXIRectangle, barAxis: number, color: number | undefined, classExt: string, tapHandler: () => void = () => { }) { if (r.height < 0) { @@ -102,11 +106,11 @@ export class HistogramBoxPrimitives extends React.Component { if (e.button === 0) tapHandler() }} + return ( { if (e.button === 0) tapHandler(); }} x={transXpercent} width={`${widthXpercent}`} y={transYpercent} height={`${heightYpercent}`} fill={color ? `${LABColor.RGBtoHexString(color)}` : "transparent"} />); } render() { @@ -118,6 +122,6 @@ export class HistogramBoxPrimitives extends React.Component -
    +
    ; } } \ No newline at end of file diff --git a/src/client/northstar/dash-nodes/HistogramLabelPrimitives.tsx b/src/client/northstar/dash-nodes/HistogramLabelPrimitives.tsx index dd62e9146..5785fe838 100644 --- a/src/client/northstar/dash-nodes/HistogramLabelPrimitives.tsx +++ b/src/client/northstar/dash-nodes/HistogramLabelPrimitives.tsx @@ -1,4 +1,4 @@ -import React = require("react") +import React = require("react"); import { action, computed, reaction } from "mobx"; import { observer } from "mobx-react"; import { Utils as DashUtils } from '../../../Utils'; @@ -13,7 +13,7 @@ import { HistogramPrimitivesProps } from "./HistogramBoxPrimitives"; export class HistogramLabelPrimitives extends React.Component { componentDidMount() { reaction(() => [this.props.HistoBox.PanelWidth, this.props.HistoBox.SizeConverter.LeftOffset, this.props.HistoBox.VisualBinRanges.length], - (fields) => HistogramLabelPrimitives.computeLabelAngle(fields[0] as number, fields[1] as number, this.props.HistoBox), { fireImmediately: true }); + (fields) => HistogramLabelPrimitives.computeLabelAngle(fields[0], fields[1], this.props.HistoBox), { fireImmediately: true }); } @action @@ -32,8 +32,9 @@ export class HistogramLabelPrimitives extends React.ComponentFontStyles.AxisLabel.fontSize + 5))); sc.MaxLabelSizes[axis].coords[axis] + 5); @@ -52,8 +53,8 @@ export class HistogramLabelPrimitives extends React.Component @@ -61,7 +62,7 @@ export class HistogramLabelPrimitives extends React.Component
    - ) + ); } return prims; }, [] as JSX.Element[]); @@ -73,7 +74,7 @@ export class HistogramLabelPrimitives extends React.Component {xaxislines} {yaxislines} -
    +
    ; } } \ No newline at end of file diff --git a/src/client/northstar/manager/Gateway.ts b/src/client/northstar/manager/Gateway.ts index ba6fe2ad5..1c8d3fd73 100644 --- a/src/client/northstar/manager/Gateway.ts +++ b/src/client/northstar/manager/Gateway.ts @@ -1,4 +1,4 @@ -import { Catalog, OperationReference, Result, CompileResults } from "../model/idea/idea" +import { Catalog, OperationReference, Result, CompileResults } from "../model/idea/idea"; import { computed, observable, action } from "mobx"; export class Gateway { diff --git a/src/client/northstar/model/ModelExtensions.ts b/src/client/northstar/model/ModelExtensions.ts index e4bf77ed8..29f80d2d1 100644 --- a/src/client/northstar/model/ModelExtensions.ts +++ b/src/client/northstar/model/ModelExtensions.ts @@ -1,7 +1,7 @@ -import { AttributeParameters, Brush, MarginAggregateParameters, SingleDimensionAggregateParameters, Solution } from '../model/idea/idea' -import { Utils } from '../utils/Utils' +import { AttributeParameters, Brush, MarginAggregateParameters, SingleDimensionAggregateParameters, Solution } from '../model/idea/idea'; +import { Utils } from '../utils/Utils'; -import { FilterModel } from '../core/filter/FilterModel' +import { FilterModel } from '../core/filter/FilterModel'; (SingleDimensionAggregateParameters as any).prototype.Equals = function (other: Object) { if (!Utils.EqualityHelper(this, other)) return false; @@ -9,13 +9,13 @@ import { FilterModel } from '../core/filter/FilterModel' (other as SingleDimensionAggregateParameters).attributeParameters!)) return false; if (!((this as SingleDimensionAggregateParameters).attributeParameters! as any).Equals((other as SingleDimensionAggregateParameters).attributeParameters)) return false; return true; -} +}; { (AttributeParameters as any).prototype.Equals = function (other: AttributeParameters) { - return (this).constructor.name === (other).constructor.name && + return (this).constructor.name === (other).constructor.name && this.rawName === other.rawName; - } + }; } { @@ -23,7 +23,7 @@ import { FilterModel } from '../core/filter/FilterModel' if (!Utils.EqualityHelper(this, other)) return false; if ((this as Solution).solutionId !== (other as Solution).solutionId) return false; return true; - } + }; } { @@ -35,7 +35,7 @@ import { FilterModel } from '../core/filter/FilterModel' if ((this as MarginAggregateParameters).aggregateFunction !== (other as MarginAggregateParameters).aggregateFunction) return false; return true; - } + }; } { @@ -44,5 +44,5 @@ import { FilterModel } from '../core/filter/FilterModel' if ((this as Brush).brushEnum !== (other as Brush).brushEnum) return false; if ((this as Brush).brushIndex !== (other as Brush).brushIndex) return false; return true; - } + }; } \ No newline at end of file diff --git a/src/client/northstar/model/ModelHelpers.ts b/src/client/northstar/model/ModelHelpers.ts index 1a58e6180..ac807b41f 100644 --- a/src/client/northstar/model/ModelHelpers.ts +++ b/src/client/northstar/model/ModelHelpers.ts @@ -64,7 +64,7 @@ export class ModelHelpers { if (aggParams) { aggregateParameters.push(aggParams); - var margin = new MarginAggregateParameters() + var margin = new MarginAggregateParameters(); margin.aggregateFunction = agg.AggregateFunction; margin.attributeParameters = ModelHelpers.GetAttributeParameters(agg.AttributeModel); margin.distinctAttributeParameters = distinctAttributeParameters; @@ -106,7 +106,7 @@ export class ModelHelpers { { rawName: am.CodeName, visualizationHints: am.VisualizationHints, - id: (am as BackendAttributeModel).Id + id: (am).Id }); } else if (am instanceof CodeAttributeModel) { @@ -114,11 +114,11 @@ export class ModelHelpers { { rawName: am.CodeName, visualizationHints: am.VisualizationHints, - code: (am as CodeAttributeModel).Code + code: (am).Code }); } else { - throw new Exception() + throw new Exception(); } } diff --git a/src/client/northstar/model/binRanges/AlphabeticVisualBinRange.ts b/src/client/northstar/model/binRanges/AlphabeticVisualBinRange.ts index 995bf4e0b..120b034f2 100644 --- a/src/client/northstar/model/binRanges/AlphabeticVisualBinRange.ts +++ b/src/client/northstar/model/binRanges/AlphabeticVisualBinRange.ts @@ -1,5 +1,5 @@ -import { AlphabeticBinRange, BinLabel } from '../../model/idea/idea' -import { VisualBinRange } from './VisualBinRange' +import { AlphabeticBinRange, BinLabel } from '../../model/idea/idea'; +import { VisualBinRange } from './VisualBinRange'; export class AlphabeticVisualBinRange extends VisualBinRange { public DataBinRange: AlphabeticBinRange; diff --git a/src/client/northstar/model/binRanges/DateTimeVisualBinRange.ts b/src/client/northstar/model/binRanges/DateTimeVisualBinRange.ts index f5872aa4c..776e643cd 100644 --- a/src/client/northstar/model/binRanges/DateTimeVisualBinRange.ts +++ b/src/client/northstar/model/binRanges/DateTimeVisualBinRange.ts @@ -1,5 +1,5 @@ -import { DateTimeBinRange, DateTimeStep, DateTimeStepGranularity } from '../idea/idea' -import { VisualBinRange } from './VisualBinRange' +import { DateTimeBinRange, DateTimeStep, DateTimeStepGranularity } from '../idea/idea'; +import { VisualBinRange } from './VisualBinRange'; export class DateTimeVisualBinRange extends VisualBinRange { public DataBinRange: DateTimeBinRange; diff --git a/src/client/northstar/model/binRanges/NominalVisualBinRange.ts b/src/client/northstar/model/binRanges/NominalVisualBinRange.ts index 407ff3ea6..42509d797 100644 --- a/src/client/northstar/model/binRanges/NominalVisualBinRange.ts +++ b/src/client/northstar/model/binRanges/NominalVisualBinRange.ts @@ -1,5 +1,5 @@ -import { NominalBinRange, BinLabel } from '../../model/idea/idea' -import { VisualBinRange } from './VisualBinRange' +import { NominalBinRange, BinLabel } from '../../model/idea/idea'; +import { VisualBinRange } from './VisualBinRange'; export class NominalVisualBinRange extends VisualBinRange { public DataBinRange: NominalBinRange; diff --git a/src/client/northstar/model/binRanges/QuantitativeVisualBinRange.ts b/src/client/northstar/model/binRanges/QuantitativeVisualBinRange.ts index 80886416b..c579c8e5f 100644 --- a/src/client/northstar/model/binRanges/QuantitativeVisualBinRange.ts +++ b/src/client/northstar/model/binRanges/QuantitativeVisualBinRange.ts @@ -1,4 +1,4 @@ -import { QuantitativeBinRange } from '../idea/idea' +import { QuantitativeBinRange } from '../idea/idea'; import { VisualBinRange } from './VisualBinRange'; import { format } from "d3-format"; diff --git a/src/client/northstar/model/binRanges/VisualBinRange.ts b/src/client/northstar/model/binRanges/VisualBinRange.ts index a0766e494..449a22e91 100644 --- a/src/client/northstar/model/binRanges/VisualBinRange.ts +++ b/src/client/northstar/model/binRanges/VisualBinRange.ts @@ -1,4 +1,4 @@ -import { BinLabel } from '../../model/idea/idea' +import { BinLabel } from '../../model/idea/idea'; export abstract class VisualBinRange { diff --git a/src/client/northstar/model/binRanges/VisualBinRangeHelper.ts b/src/client/northstar/model/binRanges/VisualBinRangeHelper.ts index 63ee61909..9671e55f8 100644 --- a/src/client/northstar/model/binRanges/VisualBinRangeHelper.ts +++ b/src/client/northstar/model/binRanges/VisualBinRangeHelper.ts @@ -16,18 +16,18 @@ export class VisualBinRangeHelper { public static GetNonAggregateVisualBinRange(dataBinRange: BinRange): VisualBinRange { if (dataBinRange instanceof NominalBinRange) { - return new NominalVisualBinRange(dataBinRange as NominalBinRange); + return new NominalVisualBinRange(dataBinRange); } else if (dataBinRange instanceof QuantitativeBinRange) { - return new QuantitativeVisualBinRange(dataBinRange as QuantitativeBinRange); + return new QuantitativeVisualBinRange(dataBinRange); } else if (dataBinRange instanceof AlphabeticBinRange) { - return new AlphabeticVisualBinRange(dataBinRange as AlphabeticBinRange); + return new AlphabeticVisualBinRange(dataBinRange); } else if (dataBinRange instanceof DateTimeBinRange) { - return new DateTimeVisualBinRange(dataBinRange as DateTimeBinRange); + return new DateTimeVisualBinRange(dataBinRange); } - throw new Exception() + throw new Exception(); } public static GetVisualBinRange(distinctAttributeParameters: AttributeParameters | undefined, dataBinRange: BinRange, histoResult: HistogramResult, attr: AttributeTransformationModel, chartType: ChartType): VisualBinRange { @@ -51,13 +51,13 @@ export class VisualBinRangeHelper { } } } - }; + } let visualBinRange = QuantitativeVisualBinRange.Initialize(minValue, maxValue, 10, false); if (chartType === ChartType.HorizontalBar || chartType === ChartType.VerticalBar) { visualBinRange = QuantitativeVisualBinRange.Initialize(Math.min(0, minValue), - Math.max(0, (visualBinRange as QuantitativeVisualBinRange).DataBinRange.maxValue!), + Math.max(0, (visualBinRange).DataBinRange.maxValue!), SETTINGS_X_BINS, false); } else if (chartType === ChartType.SinglePoint) { diff --git a/src/client/northstar/operations/BaseOperation.ts b/src/client/northstar/operations/BaseOperation.ts index a14337763..c6d5f0a15 100644 --- a/src/client/northstar/operations/BaseOperation.ts +++ b/src/client/northstar/operations/BaseOperation.ts @@ -1,4 +1,4 @@ -import { FilterModel } from '../core/filter/FilterModel' +import { FilterModel } from '../core/filter/FilterModel'; import { ErrorResult, Exception, OperationParameters, OperationReference, Result, ResultParameters } from '../model/idea/idea'; import { action, computed, observable } from "mobx"; import { Gateway } from '../manager/Gateway'; @@ -62,8 +62,9 @@ export abstract class BaseOperation { } let operationParameters = this.CreateOperationParameters(); - if (this.Result) - this.Result.progress = 0; // bcz: used to set Result to undefined, but that causes the display to blink + if (this.Result) { + this.Result.progress = 0; + } // bcz: used to set Result to undefined, but that causes the display to blink this.Error = ""; let salt = Math.random().toString(); this.RequestSalt = salt; @@ -97,7 +98,7 @@ export abstract class BaseOperation { pollPromise.Start(async () => { let result = await Gateway.Instance.GetResult(resultParameters.toJSON()); if (result instanceof ErrorResult) { - throw new Error((result as ErrorResult).message); + throw new Error((result).message); } if (this.RequestSalt === pollPromise.RequestSalt) { if (result && (!this.Result || this.Result.progress !== result.progress)) { diff --git a/src/client/northstar/operations/HistogramOperation.ts b/src/client/northstar/operations/HistogramOperation.ts index a3ddc1c98..760106023 100644 --- a/src/client/northstar/operations/HistogramOperation.ts +++ b/src/client/northstar/operations/HistogramOperation.ts @@ -62,7 +62,7 @@ export class HistogramOperation extends BaseOperation implements IBaseFilterCons @computed public get FilterString(): string { let filterModels: FilterModel[] = []; - return FilterModel.GetFilterModelsRecursive(this, new Set(), filterModels, true) + return FilterModel.GetFilterModelsRecursive(this, new Set(), filterModels, true); } @computed diff --git a/src/client/northstar/utils/ArrayUtil.ts b/src/client/northstar/utils/ArrayUtil.ts index a52ca6d96..12b8d8e77 100644 --- a/src/client/northstar/utils/ArrayUtil.ts +++ b/src/client/northstar/utils/ArrayUtil.ts @@ -50,7 +50,7 @@ export class ArrayUtil { if (filtered.length > 0) { return filtered[0]; } - throw new Exception() + throw new Exception(); } public static FirstOrDefault(arr: T[], predicate: (x: any) => boolean): T | undefined { diff --git a/src/client/northstar/utils/Extensions.ts b/src/client/northstar/utils/Extensions.ts index 7c2b7fc9d..df14d4da0 100644 --- a/src/client/northstar/utils/Extensions.ts +++ b/src/client/northstar/utils/Extensions.ts @@ -6,7 +6,7 @@ interface String { String.prototype.ReplaceAll = function (toReplace: string, replacement: string): string { var target = this; return target.split(toReplace).join(replacement); -} +}; String.prototype.Truncate = function (length: number, replacement: string): String { var target = this; @@ -14,7 +14,7 @@ String.prototype.Truncate = function (length: number, replacement: string): Stri target = target.slice(0, Math.max(0, length - replacement.length)) + replacement; } return target; -} +}; interface Math { log10(val: number): number; @@ -22,7 +22,7 @@ interface Math { Math.log10 = function (val: number): number { return Math.log(val) / Math.LN10; -} +}; declare interface ObjectConstructor { assign(...objects: Object[]): Object; diff --git a/src/client/northstar/utils/GeometryUtil.ts b/src/client/northstar/utils/GeometryUtil.ts index 6d8acea20..d5220c479 100644 --- a/src/client/northstar/utils/GeometryUtil.ts +++ b/src/client/northstar/utils/GeometryUtil.ts @@ -8,15 +8,19 @@ export class GeometryUtil { let minY: number = Number.MAX_VALUE; let maxX: number = Number.MIN_VALUE; let maxY: number = Number.MIN_VALUE; - for (var i = 0; i < points.length; i++) { - if (points[i].x < minX) - minX = points[i].x; - if (points[i].y < minY) - minY = points[i].y; - if (points[i].x > maxX) - maxX = points[i].x; - if (points[i].y > maxY) - maxY = points[i].y; + for (const point of points) { + if (point.x < minX) { + minX = point.x; + } + if (point.y < minY) { + minY = point.y; + } + if (point.x > maxX) { + maxX = point.x; + } + if (point.y > maxY) { + maxY = point.y; + } } return new PIXIRectangle(minX * scale - padding, minY * scale - padding, (maxX - minX) * scale + padding * 2, (maxY - minY) * scale + padding * 2); } @@ -35,7 +39,7 @@ export class GeometryUtil { nx = (cos * (x - cx)) + (sin * (y - cy)) + cx, ny = (cos * (y - cy)) - (sin * (x - cx)) + cy; return new PIXIPoint(nx, ny); - } + }; return points.map(p => rotate(center.x, center.y, p.x, p.y, angle)); } @@ -54,9 +58,9 @@ export class GeometryUtil { return []; } - for (let v = 0; v < points.length; v++) { - x = points[v].x; - y = points[v].y; + for (const point of points) { + x = point.x; + y = point.y; sum_x += x; sum_y += y; sum_xx += x * x; @@ -68,8 +72,8 @@ export class GeometryUtil { let b = (sum_y / count) - (m * sum_x) / count; let result: PIXIPoint[] = new Array(); - for (let v = 0; v < points.length; v++) { - x = points[v].x; + for (const point of points) { + x = point.x; y = x * m + b; result.push(new PIXIPoint(x, y)); } diff --git a/src/client/northstar/utils/MathUtil.ts b/src/client/northstar/utils/MathUtil.ts index 7aa255096..4b44f40c3 100644 --- a/src/client/northstar/utils/MathUtil.ts +++ b/src/client/northstar/utils/MathUtil.ts @@ -16,11 +16,11 @@ export class PIXIRectangle { public x: number; public y: number; public width: number; - public height: number - public get left() { return this.x } + public height: number; + public get left() { return this.x; } public get right() { return this.x + this.width; } - public get top() { return this.y } - public get bottom() { return this.top + this.height } + public get top() { return this.y; } + public get bottom() { return this.top + this.height; } public static get EMPTY() { return new PIXIRectangle(0, 0, -1, -1); } constructor(x: number, y: number, width: number, height: number) { this.x = x; @@ -127,14 +127,18 @@ export class MathUtil { } public static PointInPIXIRectangle(p: PIXIPoint, rect: PIXIRectangle): boolean { - if (p.x < rect.left - this.EPSILON) + if (p.x < rect.left - this.EPSILON) { return false; - if (p.x > rect.right + this.EPSILON) + } + if (p.x > rect.right + this.EPSILON) { return false; - if (p.y < rect.top - this.EPSILON) + } + if (p.y < rect.top - this.EPSILON) { return false; - if (p.y > rect.bottom + this.EPSILON) + } + if (p.y > rect.bottom + this.EPSILON) { return false; + } return true; } @@ -145,23 +149,27 @@ export class MathUtil { var r3 = new PIXIPoint(rect.right, rect.bottom); var r4 = new PIXIPoint(rect.left, rect.bottom); var ret = new Array(); - var dist = this.Dist(lineFrom, lineTo) + var dist = this.Dist(lineFrom, lineTo); var inter = this.LineSegmentIntersection(lineFrom, lineTo, r1, r2); if (inter && this.PointInPIXIRectangle(inter, rect) && - this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist) + this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist) { ret.push(inter); + } inter = this.LineSegmentIntersection(lineFrom, lineTo, r2, r3); if (inter && this.PointInPIXIRectangle(inter, rect) && - this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist) + this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist) { ret.push(inter); + } inter = this.LineSegmentIntersection(lineFrom, lineTo, r3, r4); if (inter && this.PointInPIXIRectangle(inter, rect) && - this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist) + this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist) { ret.push(inter); + } inter = this.LineSegmentIntersection(lineFrom, lineTo, r4, r1); if (inter && this.PointInPIXIRectangle(inter, rect) && - this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist) + this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist) { ret.push(inter); + } return ret; } @@ -178,7 +186,7 @@ export class MathUtil { } public static Dot(p1: PIXIPoint, p2: PIXIPoint): number { - return p1.x * p2.x + p1.y * p2.y + return p1.x * p2.x + p1.y * p2.y; } public static Normalize(p1: PIXIPoint) { @@ -193,7 +201,7 @@ export class MathUtil { public static DistSquared(p1: PIXIPoint, p2: PIXIPoint): number { const a = p1.x - p2.x; const b = p1.y - p2.y; - return (a * a + b * b) + return (a * a + b * b); } public static RectIntersectsRect(r1: PIXIRectangle, r2: PIXIRectangle): boolean { diff --git a/src/client/northstar/utils/SizeConverter.ts b/src/client/northstar/utils/SizeConverter.ts index b5c4a16ab..a52890ed9 100644 --- a/src/client/northstar/utils/SizeConverter.ts +++ b/src/client/northstar/utils/SizeConverter.ts @@ -32,8 +32,8 @@ export class SizeConverter { this.Initialized++; var xLabels = visualBinRanges[0].GetLabels(); var yLabels = visualBinRanges[1].GetLabels(); - var xLabelStrings = xLabels.map(l => l.label!).sort(function (a, b) { return b.length - a.length }); - var yLabelStrings = yLabels.map(l => l.label!).sort(function (a, b) { return b.length - a.length }); + var xLabelStrings = xLabels.map(l => l.label!).sort(function (a, b) { return b.length - a.length; }); + var yLabelStrings = yLabels.map(l => l.label!).sort(function (a, b) { return b.length - a.length; }); var metricsX = { width: 75 }; // RenderUtils.MeasureText(FontStyles.Default.fontFamily.toString(), 12, // FontStyles.AxisLabel.fontSize as number, //xLabelStrings[0]!.slice(0, 20)) // StyleConstants.MAX_CHAR_FOR_HISTOGRAM_LABELS)); @@ -62,19 +62,20 @@ export class SizeConverter { public DataToScreenPointRange(axis: number, bin: Bin, aggregateKey: AggregateKey) { var value = ModelHelpers.GetAggregateResult(bin, aggregateKey) as DoubleValueAggregateResult; - if (value && value.hasResult) + if (value && value.hasResult) { return [this.DataToScreenCoord(value.result!, axis) - 5, this.DataToScreenCoord(value.result!, axis) + 5]; + } return [undefined, undefined]; } public DataToScreenXAxisRange(visualBinRanges: VisualBinRange[], index: number, bin: Bin) { var value = visualBinRanges[0].GetValueFromIndex(bin.binIndex!.indices![index]); - return [this.DataToScreenX(value), this.DataToScreenX(visualBinRanges[index].AddStep(value))] + return [this.DataToScreenX(value), this.DataToScreenX(visualBinRanges[index].AddStep(value))]; } public DataToScreenYAxisRange(visualBinRanges: VisualBinRange[], index: number, bin: Bin) { var value = visualBinRanges[1].GetValueFromIndex(bin.binIndex!.indices![index]); - return [this.DataToScreenY(value), this.DataToScreenY(visualBinRanges[index].AddStep(value))] + return [this.DataToScreenY(value), this.DataToScreenY(visualBinRanges[index].AddStep(value))]; } public DataToScreenX(x: number): number { @@ -85,8 +86,9 @@ export class SizeConverter { return flip ? (this.RenderDimension) - retY : retY; } public DataToScreenCoord(v: number, axis: number) { - if (axis === 0) + if (axis === 0) { return this.DataToScreenX(v); + } return this.DataToScreenY(v); } public DataToScreenRange(minVal: number, maxVal: number, axis: number) { @@ -94,6 +96,6 @@ export class SizeConverter { let xTo = this.DataToScreenX(axis === 0 ? maxVal : this.DataMaxs[0]); let yFrom = this.DataToScreenY(axis === 1 ? minVal : this.DataMins[1]); let yTo = this.DataToScreenY(axis === 1 ? maxVal : this.DataMaxs[1]); - return { xFrom, yFrom, xTo, yTo } + return { xFrom, yFrom, xTo, yTo }; } } \ No newline at end of file diff --git a/src/client/northstar/utils/StyleContants.ts b/src/client/northstar/utils/StyleContants.ts index ac8617e3b..e9b6e0297 100644 --- a/src/client/northstar/utils/StyleContants.ts +++ b/src/client/northstar/utils/StyleContants.ts @@ -11,7 +11,7 @@ export class StyleConstants { static OPERATOR_MENU_LARGE: number = 35; static OPERATOR_MENU_SMALL: number = 25; - static BRUSH_PALETTE: number[] = [0x42b43c, 0xfa217f, 0x6a9c75, 0xfb5de7, 0x25b8ea, 0x9b5bc4, 0xda9f63, 0xe23209, 0xfb899b, 0x94a6fd] + static BRUSH_PALETTE: number[] = [0x42b43c, 0xfa217f, 0x6a9c75, 0xfb5de7, 0x25b8ea, 0x9b5bc4, 0xda9f63, 0xe23209, 0xfb899b, 0x94a6fd]; static GAP: number = 3; static BACKGROUND_COLOR: number = 0xF3F3F3; diff --git a/src/client/northstar/utils/Utils.ts b/src/client/northstar/utils/Utils.ts index c96b4cbd9..d071dec62 100644 --- a/src/client/northstar/utils/Utils.ts +++ b/src/client/northstar/utils/Utils.ts @@ -1,7 +1,7 @@ -import { IBaseBrushable } from '../core/brusher/IBaseBrushable' -import { IBaseFilterConsumer } from '../core/filter/IBaseFilterConsumer' -import { IBaseFilterProvider } from '../core/filter/IBaseFilterProvider' -import { AggregateFunction } from '../model/idea/idea' +import { IBaseBrushable } from '../core/brusher/IBaseBrushable'; +import { IBaseFilterConsumer } from '../core/filter/IBaseFilterConsumer'; +import { IBaseFilterProvider } from '../core/filter/IBaseFilterProvider'; +import { AggregateFunction } from '../model/idea/idea'; export class Utils { diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index fb489edb6..f38b8ca75 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,7 +1,7 @@ -import React = require('react') +import React = require('react'); import { observer } from 'mobx-react'; import { observable, action, computed } from 'mobx'; -import { Document } from "../../fields/Document" +import { Document } from "../../fields/Document"; import { DocumentView } from '../views/nodes/DocumentView'; import { KeyStore } from '../../fields/KeyStore'; import { FieldWaiting } from '../../fields/Field'; @@ -50,7 +50,7 @@ export class DocumentManager { if (docSrc && docSrc !== FieldWaiting && Object.is(docSrc, toFind)) { toReturn = view; } - }) + }); return (toReturn); } @@ -71,7 +71,7 @@ export class DocumentManager { toReturn.push(view); } } - }) + }); return (toReturn); } @@ -86,8 +86,8 @@ export class DocumentManager { 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 }) - }) + 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 f95b2c29d..4849ae9f7 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -103,7 +103,7 @@ export namespace DragManager { element.addEventListener("dashOnDrop", handler); return () => { element.removeEventListener("dashOnDrop", handler); - delete element.dataset.canDrop + delete element.dataset.canDrop; }; } @@ -216,8 +216,9 @@ export namespace DragManager { const moveHandler = (e: PointerEvent) => { e.stopPropagation(); e.preventDefault(); - if (dragData instanceof DocumentDragData) + if (dragData instanceof DocumentDragData) { dragData.aliasOnDrop = e.ctrlKey || e.altKey; + } if (e.shiftKey) { abortDrag(); CollectionDockingView.Instance.StartOtherDrag(docs, { diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 2a3c1da6e..92944bec0 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -1,13 +1,13 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Schema, NodeSpec, MarkSpec, DOMOutputSpecArray, NodeType } from "prosemirror-model" -import { joinUp, lift, setBlockType, toggleMark, wrapIn } from 'prosemirror-commands' -import { redo, undo } from 'prosemirror-history' -import { orderedList, bulletList, listItem, } from 'prosemirror-schema-list' +import { Schema, NodeSpec, MarkSpec, DOMOutputSpecArray, NodeType } from "prosemirror-model"; +import { joinUp, lift, setBlockType, toggleMark, wrapIn } from 'prosemirror-commands'; +import { redo, undo } from 'prosemirror-history'; +import { orderedList, bulletList, listItem, } from 'prosemirror-schema-list'; import { EditorState, Transaction, NodeSelection, } from "prosemirror-state"; import { EditorView, } from "prosemirror-view"; const pDOM: DOMOutputSpecArray = ["p", 0], blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"], - preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0] + preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0]; // :: Object @@ -24,7 +24,7 @@ export const nodes: { [index: string]: NodeSpec } = { content: "inline*", group: "block", parseDOM: [{ tag: "p" }], - toDOM() { return pDOM } + toDOM() { return pDOM; } }, // :: NodeSpec A blockquote (`
    `) wrapping one or more blocks. @@ -33,14 +33,14 @@ export const nodes: { [index: string]: NodeSpec } = { group: "block", defining: true, parseDOM: [{ tag: "blockquote" }], - toDOM() { return blockquoteDOM } + toDOM() { return blockquoteDOM; } }, // :: NodeSpec A horizontal rule (`
    `). horizontal_rule: { group: "block", parseDOM: [{ tag: "hr" }], - toDOM() { return hrDOM } + toDOM() { return hrDOM; } }, // :: NodeSpec A heading textblock, with a `level` attribute that @@ -57,7 +57,7 @@ export const nodes: { [index: string]: NodeSpec } = { { tag: "h4", attrs: { level: 4 } }, { tag: "h5", attrs: { level: 5 } }, { tag: "h6", attrs: { level: 6 } }], - toDOM(node: any) { return ["h" + node.attrs.level, 0] } + toDOM(node: any) { return ["h" + node.attrs.level, 0]; } }, // :: NodeSpec A code listing. Disallows marks or non-text inline @@ -70,7 +70,7 @@ export const nodes: { [index: string]: NodeSpec } = { code: true, defining: true, parseDOM: [{ tag: "pre", preserveWhitespace: "full" }], - toDOM() { return preDOM } + toDOM() { return preDOM; } }, // :: NodeSpec The text node. @@ -96,10 +96,10 @@ export const nodes: { [index: string]: NodeSpec } = { src: dom.getAttribute("src"), title: dom.getAttribute("title"), alt: dom.getAttribute("alt") - } + }; } }], - toDOM(node: any) { return ["img", node.attrs] } + toDOM(node: any) { return ["img", node.attrs]; } }, // :: NodeSpec A hard line break, represented in the DOM as `
    `. @@ -108,7 +108,7 @@ export const nodes: { [index: string]: NodeSpec } = { group: "inline", selectable: false, parseDOM: [{ tag: "br" }], - toDOM() { return brDOM } + toDOM() { return brDOM; } }, ordered_list: { @@ -136,7 +136,7 @@ export const nodes: { [index: string]: NodeSpec } = { ...listItem, content: 'paragraph block*' } -} +}; const emDOM: DOMOutputSpecArray = ["em", 0]; const strongDOM: DOMOutputSpecArray = ["strong", 0]; @@ -156,17 +156,17 @@ export const marks: { [index: string]: MarkSpec } = { inclusive: false, parseDOM: [{ tag: "a[href]", getAttrs(dom: any) { - return { href: dom.getAttribute("href"), title: dom.getAttribute("title") } + return { href: dom.getAttribute("href"), title: dom.getAttribute("title") }; } }], - toDOM(node: any) { return ["a", node.attrs, 0] } + toDOM(node: any) { return ["a", node.attrs, 0]; } }, // :: MarkSpec An emphasis mark. Rendered as an `` element. // Has parse rules that also match `` and `font-style: italic`. em: { parseDOM: [{ tag: "i" }, { tag: "em" }, { style: "font-style=italic" }], - toDOM() { return emDOM } + toDOM() { return emDOM; } }, // :: MarkSpec A strong mark. Rendered as ``, parse rules @@ -175,7 +175,7 @@ export const marks: { [index: string]: MarkSpec } = { parseDOM: [{ tag: "strong" }, { tag: "b" }, { style: "font-weight" }], - toDOM() { return strongDOM } + toDOM() { return strongDOM; } }, underline: { @@ -221,9 +221,9 @@ export const marks: { [index: string]: MarkSpec } = { // :: MarkSpec Code font mark. Represented as a `` element. code: { parseDOM: [{ tag: "code" }], - toDOM() { return codeDOM } + toDOM() { return codeDOM; } } -} +}; // :: Schema // This schema rougly corresponds to the document schema used by @@ -233,4 +233,4 @@ export const marks: { [index: string]: MarkSpec } = { // // To reuse elements from this schema, extend or read from its // `spec.nodes` and `spec.marks` [properties](#model.Schema.spec). -export const schema = new Schema({ nodes, marks }) \ No newline at end of file +export const schema = new Schema({ nodes, marks }); \ No newline at end of file diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index bf9b8266f..468484928 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -14,7 +14,7 @@ import { ListField } from "../../fields/ListField"; // import * as typescriptes5 from '!!raw-loader!../../../node_modules/typescript/lib/lib.es5.d.ts' // @ts-ignore -import * as typescriptlib from '!!raw-loader!./type_decls.d' +import * as typescriptlib from '!!raw-loader!./type_decls.d'; import { Documents } from "../documents/Documents"; import { Key } from "../../fields/Key"; @@ -50,7 +50,7 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an let fieldTypes = [Document, NumberField, TextField, ImageField, RichTextField, ListField, Key]; let paramNames = ["KeyStore", "Documents", ...fieldTypes.map(fn => fn.name)]; - let params: any[] = [KeyStore, Documents, ...fieldTypes] + let params: any[] = [KeyStore, Documents, ...fieldTypes]; let compiledFunction = new Function(...paramNames, `return ${script}`); let run = (args: { [name: string]: any } = {}): ScriptResult => { let argsArray: any[] = []; @@ -67,7 +67,7 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an } catch (error) { return { success: false, error }; } - } + }; return { compiled: true, run }; } @@ -90,14 +90,14 @@ class ScriptingCompilerHost { } // getDefaultLibFileName(options: ts.CompilerOptions): string { getDefaultLibFileName(options: any): string { - return 'node_modules/typescript/lib/lib.d.ts' // No idea what this means... + return 'node_modules/typescript/lib/lib.d.ts'; // No idea what this means... } writeFile(fileName: string, content: string) { const file = this.files.find(file => file.fileName === fileName); if (file) { file.content = content; } else { - this.files.push({ fileName, content }) + this.files.push({ fileName, content }); } } getCurrentDirectory(): string { diff --git a/src/client/util/ScrollBox.tsx b/src/client/util/ScrollBox.tsx index b6b088170..a209874a3 100644 --- a/src/client/util/ScrollBox.tsx +++ b/src/client/util/ScrollBox.tsx @@ -1,4 +1,4 @@ -import React = require("react") +import React = require("react"); export class ScrollBox extends React.Component { onWheel = (e: React.WheelEvent) => { @@ -16,6 +16,6 @@ export class ScrollBox extends React.Component { }} onWheel={this.onWheel}> {this.props.children}
    - ) + ); } } \ No newline at end of file diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 1bb15c86a..5ddaafc72 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -23,7 +23,7 @@ export namespace SelectionManager { @action DeselectAll(): void { - manager.SelectedDocuments.map(dv => dv.props.onActiveChanged(false)) + manager.SelectedDocuments.map(dv => dv.props.onActiveChanged(false)); manager.SelectedDocuments = []; } } @@ -46,7 +46,7 @@ export namespace SelectionManager { } } - manager.DeselectAll() + manager.DeselectAll(); if (found) manager.SelectDoc(found, false); Main.Instance.SetTextDoc(undefined, undefined); } diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 913472aa0..bd5753093 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -5,12 +5,12 @@ import { keymap } from "prosemirror-keymap"; import { EditorState, Transaction, NodeSelection } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import { schema } from "./RichTextSchema"; -import { Schema, NodeType } from "prosemirror-model" -import React = require("react") +import { Schema, NodeType } from "prosemirror-model"; +import React = require("react"); import "./TooltipTextMenu.scss"; const { toggleMark, setBlockType, wrapIn } = require("prosemirror-commands"); -import { library } from '@fortawesome/fontawesome-svg-core' -import { wrapInList, bulletList } from 'prosemirror-schema-list' +import { library } from '@fortawesome/fontawesome-svg-core'; +import { wrapInList, bulletList } from 'prosemirror-schema-list'; import { faListUl } from '@fortawesome/free-solid-svg-icons'; @@ -39,7 +39,7 @@ export class TooltipTextMenu { { command: toggleMark(schema.marks.subscript), dom: this.icon("s", "subscript") }, //this doesn't work currently - look into notion of active block { command: wrapInList(schema.nodes.bullet_list), dom: this.icon(":", "bullets") }, - ] + ]; items.forEach(({ dom }) => this.tooltip.appendChild(dom)); //pointer down handler to activate button effects @@ -52,8 +52,8 @@ export class TooltipTextMenu { //uncomment this if we want the bullet button to disappear if current selection is bulleted // dom.style.display = active ? "" : "none" } - }) - }) + }); + }); this.update(view, undefined); } @@ -99,32 +99,32 @@ export class TooltipTextMenu { return { command: setBlockType(schema.nodes.heading, { level }), dom: this.icon("H" + level, "heading") - } + }; } //updates the tooltip menu when the selection changes update(view: EditorView, lastState: EditorState | undefined) { - let state = view.state + let state = view.state; // Don't do anything if the document/selection didn't change if (lastState && lastState.doc.eq(state.doc) && - lastState.selection.eq(state.selection)) return + lastState.selection.eq(state.selection)) return; // Hide the tooltip if the selection is empty if (state.selection.empty) { - this.tooltip.style.display = "none" - return + this.tooltip.style.display = "none"; + return; } // Otherwise, reposition it and update its content - this.tooltip.style.display = "" - let { from, to } = state.selection - let start = view.coordsAtPos(from), end = view.coordsAtPos(to) + this.tooltip.style.display = ""; + let { from, to } = state.selection; + let start = view.coordsAtPos(from), end = view.coordsAtPos(to); // The box in which the tooltip is positioned, to use as base - let box = this.tooltip.offsetParent!.getBoundingClientRect() + let box = this.tooltip.offsetParent!.getBoundingClientRect(); // Find a center-ish x position from the selection endpoints (when // crossing lines, end may be more to the left) - let left = Math.max((start.left + end.left) / 2, start.left + 3) - this.tooltip.style.left = (left - box.left) + "px" + let left = Math.max((start.left + end.left) / 2, start.left + 3); + this.tooltip.style.left = (left - box.left) + "px"; let width = Math.abs(start.left - end.left) / 2; let mid = Math.min(start.left, end.left) + width; @@ -133,5 +133,5 @@ export class TooltipTextMenu { this.tooltip.style.bottom = (box.bottom - start.top) + "px"; } - destroy() { this.tooltip.remove() } + destroy() { this.tooltip.remove(); } } \ No newline at end of file diff --git a/src/client/util/Transform.ts b/src/client/util/Transform.ts index ed4282874..e9170ec36 100644 --- a/src/client/util/Transform.ts +++ b/src/client/util/Transform.ts @@ -62,19 +62,19 @@ export class Transform { return this; } - translated = (x: number, y: number): Transform => this.copy().translate(x, y) + translated = (x: number, y: number): Transform => this.copy().translate(x, y); - preTranslated = (x: number, y: number): Transform => this.copy().preTranslate(x, y) + preTranslated = (x: number, y: number): Transform => this.copy().preTranslate(x, y); - scaled = (scale: number): Transform => this.copy().scale(scale) + scaled = (scale: number): Transform => this.copy().scale(scale); - scaledAbout = (scale: number, x: number, y: number): Transform => this.copy().scaleAbout(scale, x, y) + scaledAbout = (scale: number, x: number, y: number): Transform => this.copy().scaleAbout(scale, x, y); - preScaled = (scale: number): Transform => this.copy().preScale(scale) + preScaled = (scale: number): Transform => this.copy().preScale(scale); - transformed = (transform: Transform): Transform => this.copy().transform(transform) + transformed = (transform: Transform): Transform => this.copy().transform(transform); - preTransformed = (transform: Transform): Transform => this.copy().preTransform(transform) + preTransformed = (transform: Transform): Transform => this.copy().preTransform(transform); transformPoint = (x: number, y: number): [number, number] => { x *= this._scale; @@ -84,7 +84,7 @@ export class Transform { return [x, y]; } - transformDirection = (x: number, y: number): [number, number] => [x * this._scale, y * this._scale] + transformDirection = (x: number, y: number): [number, number] => [x * this._scale, y * this._scale]; transformBounds(x: number, y: number, width: number, height: number): { x: number, y: number, width: number, height: number } { [x, y] = this.transformPoint(x, y); @@ -92,8 +92,8 @@ export class Transform { return { x, y, width, height }; } - inverse = () => new Transform(-this._translateX / this._scale, -this._translateY / this._scale, 1 / this._scale) + inverse = () => new Transform(-this._translateX / this._scale, -this._translateY / this._scale, 1 / this._scale); - copy = () => new Transform(this._translateX, this._translateY, this._scale) + copy = () => new Transform(this._translateX, this._translateY, this._scale); } \ No newline at end of file diff --git a/src/client/util/TypedEvent.ts b/src/client/util/TypedEvent.ts index 1b251da25..532ba78eb 100644 --- a/src/client/util/TypedEvent.ts +++ b/src/client/util/TypedEvent.ts @@ -36,5 +36,5 @@ export class TypedEvent { this.listenersOncer = []; } - pipe = (te: TypedEvent): Disposable => this.on((e) => te.emit(e)) + pipe = (te: TypedEvent): Disposable => this.on((e) => te.emit(e)); } \ No newline at end of file diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts index b4ea4acae..27aed4bac 100644 --- a/src/client/util/UndoManager.ts +++ b/src/client/util/UndoManager.ts @@ -1,5 +1,5 @@ import { observable, action } from "mobx"; -import 'source-map-support/register' +import 'source-map-support/register'; import { Without } from "../../Utils"; import { string } from "prop-types"; @@ -31,9 +31,9 @@ function propertyDecorator(target: any, key: string | symbol) { batch.end(); } } - }) + }); } - }) + }); } export function undoBatch(target: any, key: string | symbol, descriptor?: TypedPropertyDescriptor): any; @@ -43,11 +43,11 @@ export function undoBatch(target: any, key?: string | symbol, descriptor?: Typed return function () { let batch = UndoManager.StartBatch(""); try { - return target.apply(undefined, arguments) + return target.apply(undefined, arguments); } finally { batch.end(); } - } + }; } if (!descriptor) { propertyDecorator(target, key); @@ -58,11 +58,11 @@ export function undoBatch(target: any, key?: string | symbol, descriptor?: Typed descriptor.value = function (...args: any[]) { let batch = UndoManager.StartBatch(getBatchName(target, key)); try { - return oldFunction.apply(this, args) + return oldFunction.apply(this, args); } finally { batch.end(); } - } + }; return descriptor; } @@ -117,8 +117,8 @@ export namespace UndoManager { EndBatch(cancel); } - end = () => { this.dispose(false); } - cancel = () => { this.dispose(true); } + end = () => { this.dispose(false); }; + cancel = () => { this.dispose(true); }; } export function StartBatch(batchName: string): Batch { @@ -138,7 +138,7 @@ export namespace UndoManager { redoStack.length = 0; currentBatch = undefined; } - }) + }); export function RunInBatch(fn: () => void, batchName: string) { let batch = StartBatch(batchName); @@ -166,7 +166,7 @@ export namespace UndoManager { undoing = false; redoStack.push(commands); - }) + }); export const Redo = action(() => { if (redoStack.length === 0) { @@ -185,6 +185,6 @@ export namespace UndoManager { undoing = false; undoStack.push(commands); - }) + }); } \ No newline at end of file diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index cfa8ea7b7..615a928ad 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -2,11 +2,11 @@ import React = require("react"); import { ContextMenuItem, ContextMenuProps } from "./ContextMenuItem"; import { observable, action } from "mobx"; import { observer } from "mobx-react"; -import "./ContextMenu.scss" +import "./ContextMenu.scss"; @observer export class ContextMenu extends React.Component { - static Instance: ContextMenu + static Instance: ContextMenu; @observable private _items: Array = [{ description: "test", event: (e: React.MouseEvent) => e.preventDefault() }]; @observable private _pageX: number = 0; @@ -22,15 +22,15 @@ export class ContextMenu extends React.Component { constructor(props: Readonly<{}>) { super(props); - this.ref = React.createRef() + this.ref = React.createRef(); ContextMenu.Instance = this; } @action clearItems() { - this._items = [] - this._display = "none" + this._items = []; + this._display = "none"; } @action @@ -56,7 +56,7 @@ export class ContextMenu extends React.Component { this._searchString = ""; - this._display = "flex" + this._display = "flex"; } intersects = (x: number, y: number): boolean => { @@ -86,7 +86,7 @@ export class ContextMenu extends React.Component { {this._items.filter(prop => prop.description.toLowerCase().indexOf(this._searchString.toLowerCase()) !== -1). map(prop => )} - ) + ); } @action diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx index 4801c1555..70813f0dd 100644 --- a/src/client/views/ContextMenuItem.tsx +++ b/src/client/views/ContextMenuItem.tsx @@ -11,7 +11,7 @@ export interface SubmenuProps { } export interface ContextMenuItemProps { - type: ContextMenuProps | SubmenuProps + type: ContextMenuProps | SubmenuProps; } export class ContextMenuItem extends React.Component { @@ -20,6 +20,6 @@ export class ContextMenuItem extends React.Component {
    {this.props.description}
    - ) + ); } } \ No newline at end of file diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 285d145a2..b29fb6a2b 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -21,8 +21,8 @@ export const Flyout = higflyout.default; @observer export class DocumentDecorations extends React.Component<{}, { value: string }> { - static Instance: DocumentDecorations - private _resizer = "" + static Instance: DocumentDecorations; + private _resizer = ""; private _isPointerDown = false; private keyinput: React.RefObject; private _documents: DocumentView[] = SelectionManager.SelectedDocuments(); @@ -40,8 +40,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> constructor(props: Readonly<{}>) { - super(props) - DocumentDecorations.Instance = this + super(props); + DocumentDecorations.Instance = this; this.handleChange = this.handleChange.bind(this); this.keyinput = React.createRef(); } @@ -49,7 +49,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> @action handleChange = (event: any) => { this._title = event.target.value; - }; + } @action enterPressed = (e: any) => { @@ -59,14 +59,14 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> var text = e.target.value; if (text[0] === '#') { let command = text.slice(1, text.length); - this._fieldKey = new Key(command) + this._fieldKey = new Key(command); // if (command === "Title" || command === "title") { // this._fieldKey = KeyStore.Title; // } // else if (command === "Width" || command === "width") { // this._fieldKey = KeyStore.Width; // } - this._title = "changed" + this._title = "changed"; // TODO: Change field with switch statement } else { @@ -89,7 +89,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> 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 }); } @@ -104,7 +104,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> document.addEventListener("pointermove", this.onBackgroundMove); document.removeEventListener("pointerup", this.onBackgroundUp); document.addEventListener("pointerup", this.onBackgroundUp); - this._lastDrag = [e.clientX, e.clientY] + this._lastDrag = [e.clientX, e.clientY]; e.stopPropagation(); e.preventDefault(); } @@ -127,7 +127,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> dragComplete: action(() => this._dragging = false), }, hideSource: true - }) + }); e.stopPropagation(); } @@ -199,53 +199,53 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> onLinkerButtonDown = (e: React.PointerEvent): void => { e.stopPropagation(); - document.removeEventListener("pointermove", this.onLinkerButtonMoved) + document.removeEventListener("pointermove", this.onLinkerButtonMoved); document.addEventListener("pointermove", this.onLinkerButtonMoved); - document.removeEventListener("pointerup", this.onLinkerButtonUp) + document.removeEventListener("pointerup", this.onLinkerButtonUp); document.addEventListener("pointerup", this.onLinkerButtonUp); } onLinkerButtonUp = (e: PointerEvent): void => { - document.removeEventListener("pointermove", this.onLinkerButtonMoved) - document.removeEventListener("pointerup", this.onLinkerButtonUp) + document.removeEventListener("pointermove", this.onLinkerButtonMoved); + document.removeEventListener("pointerup", this.onLinkerButtonUp); e.stopPropagation(); } onLinkerButtonMoved = (e: PointerEvent): void => { if (this._linkerButton.current !== null) { - document.removeEventListener("pointermove", this.onLinkerButtonMoved) - document.removeEventListener("pointerup", this.onLinkerButtonUp) + document.removeEventListener("pointermove", this.onLinkerButtonMoved); + document.removeEventListener("pointerup", this.onLinkerButtonUp); let dragData = new DragManager.LinkDragData(SelectionManager.SelectedDocuments()[0]); DragManager.StartLinkDrag(this._linkerButton.current, dragData, e.pageX, e.pageY, { handlers: { dragComplete: action(() => { }), }, hideSource: false - }) + }); } e.stopPropagation(); } onLinkButtonDown = (e: React.PointerEvent): void => { e.stopPropagation(); - document.removeEventListener("pointermove", this.onLinkButtonMoved) + document.removeEventListener("pointermove", this.onLinkButtonMoved); document.addEventListener("pointermove", this.onLinkButtonMoved); - document.removeEventListener("pointerup", this.onLinkButtonUp) + document.removeEventListener("pointerup", this.onLinkButtonUp); document.addEventListener("pointerup", this.onLinkButtonUp); } onLinkButtonUp = (e: PointerEvent): void => { - document.removeEventListener("pointermove", this.onLinkButtonMoved) - document.removeEventListener("pointerup", this.onLinkButtonUp) + document.removeEventListener("pointermove", this.onLinkButtonMoved); + document.removeEventListener("pointerup", this.onLinkButtonUp); e.stopPropagation(); } onLinkButtonMoved = async (e: PointerEvent) => { if (this._linkButton.current !== null) { - document.removeEventListener("pointermove", this.onLinkButtonMoved) + document.removeEventListener("pointermove", this.onLinkButtonMoved); document.removeEventListener("pointerup", this.onLinkButtonUp); let sourceDoc = SelectionManager.SelectedDocuments()[0].props.Document; - let srcTarg = sourceDoc.GetT(KeyStore.Prototype, Document) + let srcTarg = sourceDoc.GetT(KeyStore.Prototype, Document); let draggedDocs = (srcTarg && srcTarg !== FieldWaiting) ? srcTarg.GetList(KeyStore.LinkedToDocs, [] as Document[]).map(linkDoc => (linkDoc.GetT(KeyStore.LinkedToDocs, Document)) as Document) : []; @@ -265,7 +265,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> dragComplete: action(() => { }), }, hideSource: false - }) + }); } } e.stopPropagation(); @@ -285,38 +285,38 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> case "": break; case "documentDecorations-topLeftResizer": - dX = -1 - dY = -1 - dW = -(e.movementX) - dH = -(e.movementY) + dX = -1; + dY = -1; + dW = -(e.movementX); + dH = -(e.movementY); break; case "documentDecorations-topRightResizer": - dW = e.movementX - dY = -1 - dH = -(e.movementY) + dW = e.movementX; + dY = -1; + dH = -(e.movementY); break; case "documentDecorations-topResizer": - dY = -1 - dH = -(e.movementY) + dY = -1; + dH = -(e.movementY); break; case "documentDecorations-bottomLeftResizer": - dX = -1 - dW = -(e.movementX) - dH = e.movementY + dX = -1; + dW = -(e.movementX); + dH = e.movementY; break; case "documentDecorations-bottomRightResizer": - dW = e.movementX - dH = e.movementY + dW = e.movementX; + dH = e.movementY; break; case "documentDecorations-bottomResizer": - dH = e.movementY + dH = e.movementY; break; case "documentDecorations-leftResizer": - dX = -1 - dW = -(e.movementX) + dX = -1; + dW = -(e.movementX); break; case "documentDecorations-rightResizer": - dW = e.movementX + dW = e.movementX; break; } @@ -338,14 +338,15 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> var nativeWidth = doc.GetNumber(KeyStore.NativeWidth, 0); var nativeHeight = doc.GetNumber(KeyStore.NativeHeight, 0); if (nativeWidth > 0 && nativeHeight > 0) { - if (Math.abs(dW) > Math.abs(dH)) + if (Math.abs(dW) > Math.abs(dH)) { actualdH = nativeHeight / nativeWidth * actualdW; + } else actualdW = nativeWidth / nativeHeight * actualdH; } doc.SetNumber(KeyStore.Width, actualdW); doc.SetNumber(KeyStore.Height, actualdH); } - }) + }); } onPointerUp = (e: PointerEvent): void => { @@ -362,10 +363,10 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> if (this._title === "changed" && this._documents.length > 0) { let field = this._documents[0].props.Document.Get(this._fieldKey); if (field instanceof TextField) { - return (field as TextField).GetValue(); + return (field).GetValue(); } else if (field instanceof NumberField) { - return (field as NumberField).GetValue().toString(); + return (field).GetValue().toString(); } } return this._title; @@ -388,7 +389,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> return (null); } if (isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) { - console.log("DocumentDecorations: Bounds Error") + console.log("DocumentDecorations: Bounds Error"); return (null); } @@ -414,7 +415,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> top: bounds.y - this._resizeBorderWidth / 2, pointerEvents: this._dragging ? "none" : "all", zIndex: SelectionManager.SelectedDocuments().length > 1 ? 1000 : 0, - }} onPointerDown={this.onBackgroundDown} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation() }} > + }} onPointerDown={this.onBackgroundDown} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); }} >
    ∞
    - ) + ); } } \ No newline at end of file diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index 982aacdea..2f17c6c51 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -1,7 +1,7 @@ -import React = require('react') +import React = require('react'); import { observer } from 'mobx-react'; import { observable, action, trace } from 'mobx'; -import "./EditableView.scss" +import "./EditableView.scss"; export interface EditableProps { /** @@ -22,7 +22,7 @@ export interface EditableProps { * The contents to render when not editing */ contents: any; - height: number + height: number; display?: string; } @@ -55,14 +55,14 @@ export class EditableView extends React.Component { render() { if (this.editing) { return this.editing = false)} - style={{ display: this.props.display }}> + style={{ display: this.props.display }}>; } else { return (
    this.editing = true)} > {this.props.contents}
    - ) + ); } } } \ No newline at end of file diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx index 4ecc44119..47ee8eb85 100644 --- a/src/client/views/InkingCanvas.tsx +++ b/src/client/views/InkingCanvas.tsx @@ -82,7 +82,7 @@ export class InkingCanvas extends React.Component { }); this.inkData = data; } - }; + } @action onPointerUp = (e: PointerEvent): void => { @@ -109,7 +109,7 @@ export class InkingCanvas extends React.Component { @action onPointerMove = (e: PointerEvent): void => { - e.stopPropagation() + e.stopPropagation(); e.preventDefault(); if (InkingControl.Instance.selectedTool !== InkTool.Eraser) { let data = this.inkData; // add points to new line as it is being drawn @@ -120,7 +120,7 @@ export class InkingCanvas extends React.Component { } this.inkData = data; } - }; + } relativeCoordinatesForEvent = (ex: number, ey: number): { x: number, y: number } => { let [x, y] = this.props.getScreenTransform().transformPoint(ex, ey); @@ -137,14 +137,15 @@ export class InkingCanvas extends React.Component { @computed get drawnPaths() { - let curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1) + let curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1); let paths = Array.from(this.inkData).reduce((paths, [id, strokeData]) => { - if (strokeData.page === -1 || strokeData.page === curPage) + if (strokeData.page === -1 || strokeData.page === curPage) { paths.push() + tool={strokeData.tool} deleteCallback={this.removeLine} />); + } return paths; }, [] as JSX.Element[]); return [ { {this.props.children()} {this.drawnPaths} - ) + ); } } \ No newline at end of file diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index 64ee66ec7..9a68f0671 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -1,10 +1,10 @@ import { observable, action, computed } from "mobx"; -import { CirclePicker, ColorResult } from 'react-color' +import { CirclePicker, ColorResult } from 'react-color'; import React = require("react"); import { InkTool } from "../../fields/InkField"; import { observer } from "mobx-react"; -import "./InkingControl.scss" +import "./InkingControl.scss"; import { library } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faPen, faHighlighter, faEraser, faBan } from '@fortawesome/free-solid-svg-icons'; @@ -25,7 +25,7 @@ export class InkingControl extends React.Component { constructor(props: Readonly<{}>) { super(props); - InkingControl.Instance = this + InkingControl.Instance = this; } @action @@ -66,9 +66,9 @@ export class InkingControl extends React.Component { selected = (tool: InkTool) => { if (this._selectedTool === tool) { - return { color: "#61aaa3" } + return { color: "#61aaa3" }; } - return {} + return {}; } @action @@ -111,6 +111,6 @@ export class InkingControl extends React.Component { onChange={(e: React.ChangeEvent) => this.switchWidth(e.target.value)} /> - ) + ); } } \ No newline at end of file diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 12b15a3f0..0f05da22c 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -30,7 +30,7 @@ export class InkingStroke extends React.Component { } parseData = (line: Array<{ x: number, y: number }>): string => { - return !line.length ? "" : "M " + line.map(p => (p.x + this.props.offsetX) + " " + (p.y + this.props.offsetY)).join(" L ") + return !line.length ? "" : "M " + line.map(p => (p.x + this.props.offsetX) + " " + (p.y + this.props.offsetY)).join(" L "); } createStyle() { @@ -41,7 +41,7 @@ export class InkingStroke extends React.Component { fill: "none", stroke: this._strokeColor, strokeWidth: this._strokeWidth + "px", - } + }; } } @@ -53,6 +53,6 @@ export class InkingStroke extends React.Component { return ( - ) + ); } } \ No newline at end of file diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 6376fd694..fd2e23c91 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -16,7 +16,7 @@ import { WorkspacesMenu } from '../../server/authentication/controllers/Workspac import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; import { MessageStore } from '../../server/Message'; import { Utils, returnTrue, emptyFunction } from '../../Utils'; -import * as rp from 'request-promise' +import * as rp from 'request-promise'; import { RouteStore } from '../../server/RouteStore'; import { ServerUtils } from '../../server/ServerUtil'; import { Documents } from '../documents/Documents'; @@ -74,7 +74,7 @@ export class Main extends React.Component { if (pathname.length > 1 && pathname[pathname.length - 2] === 'doc') { CurrentUserUtils.MainDocId = pathname[pathname.length - 1]; } - }; + } CurrentUserUtils.loadCurrentUser(); @@ -119,8 +119,8 @@ export class Main extends React.Component { initEventListeners = () => { // window.addEventListener("pointermove", (e) => this.reportLocation(e)) - window.addEventListener("drop", (e) => e.preventDefault(), false) // drop event handler - window.addEventListener("dragover", (e) => e.preventDefault(), false) // drag event handler + window.addEventListener("drop", (e) => e.preventDefault(), false); // drop event handler + window.addEventListener("dragover", (e) => e.preventDefault(), false); // drag event handler // click interactions for the context menu document.addEventListener("pointerdown", action(function (e: PointerEvent) { if (!ContextMenu.Instance.intersects(e.pageX, e.pageY)) { @@ -139,15 +139,15 @@ export class Main extends React.Component { } else { this.createNewWorkspace(); } - }) + }); } else { Server.GetField(CurrentUserUtils.MainDocId).then(field => { if (field instanceof Document) { - this.openWorkspace(field) + this.openWorkspace(field); } else { this.createNewWorkspace(CurrentUserUtils.MainDocId); } - }) + }); } } @@ -163,7 +163,7 @@ export class Main extends React.Component { // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container) setTimeout(() => { this.openWorkspace(mainDoc); - let pendingDocument = Documents.SchemaDocument([], { title: "New Mobile Uploads" }) + let pendingDocument = Documents.SchemaDocument([], { title: "New Mobile Uploads" }); mainDoc.Set(KeyStore.OptionalRightCollection, pendingDocument); }, 0); } @@ -182,7 +182,7 @@ export class Main extends React.Component { if (f && f.Data.length > 0) { CollectionDockingView.Instance.AddRightSplit(col); } - }) + }); } }, 100); }); @@ -191,7 +191,7 @@ export class Main extends React.Component { @observable workspacesShown: boolean = false; - areWorkspacesShown = () => this.workspacesShown + areWorkspacesShown = () => this.workspacesShown; @action toggleWorkspaces = () => { this.workspacesShown = !this.workspacesShown; @@ -199,7 +199,7 @@ export class Main extends React.Component { pwidthFunc = () => this.pwidth; pheightFunc = () => this.pheight; - focusDocument = (doc: Document) => { } + focusDocument = (doc: Document) => { }; noScaling = () => 1; @observable _textDoc?: Document = undefined; @@ -208,8 +208,9 @@ export class Main extends React.Component { SetTextDoc(textDoc?: Document, div?: HTMLDivElement) { this._textDoc = undefined; this._textDoc = textDoc; - if (div) + if (div) { this._textRect = div.getBoundingClientRect(); + } } @computed @@ -221,7 +222,7 @@ export class Main extends React.Component { let h: number = this._textRect.height; return
    { }} /> - + ; } else return (null); } @@ -241,19 +242,19 @@ export class Main extends React.Component { focus={this.focusDocument} parentActive={returnTrue} onActiveChanged={emptyFunction} - ContainingCollectionView={undefined} /> + ContainingCollectionView={undefined} />; } /* 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/c4611_sample_explain.pdf"; let weburl = "https://cs.brown.edu/courses/cs166/"; let audiourl = "http://techslides.com/demos/samples/sample.mp3"; let videourl = "http://techslides.com/demos/sample-videos/small.mp4"; - let addTextNode = action(() => Documents.TextDocument({ width: 200, height: 200, title: "a text note" })) + let addTextNode = action(() => Documents.TextDocument({ width: 200, height: 200, title: "a text note" })); let addColNode = action(() => Documents.FreeformDocument([], { width: 200, height: 200, title: "a freeform collection" })); let addSchemaNode = action(() => Documents.SchemaDocument([], { width: 200, height: 200, title: "a schema collection" })); let addTreeNode = action(() => Documents.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", copyDraggedItems: true })); @@ -261,7 +262,7 @@ export class Main extends React.Component { let addPDFNode = action(() => Documents.PdfDocument(pdfurl, { width: 200, height: 200, title: "a schema collection" })); let addImageNode = action(() => Documents.ImageDocument(imgurl, { width: 200, height: 200, title: "an image of a cat" })); let addWebNode = action(() => Documents.WebDocument(weburl, { width: 200, height: 200, title: "a sample web page" })); - let addAudioNode = action(() => Documents.AudioDocument(audiourl, { width: 200, height: 200, title: "audio node" })) + let addAudioNode = action(() => Documents.AudioDocument(audiourl, { width: 200, height: 200, title: "audio node" })); let btns: [React.RefObject, IconName, string, () => Document][] = [ [React.createRef(), "font", "Add Textbox", addTextNode], @@ -273,7 +274,7 @@ export class Main extends React.Component { [React.createRef(), "object-group", "Add Collection", addColNode], [React.createRef(), "tree", "Add Tree", addTreeNode], [React.createRef(), "table", "Add Schema", addSchemaNode], - ] + ]; return < div id="add-nodes-menu" > @@ -289,7 +290,7 @@ export class Main 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 */ @@ -298,7 +299,7 @@ export class Main extends React.Component { let workspacesRef = React.createRef(); let logoutRef = React.createRef(); - let clearDatabase = action(() => Utils.Emit(Server.Socket, MessageStore.DeleteAll, {})) + let clearDatabase = action(() => Utils.Emit(Server.Socket, MessageStore.DeleteAll, {})); return [ ,
    @@ -310,7 +311,7 @@ export class Main extends React.Component {
    ,
    - ] + ]; } render() { @@ -318,7 +319,7 @@ export class Main extends React.Component { let workspaces = this.userDocument.GetT>(KeyStore.Workspaces, ListField); if (workspaces && workspaces !== FieldWaiting) { workspaceMenu = + isShown={this.areWorkspacesShown} toggle={this.toggleWorkspaces} />; } return ( <> @@ -359,7 +360,7 @@ export class Main extends React.Component { schemaDocuments.push(field); } else { var atmod = new ColumnAttributeModel(attr); - let histoOp = new HistogramOperation(schema!.displayName!, + let histoOp = new HistogramOperation(schema.displayName!, new AttributeTransformationModel(atmod, AggregateFunction.None), new AttributeTransformationModel(atmod, AggregateFunction.Count), new AttributeTransformationModel(atmod, AggregateFunction.Count)); @@ -368,7 +369,7 @@ export class Main extends React.Component { })); }); return schemaDoc; - }) + }); } } async initializeNorthstar(): Promise { @@ -386,7 +387,7 @@ export class Main extends React.Component { } (async () => { - await Documents.initProtos() - await CurrentUserUtils.loadCurrentUser() + await Documents.initProtos(); + await CurrentUserUtils.loadCurrentUser(); ReactDOM.render(
    , document.getElementById('root')); -})() +})(); diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index 7a5ab6e3c..32462a4c1 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -66,17 +66,20 @@ export class CollectionBaseView extends React.Component { createsCycle(documentToAdd: Document, containerDocument: Document): boolean { let data = documentToAdd.GetList(KeyStore.Data, []); for (const doc of data) { - if (this.createsCycle(doc, containerDocument)) + if (this.createsCycle(doc, containerDocument)) { return true; + } } let annots = documentToAdd.GetList(KeyStore.Annotations, []); for (const annot of annots) { - if (this.createsCycle(annot, containerDocument)) + if (this.createsCycle(annot, containerDocument)) { return true; + } } for (let containerProto: FieldValue = containerDocument; containerProto && containerProto !== FieldWaiting; containerProto = containerProto.GetPrototype()) { - if (containerProto.Id === documentToAdd.Id) + if (containerProto.Id === documentToAdd.Id) { return true; + } } return false; } @@ -91,20 +94,23 @@ export class CollectionBaseView extends React.Component { } if (props.Document.Get(props.fieldKey) instanceof Field) { //TODO This won't create the field if it doesn't already exist - const value = props.Document.GetData(props.fieldKey, ListField, new Array()) + const value = props.Document.GetData(props.fieldKey, ListField, new Array()); if (!this.createsCycle(doc, props.Document)) { - if (!value.some(v => v.Id === doc.Id) || allowDuplicates) + if (!value.some(v => v.Id === doc.Id) || allowDuplicates) { value.push(doc); + } } - else + else { return false; + } } else { let proto = props.Document.GetPrototype(); if (!proto || proto === FieldWaiting || !this.createsCycle(proto, doc)) { props.Document.SetOnPrototype(props.fieldKey, new ListField([doc])); } - else + else { return false; + } } return true; } @@ -113,7 +119,7 @@ export class CollectionBaseView extends React.Component { removeDocument(doc: Document): boolean { const props = this.props; //TODO This won't create the field if it doesn't already exist - const value = props.Document.GetData(props.fieldKey, ListField, new Array()) + const value = props.Document.GetData(props.fieldKey, ListField, new Array()); let index = -1; for (let i = 0; i < value.length; i++) { if (value[i].Id === doc.Id) { @@ -125,16 +131,16 @@ export class CollectionBaseView extends React.Component { if (annotationOn === props.Document) { doc.Set(KeyStore.AnnotationOn, undefined, true); } - }) + }); if (index !== -1) { - value.splice(index, 1) + value.splice(index, 1); // SelectionManager.DeselectAll() - ContextMenu.Instance.clearItems() + ContextMenu.Instance.clearItems(); return true; } - return false + return false; } @action.bound @@ -155,12 +161,12 @@ export class CollectionBaseView extends React.Component { moveDocument: this.moveDocument, active: this.active, onActiveChanged: this.onActiveChanged, - } + }; return (
    {this.props.children(this.collectionViewType, props)}
    - ) + ); } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index c3757a377..ea6d3a247 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -32,7 +32,7 @@ export class CollectionDockingView extends React.Component) => { - if (f instanceof Document) - DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f as Document]), e.pageX, e.pageY, + if (f instanceof Document) { + DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f]), e.pageX, e.pageY, { handlers: { dragComplete: action(() => { }), }, hideSource: false - }) + }); + } })); } if (className === "lm_drag_handle" || className === "lm_close" || className === "lm_maximise" || className === "lm_minimise" || className === "lm_close_tab") { @@ -220,7 +222,7 @@ export class CollectionDockingView extends React.Component { var json = JSON.stringify(this._goldenLayout.toConfig()); - this.props.Document.SetText(KeyStore.Data, json) + this.props.Document.SetText(KeyStore.Data, json); } itemDropped = () => { @@ -236,7 +238,7 @@ export class CollectionDockingView extends React.Component { Server.GetField(this.props.documentId, action((f: Opt) => this._document = f as Document)); } - private _nativeWidth = () => this._document!.GetNumber(KeyStore.NativeWidth, this._panelWidth) - private _nativeHeight = () => this._document!.GetNumber(KeyStore.NativeHeight, this._panelHeight) - private _contentScaling = () => this._panelWidth / (this._nativeWidth() ? this._nativeWidth() : this._panelWidth) + private _nativeWidth = () => this._document!.GetNumber(KeyStore.NativeWidth, this._panelWidth); + private _nativeHeight = () => this._document!.GetNumber(KeyStore.NativeHeight, this._panelHeight); + private _contentScaling = () => this._panelWidth / (this._nativeWidth() ? this._nativeWidth() : this._panelWidth); ScreenToLocalTransform = () => { let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.current!); - return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(scale / this._contentScaling()) + return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(scale / this._contentScaling()); } render() { - if (!this._document) + if (!this._document) { return (null); + } var content =
    { onActiveChanged={emptyFunction} focus={(doc: Document) => { }} ContainingCollectionView={undefined} /> -
    + ; return { this._panelWidth = r.entry.width; this._panelHeight = r.entry.height; })}> {({ measureRef }) =>
    {content}
    } -
    + ; } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx index 32f50afc0..97bac745c 100644 --- a/src/client/views/collections/CollectionPDFView.tsx +++ b/src/client/views/collections/CollectionPDFView.tsx @@ -2,7 +2,7 @@ import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import { KeyStore } from "../../../fields/KeyStore"; import { ContextMenu } from "../ContextMenu"; -import "./CollectionPDFView.scss" +import "./CollectionPDFView.scss"; import React = require("react"); import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; import { FieldView, FieldViewProps } from "../nodes/FieldView"; @@ -44,7 +44,7 @@ export class CollectionPDFView extends React.Component { {this.props.isSelected() ? this.uIButtons : (null)} - ) + ); } render() { @@ -52,6 +52,6 @@ export class CollectionPDFView extends React.Component { {this.subView} - ) + ); } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 9b780f29b..52933818f 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -1,4 +1,4 @@ -import React = require("react") +import React = require("react"); import { library } from '@fortawesome/fontawesome-svg-core'; import { faCog, faPlus } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; @@ -41,7 +41,7 @@ class KeyToggle extends React.Component<{ keyId: string, checked: boolean, toggl if (field instanceof Key) { this.key = field; } - })) + })); } render() { @@ -49,7 +49,7 @@ class KeyToggle extends React.Component<{ keyId: string, checked: boolean, toggl return (
    this.key && this.props.toggle(this.key)} /> {this.key.Name} -
    ) + ); } return (null); } @@ -85,10 +85,10 @@ export class CollectionSchemaView extends CollectionSubView { focus: emptyFunction, active: returnFalse, onActiveChanged: emptyFunction, - } + }; let contents = ( - ) + ); let reference = React.createRef(); let onItemDown = setupDrag(reference, () => props.Document, this.props.moveDocument); let applyToDoc = (doc: Document, run: (args?: { [name: string]: any }) => any) => { @@ -106,7 +106,7 @@ export class CollectionSchemaView extends CollectionSubView { } } return false; - } + }; return (
    applyToDoc(doc, run)); } - }) + }); }}>
    - ) + ); } private getTrProps: ComponentPropsGetterR = (state, rowInfo) => { @@ -156,7 +156,7 @@ export class CollectionSchemaView extends CollectionSubView { that._selectedIndex = rowInfo.index; if (handleOriginal) { - handleOriginal() + handleOriginal(); } }), style: { @@ -182,7 +182,7 @@ export class CollectionSchemaView extends CollectionSubView { this.columns.splice(index, 1); } - }) + }); } //toggles preview side-panel of schema @@ -197,7 +197,7 @@ export class CollectionSchemaView extends CollectionSubView { @computed get findAllDocumentKeys(): { [id: string]: boolean } { const docs = this.props.Document.GetList(this.props.fieldKey, []); - let keys: { [id: string]: boolean } = {} + let keys: { [id: string]: boolean } = {}; if (this._optionsActivated > -1) { // bcz: ugh. this is untracked since otherwise a large collection of documents will blast the server for all their fields. // then as each document's fields come back, we update the documents _proxies. Each time we do this, the whole schema will be @@ -206,7 +206,7 @@ export class CollectionSchemaView extends CollectionSubView { // is displayed (unlikely) it won't show up until something else changes. untracked(() => docs.map(doc => doc.GetAllPrototypes().map(proto => proto._proxies.forEach((val: any, key: string) => keys[key] = false)))); } - this.columns.forEach(key => keys[key.Id] = true) + this.columns.forEach(key => keys[key.Id] = true); return keys; } @@ -248,10 +248,10 @@ export class CollectionSchemaView extends CollectionSubView { getContentScaling = (): number => this._contentScaling; getPanelWidth = (): number => this._panelWidth; getPanelHeight = (): number => this._panelHeight; - getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- COLLECTION_BORDER_WIDTH - this.DIVIDER_WIDTH - this._dividerX, - COLLECTION_BORDER_WIDTH).scale(1 / this._contentScaling) - getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- COLLECTION_BORDER_WIDTH - this.DIVIDER_WIDTH - this._dividerX - this._tableWidth, - COLLECTION_BORDER_WIDTH).scale(1 / this._contentScaling) + getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- COLLECTION_BORDER_WIDTH - this.DIVIDER_WIDTH - this._dividerX, - COLLECTION_BORDER_WIDTH).scale(1 / this._contentScaling); + getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- COLLECTION_BORDER_WIDTH - this.DIVIDER_WIDTH - this._dividerX - this._tableWidth, - COLLECTION_BORDER_WIDTH).scale(1 / this._contentScaling); - focusDocument = (doc: Document) => { } + focusDocument = (doc: Document) => { }; onPointerDown = (e: React.PointerEvent): void => { if (this.props.isSelected()) { @@ -273,8 +273,9 @@ export class CollectionSchemaView extends CollectionSubView { this.newKeyName = e.currentTarget.value; } onWheel = (e: React.WheelEvent): void => { - if (this.props.active()) + if (this.props.active()) { e.stopPropagation(); + } } @observable _optionsActivated: number = 0; @@ -322,9 +323,9 @@ export class CollectionSchemaView extends CollectionSubView { } - ) + ); let dividerDragger = this.splitPercentage === 0 ? (null) : -
    +
    ; //options button and menu let optionsMenu = !this.props.active() ? (null) : (
    - ) + ); } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 59b89e119..bd385c27e 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -14,7 +14,7 @@ import { NumberField } from "../../../fields/NumberField"; import { ServerUtils } from "../../../server/ServerUtil"; import { Server } from "../../Server"; import { FieldViewProps } from "../nodes/FieldView"; -import * as rp from 'request-promise' +import * as rp from 'request-promise'; export interface CollectionViewProps extends FieldViewProps { addDocument: (document: Document, allowDuplicates?: boolean) => boolean; @@ -58,8 +58,8 @@ export class CollectionSubView extends React.Component { let entry = new TupleField<[string, string], [number, number]>([textInfo, position]); cursors.push(entry); } - })) - }) + })); + }); } } @@ -77,17 +77,17 @@ export class CollectionSubView extends React.Component { added = de.data.droppedDocuments.reduce((added: boolean, d) => added || this.props.addDocument(d), false); } else if (de.data.moveDocument) { const move = de.data.moveDocument; - added = de.data.droppedDocuments.reduce((added: boolean, d) => added || move(d, this.props.Document, this.props.addDocument), false) + added = de.data.droppedDocuments.reduce((added: boolean, d) => added || move(d, this.props.Document, this.props.addDocument), false); } else { - added = de.data.droppedDocuments.reduce((added: boolean, d) => added || this.props.addDocument(d), false) + added = de.data.droppedDocuments.reduce((added: boolean, d) => added || this.props.addDocument(d), false); } e.stopPropagation(); return added; } if (de.data instanceof DragManager.LinkDragData) { let sourceDoc: Document = de.data.linkSourceDocumentView.props.Document; - if (sourceDoc) runInAction(() => { - let srcTarg = sourceDoc.GetT(KeyStore.Prototype, Document) + if (sourceDoc) { runInAction(() => { + let srcTarg = sourceDoc.GetT(KeyStore.Prototype, Document); if (srcTarg && srcTarg !== FieldWaiting) { let linkDocs = srcTarg.GetList(KeyStore.LinkedToDocs, [] as Document[]); linkDocs.map(linkDoc => { @@ -97,9 +97,10 @@ export class CollectionSubView extends React.Component { de.data.droppedDocuments.push(dropdoc); this.props.addDocument(dropdoc, false); } - }) + }); } - }) + }); + } return true; } return false; @@ -133,7 +134,7 @@ export class CollectionSubView extends React.Component { alias.SetNumber(KeyStore.Height, options.height || options.width || 300); this.props.addDocument(alias, false); } - }) + }); return undefined; } ctor = Documents.WebDocument; @@ -151,8 +152,8 @@ export class CollectionSubView extends React.Component { if (text && text.startsWith(" { let prom = new Promise(res => e.dataTransfer.items[i].getAsString(res)).then(action((s: string) => { str = s; - return rp.head(ServerUtils.prepend(RouteStore.corsProxy + "/" + s)) + return rp.head(ServerUtils.prepend(RouteStore.corsProxy + "/" + s)); })).then(res => { let type = res.headers["content-type"]; if (type) { - let doc = this.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 }) + let doc = this.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 }); if (doc) { this.props.addDocument(doc, false); } @@ -186,13 +187,13 @@ export class CollectionSubView extends React.Component { promises.push(prom); // this.props.addDocument(Documents.WebDocument(s, { ...options, width: 300, height: 300 }), false) } - let type = item.type + let type = item.type; if (item.kind === "file") { let file = item.getAsFile(); - let formData = new FormData() + let formData = new FormData(); if (file) { - formData.append('file', file) + formData.append('file', file); } let prom = fetch(upload, { @@ -201,15 +202,15 @@ export class CollectionSubView extends React.Component { }).then(async (res: Response) => { const json = await res.json(); json.map((file: any) => { - let path = window.location.origin + file + let path = window.location.origin + file; runInAction(() => { - let doc = this.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300 }) + let doc = this.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300 }); let docs = this.props.Document.GetT(KeyStore.Data, ListField); if (docs !== FieldWaiting) { if (!docs) { docs = new ListField(); - this.props.Document.Set(KeyStore.Data, docs) + this.props.Document.Set(KeyStore.Data, docs); } if (doc) { docs.Data.push(doc); diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 2bdf0baa6..659cff9fe 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -12,7 +12,7 @@ import { EditableView } from "../EditableView"; import "./CollectionTreeView.scss"; import { CollectionView } from "./CollectionView"; import { CollectionSubView } from "./CollectionSubView"; -import React = require("react") +import React = require("react"); import { COLLECTION_BORDER_WIDTH } from './CollectionBaseView'; import { props } from 'bluebird'; @@ -58,7 +58,7 @@ class TreeView extends React.Component { return true; } //TODO This should check if it was removed - this.remove(document) + this.remove(document); return addDoc(document); } @@ -69,7 +69,7 @@ class TreeView extends React.Component { case BulletType.Collapsed: bullet = "caret-right"; break; case BulletType.Collapsible: bullet = "caret-down"; break; } - return
    {bullet ? : ""}
    + return
    {bullet ? : ""}
    ; } /** @@ -93,7 +93,7 @@ class TreeView extends React.Component {
    {editableView(this.props.document.Title)}
    -
    ) +
    ); } render() { @@ -105,7 +105,7 @@ class TreeView extends React.Component { bulletType = BulletType.Collapsible; childElements =
      {children.Data.map(value => )} -
    + ; } else bulletType = BulletType.Collapsed; } @@ -115,7 +115,7 @@ class TreeView extends React.Component { {this.renderTitle()} {childElements ? childElements : (null)} - + ; } } @@ -136,7 +136,7 @@ export class CollectionTreeView extends CollectionSubView { let childrenElement = !children || children === FieldWaiting ? (null) : (children.Data.map(value => ) - ) + ); return (
    e.stopPropagation()} onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }}> diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx index 1acc76c85..b02983a2e 100644 --- a/src/client/views/collections/CollectionVideoView.tsx +++ b/src/client/views/collections/CollectionVideoView.tsx @@ -4,7 +4,7 @@ import { KeyStore } from "../../../fields/KeyStore"; import { ContextMenu } from "../ContextMenu"; import { CollectionViewType, CollectionBaseView, CollectionRenderProps } from "./CollectionBaseView"; import React = require("react"); -import "./CollectionVideoView.scss" +import "./CollectionVideoView.scss"; import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; import { FieldView, FieldViewProps } from "../nodes/FieldView"; @@ -111,7 +111,7 @@ export class CollectionVideoView extends React.Component { {this.props.isSelected() ? this.uIButtons : (null)} - ) + ); } render() { @@ -119,6 +119,6 @@ export class CollectionVideoView extends React.Component { return ( {this.subView} - ) + ); } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index b31dcc888..8abd0a02d 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -1,4 +1,4 @@ -import * as React from 'react' +import * as React from 'react'; import { FieldViewProps, FieldView } from '../nodes/FieldView'; import { CollectionBaseView, CollectionViewType, CollectionRenderProps } from './CollectionBaseView'; import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView'; @@ -13,26 +13,26 @@ import { undoBatch } from '../../util/UndoManager'; @observer export class CollectionView extends React.Component { - public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(CollectionView, fieldStr) } + public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(CollectionView, fieldStr); } private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => { let props = { ...this.props, ...renderProps }; switch (type) { - case CollectionViewType.Schema: return () - case CollectionViewType.Docking: return () - case CollectionViewType.Tree: return () + case CollectionViewType.Schema: return (); + case CollectionViewType.Docking: return (); + case CollectionViewType.Tree: return (); case CollectionViewType.Freeform: default: - return () + return (); } return (null); } onContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped() && this.props.Document.Id !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 - ContextMenu.Instance.addItem({ description: "Freeform", event: undoBatch(() => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Freeform)) }) - ContextMenu.Instance.addItem({ description: "Schema", event: undoBatch(() => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Schema)) }) - ContextMenu.Instance.addItem({ description: "Treeview", event: undoBatch(() => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Tree)) }) + ContextMenu.Instance.addItem({ description: "Freeform", event: undoBatch(() => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Freeform)) }); + ContextMenu.Instance.addItem({ description: "Schema", event: undoBatch(() => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Schema)) }); + ContextMenu.Instance.addItem({ description: "Treeview", event: undoBatch(() => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Tree)) }); } } @@ -41,6 +41,6 @@ export class CollectionView extends React.Component { {this.SubView} - ) + ); } } \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 3dfd74ec8..081b3eb6c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -32,6 +32,6 @@ export class CollectionFreeFormLinkView extends React.Component - ) + ); } } \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index c288e7abf..cf058090d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -17,7 +17,7 @@ export class CollectionFreeFormLinksView extends React.Component DocumentManager.Instance.getAllDocumentViews(this.HackToAvoidReactionFiringUnnecessarily!). map(dv => dv.props.Document.GetNumber(KeyStore.X, 0)), @@ -31,13 +31,14 @@ export class CollectionFreeFormLinksView extends React.Component) => field.Data.findIndex(brush => { let bdocs = brush ? brush.GetList(KeyStore.BrushingDocs, [] as Document[]) : []; - return (bdocs.length && ((bdocs[0] === dstTarg && bdocs[1] === srcTarg)) ? true : false) + return (bdocs.length && ((bdocs[0] === dstTarg && bdocs[1] === srcTarg)) ? true : false); }); let brushAction = (field: ListField) => { let found = findBrush(field); @@ -64,13 +65,13 @@ export class CollectionFreeFormLinksView extends React.Component sv.props.ContainingCollectionView && sv.props.ContainingCollectionView.props.Document === this.props.Document); } @@ -86,15 +87,16 @@ export class CollectionFreeFormLinksView extends React.Component { 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)) + if (!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] }); } - }) - return drawnPairs + }); + return drawnPairs; }, [] as { a: Document, b: Document, l: Document[] }[]); return connections.map(c => ); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx index 17656e4ae..fc832264d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx @@ -57,7 +57,7 @@ export class CollectionFreeFormRemoteCursors extends React.Component { if (el) this.crosshairs = el }} + ref={(el) => { if (el) this.crosshairs = el; }} width={20} height={20} style={{ @@ -93,7 +93,7 @@ export class CollectionFreeFormRemoteCursors extends React.Component ); } - }) + }); } render() { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 1c3899b24..e694bc7a7 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -49,7 +49,7 @@ export class CollectionFreeFormView extends CollectionSubView { if (dv) { SelectionManager.SelectDoc(dv, true); } - }) + }); } public getActiveDocuments = () => { @@ -70,8 +70,8 @@ export class CollectionFreeFormView extends CollectionSubView { private outerElement?: HTMLDivElement; - @computed get panX(): number { return this.props.Document.GetNumber(KeyStore.PanX, 0) } - @computed get panY(): number { return this.props.Document.GetNumber(KeyStore.PanY, 0) } + @computed get panX(): number { return this.props.Document.GetNumber(KeyStore.PanX, 0); } + @computed get panY(): number { return this.props.Document.GetNumber(KeyStore.PanY, 0); } @computed get scale(): number { return this.props.Document.GetNumber(KeyStore.Scale, 1); } @computed get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey.Id === KeyStore.Annotations.Id; } // bcz: ? Why do we need to compare Id's? @computed get nativeWidth() { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); } @@ -109,7 +109,7 @@ export class CollectionFreeFormView extends CollectionSubView { d.SetNumber(KeyStore.Height, 300); } this.bringToFront(d); - }) + }); } } return true; @@ -133,8 +133,9 @@ export class CollectionFreeFormView extends CollectionSubView { document.addEventListener("pointerup", this.onPointerUp); this._lastX = this.DownX = e.pageX; this._lastY = this.DownY = e.pageY; - if (this.props.isSelected()) + if (this.props.isSelected()) { e.stopPropagation(); + } } } @@ -182,12 +183,13 @@ export class CollectionFreeFormView extends CollectionSubView { let transform = this.getTransform(); let deltaScale = (1 - (e.deltaY / coefficient)); - if (deltaScale * this.zoomScaling < 1 && this.isAnnotationOverlay) + if (deltaScale * this.zoomScaling < 1 && this.isAnnotationOverlay) { deltaScale = 1 / this.zoomScaling; + } let [x, y] = transform.transformPoint(e.clientX, e.clientY); - let localTransform = this.getLocalTransform() - localTransform = localTransform.inverse().scaleAbout(deltaScale, x, y) + let localTransform = this.getLocalTransform(); + localTransform = localTransform.inverse().scaleAbout(deltaScale, x, y); // console.log(localTransform) this.props.Document.SetNumber(KeyStore.Scale, localTransform.Scale); @@ -209,7 +211,7 @@ export class CollectionFreeFormView extends CollectionSubView { onDrop = (e: React.DragEvent): void => { var pt = this.getTransform().transformPoint(e.pageX, e.pageY); super.onDrop(e, { x: pt[0], y: pt[1] }); - }; + } onDragOver = (): void => { } @@ -228,7 +230,7 @@ export class CollectionFreeFormView extends CollectionSubView { } return doc1.GetNumber(KeyStore.ZIndex, 0) - doc2.GetNumber(KeyStore.ZIndex, 0); }).map((doc, index) => { - doc.SetNumber(KeyStore.ZIndex, index + 1) + doc.SetNumber(KeyStore.ZIndex, index + 1); }); } @@ -268,7 +270,7 @@ export class CollectionFreeFormView extends CollectionSubView { focus: this.focusDocument, parentActive: this.props.active, onActiveChanged: this.props.active, - } + }; } @computed @@ -276,10 +278,11 @@ export class CollectionFreeFormView extends CollectionSubView { 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) => { var page = doc.GetNumber(KeyStore.Page, -1); - if (page === curPage || page === -1) + if (page === curPage || page === -1) { prev.push(); + } return prev; - }, [] as JSX.Element[]) + }, [] as JSX.Element[]); } @computed @@ -295,8 +298,8 @@ export class CollectionFreeFormView extends CollectionSubView { layoutKey={KeyStore.OverlayLayout} isTopMost={this.props.isTopMost} isSelected={() => false} select={() => { }} />); } - getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH).translate(-this.centeringShiftX, -this.centeringShiftY).transform(this.getLocalTransform()) - getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH) + getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH).translate(-this.centeringShiftX, -this.centeringShiftY).transform(this.getLocalTransform()); + getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH); getLocalTransform = (): Transform => Transform.Identity().scale(1 / this.scale).translate(this.panX, this.panY); noScaling = () => 1; childViews = () => this.views; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 704db1d4a..1e6faafb3 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -37,7 +37,7 @@ export class MarqueeView extends React.Component @action cleanupInteractions = (all: boolean = false) => { if (all) { - document.removeEventListener("pointermove", this.onPointerMove, true) + document.removeEventListener("pointermove", this.onPointerMove, true); document.removeEventListener("pointerup", this.onPointerUp, true); } else { this._used = true; @@ -52,7 +52,7 @@ export class MarqueeView extends React.Component this._downX = this._lastX = e.pageX; this._downY = this._lastY = e.pageY; this._used = false; - document.addEventListener("pointermove", this.onPointerMove, true) + document.addEventListener("pointermove", this.onPointerMove, true); document.addEventListener("pointerup", this.onPointerUp, true); document.addEventListener("keydown", this.marqueeCommand, true); } @@ -94,7 +94,7 @@ export class MarqueeView extends React.Component let top = this._downY < this._lastY ? this._downY : this._lastY; let topLeft = this.props.getTransform().transformPoint(left, top); let size = this.props.getTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY); - return { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) } + return { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) }; } @action @@ -180,9 +180,10 @@ export class MarqueeView extends React.Component var y = doc.GetNumber(KeyStore.Y, 0); var w = doc.GetNumber(KeyStore.Width, 0); var h = doc.GetNumber(KeyStore.Height, 0); - if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) - selection.push(doc) - }) + if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) { + selection.push(doc); + } + }); return selection; } @@ -190,7 +191,7 @@ export class MarqueeView extends React.Component get marqueeDiv() { let p = this.props.getContainerTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY); let v = this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY); - return
    + return
    ; } render() { diff --git a/src/client/views/collections/collectionFreeForm/PreviewCursor.tsx b/src/client/views/collections/collectionFreeForm/PreviewCursor.tsx index 599461f85..8eabb020a 100644 --- a/src/client/views/collections/collectionFreeForm/PreviewCursor.tsx +++ b/src/client/views/collections/collectionFreeForm/PreviewCursor.tsx @@ -90,7 +90,7 @@ export class PreviewCursor extends React.Component { {this.props.children}
    - ) + ); } } @@ -109,8 +109,9 @@ export class PreviewCursorPrompt extends React.Component { render() { let p = this.props.getPoint(); - if (this.props.getVisible() && this._promptRef.current) + if (this.props.getVisible() && this._promptRef.current) { this._promptRef.current.focus(); + } return
    I diff --git a/src/client/views/nodes/Annotation.tsx b/src/client/views/nodes/Annotation.tsx index e4f17940c..3e4ed6bf1 100644 --- a/src/client/views/nodes/Annotation.tsx +++ b/src/client/views/nodes/Annotation.tsx @@ -1,8 +1,8 @@ import "./ImageBox.scss"; -import React = require("react") -import { observer } from "mobx-react" +import React = require("react"); +import { observer } from "mobx-react"; import { observable, action } from 'mobx'; -import 'react-pdf/dist/Page/AnnotationLayer.css' +import 'react-pdf/dist/Page/AnnotationLayer.css'; interface IProps { Span: HTMLSpanElement; @@ -29,13 +29,13 @@ export class Annotation extends React.Component { */ onColorChange = (e: React.PointerEvent) => { if (e.currentTarget.innerHTML === "r") { - this.props.Span.style.backgroundColor = "rgba(255,0,0, 0.3)" + this.props.Span.style.backgroundColor = "rgba(255,0,0, 0.3)"; } else if (e.currentTarget.innerHTML === "b") { - this.props.Span.style.backgroundColor = "rgba(0,255, 255, 0.3)" + this.props.Span.style.backgroundColor = "rgba(0,255, 255, 0.3)"; } else if (e.currentTarget.innerHTML === "y") { - this.props.Span.style.backgroundColor = "rgba(255,255,0, 0.3)" + this.props.Span.style.backgroundColor = "rgba(255,255,0, 0.3)"; } else if (e.currentTarget.innerHTML === "g") { - this.props.Span.style.backgroundColor = "rgba(76, 175, 80, 0.3)" + this.props.Span.style.backgroundColor = "rgba(76, 175, 80, 0.3)"; } } @@ -54,11 +54,11 @@ export class Annotation extends React.Component { this.props.Highlights.splice(index, 1); } } - }) + }); //removing from CurrAnno and Annotation array this.props.Annotations.splice(index, 1); - this.props.CurrAnno.pop() + this.props.CurrAnno.pop(); //removing span from div if (this.props.Span.parentElement) { @@ -70,11 +70,11 @@ export class Annotation extends React.Component { if (item === e) { item.remove(); } - }) + }); e.remove(); } } - }) + }); } diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 1bd934c25..1493ff25b 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -1,18 +1,18 @@ -import React = require("react") +import React = require("react"); import { FieldViewProps, FieldView } from './FieldView'; import { FieldWaiting } from '../../../fields/Field'; -import { observer } from "mobx-react" +import { observer } from "mobx-react"; import { ContextMenu } from "../../views/ContextMenu"; import { observable, action } from 'mobx'; import { KeyStore } from '../../../fields/KeyStore'; import { AudioField } from "../../../fields/AudioField"; -import "./AudioBox.scss" +import "./AudioBox.scss"; import { NumberField } from "../../../fields/NumberField"; @observer export class AudioBox extends React.Component { - public static LayoutString() { return FieldView.LayoutString(AudioBox) } + public static LayoutString() { return FieldView.LayoutString(AudioBox); } constructor(props: FieldViewProps) { super(props); @@ -28,7 +28,7 @@ export class AudioBox extends React.Component { render() { - let field = this.props.Document.Get(this.props.fieldKey) + let field = this.props.Document.Get(this.props.fieldKey); let path = field === FieldWaiting ? "http://techslides.com/demos/samples/sample.mp3" : field instanceof AudioField ? field.Data.href : "http://techslides.com/demos/samples/sample.mp3"; @@ -39,6 +39,6 @@ export class AudioBox extends React.Component { Not supported.
    - ) + ); } } \ No newline at end of file diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index dcf82ccd4..77f41105f 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -35,24 +35,24 @@ export class CollectionFreeFormDocumentView extends React.Component this.nativeWidth > 0 ? this.width / this.nativeWidth : 1 + contentScaling = () => this.nativeWidth > 0 ? this.width / this.nativeWidth : 1; getTransform = (): Transform => this.props.ScreenToLocalTransform() @@ -66,7 +66,7 @@ export class CollectionFreeFormDocumentView extends React.Component + />; } panelWidth = () => this.props.Document.GetBoolean(KeyStore.Minimized, false) ? 10 : this.props.PanelWidth(); panelHeight = () => this.props.Document.GetBoolean(KeyStore.Minimized, false) ? 10 : this.props.PanelHeight(); diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 34cc326aa..5836da396 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -26,7 +26,7 @@ import { FieldViewProps } from "./FieldView"; import { Without } from "../../../Utils"; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? -type BindingProps = Without +type BindingProps = Without; export interface JsxBindings { props: BindingProps; [keyName: string]: BindingProps | Field; @@ -92,7 +92,7 @@ export class DocumentContentsView extends React.Component { console.log(test) }} - /> + onError={(test: any) => { console.log(test); }} + />; } } \ No newline at end of file diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index ab9cd2d53..9670ca6b2 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -100,8 +100,9 @@ export class DocumentView extends React.Component { if (e.shiftKey && e.buttons === 2) { if (this.props.isTopMost) { this.startDragging(e.pageX, e.pageY, e.altKey || e.ctrlKey); - } else + } else { CollectionDockingView.Instance.StartOtherDrag([this.props.Document], e); + } e.stopPropagation(); } else { if (this.active && !e.isDefaultPrevented()) { @@ -112,7 +113,7 @@ export class DocumentView extends React.Component { document.addEventListener("pointerup", this.onPointerUp); } } - }; + } private dropDisposer?: DragManager.DragDropDisposer; @@ -179,7 +180,7 @@ export class DocumentView extends React.Component { } e.stopPropagation(); e.preventDefault(); - }; + } onPointerUp = (e: PointerEvent): void => { document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); @@ -190,22 +191,22 @@ export class DocumentView extends React.Component { ) { SelectionManager.SelectDoc(this, e.ctrlKey); } - }; + } stopPropogation = (e: React.SyntheticEvent) => { e.stopPropagation(); - }; + } deleteClicked = (): void => { if (this.props.removeDocument) { this.props.removeDocument(this.props.Document); } - }; + } fieldsClicked = (e: React.MouseEvent): void => { if (this.props.addDocument) { this.props.addDocument(Documents.KVPDocument(this.props.Document, { width: 300, height: 300 }), false); } - }; + } fullScreenClicked = (e: React.MouseEvent): void => { CollectionDockingView.Instance.OpenFullScreen(this.props.Document); ContextMenu.Instance.clearItems(); @@ -214,7 +215,7 @@ export class DocumentView extends React.Component { event: this.closeFullScreenClicked }); ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); - }; + } closeFullScreenClicked = (e: React.MouseEvent): void => { CollectionDockingView.Instance.CloseFullScreen(); @@ -224,7 +225,7 @@ export class DocumentView extends React.Component { event: this.fullScreenClicked }); ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); - }; + } @action public minimize = (): void => { @@ -234,7 +235,7 @@ export class DocumentView extends React.Component { BooleanField ); SelectionManager.DeselectAll(); - }; + } @action drop = (e: Event, de: DragManager.DropEvent) => { @@ -276,7 +277,7 @@ export class DocumentView extends React.Component { ); e.stopPropagation(); } - }; + } onDrop = (e: React.DragEvent) => { if (e.isDefaultPrevented()) { @@ -290,7 +291,7 @@ export class DocumentView extends React.Component { e.stopPropagation(); e.preventDefault(); } - }; + } @action onContextMenu = (e: React.MouseEvent): void => { @@ -352,14 +353,14 @@ export class DocumentView extends React.Component { }); ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); SelectionManager.SelectDoc(this, e.ctrlKey); - }; + } isMinimized = () => { let field = this.props.Document.GetT(KeyStore.Minimized, BooleanField); if (field && field !== FieldWaiting) { return field.Data; } - }; + } @action expand = () => { @@ -368,13 +369,13 @@ export class DocumentView extends React.Component { false as boolean, BooleanField ); - }; + } - isSelected = () => SelectionManager.IsSelected(this) + isSelected = () => SelectionManager.IsSelected(this); select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); - }; + } render() { if (!this.props.Document) { diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index d9422ae9b..07c5b332c 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -1,4 +1,4 @@ -import React = require("react") +import React = require("react"); import { observer } from "mobx-react"; import { computed } from "mobx"; import { Field, FieldWaiting, FieldValue } from "../../../fields/Field"; @@ -7,7 +7,7 @@ import { TextField } from "../../../fields/TextField"; import { NumberField } from "../../../fields/NumberField"; import { RichTextField } from "../../../fields/RichTextField"; import { ImageField } from "../../../fields/ImageField"; -import { VideoField } from "../../../fields/VideoField" +import { VideoField } from "../../../fields/VideoField"; import { Key } from "../../../fields/Key"; import { FormattedTextBox } from "./FormattedTextBox"; import { ImageBox } from "./ImageBox"; @@ -57,22 +57,22 @@ export class FieldView extends React.Component { render() { const field = this.field; if (!field) { - return

    {''}

    + return

    {''}

    ; } if (field instanceof TextField) { - return

    {field.Data}

    + return

    {field.Data}

    ; } else if (field instanceof RichTextField) { - return + return ; } else if (field instanceof ImageField) { - return + return ; } else if (field instanceof VideoField) { - return + return ; } else if (field instanceof AudioField) { - return + return ; } else if (field instanceof Document) { return ( @@ -92,25 +92,26 @@ export class FieldView extends React.Component { ContainingCollectionView={undefined} parentActive={this.props.active} onActiveChanged={this.props.onActiveChanged} /> - ) + ); } else if (field instanceof ListField) { return (
    {(field as ListField).Data.map(f => f instanceof Document ? f.Title : f.GetValue().toString()).join(", ")} -
    ) +
    ); } // bcz: this belongs here, but it doesn't render well so taking it out for now // else if (field instanceof HtmlField) { // return // } else if (field instanceof NumberField) { - return

    {field.Data}

    + return

    {field.Data}

    ; } else if (field !== FieldWaiting) { - return

    {JSON.stringify(field.GetValue())}

    + return

    {JSON.stringify(field.GetValue())}

    ; } - else - return

    {"Waiting for server..."}

    + else { + return

    {"Waiting for server..."}

    ; + } } } \ No newline at end of file diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 7a94be12b..beca6cdc6 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -61,7 +61,7 @@ export class FormattedTextBox extends React.Component { ); // doc.SetData(fieldKey, JSON.stringify(state.toJSON()), RichTextField); } - }; + } get FieldDoc() { return this.props.fieldKey === KeyStore.Archives ? Main.Instance._textDoc! : this.props.Document; } get FieldKey() { return this.props.fieldKey === KeyStore.Archives ? KeyStore.Data : this.props.fieldKey; } @@ -81,12 +81,13 @@ export class FormattedTextBox extends React.Component { if (this.props.fieldKey === KeyStore.Archives) { this._inputReactionDisposer = reaction(() => Main.Instance._textDoc && Main.Instance._textDoc.Id, () => { - if (this._editorView) - this._editorView!.destroy(); + if (this._editorView) { + this._editorView.destroy(); + } this.setupEditor(config); } - ) + ); } this._reactionDisposer = reaction( @@ -153,7 +154,7 @@ export class FormattedTextBox extends React.Component { if (e.buttons === 1 && this.props.isSelected() && !e.altKey) { e.stopPropagation(); } - }; + } onPointerUp = (e: React.PointerEvent): void => { if (e.buttons === 1 && this.props.isSelected() && !e.altKey) { e.stopPropagation(); @@ -162,7 +163,7 @@ export class FormattedTextBox extends React.Component { e.preventDefault(); Main.Instance.SetTextDoc(this.props.Document, this._ref.current!); } - }; + } onFocused = (e: React.FocusEvent): void => { if (this.props.fieldKey !== KeyStore.Archives) { @@ -191,11 +192,11 @@ export class FormattedTextBox extends React.Component { // ] // }) // e.stopPropagation() - }; + } onPointerWheel = (e: React.WheelEvent): void => { e.stopPropagation(); - }; + } tooltipMenuPlugin() { return new Plugin({ diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index c5f29f7b0..6b0a3a799 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -9,13 +9,13 @@ import { KeyStore } from '../../../fields/KeyStore'; import { ContextMenu } from "../../views/ContextMenu"; import { FieldView, FieldViewProps } from './FieldView'; import "./ImageBox.scss"; -import React = require("react") +import React = require("react"); import { Utils } from '../../../Utils'; @observer export class ImageBox extends React.Component { - public static LayoutString() { return FieldView.LayoutString(ImageBox) } + public static LayoutString() { return FieldView.LayoutString(ImageBox); } private _ref: React.RefObject; private _imgRef: React.RefObject; private _downX: number = 0; @@ -39,7 +39,7 @@ export class ImageBox extends React.Component { onLoad = (target: any) => { var h = this._imgRef.current!.naturalHeight; var w = this._imgRef.current!.naturalWidth; - this.props.Document.SetNumber(KeyStore.NativeHeight, this.props.Document.GetNumber(KeyStore.NativeWidth, 0) * h / w) + this.props.Document.SetNumber(KeyStore.NativeHeight, this.props.Document.GetNumber(KeyStore.NativeWidth, 0) * h / w); } componentDidMount() { @@ -86,7 +86,7 @@ export class ImageBox extends React.Component { onMoveNextRequest={action(() => this._photoIndex = (this._photoIndex + 1) % images.length )} - />) + />); } } @@ -96,7 +96,7 @@ export class ImageBox extends React.Component { let url = field.Data.href; ContextMenu.Instance.addItem({ description: "Copy path", event: () => { - Utils.CopyText(url) + Utils.CopyText(url); } }); } @@ -111,6 +111,6 @@ export class ImageBox extends React.Component {
    Image not found {this.lightbox(path)} -
    ) +
    ); } } \ No newline at end of file diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 8a7f32e6d..bcac113f0 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -7,7 +7,7 @@ import { KeyStore } from '../../../fields/KeyStore'; import { FieldView, FieldViewProps } from './FieldView'; import "./KeyValueBox.scss"; import { KeyValuePair } from "./KeyValuePair"; -import React = require("react") +import React = require("react"); import { CompileScript, ToField } from "../../util/Scripting"; import { Key } from '../../../fields/Key'; import { observable, action } from "mobx"; @@ -15,7 +15,7 @@ import { observable, action } from "mobx"; @observer export class KeyValueBox extends React.Component { - public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(KeyValueBox, fieldStr) } + public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(KeyValueBox, fieldStr); } @observable private _keyInput: string = ""; @observable private _valueInput: string = ""; @@ -36,7 +36,7 @@ export class KeyValueBox extends React.Component { if (this._keyInput && this._valueInput) { let doc = this.props.Document.GetT(KeyStore.Data, Document); if (!doc || doc === FieldWaiting) { - return + return; } let realDoc = doc; @@ -55,8 +55,8 @@ export class KeyValueBox extends React.Component { realDoc.Set(new Key(this._keyInput), dataField); } } - this._keyInput = "" - this._valueInput = "" + this._keyInput = ""; + this._valueInput = ""; } } } @@ -73,7 +73,7 @@ export class KeyValueBox extends React.Component { createTable = () => { let doc = this.props.Document.GetT(KeyStore.Data, Document); if (!doc || doc === FieldWaiting) { - return Loading... + return Loading...; } let realDoc = doc; @@ -84,13 +84,13 @@ export class KeyValueBox extends React.Component { if (!(key in ids)) { ids[key] = key; } - }) + }); } let rows: JSX.Element[] = []; let i = 0; for (let key in ids) { - rows.push() + rows.push(); } return rows; } @@ -125,6 +125,6 @@ export class KeyValueBox extends React.Component { {this.newKeyValue()} - ) + ); } } \ No newline at end of file diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index c6a0a7296..a1050dc6e 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -1,14 +1,14 @@ 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 React = require("react"); import { FieldViewProps, FieldView } from './FieldView'; import { Opt, Field } from '../../../fields/Field'; -import { observer } from "mobx-react" +import { observer } from "mobx-react"; import { observable, action } from 'mobx'; import { Document } from '../../../fields/Document'; import { Key } from '../../../fields/Key'; -import { Server } from "../../Server" +import { Server } from "../../Server"; import { EditableView } from "../EditableView"; import { CompileScript, ToField } from "../../util/Scripting"; import { Transform } from '../../util/Transform'; @@ -25,7 +25,7 @@ export interface KeyValuePairProps { export class KeyValuePair extends React.Component { @observable - private key: Opt + private key: Opt; constructor(props: KeyValuePairProps) { super(props); @@ -41,7 +41,7 @@ export class KeyValuePair extends React.Component { render() { if (!this.key) { - return error + return error; } let props: FieldViewProps = { @@ -55,7 +55,7 @@ export class KeyValuePair extends React.Component { onActiveChanged: emptyFunction, ScreenToLocalTransform: Transform.Identity, focus: emptyFunction, - } + }; let contents = ( ); @@ -101,6 +101,6 @@ export class KeyValuePair extends React.Component { return false; }}> - ) + ); } } \ No newline at end of file diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index 4791d6029..b016a3d48 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -2,8 +2,8 @@ import { observable, computed, action } from "mobx"; import React = require("react"); import { SelectionManager } from "../../util/SelectionManager"; import { observer } from "mobx-react"; -import './LinkBox.scss' -import { KeyStore } from '../../../fields/KeyStore' +import './LinkBox.scss'; +import { KeyStore } from '../../../fields/KeyStore'; import { props } from "bluebird"; import { DocumentView } from "./DocumentView"; import { Document } from "../../../fields/Document"; @@ -30,7 +30,7 @@ interface Props { linkName: String; pairedDoc: Document; type: String; - showEditor: () => void + showEditor: () => void; } @observer @@ -49,15 +49,16 @@ export class LinkBox extends React.Component { } else if (contextDoc instanceof Document) { this.props.pairedDoc.GetTAsync(KeyStore.Page, NumberField).then((pfield: any) => { contextDoc.GetTAsync(KeyStore.CurPage, NumberField).then((cfield: any) => { - if (pfield !== cfield) + if (pfield !== cfield) { contextDoc.SetNumber(KeyStore.CurPage, pfield.Data); + } let contextView = DocumentManager.Instance.getDocumentView(contextDoc); if (contextView) { contextView.props.focus(contextDoc); } else { CollectionDockingView.Instance.AddRightSplit(contextDoc); } - }) + }); }); } }); @@ -80,7 +81,7 @@ export class LinkBox extends React.Component { if (field) { field.Data.splice(field.Data.indexOf(this.props.linkDoc)); } - }) + }); } }); this.props.linkDoc.GetTAsync(KeyStore.LinkedToDocs, Document, field => { @@ -89,7 +90,7 @@ export class LinkBox extends React.Component { if (field) { field.Data.splice(field.Data.indexOf(this.props.linkDoc)); } - }) + }); } }); } @@ -117,6 +118,6 @@ export class LinkBox extends React.Component { - ) + ); } } \ No newline at end of file diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index 3f7b4bf2d..bde50fed8 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -2,8 +2,8 @@ import { observable, computed, action } from "mobx"; import React = require("react"); import { SelectionManager } from "../../util/SelectionManager"; import { observer } from "mobx-react"; -import './LinkEditor.scss' -import { KeyStore } from '../../../fields/KeyStore' +import './LinkEditor.scss'; +import { KeyStore } from '../../../fields/KeyStore'; import { props } from "bluebird"; import { DocumentView } from "./DocumentView"; import { Document } from "../../../fields/Document"; @@ -43,7 +43,7 @@ export class LinkEditor extends React.Component {
    SAVE
    - ) + ); } @action diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index 6c2d24630..ac09da305 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -13,7 +13,7 @@ import React = require("react"); interface Props { docView: DocumentView; - changeFlyout: () => void + changeFlyout: () => void; } @observer @@ -25,9 +25,9 @@ export class LinkMenu extends React.Component { return links.map(link => { let doc = link.GetT(key, Document); if (doc && doc !== FieldWaiting) { - return this._editingLink = link)} type={type} /> + return this._editingLink = link)} type={type} />; } - }) + }); } render() { @@ -43,11 +43,11 @@ export class LinkMenu extends React.Component { {this.renderLinkItems(linkFrom, KeyStore.LinkedFromDocs, "Source: ")} - ) + ); } else { return ( this._editingLink = undefined)}> - ) + ); } } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 774d9be3e..81ceb37f6 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -17,7 +17,7 @@ import { FieldView, FieldViewProps } from './FieldView'; import "./ImageBox.scss"; import "./PDFBox.scss"; import { Sticky } from './Sticky'; //you should look at sticky and annotation, because they are used here -import React = require("react") +import React = require("react"); import { SelectionManager } from "../../util/SelectionManager"; /** ALSO LOOK AT: Annotation.tsx, Sticky.tsx @@ -55,7 +55,7 @@ import { SelectionManager } from "../../util/SelectionManager"; export class PDFBox extends React.Component { public static LayoutString() { return FieldView.LayoutString(PDFBox); } - private _mainDiv = React.createRef() + private _mainDiv = React.createRef(); private _pdf = React.createRef(); @observable private _renderAsSvg = true; @@ -72,7 +72,7 @@ export class PDFBox extends React.Component { private _currTool: any; //keeps track of current tool button reference private _drawToolOn: boolean = false; //boolean that keeps track of the drawing tool - private _drawTool = React.createRef()//drawing tool button reference + private _drawTool = React.createRef();//drawing tool button reference private _colorTool = React.createRef(); //color button reference private _currColor: string = "black"; //current color that user selected (for ink/pen) @@ -85,7 +85,7 @@ export class PDFBox extends React.Component { @observable private _perPageInfo: Object[] = []; //stores pageInfo @observable private _pageInfo: any = { area: [], divs: [], anno: [] }; //divs is array of objects linked to anno - @observable private _currAnno: any = [] + @observable private _currAnno: any = []; @observable private _interactive: boolean = false; @observable private _loaded: boolean = false; @@ -168,24 +168,24 @@ export class PDFBox extends React.Component { let obj: Object = { parentDivs: [], spans: [] }; //@ts-ignore if (range.commonAncestorContainer.className === 'react-pdf__Page__textContent') { //multiline highlighting case - obj = this.highlightNodes(range.commonAncestorContainer.childNodes) + obj = this.highlightNodes(range.commonAncestorContainer.childNodes); } else { //single line highlighting case - let parentDiv = range.commonAncestorContainer.parentElement + let parentDiv = range.commonAncestorContainer.parentElement; if (parentDiv) { if (parentDiv.className === 'react-pdf__Page__textContent') { //when highlight is overwritten - obj = this.highlightNodes(parentDiv.childNodes) + obj = this.highlightNodes(parentDiv.childNodes); } else { parentDiv.childNodes.forEach((child) => { if (child.nodeName === 'SPAN') { //@ts-ignore - obj.parentDivs.push(parentDiv) + obj.parentDivs.push(parentDiv); //@ts-ignore - child.id = "highlighted" + child.id = "highlighted"; //@ts-ignore - obj.spans.push(child) + obj.spans.push(child); child.addEventListener("mouseover", this.onEnter); //adds mouseover annotation handler } - }) + }); } } } @@ -196,21 +196,21 @@ export class PDFBox extends React.Component { } highlightNodes = (nodes: NodeListOf) => { - let temp = { parentDivs: [], spans: [] } + let temp = { parentDivs: [], spans: [] }; nodes.forEach((div) => { div.childNodes.forEach((child) => { if (child.nodeName === 'SPAN') { //@ts-ignore - temp.parentDivs.push(div) + temp.parentDivs.push(div); //@ts-ignore - child.id = "highlighted" + child.id = "highlighted"; //@ts-ignore - temp.spans.push(child) + temp.spans.push(child); child.addEventListener("mouseover", this.onEnter); //adds mouseover annotation handler } - }) + }); - }) + }); return temp; } @@ -228,8 +228,8 @@ export class PDFBox extends React.Component { index = this._pageInfo.divs.indexOf(obj); } } - }) - }) + }); + }); if (this._pageInfo.anno.length >= index + 1) { if (this._currAnno.length === 0) { @@ -239,13 +239,13 @@ export class PDFBox extends React.Component { if (this._currAnno.length === 0) { //if there are no current annotation let div = span.offsetParent; //@ts-ignore - let divX = div.style.left + let divX = div.style.left; //@ts-ignore - let divY = div.style.top + let divY = div.style.top; //slicing "px" from the end divX = divX.slice(0, divX.length - 2); //gets X of the DIV element (parent of Span) divY = divY.slice(0, divY.length - 2); //gets Y of the DIV element (parent of Span) - let annotation = + let annotation = ; this._pageInfo.anno.push(annotation); this._currAnno.push(annotation); } @@ -263,7 +263,7 @@ export class PDFBox extends React.Component { this.makeEditableAndHighlight(color); } } catch (ex) { - this.makeEditableAndHighlight(color) + this.makeEditableAndHighlight(color); } } } @@ -305,7 +305,7 @@ export class PDFBox extends React.Component { } if (this._mainDiv.current) { - let sticky = + let sticky = ; this._pageInfo.area.push(sticky); } this._toolOn = false; @@ -404,19 +404,19 @@ export class PDFBox extends React.Component { if (e instanceof HTMLCanvasElement) { this._pdfCanvas = e; - this._pdfContext = e.getContext("2d") + this._pdfContext = e.getContext("2d"); } - }) + }); } - }) + }); } // bcz: the number of pages should really be set when the document is imported. this.props.Document.SetNumber(KeyStore.NumPages, page._transport.numPages); if (this._perPageInfo.length === 0) { //Makes sure it only runs once - this._perPageInfo = [...Array(page._transport.numPages)] + this._perPageInfo = [...Array(page._transport.numPages)]; } this._loaded = true; } diff --git a/src/client/views/nodes/Sticky.tsx b/src/client/views/nodes/Sticky.tsx index 4a4d69e90..11719831b 100644 --- a/src/client/views/nodes/Sticky.tsx +++ b/src/client/views/nodes/Sticky.tsx @@ -39,7 +39,7 @@ export class Sticky extends React.Component { document.addEventListener("pointermove", this.drawMove); document.addEventListener("pointerup", this.drawUp); } - }; + } //when user drags drawMove = (e: PointerEvent): void => { @@ -49,7 +49,7 @@ export class Sticky extends React.Component { //connects the point this.ctx.lineTo(x, y); this.ctx.stroke(); - }; + } /** * when user lifts the mouse, the drawing ends @@ -58,7 +58,7 @@ export class Sticky extends React.Component { this.ctx.closePath(); console.log(this.ctx); document.removeEventListener("pointermove", this.drawMove); - }; + } render() { return ( diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index a08b320e8..314af64c9 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -1,4 +1,4 @@ -import React = require("react") +import React = require("react"); import { observer } from "mobx-react"; import { FieldWaiting, Opt } from '../../../fields/Field'; import { VideoField } from '../../../fields/VideoField'; @@ -13,8 +13,8 @@ import { number } from "prop-types"; export class VideoBox extends React.Component { private _reactionDisposer: Opt; - private _videoRef = React.createRef() - public static LayoutString() { return FieldView.LayoutString(VideoBox) } + private _videoRef = React.createRef(); + public static LayoutString() { return FieldView.LayoutString(VideoBox); } constructor(props: FieldViewProps) { super(props); @@ -58,7 +58,7 @@ export class VideoBox extends React.Component { render() { let field = this.props.Document.GetT(this.props.fieldKey, VideoField); if (!field || field === FieldWaiting) { - return
    Loading
    + return
    Loading
    ; } let path = field.Data.href; trace(); @@ -73,6 +73,6 @@ export class VideoBox extends React.Component { } - ) + ); } } \ No newline at end of file diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index c1d389001..90ce72c41 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -1,9 +1,9 @@ import "./WebBox.scss"; -import React = require("react") +import React = require("react"); import { WebField } from '../../../fields/WebField'; import { FieldViewProps, FieldView } from './FieldView'; import { FieldWaiting } from '../../../fields/Field'; -import { observer } from "mobx-react" +import { observer } from "mobx-react"; import { computed } from 'mobx'; import { KeyStore } from '../../../fields/KeyStore'; @@ -33,6 +33,6 @@ export class WebBox extends React.Component { return (
    {content} -
    ) + ); } } \ No newline at end of file diff --git a/src/debug/Test.tsx b/src/debug/Test.tsx index c8de33f41..11f2b0c4e 100644 --- a/src/debug/Test.tsx +++ b/src/debug/Test.tsx @@ -1,10 +1,10 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import JsxParser from 'react-jsx-parser' +import JsxParser from 'react-jsx-parser'; class Hello extends React.Component<{ firstName: string, lastName: string }> { render() { - return
    Hello {this.props.firstName} {this.props.lastName}
    + return
    Hello {this.props.firstName} {this.props.lastName}
    ; } } @@ -16,8 +16,8 @@ class Test extends React.Component { firstName: "First", lastName: "Last" } - } - return + }; + return ; } } diff --git a/src/debug/Viewer.tsx b/src/debug/Viewer.tsx index f13cccd24..857da1ebb 100644 --- a/src/debug/Viewer.tsx +++ b/src/debug/Viewer.tsx @@ -40,22 +40,22 @@ class ListViewer extends React.Component<{ field: ListField }>{
    {this.props.field.Data.map(field => )}
    - ) + ); } else { - content = <>[...] ({this.props.field.Id}) + content = <>[...] ({this.props.field.Id}); } return (
    {content}
    - ) + ); } } @observer class DocumentViewer extends React.Component<{ field: Document }> { - private keyMap: ObservableMap = new ObservableMap + private keyMap: ObservableMap = new ObservableMap; private disposer?: Lambda; @@ -67,12 +67,12 @@ class DocumentViewer extends React.Component<{ field: Document }> { if (field && field instanceof Key) { this.keyMap.set(id, field); } - }) + }); } }); - } - this.disposer = this.props.field._proxies.observe(f) - f() + }; + this.disposer = this.props.field._proxies.observe(f); + f(); } componentWillUnmount() { @@ -89,8 +89,8 @@ class DocumentViewer extends React.Component<{ field: Document }> { ({key ? key.Name : kv[0]}): - ) - }) + ); + }); return (
    Document ({this.props.field.Id}) @@ -98,7 +98,7 @@ class DocumentViewer extends React.Component<{ field: Document }> { {fields}
    - ) + ); } } @@ -111,15 +111,15 @@ class DebugViewer extends React.Component<{ fieldId: string }> { private error?: string; constructor(props: { fieldId: string }) { - super(props) - this.update() + super(props); + this.update(); } update() { Server.GetField(this.props.fieldId, action((field: Opt) => { this.field = field; if (!field) { - this.error = `Field with id ${this.props.fieldId} not found` + this.error = `Field with id ${this.props.fieldId} not found`; } })); @@ -130,20 +130,20 @@ class DebugViewer extends React.Component<{ fieldId: string }> { if (this.field) { // content = this.field.ToJson(); if (this.field instanceof ListField) { - content = () + content = (); } else if (this.field instanceof Document) { - content = () + content = (); } else if (this.field instanceof BasicField) { - content = () + content = (); } else if (this.field instanceof Key) { - content = () + content = (); } else { - content = (Unrecognized field type) + content = (Unrecognized field type); } } else if (this.error) { - content = Field {this.props.fieldId} not found + content = Field {this.props.fieldId} not found ; } else { - content = Field loading: {this.props.fieldId} + content = Field loading: {this.props.fieldId}; } return content; } @@ -165,8 +165,8 @@ class Viewer extends React.Component { @action onKeyPress = (e: React.KeyboardEvent) => { if (e.key === "Enter") { - this.ids.push(this.idToAdd) - this.idToAdd = "" + this.ids.push(this.idToAdd); + this.idToAdd = ""; } } @@ -180,7 +180,7 @@ class Viewer extends React.Component { {this.ids.map(id => )} - ) + ); } } diff --git a/src/fields/AudioField.ts b/src/fields/AudioField.ts index 252a5b74e..996d2556d 100644 --- a/src/fields/AudioField.ts +++ b/src/fields/AudioField.ts @@ -25,7 +25,7 @@ export class AudioField extends BasicField { type: Types.Audio, data: this.Data.href, _id: this.Id - } + }; } } \ No newline at end of file diff --git a/src/fields/BasicField.ts b/src/fields/BasicField.ts index 3083a1937..17b1fc4e8 100644 --- a/src/fields/BasicField.ts +++ b/src/fields/BasicField.ts @@ -1,4 +1,4 @@ -import { Field, FieldId } from "./Field" +import { Field, FieldId } from "./Field"; import { observable, computed, action } from "mobx"; import { Server } from "../client/Server"; import { UndoManager } from "../client/util/UndoManager"; @@ -9,7 +9,7 @@ export abstract class BasicField extends Field { this.data = data; if (save) { - Server.UpdateField(this) + Server.UpdateField(this); } } @@ -36,7 +36,7 @@ export abstract class BasicField extends Field { UndoManager.AddEvent({ undo: () => this.Data = oldValue, redo: () => this.Data = value - }) + }); Server.UpdateField(this); } diff --git a/src/fields/Document.ts b/src/fields/Document.ts index 4fa478f32..4584660fb 100644 --- a/src/fields/Document.ts +++ b/src/fields/Document.ts @@ -37,21 +37,24 @@ export class Document extends Field { } } - public Width = () => this.GetNumber(KeyStore.Width, 0) + public Width = () => this.GetNumber(KeyStore.Width, 0); public Height = () => this.GetNumber(KeyStore.Height, this.GetNumber(KeyStore.NativeWidth, 0) ? (this.GetNumber(KeyStore.NativeHeight, 0) / this.GetNumber(KeyStore.NativeWidth, 0)) * this.GetNumber(KeyStore.Width, 0) : 0); - public Scale = () => this.GetNumber(KeyStore.Scale, 1) + public Scale = () => this.GetNumber(KeyStore.Scale, 1); @computed public get Title(): string { let title = this.Get(KeyStore.Title, true); - if (title) - if (title !== FieldWaiting && title instanceof TextField) + if (title) { + if (title !== FieldWaiting && title instanceof TextField) { return title.Data; + } else return "-waiting-"; + } let parTitle = this.GetT(KeyStore.Title, TextField); - if (parTitle) + if (parTitle) { if (parTitle !== FieldWaiting) return parTitle.Data + ".alias"; else return "-waiting-.alias"; + } return "-untitled-"; } @@ -398,11 +401,13 @@ export class Document extends Field { } } else - if (field instanceof Document) // ... TODO bcz: should we copy documents or reference them - copy.Set(key!, field) - else if (field) - copy.Set(key!, field.Copy()) - }) + if (field instanceof Document) { // ... TODO bcz: should we copy documents or reference them + copy.Set(key!, field); + } + else if (field) { + copy.Set(key!, field.Copy()); + } + }); } }); return copy; diff --git a/src/fields/DocumentReference.ts b/src/fields/DocumentReference.ts index 9d3c209b4..6c0c1ef82 100644 --- a/src/fields/DocumentReference.ts +++ b/src/fields/DocumentReference.ts @@ -52,6 +52,6 @@ export class DocumentReference extends Field { type: Types.DocumentReference, data: this.document.Id, _id: this.Id - } + }; } } \ No newline at end of file diff --git a/src/fields/Field.ts b/src/fields/Field.ts index 0d0e56f77..d9db23b9e 100644 --- a/src/fields/Field.ts +++ b/src/fields/Field.ts @@ -65,5 +65,5 @@ export abstract class Field { abstract Copy(): Field; - abstract ToJson(): { _id: string, type: Types, data: any } + abstract ToJson(): { _id: string, type: Types, data: any }; } \ No newline at end of file diff --git a/src/fields/HtmlField.ts b/src/fields/HtmlField.ts index 7cbdf7e58..65665cf7a 100644 --- a/src/fields/HtmlField.ts +++ b/src/fields/HtmlField.ts @@ -20,6 +20,6 @@ export class HtmlField extends BasicField { type: Types.Html, data: this.Data, _id: this.Id, - } + }; } } \ No newline at end of file diff --git a/src/fields/ImageField.ts b/src/fields/ImageField.ts index ef616b2ad..dd843026f 100644 --- a/src/fields/ImageField.ts +++ b/src/fields/ImageField.ts @@ -24,6 +24,6 @@ export class ImageField extends BasicField { type: Types.Image, data: this.Data.href, _id: this.Id - } + }; } } \ No newline at end of file diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts index 2a4ed18e7..ab706ee30 100644 --- a/src/fields/InkField.ts +++ b/src/fields/InkField.ts @@ -36,7 +36,7 @@ export class InkField extends BasicField { type: Types.Ink, data: this.Data, _id: this.Id, - } + }; } UpdateFromServer(data: any) { diff --git a/src/fields/Key.ts b/src/fields/Key.ts index 00d78d516..c7f806b88 100644 --- a/src/fields/Key.ts +++ b/src/fields/Key.ts @@ -1,4 +1,4 @@ -import { Field, FieldId } from "./Field" +import { Field, FieldId } from "./Field"; import { Utils } from "../Utils"; import { observable } from "mobx"; import { Types } from "../server/Message"; @@ -16,7 +16,7 @@ export class Key extends Field { this.name = name; if (save) { - Server.UpdateField(this) + Server.UpdateField(this); } } @@ -45,6 +45,6 @@ export class Key extends Field { type: Types.Key, data: this.name, _id: this.Id - } + }; } } \ No newline at end of file diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts index e24e9c8ec..425408273 100644 --- a/src/fields/KeyStore.ts +++ b/src/fields/KeyStore.ts @@ -56,8 +56,9 @@ export namespace KeyStore { ]; export function KeyLookup(keyid: string) { for (const key of KeyList) { - if (key.Id === keyid) + if (key.Id === keyid) { return key; + } } return undefined; } diff --git a/src/fields/ListField.ts b/src/fields/ListField.ts index 815a3df73..b6eab5f86 100644 --- a/src/fields/ListField.ts +++ b/src/fields/ListField.ts @@ -7,7 +7,7 @@ import { Field, FieldId } from "./Field"; import { FieldMap } from "../client/SocketStub"; export class ListField extends BasicField { - private _proxies: string[] = [] + private _proxies: string[] = []; constructor(data: T[] = [], id?: FieldId, save: boolean = true) { super(data, save, id); this.updateProxies(); @@ -22,23 +22,24 @@ export class ListField extends BasicField { private observeDisposer: Lambda | undefined; private observeList(): void { if (this.observeDisposer) { - this.observeDisposer() + this.observeDisposer(); } this.observeDisposer = observe(this.Data as IObservableArray, (change: IArrayChange | IArraySplice) => { - this.updateProxies() + this.updateProxies(); if (change.type === "splice") { UndoManager.AddEvent({ undo: () => this.Data.splice(change.index, change.addedCount, ...change.removed), redo: () => this.Data.splice(change.index, change.removedCount, ...change.added) - }) + }); } else { UndoManager.AddEvent({ undo: () => this.Data[change.index] = change.oldValue, redo: () => this.Data[change.index] = change.newValue - }) + }); } - if (!this._processingServerUpdate) + if (!this._processingServerUpdate) { Server.UpdateField(this); + } }); } @@ -78,19 +79,23 @@ export class ListField extends BasicField { var proxies = this._proxies.map(p => p); var added = this.data.length < this._proxies.length; var deleted = this.data.length > this._proxies.length; - for (let i = 0; i < dataids.length && added; i++) + for (let i = 0; i < dataids.length && added; i++) { added = proxies.indexOf(dataids[i]) !== -1; - for (let i = 0; i < this._proxies.length && deleted; i++) + } + for (let i = 0; i < this._proxies.length && deleted; i++) { deleted = dataids.indexOf(proxies[i]) !== -1; + } this._processingServerUpdate = true; for (let i = 0; i < proxies.length && added; i++) { - if (dataids.indexOf(proxies[i]) === -1) + if (dataids.indexOf(proxies[i]) === -1) { this.Data.splice(i, 0, fields[proxies[i]] as T); + } } for (let i = dataids.length - 1; i >= 0 && deleted; i--) { - if (proxies.indexOf(dataids[i]) === -1) + if (proxies.indexOf(dataids[i]) === -1) { this.Data.splice(i, 1); + } } if (!added && !deleted) {// otherwise, just rebuild the whole list this.setData(proxies.map(id => fields[id] as T)); @@ -98,7 +103,7 @@ export class ListField extends BasicField { this._processingServerUpdate = false; } callback(this); - })) + })); } ToScriptString(): string { @@ -114,12 +119,12 @@ export class ListField extends BasicField { type: Types.List, data: this._proxies || [], _id: this.Id - } + }; } static FromJson(id: string, ids: string[]): ListField { let list = new ListField([], id, false); list._proxies = ids; - return list + return list; } } \ No newline at end of file diff --git a/src/fields/NumberField.ts b/src/fields/NumberField.ts index e0c8648de..45b920e31 100644 --- a/src/fields/NumberField.ts +++ b/src/fields/NumberField.ts @@ -1,4 +1,4 @@ -import { BasicField } from "./BasicField" +import { BasicField } from "./BasicField"; import { Types } from "../server/Message"; import { FieldId } from "./Field"; @@ -20,6 +20,6 @@ export class NumberField extends BasicField { _id: this.Id, type: Types.Number, data: this.Data - } + }; } } \ No newline at end of file diff --git a/src/fields/PDFField.ts b/src/fields/PDFField.ts index 436c1cf2b..65e179894 100644 --- a/src/fields/PDFField.ts +++ b/src/fields/PDFField.ts @@ -1,6 +1,6 @@ import { BasicField } from "./BasicField"; import { Field, FieldId } from "./Field"; -import { observable } from "mobx" +import { observable } from "mobx"; import { Types } from "../server/Message"; @@ -27,7 +27,7 @@ export class PDFField extends BasicField { type: Types.PDF, data: this.Data.href, _id: this.Id - } + }; } @observable diff --git a/src/fields/RichTextField.ts b/src/fields/RichTextField.ts index 5efb43314..6f7b3074a 100644 --- a/src/fields/RichTextField.ts +++ b/src/fields/RichTextField.ts @@ -20,7 +20,7 @@ export class RichTextField extends BasicField { type: Types.RichText, data: this.Data, _id: this.Id - } + }; } } \ No newline at end of file diff --git a/src/fields/TextField.ts b/src/fields/TextField.ts index 71d8ea310..69d26f42f 100644 --- a/src/fields/TextField.ts +++ b/src/fields/TextField.ts @@ -1,4 +1,4 @@ -import { BasicField } from "./BasicField" +import { BasicField } from "./BasicField"; import { FieldId } from "./Field"; import { Types } from "../server/Message"; @@ -20,6 +20,6 @@ export class TextField extends BasicField { type: Types.Text, data: this.Data, _id: this.Id - } + }; } } \ No newline at end of file diff --git a/src/fields/TupleField.ts b/src/fields/TupleField.ts index e2162c751..ad0f6f350 100644 --- a/src/fields/TupleField.ts +++ b/src/fields/TupleField.ts @@ -21,7 +21,7 @@ export class TupleField extends BasicField<[T, U]> { UndoManager.AddEvent({ undo: () => this.Data[change.index] = change.oldValue, redo: () => this.Data[change.index] = change.newValue - }) + }); Server.UpdateField(this); } else { throw new Error("Why are you messing with the length of a tuple, huh?"); @@ -31,7 +31,7 @@ export class TupleField extends BasicField<[T, U]> { protected setData(value: [T, U]) { if (this.observeDisposer) { - this.observeDisposer() + this.observeDisposer(); } this.data = observable(value) as (T | U)[] as [T, U]; this.observeTuple(); @@ -54,6 +54,6 @@ export class TupleField extends BasicField<[T, U]> { type: Types.Tuple, data: this.Data, _id: this.Id - } + }; } } \ No newline at end of file diff --git a/src/fields/VideoField.ts b/src/fields/VideoField.ts index 992cc1641..d7cd7e968 100644 --- a/src/fields/VideoField.ts +++ b/src/fields/VideoField.ts @@ -24,7 +24,7 @@ export class VideoField extends BasicField { type: Types.Video, data: this.Data.href, _id: this.Id - } + }; } } \ No newline at end of file diff --git a/src/fields/WebField.ts b/src/fields/WebField.ts index 0cbcc6d33..6023e9e6b 100644 --- a/src/fields/WebField.ts +++ b/src/fields/WebField.ts @@ -24,7 +24,7 @@ export class WebField extends BasicField { type: Types.Web, data: this.Data.href, _id: this.Id - } + }; } } \ No newline at end of file diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx index c3684a0eb..ec89a1194 100644 --- a/src/mobile/ImageUpload.tsx +++ b/src/mobile/ImageUpload.tsx @@ -22,7 +22,7 @@ import { Opt } from '../fields/Field'; // } const onFileLoad = async (file: any) => { - let imgPrev = document.getElementById("img_preview") + let imgPrev = document.getElementById("img_preview"); if (imgPrev) { let files: File[] = file.target.files; if (files.length !== 0) { @@ -30,15 +30,15 @@ const onFileLoad = async (file: any) => { let formData = new FormData(); formData.append("file", files[0]); - const upload = window.location.origin + "/upload" + const upload = window.location.origin + "/upload"; const res = await fetch(upload, { method: 'POST', body: formData }); const json = await res.json(); json.map(async (file: any) => { - let path = window.location.origin + file - var doc: Document = Documents.ImageDocument(path, { nativeWidth: 200, width: 200 }) + let path = window.location.origin + file; + var doc: Document = Documents.ImageDocument(path, { nativeWidth: 200, width: 200 }); const res = await rp.get(ServerUtils.prepend(RouteStore.getUserDocumentId)); if (!res) { @@ -47,12 +47,12 @@ const onFileLoad = async (file: any) => { const field = await Server.GetField(res); let pending: Opt; if (field instanceof Document) { - pending = await field.GetTAsync(KeyStore.OptionalRightCollection, Document) + pending = await field.GetTAsync(KeyStore.OptionalRightCollection, Document); } if (pending) { pending.GetOrCreateAsync(KeyStore.Data, ListField, list => { list.Data.push(doc); - }) + }); } }); @@ -61,7 +61,7 @@ const onFileLoad = async (file: any) => { //imgPrev.setAttribute("src", window.location.origin + files[0].name) } } -} +}; ReactDOM.render((
    diff --git a/src/server/Client.ts b/src/server/Client.ts index 6b8841658..02402a5a0 100644 --- a/src/server/Client.ts +++ b/src/server/Client.ts @@ -2,14 +2,14 @@ import { computed } from "mobx"; export class Client { constructor(guid: string) { - this.guid = guid + this.guid = guid; } private guid: string; @computed public get GUID(): string { - return this.guid + return this.guid; } } \ No newline at end of file diff --git a/src/server/authentication/config/passport.ts b/src/server/authentication/config/passport.ts index b6fe15655..d42741410 100644 --- a/src/server/authentication/config/passport.ts +++ b/src/server/authentication/config/passport.ts @@ -1,4 +1,4 @@ -import * as passport from 'passport' +import * as passport from 'passport'; import * as passportLocal from 'passport-local'; import * as mongodb from 'mongodb'; import * as _ from "lodash"; @@ -22,7 +22,7 @@ passport.deserializeUser((id, done) => { passport.use(new LocalStrategy({ usernameField: 'email', passReqToCallback: true }, (req, email, password, done) => { User.findOne({ email: email.toLowerCase() }, (error: any, user: any) => { if (error) return done(error); - if (!user) return done(undefined, false, { message: "Invalid email or password" }) // invalid email + if (!user) return done(undefined, false, { message: "Invalid email or password" }); // invalid email user.comparePassword(password, (error: Error, isMatch: boolean) => { if (error) return done(error); if (!isMatch) return done(undefined, false, { message: "Invalid email or password" }); // invalid password @@ -37,7 +37,7 @@ export let isAuthenticated = (req: Request, res: Response, next: NextFunction) = return next(); } return res.redirect(RouteStore.login); -} +}; export let isAuthorized = (req: Request, res: Response, next: NextFunction) => { const provider = req.path.split("/").slice(-1)[0]; diff --git a/src/server/authentication/controllers/WorkspacesMenu.tsx b/src/server/authentication/controllers/WorkspacesMenu.tsx index 835432c8e..b08c1aebe 100644 --- a/src/server/authentication/controllers/WorkspacesMenu.tsx +++ b/src/server/authentication/controllers/WorkspacesMenu.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { observable, action, configure, reaction, computed, ObservableMap, runInAction } from 'mobx'; import { observer } from "mobx-react"; -import './WorkspacesMenu.css' +import './WorkspacesMenu.css'; import { Document } from '../../../fields/Document'; import { EditableView } from '../../../client/views/EditableView'; import { KeyStore } from '../../../fields/KeyStore'; diff --git a/src/server/authentication/controllers/user_controller.ts b/src/server/authentication/controllers/user_controller.ts index 2bbb334b5..1dacdf3fa 100644 --- a/src/server/authentication/controllers/user_controller.ts +++ b/src/server/authentication/controllers/user_controller.ts @@ -109,12 +109,12 @@ export let postLogin = (req: Request, res: Response, next: NextFunction) => { } passport.authenticate("local", (err: Error, user: DashUserModel, info: IVerifyOptions) => { - if (err) { next(err); return } + if (err) { next(err); return; } if (!user) { return res.redirect(RouteStore.signup); } req.logIn(user, (err) => { - if (err) { next(err); return } + if (err) { next(err); return; } res.redirect(RouteStore.home); }); })(req, res, next); @@ -132,14 +132,14 @@ export let getLogout = (req: Request, res: Response) => { sess.destroy((err) => { if (err) { console.log(err); } }); } res.redirect(RouteStore.login); -} +}; export let getForgot = function (req: Request, res: Response) { res.render("forgot.pug", { title: "Recover Password", user: req.user, }); -} +}; export let postForgot = function (req: Request, res: Response, next: NextFunction) { const email = req.body.email; @@ -152,14 +152,14 @@ export let postForgot = function (req: Request, res: Response, next: NextFunctio return; } done(null, buffer.toString('hex')); - }) + }); }, function (token: string, done: any) { User.findOne({ email }, function (err, user: DashUserModel) { if (!user) { // NO ACCOUNT WITH SUBMITTED EMAIL res.redirect(RouteStore.forgot); - return + return; } user.passwordResetToken = token; user.passwordResetExpires = new Date(Date.now() + 3600000); // 1 HOUR @@ -193,8 +193,8 @@ export let postForgot = function (req: Request, res: Response, next: NextFunctio ], function (err) { if (err) return next(err); res.redirect(RouteStore.forgot); - }) -} + }); +}; export let getReset = function (req: Request, res: Response) { User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } }, function (err, user: DashUserModel) { @@ -206,7 +206,7 @@ export let getReset = function (req: Request, res: Response) { user: req.user, }); }); -} +}; export let postReset = function (req: Request, res: Response) { async.waterfall([ @@ -263,4 +263,4 @@ export let postReset = function (req: Request, res: Response) { ], function (err) { res.redirect(RouteStore.login); }); -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 0ac85b446..13eddafbf 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -70,7 +70,7 @@ export class CurrentUserUtils { let doc = new Document(id); doc.Set(KeyStore.Workspaces, new ListField()); - doc.Set(KeyStore.OptionalRightCollection, Documents.SchemaDocument([], { title: "Pending documents" })) + doc.Set(KeyStore.OptionalRightCollection, Documents.SchemaDocument([], { title: "Pending documents" })); return doc; } @@ -81,7 +81,7 @@ export class CurrentUserUtils { CurrentUserUtils.curr_id = obj.id as string; CurrentUserUtils.curr_email = obj.email as string; } else { - throw new Error("There should be a user! Why does Dash think there isn't one?") + throw new Error("There should be a user! Why does Dash think there isn't one?"); } }); let userDocPromise = rp.get(ServerUtils.prepend(RouteStore.getUserDocumentId)).then(id => { @@ -92,9 +92,9 @@ export class CurrentUserUtils { } else { this.user_document = this.createUserDocument(id); } - }) + }); } else { - throw new Error("There should be a user id! Why does Dash think there isn't one?") + throw new Error("There should be a user id! Why does Dash think there isn't one?"); } }); return Promise.all([userPromise, userDocPromise]); diff --git a/src/server/authentication/models/user_model.ts b/src/server/authentication/models/user_model.ts index 81580aad5..1c6926517 100644 --- a/src/server/authentication/models/user_model.ts +++ b/src/server/authentication/models/user_model.ts @@ -2,7 +2,7 @@ import * as bcrypt from "bcrypt-nodejs"; //@ts-ignore import * as mongoose from "mongoose"; -var url = 'mongodb://localhost:27017/Dash' +var url = 'mongodb://localhost:27017/Dash'; mongoose.connect(url, { useNewUrlParser: true }); diff --git a/src/server/database.ts b/src/server/database.ts index 415acc09a..0bc806253 100644 --- a/src/server/database.ts +++ b/src/server/database.ts @@ -1,15 +1,15 @@ import * as mongodb from 'mongodb'; export class Database { - public static Instance = new Database() + public static Instance = new Database(); private MongoClient = mongodb.MongoClient; private url = 'mongodb://localhost:27017/Dash'; private db?: mongodb.Db; constructor() { this.MongoClient.connect(this.url, (err, client) => { - this.db = client.db() - }) + this.db = client.db(); + }); } private currentWrites: { [_id: string]: Promise } = {}; @@ -30,19 +30,19 @@ export class Database { // console.log(JSON.stringify(res.result)); // } if (this.currentWrites[id] === promise) { - delete this.currentWrites[id] + delete this.currentWrites[id]; } if (resolve) { resolve(); } callback(); }); - } + }; if (prom) { const newProm: Promise = prom.then(() => run(newProm)); this.currentWrites[id] = newProm; } else { - const newProm: Promise = new Promise(res => run(newProm, res)) + const newProm: Promise = new Promise(res => run(newProm, res)); this.currentWrites[id] = newProm; } } @@ -61,7 +61,7 @@ export class Database { let collection = this.db.collection(collectionName); collection.deleteMany({}, res); } - }) + }); } public insert(kvpairs: any) { @@ -70,7 +70,7 @@ export class Database { collection.insertOne(kvpairs, (err: any, res: any) => { if (err) { // console.log(err) - return + return; } }); } @@ -81,30 +81,30 @@ export class Database { if (this.db) { let collection = this.db.collection('documents'); collection.findOne({ _id: id }, (err: any, res: any) => { - result = res + result = res; if (!result) { - fn(undefined) + fn(undefined); } - fn(result) - }) - }; + fn(result); + }); + } } public getDocuments(ids: string[], fn: (res: any) => void) { if (this.db) { let collection = this.db.collection('documents'); - let cursor = collection.find({ _id: { "$in": ids } }) + let cursor = collection.find({ _id: { "$in": ids } }); cursor.toArray((err, docs) => { if (err) { console.log(err.message); console.log(err.errmsg); } fn(docs); - }) - }; + }); + } } public print() { - console.log("db says hi!") + console.log("db says hi!"); } } diff --git a/src/server/index.ts b/src/server/index.ts index f60e6e293..b9c7448b4 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -1,10 +1,10 @@ -import * as express from 'express' -const app = express() -import * as webpack from 'webpack' +import * as express from 'express'; +const app = express(); +import * as webpack from 'webpack'; import * as wdm from 'webpack-dev-middleware'; import * as whm from 'webpack-hot-middleware'; -import * as path from 'path' -import * as formidable from 'formidable' +import * as path from 'path'; +import * as formidable from 'formidable'; import * as passport from 'passport'; import { MessageStore, Transferable } from "./Message"; import { Client } from './Client'; @@ -13,7 +13,7 @@ import { Utils } from '../Utils'; import { ObservableMap } from 'mobx'; import { FieldId, Field } from '../fields/Field'; import { Database } from './database'; -import * as io from 'socket.io' +import * as io from 'socket.io'; import { getLogin, postLogin, getSignup, postSignup, getLogout, postReset, getForgot, postForgot, getReset } from './authentication/controllers/user_controller'; const config = require('../../webpack.config'); const compiler = webpack(config); @@ -31,19 +31,19 @@ const MongoStore = require('connect-mongo')(session); const mongoose = require('mongoose'); import { DashUserModel } from './authentication/models/user_model'; import * as fs from 'fs'; -import * as request from 'request' +import * as request from 'request'; import { RouteStore } from './RouteStore'; -import { exec } from 'child_process' +import { exec } from 'child_process'; const download = (url: string, dest: fs.PathLike) => { request.get(url).pipe(fs.createWriteStream(dest)); -} +}; const mongoUrl = 'mongodb://localhost:27017/Dash'; -mongoose.connect(mongoUrl) +mongoose.connect(mongoUrl); mongoose.connection.on('connected', function () { console.log("connected"); -}) +}); // SESSION MANAGEMENT AND AUTHENTICATION MIDDLEWARE // ORDER OF IMPORTS MATTERS @@ -73,7 +73,7 @@ app.use((req, res, next) => { app.get("/hello", (req, res) => { res.send("

    Hello

    "); -}) +}); enum Method { GET, @@ -98,7 +98,7 @@ function addSecureRoute(method: Method, const dashUser: DashUserModel = req.user; if (!dashUser) return onRejection(res); handler(dashUser, res, req); - } + }; subscribers.forEach(route => { switch (method) { case Method.GET: @@ -116,7 +116,7 @@ function addSecureRoute(method: Method, let FieldStore: ObservableMap = new ObservableMap(); app.use(express.static(__dirname + RouteStore.public)); -app.use(RouteStore.images, express.static(__dirname + RouteStore.public)) +app.use(RouteStore.images, express.static(__dirname + RouteStore.public)); app.get("/pull", (req, res) => { exec('"C:\\Program Files\\Git\\git-bash.exe" -c "git pull"', (err, stdout, stderr) => { @@ -125,7 +125,7 @@ app.get("/pull", (req, res) => { return; } res.redirect("/"); - }) + }); }); // GETTERS @@ -178,13 +178,13 @@ addSecureRoute( addSecureRoute( Method.POST, (user, res, req) => { - let form = new formidable.IncomingForm() - form.uploadDir = __dirname + "/public/files/" - form.keepExtensions = true + let form = new formidable.IncomingForm(); + form.uploadDir = __dirname + "/public/files/"; + form.keepExtensions = true; // let path = req.body.path; - console.log("upload") + console.log("upload"); form.parse(req, (err, fields, files) => { - console.log("parsing") + console.log("parsing"); let names: any[] = []; for (const name in files) { let file = files[name]; @@ -211,8 +211,8 @@ app.post(RouteStore.login, postLogin); app.get(RouteStore.logout, getLogout); // FORGOT PASSWORD EMAIL HANDLING -app.get(RouteStore.forgot, getForgot) -app.post(RouteStore.forgot, postForgot) +app.get(RouteStore.forgot, getForgot); +app.post(RouteStore.forgot, postForgot); // RESET PASSWORD EMAIL HANDLING app.get(RouteStore.reset, getReset); @@ -232,32 +232,32 @@ app.get(RouteStore.deleteAll, (req, res) => { app.use(wdm(compiler, { publicPath: config.output.publicPath -})) +})); -app.use(whm(compiler)) +app.use(whm(compiler)); // start the Express server app.listen(port, () => { console.log(`server started at http://localhost:${port}`); -}) +}); const server = io(); interface Map { [key: string]: Client; } -let clients: Map = {} +let clients: Map = {}; server.on("connection", function (socket: Socket) { - console.log("a user has connected") + console.log("a user has connected"); - Utils.Emit(socket, MessageStore.Foo, "handshooken") + Utils.Emit(socket, MessageStore.Foo, "handshooken"); - Utils.AddServerHandler(socket, MessageStore.Bar, barReceived) - Utils.AddServerHandler(socket, MessageStore.SetField, (args) => setField(socket, args)) - Utils.AddServerHandlerCallback(socket, MessageStore.GetField, getField) - Utils.AddServerHandlerCallback(socket, MessageStore.GetFields, getFields) - Utils.AddServerHandler(socket, MessageStore.DeleteAll, deleteFields) -}) + Utils.AddServerHandler(socket, MessageStore.Bar, barReceived); + Utils.AddServerHandler(socket, MessageStore.SetField, (args) => setField(socket, args)); + Utils.AddServerHandlerCallback(socket, MessageStore.GetField, getField); + Utils.AddServerHandlerCallback(socket, MessageStore.GetFields, getFields); + Utils.AddServerHandler(socket, MessageStore.DeleteAll, deleteFields); +}); function deleteFields() { return Database.Instance.deleteAll(); @@ -276,12 +276,12 @@ function barReceived(guid: String) { function getField([id, callback]: [string, (result: any) => void]) { Database.Instance.getDocument(id, (result: any) => { if (result) { - callback(result) + callback(result); } else { - callback(undefined) + callback(undefined); } - }) + }); } function getFields([ids, callback]: [string[], (result: any) => void]) { @@ -291,7 +291,7 @@ function getFields([ids, callback]: [string[], (result: any) => void]) { function setField(socket: Socket, newValue: Transferable) { Database.Instance.update(newValue._id, newValue, () => { socket.broadcast.emit(MessageStore.SetField.Message, newValue); - }) + }); } server.listen(serverPort); diff --git a/test/test.ts b/test/test.ts index db24cae5f..16cace026 100644 --- a/test/test.ts +++ b/test/test.ts @@ -1,6 +1,6 @@ import { NumberField } from "../src/fields/NumberField"; import { expect } from 'chai'; -import 'mocha' +import 'mocha'; import { Key } from "../src/fields/Key"; import { Document } from "../src/fields/Document"; import { autorun, reaction } from "mobx"; @@ -17,7 +17,7 @@ describe('Number Controller', () => { it('Should update', () => { const numController = new NumberField(15); let ran = false; - reaction(() => numController.Data, (data) => { ran = true; }) + reaction(() => numController.Data, (data) => { ran = true; }); expect(ran).to.equal(false); numController.Data = 5; expect(ran).to.equal(true); @@ -42,7 +42,7 @@ describe("Document", () => { let key = new Key("Test"); let key2 = new Key("Test2"); let ran = false; - reaction(() => doc.Get(key), (field) => { ran = true }); + reaction(() => doc.Get(key), (field) => { ran = true; }); expect(ran).to.equal(false); doc.Set(key2, new NumberField(4)); diff --git a/tslint.json b/tslint.json index 54876916e..aa4dee4e5 100644 --- a/tslint.json +++ b/tslint.json @@ -27,14 +27,14 @@ "arrow-return-shorthand": true, // "object-literal-shorthand": true, // "object-literal-sort-keys": true, - // "semicolon": [ - // true, - // "always" - // ], - // "curly": [ - // true, - // "ignore-same-line" - // ], + "semicolon": [ + true, + "always" + ], + "curly": [ + true, + "ignore-same-line" + ], // "quotemark": [ // true, // "double", -- cgit v1.2.3-70-g09d2 From fd8e6bf2cacf8bed7c3363eeb777c731741266d5 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 11 Apr 2019 20:19:57 -0400 Subject: made COLLECTION_BORDER_WIDTH a CSS variable accessible from code. changed global css variable file name --- src/client/util/DragManager.ts | 5 ++-- src/client/util/TooltipTextMenu.scss | 2 +- src/client/views/ContextMenu.scss | 2 +- src/client/views/DocumentDecorations.scss | 2 +- src/client/views/InkingCanvas.scss | 2 +- src/client/views/InkingControl.scss | 2 +- src/client/views/Main.scss | 2 +- src/client/views/_global_variables.scss | 28 ------------------- src/client/views/_global_variables.scss.d.ts | 7 ----- src/client/views/_global_variables.ts | 8 ------ .../views/collections/CollectionBaseView.tsx | 1 - .../views/collections/CollectionDockingView.scss | 6 +++++ .../views/collections/CollectionDockingView.tsx | 9 +------ .../views/collections/CollectionSchemaView.scss | 6 +++-- .../views/collections/CollectionSchemaView.tsx | 10 ++++--- .../views/collections/CollectionTreeView.scss | 6 +++-- .../views/collections/CollectionTreeView.tsx | 4 +-- .../CollectionFreeFormRemoteCursors.scss | 2 +- .../collectionFreeForm/CollectionFreeFormView.scss | 10 ++++--- .../collectionFreeForm/CollectionFreeFormView.tsx | 31 ++++++++++++---------- src/client/views/globalCssVariables.scss | 29 ++++++++++++++++++++ src/client/views/globalCssVariables.scss.d.ts | 8 ++++++ src/client/views/nodes/DocumentView.scss | 2 +- src/client/views/nodes/FormattedTextBox.scss | 2 +- src/client/views/nodes/KeyValueBox.scss | 2 +- src/client/views/nodes/KeyValuePair.scss | 2 +- src/client/views/nodes/LinkBox.scss | 2 +- src/client/views/nodes/LinkEditor.scss | 2 +- 28 files changed, 99 insertions(+), 95 deletions(-) delete mode 100644 src/client/views/_global_variables.scss delete mode 100644 src/client/views/_global_variables.scss.d.ts delete mode 100644 src/client/views/_global_variables.ts create mode 100644 src/client/views/globalCssVariables.scss create mode 100644 src/client/views/globalCssVariables.scss.d.ts (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 333c474c1..3d8bbb49c 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -5,8 +5,7 @@ import { CollectionDockingView } from "../views/collections/CollectionDockingVie import { DocumentDecorations } from "../views/DocumentDecorations"; import { Main } from "../views/Main"; import { DocumentView } from "../views/nodes/DocumentView"; -// import globalStyles from "../views/_global_variables"; -import * as globalStyles from "../views/_global_variables.scss"; // bcz: why doesn't this work? +import * as globalCssVariables from "../views/globalCssVariables.scss"; export function setupDrag(_reference: React.RefObject, docFunc: () => Document, moveFunc?: DragManager.MoveFunction, copyOnDrop: boolean = false) { let onRowMove = action((e: PointerEvent): void => { @@ -178,7 +177,7 @@ export namespace DragManager { dragElement.style.bottom = ""; dragElement.style.left = "0"; dragElement.style.transformOrigin = "0 0"; - dragElement.style.zIndex = globalStyles.contextMenuZindex;// "1000"; + dragElement.style.zIndex = globalCssVariables.contextMenuZindex;// "1000"; dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`; dragElement.style.width = `${rect.width / scaleX}px`; dragElement.style.height = `${rect.height / scaleY}px`; diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss index ea580d104..7deea3be6 100644 --- a/src/client/util/TooltipTextMenu.scss +++ b/src/client/util/TooltipTextMenu.scss @@ -1,4 +1,4 @@ -@import "../views/global_variables"; +@import "../views/globalCssVariables"; .tooltipMenu { position: absolute; diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss index 5acf598cf..fe884ca85 100644 --- a/src/client/views/ContextMenu.scss +++ b/src/client/views/ContextMenu.scss @@ -1,4 +1,4 @@ -@import "global_variables"; +@import "globalCssVariables"; .contextMenu-cont { position: absolute; display: flex; diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index 321bda384..ce84b6106 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -1,4 +1,4 @@ -@import "global_variables"; +@import "globalCssVariables"; .documentDecorations { position: absolute; diff --git a/src/client/views/InkingCanvas.scss b/src/client/views/InkingCanvas.scss index 42ae38c73..2c550051c 100644 --- a/src/client/views/InkingCanvas.scss +++ b/src/client/views/InkingCanvas.scss @@ -1,4 +1,4 @@ -@import "global_variables"; +@import "globalCssVariables"; .inkingCanvas { opacity:0.99; diff --git a/src/client/views/InkingControl.scss b/src/client/views/InkingControl.scss index 0d8fd8784..ba4ec41af 100644 --- a/src/client/views/InkingControl.scss +++ b/src/client/views/InkingControl.scss @@ -1,4 +1,4 @@ -@import "global_variables"; +@import "globalCssVariables"; .inking-control { position: absolute; left: 70px; diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss index 7329b8eb6..f3af26f37 100644 --- a/src/client/views/Main.scss +++ b/src/client/views/Main.scss @@ -1,4 +1,4 @@ -@import "global_variables"; +@import "globalCssVariables"; @import "nodeModuleOverrides"; html, body { diff --git a/src/client/views/_global_variables.scss b/src/client/views/_global_variables.scss deleted file mode 100644 index cd6af2dac..000000000 --- a/src/client/views/_global_variables.scss +++ /dev/null @@ -1,28 +0,0 @@ -@import url("https://fonts.googleapis.com/css?family=Noto+Sans:400,700|Crimson+Text:400,400i,700"); -// colors -$light-color: #fcfbf7; -$light-color-secondary: rgb(241, 239, 235); -$main-accent: #61aaa3; -// $alt-accent: #cdd5ec; -// $alt-accent: #cdeceb; -$alt-accent: #59dff7; -$lighter-alt-accent: rgb(207, 220, 240); -$intermediate-color: #9c9396; -$dark-color: #121721; -// fonts -$sans-serif: "Noto Sans", sans-serif; -// $sans-serif: "Roboto Slab", sans-serif; -$serif: "Crimson Text", serif; -// misc values -$border-radius: 0.3em; -// - - // dragged items -$contextMenu-zindex: 1000; // context menu shows up over everything -$mainTextInput-zindex: 999; // then text input overlay so that it's context menu will appear over decorations, etc -$docDecorations-zindex: 998; // then doc decorations appear over everything else -$remoteCursors-zindex: 997; // ... not sure what level the remote cursors should go -- is this right? - -:export { - contextMenuZindex: $contextMenu-zindex; -} \ No newline at end of file diff --git a/src/client/views/_global_variables.scss.d.ts b/src/client/views/_global_variables.scss.d.ts deleted file mode 100644 index ebee7dbce..000000000 --- a/src/client/views/_global_variables.scss.d.ts +++ /dev/null @@ -1,7 +0,0 @@ - -interface IGlobalScss { - contextMenuZindex: string; // context menu shows up over everything -} -declare const globalStyleVariables: IGlobalScss; - -export = globalStyleVariables; \ No newline at end of file diff --git a/src/client/views/_global_variables.ts b/src/client/views/_global_variables.ts deleted file mode 100644 index caedc7aad..000000000 --- a/src/client/views/_global_variables.ts +++ /dev/null @@ -1,8 +0,0 @@ -import * as globalStyleVariables from "../views/_global_variables.scss"; - -export interface IGlobalScss { - contextMenuZindex: string; // context menu shows up over everything -} -let globalStyles = globalStyleVariables as any as IGlobalScss; - -export default globalStyles; \ No newline at end of file diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index ac320eda3..0ace700ce 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -34,7 +34,6 @@ export interface CollectionViewProps extends FieldViewProps { contentRef?: React.Ref; } -export const COLLECTION_BORDER_WIDTH = 1; @observer export class CollectionBaseView extends React.Component { diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss index 583d50c5b..13dd9faa8 100644 --- a/src/client/views/collections/CollectionDockingView.scss +++ b/src/client/views/collections/CollectionDockingView.scss @@ -1,8 +1,14 @@ +@import "../../views/globalCssVariables.scss"; + .collectiondockingview-content { height: 100%; } .collectiondockingview-container { + width: 100%; + height: 100%; + border-style: solid; + border-width: $COLLECTION_BORDER_WIDTH; position: absolute; top: 0; left: 0; diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 212cf8a69..05470f405 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -13,7 +13,6 @@ import { Server } from "../../Server"; import { undoBatch } from "../../util/UndoManager"; import { DocumentView } from "../nodes/DocumentView"; import "./CollectionDockingView.scss"; -import { COLLECTION_BORDER_WIDTH } from "./CollectionBaseView"; import React = require("react"); import { SubCollectionViewProps } from "./CollectionSubView"; import { ServerUtils } from "../../../server/ServerUtil"; @@ -271,13 +270,7 @@ export class CollectionDockingView extends React.Component + onPointerDown={this.onPointerDown} onPointerUp={this.onPointerUp} ref={this._containerRef} /> ); } } diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss index c3a2e88ac..40e49bb5f 100644 --- a/src/client/views/collections/CollectionSchemaView.scss +++ b/src/client/views/collections/CollectionSchemaView.scss @@ -1,4 +1,4 @@ -@import "../global_variables"; +@import "../globalCssVariables"; //options menu styling #schemaOptionsMenuBtn { @@ -53,7 +53,9 @@ .collectionSchemaView-container { - border: 1px solid $intermediate-color; + border-width: $COLLECTION_BORDER_WIDTH; + border-color : $intermediate-color; + border-style: solid; border-radius: $border-radius; box-sizing: border-box; position: absolute; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index f1b3e1b8f..0cb88eab0 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -25,7 +25,7 @@ import "./CollectionSchemaView.scss"; import { CollectionView } from "./CollectionView"; import { CollectionSubView } from "./CollectionSubView"; import { TextField } from "../../../fields/TextField"; -import { COLLECTION_BORDER_WIDTH } from "./CollectionBaseView"; +import * as globalCssVariables from "../../views/globalCssVariables.scss"; import { emptyFunction, returnFalse } from "../../../Utils"; @@ -245,11 +245,13 @@ export class CollectionSchemaView extends CollectionSubView { this._contentScaling = r.entry.width / selected!.GetNumber(KeyStore.NativeWidth, r.entry.width); } + @computed + get borderWidth() { return globalCssVariables.COLLECTION_BORDER_WIDTH; } getContentScaling = (): number => this._contentScaling; getPanelWidth = (): number => this._panelWidth; getPanelHeight = (): number => this._panelHeight; - getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- COLLECTION_BORDER_WIDTH - this.DIVIDER_WIDTH - this._dividerX, - COLLECTION_BORDER_WIDTH).scale(1 / this._contentScaling); - getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- COLLECTION_BORDER_WIDTH - this.DIVIDER_WIDTH - this._dividerX - this._tableWidth, - COLLECTION_BORDER_WIDTH).scale(1 / this._contentScaling); + getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- this.borderWidth - this.DIVIDER_WIDTH - this._dividerX, - this.borderWidth).scale(1 / this._contentScaling); + getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- this.borderWidth - this.DIVIDER_WIDTH - this._dividerX - this._tableWidth, - this.borderWidth).scale(1 / this._contentScaling); focusDocument = (doc: Document) => { }; @@ -349,7 +351,7 @@ export class CollectionSchemaView extends CollectionSubView { ); return ( -
    +
    this.onDrop(e, {})} ref={this.createDropTarget}> {({ measureRef }) => diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index f2affbf55..973eead97 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -1,7 +1,9 @@ -@import "../global_variables"; +@import "../globalCssVariables"; .collectionTreeView-dropTarget { - border: 0px solid transparent; + border-width: $COLLECTION_BORDER_WIDTH; + border-color: transparent; + border-style: solid; border-radius: $border-radius; box-sizing: border-box; height: 100%; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 659cff9fe..20ec16822 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -11,9 +11,9 @@ import { setupDrag, DragManager } from "../../util/DragManager"; import { EditableView } from "../EditableView"; import "./CollectionTreeView.scss"; import { CollectionView } from "./CollectionView"; +import * as globalCssVariables from "../../views/globalCssVariables.scss"; import { CollectionSubView } from "./CollectionSubView"; import React = require("react"); -import { COLLECTION_BORDER_WIDTH } from './CollectionBaseView'; import { props } from 'bluebird'; @@ -139,7 +139,7 @@ export class CollectionTreeView extends CollectionSubView { ); return ( -
    e.stopPropagation()} onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }}> +
    e.stopPropagation()} onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget}>
    ); } - getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH).translate(-this.centeringShiftX, -this.centeringShiftY).transform(this.getLocalTransform()); - getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH); + @computed + get borderWidth() { + return this.isAnnotationOverlay ? 0 : globalCssVariables.COLLECTION_BORDER_WIDTH; + } + getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth).translate(-this.centeringShiftX, -this.centeringShiftY).transform(this.getLocalTransform()); + getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth); getLocalTransform = (): Transform => Transform.Identity().scale(1 / this.scale).translate(this.panX, this.panY); noScaling = () => 1; childViews = () => this.views; @@ -317,8 +321,7 @@ export class CollectionFreeFormView extends CollectionSubView {
    super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY))} - onDrop={this.onDrop.bind(this)} onDragOver={this.onDragOver} onWheel={this.onPointerWheel} - style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }} ref={this.createDropTarget}> + onDrop={this.onDrop.bind(this)} onDragOver={this.onDragOver} onWheel={this.onPointerWheel} ref={this.createDropTarget}> diff --git a/src/client/views/globalCssVariables.scss b/src/client/views/globalCssVariables.scss new file mode 100644 index 000000000..5c8e9c8fc --- /dev/null +++ b/src/client/views/globalCssVariables.scss @@ -0,0 +1,29 @@ +@import url("https://fonts.googleapis.com/css?family=Noto+Sans:400,700|Crimson+Text:400,400i,700"); +// colors +$light-color: #fcfbf7; +$light-color-secondary: rgb(241, 239, 235); +$main-accent: #61aaa3; +// $alt-accent: #cdd5ec; +// $alt-accent: #cdeceb; +$alt-accent: #59dff7; +$lighter-alt-accent: rgb(207, 220, 240); +$intermediate-color: #9c9396; +$dark-color: #121721; +// fonts +$sans-serif: "Noto Sans", sans-serif; +// $sans-serif: "Roboto Slab", sans-serif; +$serif: "Crimson Text", serif; +// misc values +$border-radius: 0.3em; +// + + // dragged items +$contextMenu-zindex: 1000; // context menu shows up over everything +$mainTextInput-zindex: 999; // then text input overlay so that it's context menu will appear over decorations, etc +$docDecorations-zindex: 998; // then doc decorations appear over everything else +$remoteCursors-zindex: 997; // ... not sure what level the remote cursors should go -- is this right? +$COLLECTION_BORDER_WIDTH: 1; +:export { + contextMenuZindex: $contextMenu-zindex; + COLLECTION_BORDER_WIDTH: $COLLECTION_BORDER_WIDTH; +} \ No newline at end of file diff --git a/src/client/views/globalCssVariables.scss.d.ts b/src/client/views/globalCssVariables.scss.d.ts new file mode 100644 index 000000000..e874b815d --- /dev/null +++ b/src/client/views/globalCssVariables.scss.d.ts @@ -0,0 +1,8 @@ + +interface IGlobalScss { + contextMenuZindex: string; // context menu shows up over everything + COLLECTION_BORDER_WIDTH: number; +} +declare const globalCssVariables: IGlobalScss; + +export = globalCssVariables; \ No newline at end of file diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index 5126e69f9..039d1bcc6 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -1,4 +1,4 @@ -@import "../global_variables"; +@import "../globalCssVariables"; .documentView-node { position: absolute; diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss index d2ba52cf9..3978c3d38 100644 --- a/src/client/views/nodes/FormattedTextBox.scss +++ b/src/client/views/nodes/FormattedTextBox.scss @@ -1,4 +1,4 @@ -@import "../global_variables"; +@import "../globalCssVariables"; .ProseMirror { width: 100%; height: auto; diff --git a/src/client/views/nodes/KeyValueBox.scss b/src/client/views/nodes/KeyValueBox.scss index 63ae75424..f60edc675 100644 --- a/src/client/views/nodes/KeyValueBox.scss +++ b/src/client/views/nodes/KeyValueBox.scss @@ -1,4 +1,4 @@ -@import "../global_variables"; +@import "../globalCssVariables"; .keyValueBox-cont { overflow-y: scroll; height: 100%; diff --git a/src/client/views/nodes/KeyValuePair.scss b/src/client/views/nodes/KeyValuePair.scss index 64e871e1c..0bb92e5a3 100644 --- a/src/client/views/nodes/KeyValuePair.scss +++ b/src/client/views/nodes/KeyValuePair.scss @@ -1,4 +1,4 @@ -@import "../global_variables"; +@import "../globalCssVariables"; .container{ display: flex; diff --git a/src/client/views/nodes/LinkBox.scss b/src/client/views/nodes/LinkBox.scss index 5d5f782d2..8bc70b48f 100644 --- a/src/client/views/nodes/LinkBox.scss +++ b/src/client/views/nodes/LinkBox.scss @@ -1,4 +1,4 @@ -@import "../global_variables"; +@import "../globalCssVariables"; .link-container { width: 100%; height: 35px; diff --git a/src/client/views/nodes/LinkEditor.scss b/src/client/views/nodes/LinkEditor.scss index fb0c69cff..ea2e7289c 100644 --- a/src/client/views/nodes/LinkEditor.scss +++ b/src/client/views/nodes/LinkEditor.scss @@ -1,4 +1,4 @@ -@import "../global_variables"; +@import "../globalCssVariables"; .edit-container { width: 100%; height: auto; -- cgit v1.2.3-70-g09d2 From 0c9d3637bde9825efe93c55afce8270b2a2e979c Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 12 Apr 2019 11:18:23 -0400 Subject: cleaned up and enabled linking to a top-level document --- src/client/util/DragManager.ts | 29 ++++++++++++++++++++- src/client/views/DocumentDecorations.tsx | 27 ++----------------- src/client/views/Main.tsx | 4 +-- .../views/collections/CollectionDockingView.tsx | 30 +++------------------- .../views/collections/CollectionSchemaView.tsx | 4 +-- .../views/collections/CollectionTreeView.tsx | 4 +-- src/client/views/nodes/DocumentView.tsx | 3 --- 7 files changed, 39 insertions(+), 62 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 2562ba18a..6a7047725 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -6,8 +6,10 @@ 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"; -export function setupDrag(_reference: React.RefObject, docFunc: () => Document, moveFunc?: DragManager.MoveFunction, copyOnDrop: boolean = false) { +export function SetupDrag(_reference: React.RefObject, docFunc: () => Document, moveFunc?: DragManager.MoveFunction, copyOnDrop: boolean = false) { let onRowMove = action((e: PointerEvent): void => { e.stopPropagation(); e.preventDefault(); @@ -39,6 +41,31 @@ export function setupDrag(_reference: React.RefObject, docFunc: return onItemDown; } +export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: number, sourceDoc: Document) { + let srcTarg = sourceDoc.GetT(KeyStore.Prototype, Document); + let draggedDocs = (srcTarg && srcTarg !== FieldWaiting) ? + srcTarg.GetList(KeyStore.LinkedToDocs, [] as Document[]).map(linkDoc => + (linkDoc.GetT(KeyStore.LinkedToDocs, Document)) as Document) : []; + let draggedFromDocs = (srcTarg && srcTarg !== FieldWaiting) ? + srcTarg.GetList(KeyStore.LinkedFromDocs, [] as Document[]).map(linkDoc => + (linkDoc.GetT(KeyStore.LinkedFromDocs, Document)) as Document) : []; + draggedDocs.push(...draggedFromDocs); + if (draggedDocs.length) { + let moddrag = [] as Document[]; + for (const draggedDoc of draggedDocs) { + let doc = await draggedDoc.GetTAsync(KeyStore.AnnotationOn, Document); + if (doc) moddrag.push(doc); + } + let dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs); + DragManager.StartDocumentDrag([dragEle], dragData, x, y, { + handlers: { + dragComplete: action(emptyFunction), + }, + hideSource: false + }); + } +} + export namespace DragManager { export function Root() { const root = document.getElementById("root"); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 69623b8f0..03b16fb0b 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -7,7 +7,7 @@ import { ListField } from "../../fields/ListField"; import { NumberField } from "../../fields/NumberField"; import { Document } from "../../fields/Document"; import { TextField } from "../../fields/TextField"; -import { DragManager } from "../util/DragManager"; +import { DragManager, DragLinksAsDocuments } from "../util/DragManager"; import { SelectionManager } from "../util/SelectionManager"; import { CollectionView } from "./collections/CollectionView"; import './DocumentDecorations.scss'; @@ -246,34 +246,11 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> document.removeEventListener("pointermove", this.onLinkButtonMoved); document.removeEventListener("pointerup", this.onLinkButtonUp); - let sourceDoc = SelectionManager.SelectedDocuments()[0].props.Document; - let srcTarg = sourceDoc.GetT(KeyStore.Prototype, Document); - let draggedDocs = (srcTarg && srcTarg !== FieldWaiting) ? - srcTarg.GetList(KeyStore.LinkedToDocs, [] as Document[]).map(linkDoc => - (linkDoc.GetT(KeyStore.LinkedToDocs, Document)) as Document) : []; - let draggedFromDocs = (srcTarg && srcTarg !== FieldWaiting) ? - srcTarg.GetList(KeyStore.LinkedFromDocs, [] as Document[]).map(linkDoc => - (linkDoc.GetT(KeyStore.LinkedFromDocs, Document)) as Document) : []; - draggedDocs.push(...draggedFromDocs); - if (draggedDocs.length) { - let moddrag = [] as Document[]; - for (const draggedDoc of draggedDocs) { - let doc = await draggedDoc.GetTAsync(KeyStore.AnnotationOn, Document); - if (doc) moddrag.push(doc); - } - let dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs); - DragManager.StartDocumentDrag([this._linkButton.current], dragData, e.x, e.y, { - handlers: { - dragComplete: action(emptyFunction), - }, - hideSource: false - }); - } + DragLinksAsDocuments(this._linkButton.current, e.x, e.y, SelectionManager.SelectedDocuments()[0].props.Document); } e.stopPropagation(); } - onPointerMove = (e: PointerEvent): void => { e.stopPropagation(); e.preventDefault(); diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index c4c4a6bf9..60f7098fb 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -28,7 +28,7 @@ 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, DragManager } from '../util/DragManager'; import { Transform } from '../util/Transform'; import { UndoManager } from '../util/UndoManager'; import { CollectionDockingView } from './collections/CollectionDockingView'; @@ -344,7 +344,7 @@ export class Main extends React.Component {
      {btns.map(btn =>
    • -
    • )} diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index f2f41e821..e54c25474 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -16,7 +16,7 @@ import "./CollectionDockingView.scss"; import React = require("react"); import { SubCollectionViewProps } from "./CollectionSubView"; import { ServerUtils } from "../../../server/ServerUtil"; -import { DragManager } from "../../util/DragManager"; +import { DragManager, DragLinksAsDocuments } from "../../util/DragManager"; import { TextField } from "../../../fields/TextField"; import { ListField } from "../../../fields/ListField"; @@ -201,32 +201,8 @@ export class CollectionDockingView extends React.Component) => { - if (sourceDoc instanceof Document) { - let srcTarg = sourceDoc.GetT(KeyStore.Prototype, Document); - let draggedDocs = (srcTarg && srcTarg !== FieldWaiting) ? - srcTarg.GetList(KeyStore.LinkedToDocs, [] as Document[]).map(linkDoc => - (linkDoc.GetT(KeyStore.LinkedToDocs, Document)) as Document) : []; - let draggedFromDocs = (srcTarg && srcTarg !== FieldWaiting) ? - srcTarg.GetList(KeyStore.LinkedFromDocs, [] as Document[]).map(linkDoc => - (linkDoc.GetT(KeyStore.LinkedFromDocs, Document)) as Document) : []; - draggedDocs.push(...draggedFromDocs); - if (draggedDocs.length) { - let moddrag = [] as Document[]; - for (const draggedDoc of draggedDocs) { - let doc = await draggedDoc.GetTAsync(KeyStore.AnnotationOn, Document); - if (doc) moddrag.push(doc); - } - let dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs); - DragManager.StartDocumentDrag([tab], dragData, x, y, { - handlers: { - dragComplete: action(emptyFunction), - }, - hideSource: false - }); - } - } - })); + Server.GetField(docid, action(async (sourceDoc: Opt) => + (sourceDoc instanceof Document) && DragLinksAsDocuments(tab, x, y, sourceDoc))); } else if ((className === "lm_title" || className === "lm_tab lm_active") && !e.shiftKey) { e.stopPropagation(); diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 0cb88eab0..f53b2a76e 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -13,7 +13,7 @@ import { Key } from "../../../fields/Key"; import { KeyStore } from "../../../fields/KeyStore"; import { ListField } from "../../../fields/ListField"; import { Server } from "../../Server"; -import { setupDrag } from "../../util/DragManager"; +import { SetupDrag } from "../../util/DragManager"; import { CompileScript, ToField } from "../../util/Scripting"; import { Transform } from "../../util/Transform"; import { anchorPoints, Flyout } from "../DocumentDecorations"; @@ -90,7 +90,7 @@ export class CollectionSchemaView extends CollectionSubView { ); let reference = React.createRef(); - let onItemDown = setupDrag(reference, () => props.Document, this.props.moveDocument); + let onItemDown = SetupDrag(reference, () => props.Document, this.props.moveDocument); let applyToDoc = (doc: Document, run: (args?: { [name: string]: any }) => any) => { const res = run({ this: doc }); if (!res.success) return false; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 20ec16822..51a02fc25 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -7,7 +7,7 @@ import { Document } from "../../../fields/Document"; import { FieldWaiting } from "../../../fields/Field"; import { KeyStore } from "../../../fields/KeyStore"; import { ListField } from "../../../fields/ListField"; -import { setupDrag, DragManager } from "../../util/DragManager"; +import { SetupDrag, DragManager } from "../../util/DragManager"; import { EditableView } from "../EditableView"; import "./CollectionTreeView.scss"; import { CollectionView } from "./CollectionView"; @@ -77,7 +77,7 @@ class TreeView extends React.Component { */ renderTitle() { let reference = React.createRef(); - let onItemDown = setupDrag(reference, () => this.props.document, this.props.moveDocument, this.props.copyOnDrag); + let onItemDown = SetupDrag(reference, () => this.props.document, this.props.moveDocument, this.props.copyOnDrag); let editableView = (titleString: string) => ( { if (de.data instanceof DragManager.LinkDragData) { let sourceDoc: Document = de.data.linkSourceDocument; let destDoc: Document = this.props.Document; - if (this.props.isTopMost) { - return; - } let linkDoc: Document = new Document(); destDoc.GetTAsync(KeyStore.Prototype, Document).then(protoDest => -- cgit v1.2.3-70-g09d2 From 95dff75290edc9b13e9741eefe2bc5851a60dde6 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 18 Apr 2019 13:51:25 -0400 Subject: fixed workspace switching exception. started to refresh minimize functionality. --- src/client/util/DragManager.ts | 2 +- .../views/collections/CollectionDockingView.tsx | 11 +++++---- .../views/collections/CollectionTreeView.tsx | 9 +++----- .../collectionFreeForm/CollectionFreeFormView.tsx | 23 +++++++++++-------- .../collections/collectionFreeForm/MarqueeView.tsx | 10 +++++---- src/client/views/globalCssVariables.scss | 2 ++ src/client/views/globalCssVariables.scss.d.ts | 1 + .../views/nodes/CollectionFreeFormDocumentView.tsx | 16 +++++++++---- src/client/views/nodes/DocumentView.scss | 13 +++++------ src/client/views/nodes/DocumentView.tsx | 26 +++++++++++++++++----- src/fields/KeyStore.ts | 4 +++- 11 files changed, 76 insertions(+), 41 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 64ea4342e..426c9fc3d 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -250,7 +250,7 @@ export namespace DragManager { dragData.aliasOnDrop = e.ctrlKey || e.altKey; } if (e.shiftKey) { - abortDrag(); + AbortDrag(); CollectionDockingView.Instance.StartOtherDrag(docs, { pageX: e.pageX, pageY: e.pageY, diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 39e27b601..e4c647635 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -19,7 +19,7 @@ import { ServerUtils } from "../../../server/ServerUtil"; import { DragManager, DragLinksAsDocuments } from "../../util/DragManager"; import { TextField } from "../../../fields/TextField"; import { ListField } from "../../../fields/ListField"; -import { thisExpression } from "babel-types"; +import { Transform } from '../../util/Transform' @observer export class CollectionDockingView extends React.Component { @@ -336,9 +336,12 @@ export class DockedFrameRenderer extends React.Component { } ScreenToLocalTransform = () => { - let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.current!.children[0].firstChild as HTMLElement); - scale = Utils.GetScreenTransform(this._mainCont.current!).scale; - return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(scale / this.contentScaling()); + if (this._mainCont.current && this._mainCont.current.children) { + let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.current!.children[0].firstChild as HTMLElement); + scale = Utils.GetScreenTransform(this._mainCont.current!).scale; + return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(scale / this.contentScaling()); + } + return Transform.Identity(); } get previewPanelCenteringOffset() { return (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2; } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 51a02fc25..e0387f4b4 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -1,20 +1,17 @@ import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; import { faCaretDown, faCaretRight, faTrashAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, observable, trace } from "mobx"; +import { action, observable } 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 { SetupDrag, DragManager } from "../../util/DragManager"; +import { DragManager, SetupDrag } from "../../util/DragManager"; import { EditableView } from "../EditableView"; -import "./CollectionTreeView.scss"; -import { CollectionView } from "./CollectionView"; -import * as globalCssVariables from "../../views/globalCssVariables.scss"; import { CollectionSubView } from "./CollectionSubView"; +import "./CollectionTreeView.scss"; import React = require("react"); -import { props } from 'bluebird'; export interface TreeViewProps { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 159db6279..af6819cd4 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -71,20 +71,25 @@ export class CollectionFreeFormView extends CollectionSubView { @action drop = (e: Event, de: DragManager.DropEvent) => { if (super.drop(e, de) && de.data instanceof DragManager.DocumentDragData) { + console.log("DROP Aat " + de.x + " off " + de.data.xOffset); 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); + let dragDoc = de.data.droppedDocuments[0]; + let dropX = dragDoc.GetNumber(KeyStore.X, 0); + let dropY = dragDoc.GetNumber(KeyStore.Y, 0); de.data.droppedDocuments.map(d => { + let minimized = d.GetBoolean(KeyStore.Minimized, false); 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)) { - let nw = d.GetNumber(KeyStore.NativeWidth, 0); - let nh = d.GetNumber(KeyStore.NativeHeight, 0); - d.SetNumber(KeyStore.Height, nw && nh ? nh / nw * d.Width() : 300); + if (!minimized) { + if (!d.GetNumber(KeyStore.Width, 0)) { + d.SetNumber(KeyStore.Width, 300); + } + if (!d.GetNumber(KeyStore.Height, 0)) { + let nw = d.GetNumber(KeyStore.NativeWidth, 0); + let nh = d.GetNumber(KeyStore.NativeHeight, 0); + d.SetNumber(KeyStore.Height, nw && nh ? nh / nw * d.Width() : 300); + } } this.bringToFront(d); }); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 65f461b27..8b94374fa 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -11,6 +11,7 @@ import { undoBatch } from "../../../util/UndoManager"; import { InkingCanvas } from "../../InkingCanvas"; import { PreviewCursor } from "../../PreviewCursor"; import { CollectionFreeFormView } from "./CollectionFreeFormView"; +import { MINIMIZED_ICON_SIZE } from '../../../views/globalCssVariables.scss' import "./MarqueeView.scss"; import React = require("react"); @@ -207,11 +208,12 @@ export class MarqueeView extends React.Component let selRect = this.Bounds; let selection: Document[] = []; this.props.activeDocuments().map(doc => { + let minimized = doc.GetBoolean(KeyStore.Minimized, false); var z = doc.GetNumber(KeyStore.Zoom, 1); - var x = doc.GetNumber(KeyStore.X, 0); - var y = doc.GetNumber(KeyStore.Y, 0); - var w = doc.Width() / z; - var h = doc.Height() / z; + var x = doc.GetNumber(KeyStore.X, 0) + (minimized ? doc.GetNumber(KeyStore.MinimizedX, 0) : 0); + var y = doc.GetNumber(KeyStore.Y, 0) + (minimized ? doc.GetNumber(KeyStore.MinimizedY, 0) : 0); + var w = minimized ? MINIMIZED_ICON_SIZE : doc.Width() / z; + var h = minimized ? MINIMIZED_ICON_SIZE : doc.Height() / z; if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) { selection.push(doc); } diff --git a/src/client/views/globalCssVariables.scss b/src/client/views/globalCssVariables.scss index 5c8e9c8fc..f154f8158 100644 --- a/src/client/views/globalCssVariables.scss +++ b/src/client/views/globalCssVariables.scss @@ -23,7 +23,9 @@ $mainTextInput-zindex: 999; // then text input overlay so that it's context menu $docDecorations-zindex: 998; // then doc decorations appear over everything else $remoteCursors-zindex: 997; // ... not sure what level the remote cursors should go -- is this right? $COLLECTION_BORDER_WIDTH: 1; +$MINIMIZED_ICON_SIZE:25; :export { contextMenuZindex: $contextMenu-zindex; COLLECTION_BORDER_WIDTH: $COLLECTION_BORDER_WIDTH; + MINIMIZED_ICON_SIZE: $MINIMIZED_ICON_SIZE; } \ No newline at end of file diff --git a/src/client/views/globalCssVariables.scss.d.ts b/src/client/views/globalCssVariables.scss.d.ts index ed8573f41..cc77d987a 100644 --- a/src/client/views/globalCssVariables.scss.d.ts +++ b/src/client/views/globalCssVariables.scss.d.ts @@ -2,6 +2,7 @@ interface IGlobalScss { contextMenuZindex: string; // context menu shows up over everything COLLECTION_BORDER_WIDTH: string; + MINIMIZED_ICON_SIZE: string; } declare const globalCssVariables: IGlobalScss; diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 97d53a47e..a3689414d 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -17,7 +17,7 @@ export class CollectionFreeFormDocumentView extends React.Component this.props.ScreenToLocalTransform() - .translate(-this.props.Document.GetNumber(KeyStore.X, 0), -this.props.Document.GetNumber(KeyStore.Y, 0)) + .translate(-this.X, -this.Y) .scale(1 / this.contentScaling()).scale(1 / this.zoom) contentScaling = () => this.nativeWidth > 0 ? this.width / this.nativeWidth : 1; - panelWidth = () => this.props.Document.GetBoolean(KeyStore.Minimized, false) ? 10 : this.props.PanelWidth(); - panelHeight = () => this.props.Document.GetBoolean(KeyStore.Minimized, false) ? 10 : this.props.PanelHeight(); + panelWidth = () => this.isMinimized ? 10 : this.props.PanelWidth(); + panelHeight = () => this.isMinimized ? 10 : this.props.PanelHeight(); @computed get docView() { @@ -64,6 +70,8 @@ export class CollectionFreeFormDocumentView extends React.Component; } + get isMinimized() { return this.props.Document.GetBoolean(KeyStore.Minimized, false); } + render() { let zoomFade = 1; //var zoom = doc.GetNumber(KeyStore.Zoom, 1); diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index 690ee50e8..85c305b5a 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -13,7 +13,6 @@ } .top { - background: #232323; height: 20px; cursor: pointer; } @@ -32,13 +31,13 @@ .documentView-node-topmost { background: white; } - .minimized-box { - height: 10px; - width: 10px; - border-radius: 2px; - background: $dark-color; - transform-origin: left top; + position: absolute; + left:0; + top:0; + width:$MINIMIZED_ICON_SIZE; + height:$MINIMIZED_ICON_SIZE; + transform-origin: left top; } .minimized-box:hover { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index d74f9fc57..af52e44e1 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,12 +1,13 @@ +import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; +import { faCaretUp, faObjectGroup, faStickyNote, faFilePdf, faFilm, faImage } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 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 { Field, Opt } from "../../../fields/Field"; import { Key } from "../../../fields/Key"; import { KeyStore } from "../../../fields/KeyStore"; import { ListField } from "../../../fields/ListField"; -import { TextField } from "../../../fields/TextField"; import { ServerUtils } from "../../../server/ServerUtil"; import { emptyFunction, Utils } from "../../../Utils"; import { Documents } from "../../documents/Documents"; @@ -19,12 +20,19 @@ import { CollectionDockingView } from "../collections/CollectionDockingView"; import { CollectionPDFView } from "../collections/CollectionPDFView"; import { CollectionVideoView } from "../collections/CollectionVideoView"; import { CollectionView } from "../collections/CollectionView"; +import { MINIMIZED_ICON_SIZE } from "../../views/globalCssVariables.scss"; import { ContextMenu } from "../ContextMenu"; import { DocumentContentsView } from "./DocumentContentsView"; import "./DocumentView.scss"; import React = require("react"); +library.add(faCaretUp); +library.add(faObjectGroup); +library.add(faStickyNote); +library.add(faFilePdf); +library.add(faFilm); + export interface DocumentViewProps { ContainingCollectionView: Opt; Document: Document; @@ -216,6 +224,8 @@ export class DocumentView extends React.Component { @action public minimize = (): void => { this.props.Document.SetBoolean(KeyStore.Minimized, true); + this.props.Document.SetNumber(KeyStore.MinimizedX, 0); + this.props.Document.SetNumber(KeyStore.MinimizedY, 0); SelectionManager.DeselectAll(); } @@ -314,8 +324,14 @@ export class DocumentView extends React.Component { var nativeWidth = this.nativeWidth > 0 ? this.nativeWidth.toString() + "px" : "100%"; if (this.isMinimized()) { - return
      ; + let button = this.layout.indexOf("PDFBox") !== -1 ? faFilePdf : + this.layout.indexOf("ImageBox") !== -1 ? faImage : + this.layout.indexOf("Formatted") !== -1 ? faStickyNote : + this.layout.indexOf("Collection") !== -1 ? faObjectGroup : + faCaretUp; + return
      + +
      } return (
      Date: Wed, 24 Apr 2019 23:17:44 -0400 Subject: added borderRadius and fixed a bunch of broken props along the way. --- src/client/documents/Documents.ts | 2 ++ src/client/views/Main.scss | 3 ++- src/client/views/Main.tsx | 3 ++- src/client/views/MainOverlayTextBox.tsx | 4 ++-- src/client/views/collections/CollectionBaseView.tsx | 7 ++++--- src/client/views/collections/CollectionDockingView.tsx | 3 ++- src/client/views/collections/CollectionSchemaView.tsx | 6 +++++- src/client/views/collections/CollectionSubView.tsx | 1 + src/client/views/collections/CollectionTreeView.tsx | 5 ++++- .../collectionFreeForm/CollectionFreeFormView.scss | 1 + .../collectionFreeForm/CollectionFreeFormView.tsx | 14 +++++++++++--- .../views/collections/collectionFreeForm/MarqueeView.tsx | 7 ++++--- src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 11 +++++++++++ src/client/views/nodes/DocumentView.tsx | 2 ++ src/client/views/nodes/FieldView.tsx | 7 ++++++- src/client/views/nodes/FormattedTextBox.scss | 3 +-- src/client/views/nodes/FormattedTextBox.tsx | 5 ++++- src/client/views/nodes/ImageBox.scss | 3 --- src/client/views/nodes/ImageBox.tsx | 2 +- src/fields/KeyStore.ts | 3 ++- 20 files changed, 67 insertions(+), 25 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 0e6661819..879b114b6 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -55,6 +55,7 @@ export interface DocumentOptions { backgroundColor?: string; copyDraggedItems?: boolean; documentText?: string; + borderRounding?: number; } export namespace Documents { @@ -108,6 +109,7 @@ export namespace Documents { if (options.layout !== undefined) { doc.SetText(KeyStore.Layout, options.layout); } if (options.layoutKeys !== undefined) { doc.Set(KeyStore.LayoutKeys, new ListField(options.layoutKeys)); } if (options.copyDraggedItems !== undefined) { doc.SetBoolean(KeyStore.CopyDraggedItems, options.copyDraggedItems); } + if (options.borderRounding !== undefined) { doc.SetNumber(KeyStore.BorderRounding, options.borderRounding); } return doc; } diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss index 4373534b2..3edc79d39 100644 --- a/src/client/views/Main.scss +++ b/src/client/views/Main.scss @@ -42,7 +42,8 @@ h1 { } .jsx-parser { - width:100% + width:100%; + pointer-events: none; } p { diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 934616aca..057f68ed9 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -17,7 +17,7 @@ import { CurrentUserUtils } from '../../server/authentication/models/current_use import { MessageStore } from '../../server/Message'; import { RouteStore } from '../../server/RouteStore'; import { ServerUtils } from '../../server/ServerUtil'; -import { emptyDocFunction, emptyFunction, returnTrue, Utils, returnOne } from '../../Utils'; +import { emptyDocFunction, emptyFunction, returnTrue, Utils, returnOne, returnZero } from '../../Utils'; import { Documents } from '../documents/Documents'; import { ColumnAttributeModel } from '../northstar/core/attribute/AttributeModel'; import { AttributeTransformationModel } from '../northstar/core/attribute/AttributeTransformationModel'; @@ -197,6 +197,7 @@ export class Main extends React.Component { isTopMost={true} selectOnLoad={false} focus={emptyDocFunction} + borderRounding={returnZero} parentActive={returnTrue} whenActiveChanged={emptyFunction} ContainingCollectionView={undefined} />} diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx index 9aaaad084..f98f6e8a5 100644 --- a/src/client/views/MainOverlayTextBox.tsx +++ b/src/client/views/MainOverlayTextBox.tsx @@ -5,7 +5,7 @@ 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 { emptyDocFunction, emptyFunction, returnTrue, returnZero } from '../../Utils'; import '../northstar/model/ModelExtensions'; import '../northstar/utils/Extensions'; import { DragManager } from '../util/DragManager'; @@ -97,7 +97,7 @@ export class MainOverlayTextBox extends React.Component style={{ width: `${this.TextDoc.Width()}px`, height: `${this.TextDoc.Height()}px` }}> + ScreenToLocalTransform={this._textXf} borderRounding={returnZero} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyDocFunction} />
      ; } diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index 097236c1d..e65770137 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -2,13 +2,12 @@ import { action, computed } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Document } from '../../../fields/Document'; -import { Field, FieldValue, FieldWaiting } from '../../../fields/Field'; +import { FieldValue, FieldWaiting } from '../../../fields/Field'; import { KeyStore } from '../../../fields/KeyStore'; import { ListField } from '../../../fields/ListField'; import { NumberField } from '../../../fields/NumberField'; import { ContextMenu } from '../ContextMenu'; import { FieldViewProps } from '../nodes/FieldView'; -import { TextField } from '../../../fields/TextField'; export enum CollectionViewType { Invalid, @@ -190,7 +189,9 @@ export class CollectionBaseView extends React.Component { }; const viewtype = this.collectionViewType; return ( -
      +
      {viewtype !== undefined ? this.props.children(viewtype, props) : (null)}
      ); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 84159635a..eb94a4e6d 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -8,7 +8,7 @@ import { Document } from "../../../fields/Document"; import { KeyStore } from "../../../fields/KeyStore"; import Measure from "react-measure"; import { FieldId, Opt, Field, FieldWaiting } from "../../../fields/Field"; -import { Utils, returnTrue, emptyFunction, emptyDocFunction, returnOne } from "../../../Utils"; +import { Utils, returnTrue, emptyFunction, emptyDocFunction, returnOne, returnZero } from "../../../Utils"; import { Server } from "../../Server"; import { undoBatch } from "../../util/UndoManager"; import { DocumentView } from "../nodes/DocumentView"; @@ -368,6 +368,7 @@ export class DockedFrameRenderer extends React.Component { parentActive={returnTrue} whenActiveChanged={emptyFunction} focus={emptyDocFunction} + borderRounding={returnZero} ContainingCollectionView={undefined} />
      ); } diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index cb7a67991..dc2910ede 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -12,7 +12,7 @@ import { Field, Opt } from "../../../fields/Field"; import { Key } from "../../../fields/Key"; import { KeyStore } from "../../../fields/KeyStore"; import { ListField } from "../../../fields/ListField"; -import { emptyDocFunction, emptyFunction, returnFalse } from "../../../Utils"; +import { emptyDocFunction, emptyFunction, returnFalse, returnZero } from "../../../Utils"; import { Server } from "../../Server"; import { SetupDrag } from "../../util/DragManager"; import { CompileScript, ToField } from "../../util/Scripting"; @@ -77,6 +77,9 @@ export class CollectionSchemaView extends CollectionSubView { focus: emptyDocFunction, active: returnFalse, whenActiveChanged: emptyFunction, + borderRounding: this.props.borderRounding, + PanelHeight: returnZero, + PanelWidth: returnZero, }; let contents = ( @@ -275,6 +278,7 @@ export class CollectionSchemaView extends CollectionSubView { focus={emptyDocFunction} parentActive={this.props.active} whenActiveChanged={this.props.whenActiveChanged} + borderRounding={this.props.borderRounding} />
      boolean) => boolean; PanelWidth: () => number; PanelHeight: () => number; + borderRounding: () => number; } export interface SubCollectionViewProps extends CollectionViewProps { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index e0387f4b4..b9d1f0bd9 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -136,7 +136,10 @@ export class CollectionTreeView extends CollectionSubView { ); return ( -
      e.stopPropagation()} onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget}> +
      e.stopPropagation()} + onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget}>
      .jsx-parser { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 7492d8744..9e2c9bf9a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -257,6 +257,7 @@ export class CollectionFreeFormView extends CollectionSubView { focus: this.focusDocument, parentActive: this.props.active, whenActiveChanged: this.props.active, + borderRounding: this.props.borderRounding, }; } @@ -288,11 +289,17 @@ export class CollectionFreeFormView extends CollectionSubView { const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`; return (
      - + + {this.props.Document.Title} + + */} + - @@ -340,6 +347,7 @@ interface CollectionFreeFormViewPannableContentsProps { panX: () => number; panY: () => number; zoomScaling: () => number; + borderRadius: () => number; } @observer @@ -350,7 +358,7 @@ class CollectionFreeFormViewPannableContents extends React.Component otherwise, reactions won't fire - return
      + return
      {this.props.children}
      ; } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 898fff019..e724ab47e 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -14,7 +14,6 @@ import { CollectionFreeFormView } from "./CollectionFreeFormView"; import "./MarqueeView.scss"; import React = require("react"); import { Utils } from "../../../../Utils"; -import { ListField } from "../../../../fields/ListField"; interface MarqueeViewProps { getContainerTransform: () => Transform; @@ -25,6 +24,7 @@ interface MarqueeViewProps { selectDocuments: (docs: Document[]) => void; removeDocument: (doc: Document) => boolean; addLiveTextDocument: (doc: Document) => void; + borderRadius: () => number; } @observer @@ -138,7 +138,7 @@ export class MarqueeView extends React.Component this.cleanupInteractions(true); e.stopPropagation(); } - if (e.key === "c" || e.key === "r") { + if (e.key === "c" || e.key === "r" || e.key === "e") { e.stopPropagation(); let bounds = this.Bounds; let selected = this.marqueeSelect().map(d => { @@ -156,6 +156,7 @@ export class MarqueeView extends React.Component y: bounds.top, panx: 0, pany: 0, + borderRounding: e.key === "e" ? -1 : undefined, backgroundColor: selected.length ? "white" : "", scale: zoomBasis, width: bounds.width * zoomBasis, @@ -250,7 +251,7 @@ export class MarqueeView extends React.Component } render() { - return
      + return
      {this.props.children} {!this._visible ? (null) : this.marqueeDiv}
      ; diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 420324d40..8caf5ff31 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -65,6 +65,7 @@ export class CollectionFreeFormDocumentView extends React.Component; } @@ -148,6 +149,15 @@ export class CollectionFreeFormDocumentView extends React.Component { + let br = this.props.Document.GetNumber(KeyStore.BorderRounding, 0); + return br >= 0 ? br : + this.props.Document.GetNumber(KeyStore.NativeWidth, 0) === 0 ? + Math.min(this.props.PanelWidth(), this.props.PanelHeight()) + : + Math.min(this.props.Document.GetNumber(KeyStore.NativeWidth, 0), this.props.Document.GetNumber(KeyStore.NativeHeight, 0)); + } + render() { let maximizedDoc = this.props.Document.GetT(KeyStore.MaximizedDoc, Document); let zoomFade = 1; @@ -168,6 +178,7 @@ export class CollectionFreeFormDocumentView extends React.Component boolean; whenActiveChanged: (isActive: boolean) => void; toggleMinimized: () => void; + borderRounding: () => number; } export interface JsxArgs extends DocumentViewProps { Keys: { [name: string]: Key }; @@ -277,6 +278,7 @@ export class DocumentView extends React.Component {
      boolean; whenActiveChanged: (isActive: boolean) => void; focus: (doc: Document) => void; + PanelWidth: () => number; + PanelHeight: () => number; + borderRounding: () => number; } @observer @@ -100,6 +103,8 @@ export class FieldView extends React.Component { layoutKey={KeyStore.Layout} ContainingCollectionView={this.props.ContainingCollectionView} parentActive={this.props.active} + borderRounding={returnZero} + toggleMinimized={emptyFunction} whenActiveChanged={this.props.whenActiveChanged} /> ); } diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss index 5eb2bf7ce..59857bab3 100644 --- a/src/client/views/nodes/FormattedTextBox.scss +++ b/src/client/views/nodes/FormattedTextBox.scss @@ -22,11 +22,10 @@ overflow-x: hidden; color: initial; height: 100%; - pointer-events: all; + pointer-events: none; } .formattedTextBox-cont-hidden { overflow: hidden; - pointer-events: none; } .menuicon { diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 9cd58c73a..4b2fdbaff 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -273,7 +273,10 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte let color = this.props.Document.GetText(KeyStore.BackgroundColor, ""); return (
      { let nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 1); let interactive = InkingControl.Instance.selectedTool ? "" : "interactive" return ( -
      +
      Image not found {paths.length > 1 ? this.dots(paths) : (null)} {this.lightbox(paths)} diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts index 0a96beb50..aa18a9f33 100644 --- a/src/fields/KeyStore.ts +++ b/src/fields/KeyStore.ts @@ -49,12 +49,13 @@ export namespace KeyStore { export const MinimizedDoc = new Key("MinimizedDoc"); export const MaximizedDoc = new Key("MaximizedDoc"); export const CopyDraggedItems = new Key("CopyDraggedItems"); + export const BorderRounding = new Key("BorderRounding"); export const KeyList: Key[] = [Prototype, X, Y, Page, Title, Author, PanX, PanY, Scale, NativeWidth, NativeHeight, Width, Height, ZIndex, ZoomBasis, Data, Annotations, ViewType, Layout, BackgroundColor, BackgroundLayout, OverlayLayout, LayoutKeys, LayoutFields, ColumnsKey, SchemaSplitPercentage, Caption, ActiveWorkspace, DocumentText, BrushingDocs, LinkedToDocs, LinkedFromDocs, LinkDescription, LinkTags, Thumbnail, ThumbnailPage, CurPage, AnnotationOn, NumPages, Ink, Cursors, OptionalRightCollection, - Archives, Workspaces, IsMinimized, MinimizedDoc, MaximizedDoc, CopyDraggedItems + Archives, Workspaces, IsMinimized, MinimizedDoc, MaximizedDoc, CopyDraggedItems, BorderRounding ]; export function KeyLookup(keyid: string) { for (const key of KeyList) { -- cgit v1.2.3-70-g09d2 From 6a62e0a16abc3bbadfb179750463e3d361f1c759 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 25 Apr 2019 09:15:22 -0400 Subject: silly me - borderRadius can be inherited --- src/client/views/Main.scss | 1 + src/client/views/Main.tsx | 1 - src/client/views/MainOverlayTextBox.tsx | 2 +- src/client/views/collections/CollectionBaseView.tsx | 2 +- src/client/views/collections/CollectionDockingView.tsx | 1 - src/client/views/collections/CollectionSchemaView.tsx | 2 -- src/client/views/collections/CollectionSubView.tsx | 1 - src/client/views/collections/CollectionTreeView.tsx | 2 +- .../collections/collectionFreeForm/CollectionFreeFormView.tsx | 10 ++++------ .../views/collections/collectionFreeForm/MarqueeView.tsx | 3 +-- src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 1 - src/client/views/nodes/DocumentView.tsx | 3 +-- src/client/views/nodes/FieldView.tsx | 2 -- src/client/views/nodes/FormattedTextBox.tsx | 2 +- 14 files changed, 11 insertions(+), 22 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss index 3edc79d39..2f899ff28 100644 --- a/src/client/views/Main.scss +++ b/src/client/views/Main.scss @@ -44,6 +44,7 @@ h1 { .jsx-parser { width:100%; pointer-events: none; + border-radius: inherit; } p { diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 057f68ed9..c6b3f06d8 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -197,7 +197,6 @@ export class Main extends React.Component { isTopMost={true} selectOnLoad={false} focus={emptyDocFunction} - borderRounding={returnZero} parentActive={returnTrue} whenActiveChanged={emptyFunction} ContainingCollectionView={undefined} />} diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx index f98f6e8a5..be8d67925 100644 --- a/src/client/views/MainOverlayTextBox.tsx +++ b/src/client/views/MainOverlayTextBox.tsx @@ -97,7 +97,7 @@ export class MainOverlayTextBox extends React.Component style={{ width: `${this.TextDoc.Width()}px`, height: `${this.TextDoc.Height()}px` }}> + ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyDocFunction} />
      ; } diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index e65770137..87b767c93 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -190,7 +190,7 @@ export class CollectionBaseView extends React.Component { const viewtype = this.collectionViewType; return (
      {viewtype !== undefined ? this.props.children(viewtype, props) : (null)}
      diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index eb94a4e6d..3267f2455 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -368,7 +368,6 @@ export class DockedFrameRenderer extends React.Component { parentActive={returnTrue} whenActiveChanged={emptyFunction} focus={emptyDocFunction} - borderRounding={returnZero} ContainingCollectionView={undefined} />
      ); } diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index dc2910ede..b6d5f1bfa 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -77,7 +77,6 @@ export class CollectionSchemaView extends CollectionSubView { focus: emptyDocFunction, active: returnFalse, whenActiveChanged: emptyFunction, - borderRounding: this.props.borderRounding, PanelHeight: returnZero, PanelWidth: returnZero, }; @@ -278,7 +277,6 @@ export class CollectionSchemaView extends CollectionSubView { focus={emptyDocFunction} parentActive={this.props.active} whenActiveChanged={this.props.whenActiveChanged} - borderRounding={this.props.borderRounding} />
      boolean) => boolean; PanelWidth: () => number; PanelHeight: () => number; - borderRounding: () => number; } export interface SubCollectionViewProps extends CollectionViewProps { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index b9d1f0bd9..905b48db7 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -137,7 +137,7 @@ export class CollectionTreeView extends CollectionSubView { return (
      e.stopPropagation()} onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget}>
      diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 9e2c9bf9a..56342c84c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -257,7 +257,6 @@ export class CollectionFreeFormView extends CollectionSubView { focus: this.focusDocument, parentActive: this.props.active, whenActiveChanged: this.props.active, - borderRounding: this.props.borderRounding, }; } @@ -289,17 +288,17 @@ export class CollectionFreeFormView extends CollectionSubView { const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`; return (
      {/* {this.props.Document.Title} */} - - @@ -347,7 +346,6 @@ interface CollectionFreeFormViewPannableContentsProps { panX: () => number; panY: () => number; zoomScaling: () => number; - borderRadius: () => number; } @observer @@ -358,7 +356,7 @@ class CollectionFreeFormViewPannableContents extends React.Component otherwise, reactions won't fire - return
      + return
      {this.props.children}
      ; } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index e724ab47e..b511a6e3d 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -24,7 +24,6 @@ interface MarqueeViewProps { selectDocuments: (docs: Document[]) => void; removeDocument: (doc: Document) => boolean; addLiveTextDocument: (doc: Document) => void; - borderRadius: () => number; } @observer @@ -251,7 +250,7 @@ export class MarqueeView extends React.Component } render() { - return
      + return
      {this.props.children} {!this._visible ? (null) : this.marqueeDiv}
      ; diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 8caf5ff31..4537ce3eb 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -65,7 +65,6 @@ export class CollectionFreeFormDocumentView extends React.Component; } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index ddf99c109..cec6e6468 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -41,7 +41,6 @@ export interface DocumentViewProps { parentActive: () => boolean; whenActiveChanged: (isActive: boolean) => void; toggleMinimized: () => void; - borderRounding: () => number; } export interface JsxArgs extends DocumentViewProps { Keys: { [name: string]: Key }; @@ -278,7 +277,7 @@ export class DocumentView extends React.Component {
      void; PanelWidth: () => number; PanelHeight: () => number; - borderRounding: () => number; } @observer @@ -103,7 +102,6 @@ export class FieldView extends React.Component { layoutKey={KeyStore.Layout} ContainingCollectionView={this.props.ContainingCollectionView} parentActive={this.props.active} - borderRounding={returnZero} toggleMinimized={emptyFunction} whenActiveChanged={this.props.whenActiveChanged} /> ); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 4b2fdbaff..5b17763e7 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -274,7 +274,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte return (
      Date: Fri, 26 Apr 2019 21:14:50 -0400 Subject: More --- src/client/views/InkingCanvas.tsx | 29 +- src/client/views/InkingControl.tsx | 7 +- .../views/collections/CollectionTreeView.tsx | 51 ++- src/client/views/nodes/LinkEditor.tsx | 16 +- src/debug/Viewer.tsx | 368 ++++++++++----------- src/fields/ScriptField.ts | 174 +++++----- src/mobile/ImageUpload.tsx | 34 +- src/new_fields/Doc.ts | 4 +- .../authentication/controllers/WorkspacesMenu.tsx | 16 +- 9 files changed, 344 insertions(+), 355 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx index 47ee8eb85..1e26893c5 100644 --- a/src/client/views/InkingCanvas.tsx +++ b/src/client/views/InkingCanvas.tsx @@ -1,9 +1,5 @@ import { action, computed, trace, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Document } from "../../fields/Document"; -import { FieldWaiting } from "../../fields/Field"; -import { InkField, InkTool, StrokeData, StrokeMap } from "../../fields/InkField"; -import { KeyStore } from "../../fields/KeyStore"; import { Utils } from "../../Utils"; import { Transform } from "../util/Transform"; import "./InkingCanvas.scss"; @@ -11,10 +7,13 @@ import { InkingControl } from "./InkingControl"; import { InkingStroke } from "./InkingStroke"; import React = require("react"); import { undoBatch, UndoManager } from "../util/UndoManager"; +import { StrokeData, InkField, InkTool } from "../../new_fields/InkField"; +import { Doc } from "../../new_fields/Doc"; +import { Cast, PromiseValue, NumCast } from "../../new_fields/Types"; interface InkCanvasProps { getScreenTransform: () => Transform; - Document: Document; + Document: Doc; children: () => JSX.Element[]; } @@ -23,7 +22,7 @@ export class InkingCanvas extends React.Component { maxCanvasDim = 8192 / 2; // 1/2 of the maximum canvas dimension for Chrome @observable inkMidX: number = 0; @observable inkMidY: number = 0; - private previousState?: StrokeMap; + private previousState?: Map; private _currentStrokeId: string = ""; public static IntersectStrokeRect(stroke: StrokeData, selRect: { left: number, top: number, width: number, height: number }): boolean { return stroke.pathData.reduce((inside: boolean, val) => inside || @@ -33,9 +32,9 @@ export class InkingCanvas extends React.Component { } componentDidMount() { - this.props.Document.GetTAsync(KeyStore.Ink, InkField, ink => runInAction(() => { + PromiseValue(Cast(this.props.Document.ink, InkField)).then(ink => runInAction(() => { if (ink) { - let bounds = Array.from(ink.Data).reduce(([mix, max, miy, may], [id, strokeData]) => + let bounds = Array.from(ink.inkData).reduce(([mix, max, miy, may], [id, strokeData]) => strokeData.pathData.reduce(([mix, max, miy, may], p) => [Math.min(mix, p.x), Math.max(max, p.x), Math.min(miy, p.y), Math.max(may, p.y)], [mix, max, miy, may]), @@ -47,13 +46,13 @@ export class InkingCanvas extends React.Component { } @computed - get inkData(): StrokeMap { - let map = this.props.Document.GetT(KeyStore.Ink, InkField); - return !map || map === FieldWaiting ? new Map : new Map(map.Data); + get inkData(): Map { + let map = Cast(this.props.Document.ink, InkField); + return !map ? new Map : new Map(map.inkData); } - set inkData(value: StrokeMap) { - this.props.Document.SetDataOnPrototype(KeyStore.Ink, value, InkField); + set inkData(value: Map) { + Doc.SetOnPrototype(this.props.Document, "ink", new InkField(value)); } @action @@ -78,7 +77,7 @@ export class InkingCanvas extends React.Component { color: InkingControl.Instance.selectedColor, width: InkingControl.Instance.selectedWidth, tool: InkingControl.Instance.selectedTool, - page: this.props.Document.GetNumber(KeyStore.CurPage, -1) + page: NumCast(this.props.Document.curPage, -1) }); this.inkData = data; } @@ -137,7 +136,7 @@ export class InkingCanvas extends React.Component { @computed get drawnPaths() { - let curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1); + let curPage = NumCast(this.props.Document.curPage, -1); let paths = Array.from(this.inkData).reduce((paths, [id, strokeData]) => { if (strokeData.page === -1 || strokeData.page === curPage) { paths.push( void; + document: Doc; + deleteDoc: (doc: Doc) => void; moveDocument: DragManager.MoveFunction; copyOnDrag: boolean; } @@ -43,9 +42,9 @@ class TreeView extends React.Component { @action remove = (document: Document) => { - var children = this.props.document.GetT>(KeyStore.Data, ListField); - if (children && children !== FieldWaiting) { - children.Data.splice(children.Data.indexOf(document), 1); + var children = Cast(this.props.document.data, listSpec(Doc)); + if (children) { + children.splice(children.indexOf(document), 1); } } @@ -80,15 +79,15 @@ class TreeView extends React.Component { display={"inline"} contents={titleString} height={36} - GetValue={() => this.props.document.Title} + GetValue={() => StrCast(this.props.document.title)} SetValue={(value: string) => { - this.props.document.SetText(KeyStore.Title, value); + this.props.document.title = value; return true; }} />); return (
      - {editableView(this.props.document.Title)} + {editableView(StrCast(this.props.document.title))}
      ); } @@ -96,12 +95,12 @@ class TreeView extends React.Component { render() { let bulletType = BulletType.List; let childElements: JSX.Element | undefined = undefined; - var children = this.props.document.GetT>(KeyStore.Data, ListField); - if (children && children !== FieldWaiting) { // add children for a collection + var children = Cast(this.props.document.data, listSpec(Doc)); + if (children) { // add children for a collection if (!this._collapsed) { bulletType = BulletType.Collapsible; childElements =
        - {children.Data.map(value => )} + {children.map(value => )}
      ; } else bulletType = BulletType.Collapsed; @@ -117,22 +116,22 @@ class TreeView extends React.Component { } @observer -export class CollectionTreeView extends CollectionSubView { +export class CollectionTreeView extends CollectionSubView(Document) { @action remove = (document: Document) => { - var children = this.props.Document.GetT>(KeyStore.Data, ListField); - if (children && children !== FieldWaiting) { - children.Data.splice(children.Data.indexOf(document), 1); + const children = this.children; + if (children) { + children.splice(children.indexOf(document), 1); } } render() { - let children = this.props.Document.GetT>(KeyStore.Data, ListField); - let copyOnDrag = this.props.Document.GetBoolean(KeyStore.CopyDraggedItems, false); - let childrenElement = !children || children === FieldWaiting ? (null) : - (children.Data.map(value => - ) + const children = this.children; + let copyOnDrag = BoolCast(this.props.Document.copyDraggedItems, false); + let childrenElement = !children ? (null) : + (children.map(value => + ) ); return ( @@ -145,9 +144,9 @@ export class CollectionTreeView extends CollectionSubView { contents={this.props.Document.Title} display={"inline"} height={72} - GetValue={() => this.props.Document.Title} + GetValue={() => StrCast(this.props.Document.title)} SetValue={(value: string) => { - this.props.Document.SetText(KeyStore.Title, value); + this.props.Document.title = value; return true; }} />
      diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index bde50fed8..f82c6e9cb 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -3,31 +3,29 @@ import React = require("react"); import { SelectionManager } from "../../util/SelectionManager"; import { observer } from "mobx-react"; import './LinkEditor.scss'; -import { KeyStore } from '../../../fields/KeyStore'; import { props } from "bluebird"; import { DocumentView } from "./DocumentView"; -import { Document } from "../../../fields/Document"; -import { TextField } from "../../../fields/TextField"; import { link } from "fs"; +import { StrCast } from "../../../new_fields/Types"; +import { Doc } from "../../../new_fields/Doc"; interface Props { - linkDoc: Document; + linkDoc: Doc; showLinks: () => void; } @observer export class LinkEditor extends React.Component { - @observable private _nameInput: string = this.props.linkDoc.GetText(KeyStore.Title, ""); - @observable private _descriptionInput: string = this.props.linkDoc.GetText(KeyStore.LinkDescription, ""); + @observable private _nameInput: string = StrCast(this.props.linkDoc.title); + @observable private _descriptionInput: string = StrCast(this.props.linkDoc.linkDescription); onSaveButtonPressed = (e: React.PointerEvent): void => { - console.log("view down"); e.stopPropagation(); - this.props.linkDoc.SetData(KeyStore.Title, this._nameInput, TextField); - this.props.linkDoc.SetData(KeyStore.LinkDescription, this._descriptionInput, TextField); + this.props.linkDoc.title = this._nameInput; + this.props.linkDoc.linkDescription = this._descriptionInput; this.props.showLinks(); } diff --git a/src/debug/Viewer.tsx b/src/debug/Viewer.tsx index 857da1ebb..4cac09dee 100644 --- a/src/debug/Viewer.tsx +++ b/src/debug/Viewer.tsx @@ -3,190 +3,184 @@ import "normalize.css"; import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { observer } from 'mobx-react'; -import { Document } from '../fields/Document'; -import { BasicField } from '../fields/BasicField'; -import { ListField } from '../fields/ListField'; -import { Key } from '../fields/Key'; -import { Opt, Field } from '../fields/Field'; -import { Server } from '../client/Server'; - -configure({ - enforceActions: "observed" -}); - -@observer -class FieldViewer extends React.Component<{ field: BasicField }> { - render() { - return {JSON.stringify(this.props.field.Data)} ({this.props.field.Id}); - } -} - -@observer -class KeyViewer extends React.Component<{ field: Key }> { - render() { - return this.props.field.Name; - } -} - -@observer -class ListViewer extends React.Component<{ field: ListField }>{ - @observable - expanded = false; - - render() { - let content; - if (this.expanded) { - content = ( -
      - {this.props.field.Data.map(field => )} -
      - ); - } else { - content = <>[...] ({this.props.field.Id}); - } - return ( -
      - - {content} -
      - ); - } -} - -@observer -class DocumentViewer extends React.Component<{ field: Document }> { - private keyMap: ObservableMap = new ObservableMap; - - private disposer?: Lambda; - - componentDidMount() { - let f = () => { - Array.from(this.props.field._proxies.keys()).forEach(id => { - if (!this.keyMap.has(id)) { - Server.GetField(id, (field) => { - if (field && field instanceof Key) { - this.keyMap.set(id, field); - } - }); - } - }); - }; - this.disposer = this.props.field._proxies.observe(f); - f(); - } - - componentWillUnmount() { - if (this.disposer) { - this.disposer(); - } - } - - render() { - let fields = Array.from(this.props.field._proxies.entries()).map(kv => { - let key = this.keyMap.get(kv[0]); - return ( -
      - ({key ? key.Name : kv[0]}): - -
      - ); - }); - return ( -
      - Document ({this.props.field.Id}) -
      - {fields} -
      -
      - ); - } -} - -@observer -class DebugViewer extends React.Component<{ fieldId: string }> { - @observable - private field?: Field; - - @observable - private error?: string; - - constructor(props: { fieldId: string }) { - super(props); - this.update(); - } - - update() { - Server.GetField(this.props.fieldId, action((field: Opt) => { - this.field = field; - if (!field) { - this.error = `Field with id ${this.props.fieldId} not found`; - } - })); - - } - - render() { - let content; - if (this.field) { - // content = this.field.ToJson(); - if (this.field instanceof ListField) { - content = (); - } else if (this.field instanceof Document) { - content = (); - } else if (this.field instanceof BasicField) { - content = (); - } else if (this.field instanceof Key) { - content = (); - } else { - content = (Unrecognized field type); - } - } else if (this.error) { - content = Field {this.props.fieldId} not found ; - } else { - content = Field loading: {this.props.fieldId}; - } - return content; - } -} - -@observer -class Viewer extends React.Component { - @observable - private idToAdd: string = ''; - - @observable - private ids: string[] = []; - - @action - inputOnChange = (e: React.ChangeEvent) => { - this.idToAdd = e.target.value; - } - - @action - onKeyPress = (e: React.KeyboardEvent) => { - if (e.key === "Enter") { - this.ids.push(this.idToAdd); - this.idToAdd = ""; - } - } - - render() { - return ( - <> - -
      - {this.ids.map(id => )} -
      - - ); - } -} - -ReactDOM.render(( -
      - -
      ), - document.getElementById('root') -); \ No newline at end of file + +// configure({ +// enforceActions: "observed" +// }); + +// @observer +// class FieldViewer extends React.Component<{ field: BasicField }> { +// render() { +// return {JSON.stringify(this.props.field.Data)} ({this.props.field.Id}); +// } +// } + +// @observer +// class KeyViewer extends React.Component<{ field: Key }> { +// render() { +// return this.props.field.Name; +// } +// } + +// @observer +// class ListViewer extends React.Component<{ field: ListField }>{ +// @observable +// expanded = false; + +// render() { +// let content; +// if (this.expanded) { +// content = ( +//
      +// {this.props.field.Data.map(field => )} +//
      +// ); +// } else { +// content = <>[...] ({this.props.field.Id}); +// } +// return ( +//
      +// +// {content} +//
      +// ); +// } +// } + +// @observer +// class DocumentViewer extends React.Component<{ field: Document }> { +// private keyMap: ObservableMap = new ObservableMap; + +// private disposer?: Lambda; + +// componentDidMount() { +// let f = () => { +// Array.from(this.props.field._proxies.keys()).forEach(id => { +// if (!this.keyMap.has(id)) { +// Server.GetField(id, (field) => { +// if (field && field instanceof Key) { +// this.keyMap.set(id, field); +// } +// }); +// } +// }); +// }; +// this.disposer = this.props.field._proxies.observe(f); +// f(); +// } + +// componentWillUnmount() { +// if (this.disposer) { +// this.disposer(); +// } +// } + +// render() { +// let fields = Array.from(this.props.field._proxies.entries()).map(kv => { +// let key = this.keyMap.get(kv[0]); +// return ( +//
      +// ({key ? key.Name : kv[0]}): +// +//
      +// ); +// }); +// return ( +//
      +// Document ({this.props.field.Id}) +//
      +// {fields} +//
      +//
      +// ); +// } +// } + +// @observer +// class DebugViewer extends React.Component<{ fieldId: string }> { +// @observable +// private field?: Field; + +// @observable +// private error?: string; + +// constructor(props: { fieldId: string }) { +// super(props); +// this.update(); +// } + +// update() { +// Server.GetField(this.props.fieldId, action((field: Opt) => { +// this.field = field; +// if (!field) { +// this.error = `Field with id ${this.props.fieldId} not found`; +// } +// })); + +// } + +// render() { +// let content; +// if (this.field) { +// // content = this.field.ToJson(); +// if (this.field instanceof ListField) { +// content = (); +// } else if (this.field instanceof Document) { +// content = (); +// } else if (this.field instanceof BasicField) { +// content = (); +// } else if (this.field instanceof Key) { +// content = (); +// } else { +// content = (Unrecognized field type); +// } +// } else if (this.error) { +// content = Field {this.props.fieldId} not found ; +// } else { +// content = Field loading: {this.props.fieldId}; +// } +// return content; +// } +// } + +// @observer +// class Viewer extends React.Component { +// @observable +// private idToAdd: string = ''; + +// @observable +// private ids: string[] = []; + +// @action +// inputOnChange = (e: React.ChangeEvent) => { +// this.idToAdd = e.target.value; +// } + +// @action +// onKeyPress = (e: React.KeyboardEvent) => { +// if (e.key === "Enter") { +// this.ids.push(this.idToAdd); +// this.idToAdd = ""; +// } +// } + +// render() { +// return ( +// <> +// +//
      +// {this.ids.map(id => )} +//
      +// +// ); +// } +// } + +// ReactDOM.render(( +//
      +// +//
      ), +// document.getElementById('root') +// ); \ No newline at end of file diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts index 7f87be45d..ae532c9e2 100644 --- a/src/fields/ScriptField.ts +++ b/src/fields/ScriptField.ts @@ -1,101 +1,101 @@ -import { Field, FieldId } from "./Field"; -import { Types } from "../server/Message"; -import { CompileScript, ScriptOptions, CompiledScript } from "../client/util/Scripting"; -import { Server } from "../client/Server"; -import { Without } from "../Utils"; +// import { Field, FieldId } from "./Field"; +// import { Types } from "../server/Message"; +// import { CompileScript, ScriptOptions, CompiledScript } from "../client/util/Scripting"; +// import { Server } from "../client/Server"; +// import { Without } from "../Utils"; -export interface SerializableOptions extends Without { - capturedIds: { [id: string]: string }; -} +// export interface SerializableOptions extends Without { +// capturedIds: { [id: string]: string }; +// } -export interface ScriptData { - script: string; - options: SerializableOptions; -} +// export interface ScriptData { +// script: string; +// options: SerializableOptions; +// } -export class ScriptField extends Field { - private _script?: CompiledScript; - get script(): CompiledScript { - return this._script!; - } - private options?: ScriptData; +// export class ScriptField extends Field { +// private _script?: CompiledScript; +// get script(): CompiledScript { +// return this._script!; +// } +// private options?: ScriptData; - constructor(script?: CompiledScript, id?: FieldId, save: boolean = true) { - super(id); +// constructor(script?: CompiledScript, id?: FieldId, save: boolean = true) { +// super(id); - this._script = script; +// this._script = script; - if (save) { - Server.UpdateField(this); - } - } +// if (save) { +// Server.UpdateField(this); +// } +// } - ToScriptString() { - return "new ScriptField(...)"; - } +// ToScriptString() { +// return "new ScriptField(...)"; +// } - GetValue() { - return this.script; - } +// GetValue() { +// return this.script; +// } - TrySetValue(): boolean { - throw new Error("Script fields currently can't be modified"); - } +// TrySetValue(): boolean { +// throw new Error("Script fields currently can't be modified"); +// } - UpdateFromServer() { - throw new Error("Script fields currently can't be updated"); - } +// UpdateFromServer() { +// throw new Error("Script fields currently can't be updated"); +// } - static FromJson(id: string, data: ScriptData): ScriptField { - let field = new ScriptField(undefined, id, false); - field.options = data; - return field; - } +// static FromJson(id: string, data: ScriptData): ScriptField { +// let field = new ScriptField(undefined, id, false); +// field.options = data; +// return field; +// } - init(callback: (res: Field) => any) { - const options = this.options!; - const keys = Object.keys(options.options.capturedIds); - Server.GetFields(keys).then(fields => { - let captured: { [name: string]: Field } = {}; - keys.forEach(key => captured[options.options.capturedIds[key]] = fields[key]); - const opts: ScriptOptions = { - addReturn: options.options.addReturn, - params: options.options.params, - requiredType: options.options.requiredType, - capturedVariables: captured - }; - const script = CompileScript(options.script, opts); - if (!script.compiled) { - throw new Error("Can't compile script"); - } - this._script = script; - callback(this); - }); - } +// init(callback: (res: Field) => any) { +// const options = this.options!; +// const keys = Object.keys(options.options.capturedIds); +// Server.GetFields(keys).then(fields => { +// let captured: { [name: string]: Field } = {}; +// keys.forEach(key => captured[options.options.capturedIds[key]] = fields[key]); +// const opts: ScriptOptions = { +// addReturn: options.options.addReturn, +// params: options.options.params, +// requiredType: options.options.requiredType, +// capturedVariables: captured +// }; +// const script = CompileScript(options.script, opts); +// if (!script.compiled) { +// throw new Error("Can't compile script"); +// } +// this._script = script; +// callback(this); +// }); +// } - ToJson() { - const { options, originalScript } = this.script; - let capturedIds: { [id: string]: string } = {}; - for (const capt in options.capturedVariables) { - capturedIds[options.capturedVariables[capt].Id] = capt; - } - const opts: SerializableOptions = { - ...options, - capturedIds - }; - delete (opts as any).capturedVariables; - return { - id: this.Id, - type: Types.Script, - data: { - script: originalScript, - options: opts, - }, - }; - } +// ToJson() { +// const { options, originalScript } = this.script; +// let capturedIds: { [id: string]: string } = {}; +// for (const capt in options.capturedVariables) { +// capturedIds[options.capturedVariables[capt].Id] = capt; +// } +// const opts: SerializableOptions = { +// ...options, +// capturedIds +// }; +// delete (opts as any).capturedVariables; +// return { +// id: this.Id, +// type: Types.Script, +// data: { +// script: originalScript, +// options: opts, +// }, +// }; +// } - Copy(): Field { - //Script fields are currently immutable, so we can fake copy them - return this; - } -} \ No newline at end of file +// Copy(): Field { +// //Script fields are currently immutable, so we can fake copy them +// return this; +// } +// } \ No newline at end of file diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx index ec89a1194..1f9e160ce 100644 --- a/src/mobile/ImageUpload.tsx +++ b/src/mobile/ImageUpload.tsx @@ -1,15 +1,14 @@ import * as ReactDOM from 'react-dom'; import * as rp from 'request-promise'; -import { Documents } from '../client/documents/Documents'; -import { Server } from '../client/Server'; -import { Document } from '../fields/Document'; -import { KeyStore } from '../fields/KeyStore'; -import { ListField } from '../fields/ListField'; +import { Docs } from '../client/documents/Documents'; import { RouteStore } from '../server/RouteStore'; -import { ServerUtils } from '../server/ServerUtil'; import "./ImageUpload.scss"; import React = require('react'); -import { Opt } from '../fields/Field'; +import { DocServer } from '../client/DocServer'; +import { Opt, Doc } from '../new_fields/Doc'; +import { Cast } from '../new_fields/Types'; +import { listSpec } from '../new_fields/Schema'; +import { List } from '../new_fields/List'; @@ -38,21 +37,24 @@ const onFileLoad = async (file: any) => { const json = await res.json(); json.map(async (file: any) => { let path = window.location.origin + file; - var doc: Document = Documents.ImageDocument(path, { nativeWidth: 200, width: 200 }); + var doc = Docs.ImageDocument(path, { nativeWidth: 200, width: 200 }); - const res = await rp.get(ServerUtils.prepend(RouteStore.getUserDocumentId)); + const res = await rp.get(DocServer.prepend(RouteStore.getUserDocumentId)); if (!res) { throw new Error("No user id returned"); } - const field = await Server.GetField(res); - let pending: Opt; - if (field instanceof Document) { - pending = await field.GetTAsync(KeyStore.OptionalRightCollection, Document); + const field = await DocServer.GetRefField(res); + let pending: Opt; + if (field instanceof Doc) { + pending = await Cast(field.optionalRightCollection, Doc); } if (pending) { - pending.GetOrCreateAsync(KeyStore.Data, ListField, list => { - list.Data.push(doc); - }); + const data = await Cast(pending.data, listSpec(Doc)); + if (data) { + data.push(doc); + } else { + pending.data = new List([doc]); + } } }); diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index a5f495477..6dfa37c1b 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -166,6 +166,4 @@ export namespace Doc { return delegate; } export const Prototype = Symbol("Prototype"); -} - -export const GetAsync = Doc.GetAsync; \ No newline at end of file +} \ No newline at end of file diff --git a/src/server/authentication/controllers/WorkspacesMenu.tsx b/src/server/authentication/controllers/WorkspacesMenu.tsx index b08c1aebe..29327e5ad 100644 --- a/src/server/authentication/controllers/WorkspacesMenu.tsx +++ b/src/server/authentication/controllers/WorkspacesMenu.tsx @@ -2,15 +2,15 @@ import * as React from 'react'; import { observable, action, configure, reaction, computed, ObservableMap, runInAction } from 'mobx'; import { observer } from "mobx-react"; import './WorkspacesMenu.css'; -import { Document } from '../../../fields/Document'; import { EditableView } from '../../../client/views/EditableView'; -import { KeyStore } from '../../../fields/KeyStore'; +import { Doc, Id } from '../../../new_fields/Doc'; +import { StrCast } from '../../../new_fields/Types'; export interface WorkspaceMenuProps { - active: Document | undefined; - open: (workspace: Document) => void; + active: Doc | undefined; + open: (workspace: Doc) => void; new: () => void; - allWorkspaces: Document[]; + allWorkspaces: Doc[]; isShown: () => boolean; toggle: () => void; } @@ -60,7 +60,7 @@ export class WorkspacesMenu extends React.Component { /> {this.props.allWorkspaces.map((s, i) =>
      { e.preventDefault(); this.props.open(s); @@ -73,9 +73,9 @@ export class WorkspacesMenu extends React.Component { {i + 1} - s.Title} + GetValue={() => StrCast(s.title)} SetValue={(title: string): boolean => { - s.SetText(KeyStore.Title, title); + s.title = title; return true; }} contents={s.Title} -- cgit v1.2.3-70-g09d2 From d4a77dd055685dd81a762ef40e0c3b7606586e9c Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sat, 27 Apr 2019 21:40:17 -0400 Subject: Split more files up --- src/client/DocServer.ts | 3 +- src/client/northstar/dash-fields/HistogramField.ts | 2 +- src/client/northstar/dash-nodes/HistogramBox.tsx | 3 +- src/client/util/Scripting.ts | 1 + src/client/views/Main.tsx | 3 +- .../views/collections/CollectionBaseView.tsx | 3 +- .../views/collections/CollectionDockingView.tsx | 3 +- .../views/collections/CollectionSchemaView.tsx | 3 +- src/client/views/collections/CollectionSubView.tsx | 3 +- .../views/collections/CollectionTreeView.tsx | 3 +- .../CollectionFreeFormLinksView.tsx | 3 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 3 +- src/client/views/nodes/DocumentView.tsx | 3 +- src/client/views/nodes/FieldView.tsx | 1 + src/client/views/nodes/LinkMenu.tsx | 3 +- src/new_fields/Doc.ts | 37 +++------------------- src/new_fields/HtmlField.ts | 2 +- src/new_fields/IconField.ts | 2 +- src/new_fields/InkField.ts | 2 +- src/new_fields/List.ts | 3 +- src/new_fields/ObjectField.ts | 17 ++++++++++ src/new_fields/Proxy.ts | 4 ++- src/new_fields/RefField.ts | 18 +++++++++++ src/new_fields/RichTextField.ts | 2 +- src/new_fields/Schema.ts | 8 ++--- src/new_fields/Types.ts | 2 +- src/new_fields/URLField.ts | 2 +- src/new_fields/util.ts | 4 ++- .../authentication/controllers/WorkspacesMenu.tsx | 3 +- src/server/database.ts | 2 ++ 30 files changed, 88 insertions(+), 60 deletions(-) create mode 100644 src/new_fields/ObjectField.ts create mode 100644 src/new_fields/RefField.ts (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 3f17baec6..c7cbfce37 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -1,8 +1,9 @@ import * as OpenSocket from 'socket.io-client'; import { MessageStore, Types, Message } from "./../server/Message"; -import { Opt, FieldWaiting, RefField, HandleUpdate } from '../new_fields/Doc'; +import { Opt, FieldWaiting } from '../new_fields/Doc'; import { Utils } from '../Utils'; import { SerializationHelper } from './util/SerializationHelper'; +import { RefField, HandleUpdate } from '../new_fields/RefField'; export namespace DocServer { const _cache: { [id: string]: RefField | Promise> } = {}; diff --git a/src/client/northstar/dash-fields/HistogramField.ts b/src/client/northstar/dash-fields/HistogramField.ts index 118f4cf7f..730289536 100644 --- a/src/client/northstar/dash-fields/HistogramField.ts +++ b/src/client/northstar/dash-fields/HistogramField.ts @@ -3,7 +3,7 @@ import { custom, serializable } from "serializr"; import { ColumnAttributeModel } from "../../../client/northstar/core/attribute/AttributeModel"; import { AttributeTransformationModel } from "../../../client/northstar/core/attribute/AttributeTransformationModel"; import { HistogramOperation } from "../../../client/northstar/operations/HistogramOperation"; -import { ObjectField } from "../../../new_fields/Doc"; +import { ObjectField } from "../../../new_fields/ObjectField"; import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; import { OmitKeys } from "../../../Utils"; import { Deserializable } from "../../util/SerializationHelper"; diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx index 4a65b14cf..a9c68ccba 100644 --- a/src/client/northstar/dash-nodes/HistogramBox.tsx +++ b/src/client/northstar/dash-nodes/HistogramBox.tsx @@ -20,7 +20,8 @@ import { HistogramLabelPrimitives } from "./HistogramLabelPrimitives"; import { StyleConstants } from "../utils/StyleContants"; import { NumCast, Cast } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; -import { Doc, Id } from "../../../new_fields/Doc"; +import { Doc } from "../../../new_fields/Doc"; +import { Id } from "../../../new_fields/RefField"; @observer diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index dbec82340..e45f61c11 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -11,6 +11,7 @@ import { Docs } from "../documents/Documents"; import { Doc, Field } from '../../new_fields/Doc'; import { ImageField, PdfField, VideoField, AudioField } from '../../new_fields/URLField'; import { List } from '../../new_fields/List'; +import { RichTextField } from '../../new_fields/RichTextField'; export interface ScriptSucccess { success: true; diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 1e3d4e259..4a68d1c68 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -33,10 +33,11 @@ import { MainOverlayTextBox } from './MainOverlayTextBox'; import { DocumentView } from './nodes/DocumentView'; import { PreviewCursor } from './PreviewCursor'; import { SelectionManager } from '../util/SelectionManager'; -import { FieldResult, Field, Doc, Id, Opt } from '../../new_fields/Doc'; +import { FieldResult, Field, Doc, Opt } from '../../new_fields/Doc'; import { Cast, FieldValue, StrCast } from '../../new_fields/Types'; import { DocServer } from '../DocServer'; import { listSpec } from '../../new_fields/Schema'; +import { Id } from '../../new_fields/RefField'; @observer diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index 4807dc40a..b2fba1415 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -4,9 +4,10 @@ import * as React from 'react'; import { ContextMenu } from '../ContextMenu'; import { FieldViewProps } from '../nodes/FieldView'; import { Cast, FieldValue, PromiseValue, NumCast } from '../../../new_fields/Types'; -import { Doc, FieldResult, Opt, Id } from '../../../new_fields/Doc'; +import { Doc, FieldResult, Opt } from '../../../new_fields/Doc'; import { listSpec } from '../../../new_fields/Schema'; import { List } from '../../../new_fields/List'; +import { Id } from '../../../new_fields/RefField'; export enum CollectionViewType { Invalid, diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 2ff409b9b..1574562c6 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -13,11 +13,12 @@ import React = require("react"); import { SubCollectionViewProps } from "./CollectionSubView"; import { DragManager, DragLinksAsDocuments } from "../../util/DragManager"; import { Transform } from '../../util/Transform'; -import { Doc, Id, Opt, Field, FieldId } from "../../../new_fields/Doc"; +import { Doc, Opt, Field } from "../../../new_fields/Doc"; import { Cast, NumCast } from "../../../new_fields/Types"; import { List } from "../../../new_fields/List"; import { DocServer } from "../../DocServer"; import { listSpec } from "../../../new_fields/Schema"; +import { Id, FieldId } from "../../../new_fields/RefField"; @observer export class CollectionDockingView extends React.Component { diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 874170f3d..58d20819b 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -19,10 +19,11 @@ import { DocumentView } from "../nodes/DocumentView"; import { FieldView, FieldViewProps } from "../nodes/FieldView"; import "./CollectionSchemaView.scss"; import { CollectionSubView } from "./CollectionSubView"; -import { Opt, Field, Doc, Id } from "../../../new_fields/Doc"; +import { Opt, Field, Doc } from "../../../new_fields/Doc"; import { Cast, FieldValue, NumCast } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; import { List } from "../../../new_fields/List"; +import { Id } from "../../../new_fields/RefField"; // bcz: need to add drag and drop of rows and columns. This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657 diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 558a8728f..4d090b680 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -10,12 +10,13 @@ import * as rp from 'request-promise'; import { CollectionView } from "./CollectionView"; import { CollectionPDFView } from "./CollectionPDFView"; import { CollectionVideoView } from "./CollectionVideoView"; -import { Doc, ObjectField, Opt } from "../../../new_fields/Doc"; +import { Doc, Opt } from "../../../new_fields/Doc"; import { DocComponent } from "../DocComponent"; import { listSpec } from "../../../new_fields/Schema"; import { Cast, PromiseValue } from "../../../new_fields/Types"; import { List } from "../../../new_fields/List"; import { DocServer } from "../../DocServer"; +import { ObjectField } from "../../../new_fields/ObjectField"; export interface CollectionViewProps extends FieldViewProps { addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index c9d8d83c8..7ec9a8549 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -10,7 +10,8 @@ import "./CollectionTreeView.scss"; import React = require("react"); import { Document, listSpec } from '../../../new_fields/Schema'; import { Cast, StrCast, BoolCast } from '../../../new_fields/Types'; -import { Doc, Id } from '../../../new_fields/Doc'; +import { Doc } from '../../../new_fields/Doc'; +import { Id } from '../../../new_fields/RefField'; export interface TreeViewProps { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index ce9995630..f693d55e8 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -7,10 +7,11 @@ import { CollectionViewProps } from "../CollectionSubView"; import "./CollectionFreeFormLinksView.scss"; import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView"; import React = require("react"); -import { Doc, Id } from "../../../../new_fields/Doc"; +import { Doc } from "../../../../new_fields/Doc"; import { Cast, FieldValue, NumCast, StrCast } from "../../../../new_fields/Types"; import { listSpec } from "../../../../new_fields/Schema"; import { List } from "../../../../new_fields/List"; +import { Id } from "../../../../new_fields/RefField"; @observer export class CollectionFreeFormLinksView extends React.Component { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 047fbad18..18107e98a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -19,10 +19,11 @@ import { MarqueeView } from "./MarqueeView"; import React = require("react"); import v5 = require("uuid/v5"); import { createSchema, makeInterface, listSpec } from "../../../../new_fields/Schema"; -import { Doc, Id } from "../../../../new_fields/Doc"; +import { Doc } from "../../../../new_fields/Doc"; import { FieldValue, Cast, NumCast } from "../../../../new_fields/Types"; import { pageSchema } from "../../nodes/ImageBox"; import { List } from "../../../../new_fields/List"; +import { Id } from "../../../../new_fields/RefField"; export const panZoomSchema = createSchema({ panX: "number", diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index aabc1633e..c304b6a35 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -15,7 +15,7 @@ import { ContextMenu } from "../ContextMenu"; import { DocumentContentsView } from "./DocumentContentsView"; import "./DocumentView.scss"; import React = require("react"); -import { Field, Opt, Doc, Id } from "../../../new_fields/Doc"; +import { Opt, Doc } from "../../../new_fields/Doc"; import { DocComponent } from "../DocComponent"; import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema"; import { FieldValue, Cast, PromiseValue } from "../../../new_fields/Types"; @@ -24,6 +24,7 @@ import { CollectionFreeFormView } from "../collections/collectionFreeForm/Collec import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; import { MarqueeView } from "../collections/collectionFreeForm/MarqueeView"; import { DocServer } from "../../DocServer"; +import { Id } from "../../../new_fields/RefField"; const linkSchema = createSchema({ title: "string", diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index c00c47fc4..dc36c5914 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -16,6 +16,7 @@ import { Opt, Doc, FieldResult } from "../../../new_fields/Doc"; import { List } from "../../../new_fields/List"; import { ImageField, VideoField, AudioField } from "../../../new_fields/URLField"; import { IconField } from "../../../new_fields/IconField"; +import { RichTextField } from "../../../new_fields/RichTextField"; // diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index 3ecc8555d..e21adebbc 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -5,9 +5,10 @@ import { LinkBox } from "./LinkBox"; import { LinkEditor } from "./LinkEditor"; import './LinkMenu.scss'; import React = require("react"); -import { Doc, Id } from "../../../new_fields/Doc"; +import { Doc } from "../../../new_fields/Doc"; import { Cast, FieldValue } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; +import { Id } from "../../../new_fields/RefField"; interface Props { docView: DocumentView; diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index fb7b6e360..4ef2a465f 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -8,38 +8,8 @@ import { Cast, ToConstructor, PromiseValue, FieldValue } from "./Types"; import { UndoManager, undoBatch } from "../client/util/UndoManager"; import { listSpec } from "./Schema"; import { List } from "./List"; - -export type FieldId = string; -export const HandleUpdate = Symbol("HandleUpdate"); -export const Id = Symbol("Id"); -export abstract class RefField { - @serializable(alias("id", primitive())) - private __id: FieldId; - readonly [Id]: FieldId; - - constructor(id?: FieldId) { - this.__id = id || Utils.GenerateGuid(); - this[Id] = this.__id; - } - - protected [HandleUpdate]?(diff: any): void; -} - -export const Update = Symbol("Update"); -export const OnUpdate = Symbol("OnUpdate"); -export const Parent = Symbol("Parent"); -export class ObjectField { - protected [OnUpdate]?: (diff?: any) => void; - private [Parent]?: Doc; - readonly [Id] = ""; -} - -export namespace ObjectField { - export function MakeCopy(field: ObjectField) { - //TODO Types - return field; - } -} +import { ObjectField } from "./ObjectField"; +import { RefField, FieldId, Id } from "./RefField"; export function IsField(field: any): field is Field { return (typeof field === "string") @@ -53,6 +23,7 @@ export type Opt = T | undefined; export type FieldWaiting = T extends undefined ? never : Promise; export type FieldResult = Opt | FieldWaiting>; +export const Update = Symbol("Update"); export const Self = Symbol("Self"); @Deserializable("doc").withFields(["id"]) @@ -161,7 +132,7 @@ export namespace Doc { copy[key] = field; } } - }) + }); return copy; } diff --git a/src/new_fields/HtmlField.ts b/src/new_fields/HtmlField.ts index 76fdb1f62..808a3499b 100644 --- a/src/new_fields/HtmlField.ts +++ b/src/new_fields/HtmlField.ts @@ -1,6 +1,6 @@ import { Deserializable } from "../client/util/SerializationHelper"; import { serializable, primitive } from "serializr"; -import { ObjectField } from "./Doc"; +import { ObjectField } from "./ObjectField"; @Deserializable("html") export class HtmlField extends ObjectField { diff --git a/src/new_fields/IconField.ts b/src/new_fields/IconField.ts index 32f3aa4d5..46f111f8e 100644 --- a/src/new_fields/IconField.ts +++ b/src/new_fields/IconField.ts @@ -1,6 +1,6 @@ import { Deserializable } from "../client/util/SerializationHelper"; import { serializable, primitive } from "serializr"; -import { ObjectField } from "./Doc"; +import { ObjectField } from "./ObjectField"; @Deserializable("icon") export class IconField extends ObjectField { diff --git a/src/new_fields/InkField.ts b/src/new_fields/InkField.ts index 49e6bf61e..42223c494 100644 --- a/src/new_fields/InkField.ts +++ b/src/new_fields/InkField.ts @@ -1,6 +1,6 @@ import { Deserializable } from "../client/util/SerializationHelper"; import { serializable, custom, createSimpleSchema, list, object, map } from "serializr"; -import { ObjectField } from "./Doc"; +import { ObjectField } from "./ObjectField"; export enum InkTool { None, diff --git a/src/new_fields/List.ts b/src/new_fields/List.ts index f01ac210a..428f661c9 100644 --- a/src/new_fields/List.ts +++ b/src/new_fields/List.ts @@ -1,8 +1,9 @@ import { Deserializable, autoObject } from "../client/util/SerializationHelper"; -import { Field, ObjectField, Update, OnUpdate, Self } from "./Doc"; +import { Field, Update, Self } from "./Doc"; import { setter, getter } from "./util"; import { serializable, alias, list } from "serializr"; import { observable } from "mobx"; +import { ObjectField, OnUpdate } from "./ObjectField"; @Deserializable("list") class ListImpl extends ObjectField { diff --git a/src/new_fields/ObjectField.ts b/src/new_fields/ObjectField.ts new file mode 100644 index 000000000..9cac2c528 --- /dev/null +++ b/src/new_fields/ObjectField.ts @@ -0,0 +1,17 @@ +import { Doc } from "./Doc"; + +export const OnUpdate = Symbol("OnUpdate"); +export const Parent = Symbol("Parent"); +const Id = Symbol("Object Id"); +export class ObjectField { + protected [OnUpdate]?: (diff?: any) => void; + private [Parent]?: Doc; + readonly [Id] = ""; +} + +export namespace ObjectField { + export function MakeCopy(field: ObjectField) { + //TODO Types + return field; + } +} diff --git a/src/new_fields/Proxy.ts b/src/new_fields/Proxy.ts index 2aa78731e..56e41cc0f 100644 --- a/src/new_fields/Proxy.ts +++ b/src/new_fields/Proxy.ts @@ -1,8 +1,10 @@ import { Deserializable } from "../client/util/SerializationHelper"; -import { RefField, Id, ObjectField, FieldWaiting } from "./Doc"; +import { FieldWaiting } from "./Doc"; import { primitive, serializable } from "serializr"; import { observable, action } from "mobx"; import { DocServer } from "../client/DocServer"; +import { RefField, Id } from "./RefField"; +import { ObjectField } from "./ObjectField"; @Deserializable("proxy") export class ProxyField extends ObjectField { diff --git a/src/new_fields/RefField.ts b/src/new_fields/RefField.ts new file mode 100644 index 000000000..202c65f21 --- /dev/null +++ b/src/new_fields/RefField.ts @@ -0,0 +1,18 @@ +import { serializable, primitive, alias } from "serializr"; +import { Utils } from "../Utils"; + +export type FieldId = string; +export const HandleUpdate = Symbol("HandleUpdate"); +export const Id = Symbol("Id"); +export abstract class RefField { + @serializable(alias("id", primitive())) + private __id: FieldId; + readonly [Id]: FieldId; + + constructor(id?: FieldId) { + this.__id = id || Utils.GenerateGuid(); + this[Id] = this.__id; + } + + protected [HandleUpdate]?(diff: any): void; +} diff --git a/src/new_fields/RichTextField.ts b/src/new_fields/RichTextField.ts index 156e4efd9..0fa3cf73c 100644 --- a/src/new_fields/RichTextField.ts +++ b/src/new_fields/RichTextField.ts @@ -1,4 +1,4 @@ -import { ObjectField } from "./Doc"; +import { ObjectField } from "./ObjectField"; import { serializable } from "serializr"; export class RichTextField extends ObjectField { diff --git a/src/new_fields/Schema.ts b/src/new_fields/Schema.ts index 5081521c7..7444878fe 100644 --- a/src/new_fields/Schema.ts +++ b/src/new_fields/Schema.ts @@ -21,15 +21,15 @@ export function makeInterface(...schemas: T): (doc?: Doc) } } const proto = new Proxy({}, { - get(target: any, prop) { - const field = target.doc[prop]; + get(target: any, prop, receiver) { + const field = receiver.doc[prop]; if (prop in schema) { return Cast(field, (schema as any)[prop]); } return field; }, - set(target: any, prop, value) { - target.doc[prop] = value; + set(target: any, prop, value, receiver) { + receiver.doc[prop] = value; return true; } }); diff --git a/src/new_fields/Types.ts b/src/new_fields/Types.ts index 7fa18673f..3f8eabd45 100644 --- a/src/new_fields/Types.ts +++ b/src/new_fields/Types.ts @@ -1,4 +1,4 @@ -import { Field, Opt, FieldWaiting, FieldResult, RefField } from "./Doc"; +import { Field, Opt, FieldResult } from "./Doc"; import { List } from "./List"; export type ToType | ListSpec> = diff --git a/src/new_fields/URLField.ts b/src/new_fields/URLField.ts index 1da245e73..95c679df7 100644 --- a/src/new_fields/URLField.ts +++ b/src/new_fields/URLField.ts @@ -1,6 +1,6 @@ import { Deserializable } from "../client/util/SerializationHelper"; import { serializable, custom } from "serializr"; -import { ObjectField } from "./Doc"; +import { ObjectField } from "./ObjectField"; function url() { return custom( diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts index 2d9721b2e..011e8c8d9 100644 --- a/src/new_fields/util.ts +++ b/src/new_fields/util.ts @@ -1,8 +1,10 @@ import { UndoManager } from "../client/util/UndoManager"; -import { Update, OnUpdate, Parent, ObjectField, RefField, Doc, Id, Field } from "./Doc"; +import { Update, Doc, Field } from "./Doc"; import { SerializationHelper } from "../client/util/SerializationHelper"; import { ProxyField } from "./Proxy"; import { FieldValue } from "./Types"; +import { RefField, Id } from "./RefField"; +import { ObjectField, Parent, OnUpdate } from "./ObjectField"; export function setter(target: any, prop: string | symbol | number, value: any, receiver: any): boolean { if (SerializationHelper.IsSerializing()) { diff --git a/src/server/authentication/controllers/WorkspacesMenu.tsx b/src/server/authentication/controllers/WorkspacesMenu.tsx index 29327e5ad..91756315d 100644 --- a/src/server/authentication/controllers/WorkspacesMenu.tsx +++ b/src/server/authentication/controllers/WorkspacesMenu.tsx @@ -3,8 +3,9 @@ import { observable, action, configure, reaction, computed, ObservableMap, runIn import { observer } from "mobx-react"; import './WorkspacesMenu.css'; import { EditableView } from '../../../client/views/EditableView'; -import { Doc, Id } from '../../../new_fields/Doc'; +import { Doc } from '../../../new_fields/Doc'; import { StrCast } from '../../../new_fields/Types'; +import { Id } from '../../../new_fields/RefField'; export interface WorkspaceMenuProps { active: Doc | undefined; diff --git a/src/server/database.ts b/src/server/database.ts index a61b4d823..6b3b6797f 100644 --- a/src/server/database.ts +++ b/src/server/database.ts @@ -60,11 +60,13 @@ export class Database { } public getDocument(id: string, fn: (result?: Transferable) => void, collectionName = Database.DocumentsCollection) { + console.log("getDocument"); this.db && this.db.collection(collectionName).findOne({ id: id }, (err, result) => fn(result ? ({ id: result._id, type: result.type, data: result.data }) : undefined)); } public getDocuments(ids: string[], fn: (result: Transferable[]) => void, collectionName = Database.DocumentsCollection) { + console.log("getDocuments"); this.db && this.db.collection(collectionName).find({ id: { "$in": ids } }).toArray((err, docs) => { if (err) { console.log(err.message); -- cgit v1.2.3-70-g09d2 From ecb4c692c6f317c4108cb065874003fc51242f7a Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 1 May 2019 20:43:26 -0400 Subject: made tree view work for docking views --- src/client/documents/Documents.ts | 5 +++-- src/client/views/Main.tsx | 11 ++++++----- .../views/collections/CollectionDockingView.tsx | 22 +++++++++++++++++----- .../views/collections/CollectionTreeView.tsx | 9 +++++++-- src/client/views/nodes/DocumentView.tsx | 4 +++- 5 files changed, 36 insertions(+), 15 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 4045fb22b..6ca2567dc 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -57,6 +57,7 @@ export interface DocumentOptions { documentText?: string; borderRounding?: number; schemaColumns?: List; + dockingConfig?: string; // [key: string]: Opt; } const delegateKeys = ["x", "y", "width", "height", "panX", "panY"]; @@ -247,8 +248,8 @@ export namespace Docs { export function TreeDocument(documents: Array, options: DocumentOptions) { return CreateInstance(collProto, new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Tree }); } - export function DockDocument(config: string, options: DocumentOptions) { - return CreateInstance(collProto, config, { ...options, viewType: CollectionViewType.Docking }); + export function DockDocument(documents: Array, config: string, options: DocumentOptions) { + return CreateInstance(collProto, new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }); } export function CaptionDocument(doc: Doc) { diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 3f8314d5f..2636b08bd 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -150,7 +150,7 @@ export class Main extends React.Component { if (list) { let freeformDoc = Docs.FreeformDocument([], { x: 0, y: 400, title: "mini collection" }); var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc)] }] }; - let mainDoc = Docs.DockDocument(JSON.stringify(dockingLayout), { title: `Main Container ${list.length + 1}` }); + let mainDoc = Docs.DockDocument([freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${list.length + 1}` }); list.push(mainDoc); CurrentUserUtils.MainDocId = mainDoc[Id]; // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container) @@ -208,8 +208,8 @@ export class Main extends React.Component { } /* 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() { + nodesMenu() { + let imgurl = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg"; let pdfurl = "http://www.adobe.com/support/products/enterprise/knowledgecenter/media/c27211_sample_explain.pdf"; let weburl = "https://cs.brown.edu/courses/cs166/"; @@ -219,7 +219,8 @@ export class Main extends React.Component { let addTextNode = action(() => Docs.TextDocument({ borderRounding: -1, width: 200, height: 200, title: "a text note" })); let addColNode = action(() => Docs.FreeformDocument([], { width: 200, height: 200, title: "a freeform collection" })); let addSchemaNode = action(() => Docs.SchemaDocument([], { width: 200, height: 200, title: "a schema collection" })); - let addTreeNode = action(() => Docs.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", copyDraggedItems: true })); + let addTreeNode = action(() => Docs.TreeDocument([this.mainContainer!], { width: 250, height: 400, title: "Library:" + StrCast(this.mainContainer!.title), copyDraggedItems: true })); + // let addTreeNode = action(() => Docs.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", copyDraggedItems: true })); let addVideoNode = action(() => Docs.VideoDocument(videourl, { width: 200, title: "video node" })); let addPDFNode = action(() => Docs.PdfDocument(pdfurl, { width: 200, height: 200, title: "a pdf doc" })); let addImageNode = action(() => Docs.ImageDocument(imgurl, { width: 200, title: "an image of a cat" })); @@ -294,7 +295,7 @@ export class Main extends React.Component { {this.mainContent} - {this.nodesMenu} + {this.nodesMenu()} {this.miscButtons} {this.workspaceMenu} diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 6c8f3844a..2facd404f 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -1,7 +1,7 @@ import * as GoldenLayout from "golden-layout"; import 'golden-layout/src/css/goldenlayout-base.css'; import 'golden-layout/src/css/goldenlayout-dark-theme.css'; -import { action, observable, reaction, trace } from "mobx"; +import { action, observable, reaction, trace, runInAction } from "mobx"; import { observer } from "mobx-react"; import * as ReactDOM from 'react-dom'; import Measure from "react-measure"; @@ -14,7 +14,7 @@ import { SubCollectionViewProps } from "./CollectionSubView"; import { DragManager, DragLinksAsDocuments } from "../../util/DragManager"; import { Transform } from '../../util/Transform'; import { Doc, Opt, Field } from "../../../new_fields/Doc"; -import { Cast, NumCast } from "../../../new_fields/Types"; +import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { List } from "../../../new_fields/List"; import { DocServer } from "../../DocServer"; import { listSpec } from "../../../new_fields/Schema"; @@ -87,6 +87,10 @@ export class CollectionDockingView extends React.Component void = () => { if (this._containerRef.current) { reaction( - () => Cast(this.props.Document.data, "string", ""), + () => StrCast(this.props.Document.dockingConfig), () => { if (!this._goldenLayout || this._ignoreStateChange !== JSON.stringify(this._goldenLayout.toConfig())) { setTimeout(() => this.setupGoldenLayout(), 1); @@ -238,7 +242,7 @@ export class CollectionDockingView extends React.Component { var json = JSON.stringify(this._goldenLayout.toConfig()); - this.props.Document.data = json; + this.props.Document.dockingConfig = json; if (this.undohack && !this.hack) { this.undohack.end(); this.undohack = undefined; @@ -285,6 +289,14 @@ export class CollectionDockingView extends React.Component runInAction(() => { + if (f instanceof Doc) { + let docs = Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc)); + docs && docs.map((d, i) => d[Id] === f[Id] && docs!.splice(i, 1)); + // bcz: this seems like it should work, but it only does occasionally -- usually I get -1 + // docs && docs.indexOf(f) !== -1 && docs.splice(docs.indexOf(f), 1); + } + })); tab.contentItem.remove(); }); } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 7ec9a8549..1a6f1121d 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -12,6 +12,7 @@ import { Document, listSpec } from '../../../new_fields/Schema'; import { Cast, StrCast, BoolCast } from '../../../new_fields/Types'; import { Doc } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/RefField'; +import { Utils } from '../../../Utils'; export interface TreeViewProps { @@ -101,7 +102,9 @@ class TreeView extends React.Component { if (!this._collapsed) { bulletType = BulletType.Collapsible; childElements =
        - {children.map(value => )} + {/* // bcz: should this work? + {children.map(value => )} */} + {children.map(value => )}
      ; } else bulletType = BulletType.Collapsed; @@ -132,7 +135,9 @@ export class CollectionTreeView extends CollectionSubView(Document) { let copyOnDrag = BoolCast(this.props.Document.copyDraggedItems, false); let childrenElement = !children ? (null) : (children.map(value => - ) + //bcz: shouldn't this work? - I think value[Id] is undefined sometimes + // ) + ) ); return ( diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index f58dc4a02..b35d68c4b 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -198,7 +198,9 @@ export class DocumentView extends DocComponent(Docu CollectionDockingView.Instance.AddRightSplit(kvp); } fullScreenClicked = (e: React.MouseEvent): void => { - const doc = Doc.MakeDelegate(FieldValue(this.Document.proto)); + const doc = Doc.MakeDelegate(FieldValue(this.props.Document.proto)); + // bcz .. should this work? + // const doc = Doc.MakeDelegate(FieldValue(this.Document.proto)); if (doc) { CollectionDockingView.Instance.OpenFullScreen(doc); } -- cgit v1.2.3-70-g09d2 From 6d52cfb2a7b2b575f448e7d7c3f02a97afa0027b Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 1 May 2019 22:38:49 -0400 Subject: fixed drag drop making source documents disappear. improved TreeView collapsing. Chnaged schemaView options placement --- src/client/util/DragManager.ts | 17 +++++++++++------ src/client/views/collections/CollectionSchemaView.tsx | 2 +- src/client/views/collections/CollectionTreeView.tsx | 4 ++-- 3 files changed, 14 insertions(+), 9 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index a149da52f..4acf609f4 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -301,15 +301,20 @@ export namespace DragManager { function dispatchDrag(dragEles: HTMLElement[], e: PointerEvent, dragData: { [index: string]: any }, options?: DragOptions, finishDrag?: (dragData: { [index: string]: any }) => void) { let removed = dragEles.map(dragEle => { - let parent = dragEle.parentElement; - if (parent) parent.removeChild(dragEle); - return [dragEle, parent]; + // let parent = dragEle.parentElement; + // if (parent) parent.removeChild(dragEle); + let ret = [dragEle, dragEle.style.width, dragEle.style.height]; + dragEle.style.width = "0"; + dragEle.style.height = "0"; + return ret; }); const target = document.elementFromPoint(e.x, e.y); removed.map(r => { - let dragEle = r[0]; - let parent = r[1]; - if (parent && dragEle) parent.appendChild(dragEle); + let dragEle = r[0] as HTMLElement; + dragEle.style.width = r[1] as string; + dragEle.style.height = r[2] as string; + // let parent = r[1]; + // if (parent && dragEle) parent.appendChild(dragEle); }); if (target) { if (finishDrag) finishDrag(dragData); diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 1f26941c2..67784fa81 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -295,7 +295,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { get tableOptionsPanel() { return !this.props.active() ? (null) : (
      Options
      diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 1a6f1121d..2a620a5e6 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -104,7 +104,7 @@ class TreeView extends React.Component { childElements =
        {/* // bcz: should this work? {children.map(value => )} */} - {children.map(value => )} + {children.map(value => )}
      ; } else bulletType = BulletType.Collapsed; @@ -137,7 +137,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { (children.map(value => //bcz: shouldn't this work? - I think value[Id] is undefined sometimes // ) - ) + ) ); return ( -- cgit v1.2.3-70-g09d2 From be3719bbbede85b4dd099f436b4f7d3bade157cb Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Wed, 1 May 2019 23:49:23 -0400 Subject: Fixed treeview stuff --- src/client/views/collections/CollectionSubView.tsx | 6 ++++-- src/client/views/collections/CollectionTreeView.tsx | 9 ++------- 2 files changed, 6 insertions(+), 9 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 2c2d74302..bcbde24f0 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -13,7 +13,7 @@ import { CollectionVideoView } from "./CollectionVideoView"; import { Doc, Opt } from "../../../new_fields/Doc"; import { DocComponent } from "../DocComponent"; import { listSpec } from "../../../new_fields/Schema"; -import { Cast, PromiseValue } from "../../../new_fields/Types"; +import { Cast, PromiseValue, FieldValue } from "../../../new_fields/Types"; import { List } from "../../../new_fields/List"; import { DocServer } from "../../DocServer"; import { ObjectField } from "../../../new_fields/ObjectField"; @@ -48,7 +48,9 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { } get children() { - return Cast(this.props.Document[this.props.fieldKey], listSpec(Doc)); + //TODO tfs: This might not be what we want? + //This linter error can't be fixed because of how js arguments work, so don't switch this to filter(FieldValue) + return Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []).filter(doc => FieldValue(doc)); } @action diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 2a620a5e6..f148c2b2f 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -102,9 +102,7 @@ class TreeView extends React.Component { if (!this._collapsed) { bulletType = BulletType.Collapsible; childElements =
        - {/* // bcz: should this work? - {children.map(value => )} */} - {children.map(value => )} + {children.map(value => )}
      ; } else bulletType = BulletType.Collapsed; @@ -135,10 +133,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { let copyOnDrag = BoolCast(this.props.Document.copyDraggedItems, false); let childrenElement = !children ? (null) : (children.map(value => - //bcz: shouldn't this work? - I think value[Id] is undefined sometimes - // ) - ) - ); + )); return (
      Date: Thu, 2 May 2019 12:19:31 -0400 Subject: library and workspace stuff. --- src/client/views/Main.tsx | 28 ++----- src/client/views/TemplateMenu.tsx | 7 +- .../views/collections/CollectionDockingView.tsx | 27 ++----- .../views/collections/CollectionTreeView.scss | 1 - .../views/collections/CollectionTreeView.tsx | 57 ++++++++++---- src/client/views/collections/CollectionView.tsx | 1 + src/client/views/nodes/DocumentView.tsx | 10 +-- .../authentication/controllers/WorkspacesMenu.css | 3 - .../authentication/controllers/WorkspacesMenu.tsx | 90 ---------------------- 9 files changed, 60 insertions(+), 164 deletions(-) delete mode 100644 src/server/authentication/controllers/WorkspacesMenu.css delete mode 100644 src/server/authentication/controllers/WorkspacesMenu.tsx (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 4e918221c..b2ca4a196 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -100,8 +100,7 @@ export class Main extends React.Component { onHistory = () => { if (window.location.pathname !== RouteStore.home) { let pathname = window.location.pathname.split("/"); - CurrentUserUtils.MainDocId = pathname[pathname.length - 1]; - DocServer.GetRefField(CurrentUserUtils.MainDocId).then(action((field: Opt) => { + DocServer.GetRefField(pathname[pathname.length - 1]).then(action((field: Opt) => { if (field instanceof Doc) { this.openWorkspace(field, true); } @@ -132,7 +131,6 @@ export class Main extends React.Component { if (!CurrentUserUtils.MainDocId) { const doc = await Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc); if (doc) { - CurrentUserUtils.MainDocId = doc[Id]; this.openWorkspace(doc); } else { this.createNewWorkspace(); @@ -148,11 +146,12 @@ export class Main extends React.Component { createNewWorkspace = async (id?: string) => { const list = Cast(CurrentUserUtils.UserDocument.data, listSpec(Doc)); if (list) { + let libraryDoc = Docs.TreeDocument([CurrentUserUtils.UserDocument], { x: 0, y: 400, title: `Library: ${CurrentUserUtils.email} ${list.length + 1}` }); + libraryDoc.excludeFromLibrary = true; let freeformDoc = Docs.FreeformDocument([], { x: 0, y: 400, title: `WS collection ${list.length + 1}` }); - var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc)] }] }; - let mainDoc = Docs.DockDocument([freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${list.length + 1}` }); + var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(libraryDoc, 150), CollectionDockingView.makeDocumentConfig(freeformDoc, 600)] }] }; + let mainDoc = Docs.DockDocument([libraryDoc, freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${list.length + 1}` }); list.push(mainDoc); - CurrentUserUtils.MainDocId = mainDoc[Id]; // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container) setTimeout(() => { this.openWorkspace(mainDoc); @@ -164,6 +163,7 @@ export class Main extends React.Component { @action openWorkspace = async (doc: Doc, fromHistory = false) => { + CurrentUserUtils.MainDocId = doc[Id]; this.mainContainer = doc; fromHistory || window.history.pushState(null, StrCast(doc.title), "/doc/" + doc[Id]); const col = await Cast(CurrentUserUtils.UserDocument.optionalRightCollection, Doc); @@ -259,9 +259,7 @@ export class Main extends React.Component { /* @TODO this should really be moved into a moveable toolbar component, but for now let's put it here to meet the deadline */ @computed get miscButtons() { - let workspacesRef = React.createRef(); let logoutRef = React.createRef(); - let toggleWorkspaces = () => runInAction(() => this._workspacesShown = !this._workspacesShown); return [ , @@ -270,24 +268,11 @@ export class Main extends React.Component {
      , -
      -
      ,
      ]; } - @computed - get workspaceMenu() { - let areWorkspacesShown = () => this._workspacesShown; - let toggleWorkspaces = () => runInAction(() => this._workspacesShown = !this._workspacesShown); - let workspaces = Cast(CurrentUserUtils.UserDocument.data, listSpec(Doc)); - return (!workspaces || !this.mainContainer) ? (null) : - ; - } - render() { return (
      @@ -297,7 +282,6 @@ export class Main extends React.Component { {this.nodesMenu()} {this.miscButtons} - {this.workspaceMenu}
      diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index 7be846e05..f29d9c8a1 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -59,12 +59,9 @@ export class TemplateMenu extends React.Component { } render() { - trace(); let templateMenu: Array = []; - this.props.templates.forEach((checked, template) => { - console.log("checked + " + checked + " " + this.props.templates.get(template)); - templateMenu.push(); - }); + this.props.templates.forEach((checked, template) => + templateMenu.push()); return (
      diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 014773ab6..cfb1aef7d 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -19,15 +19,17 @@ import { List } from "../../../new_fields/List"; import { DocServer } from "../../DocServer"; import { listSpec } from "../../../new_fields/Schema"; import { Id, FieldId } from "../../../new_fields/RefField"; +import { faSignInAlt } from "@fortawesome/free-solid-svg-icons"; @observer export class CollectionDockingView extends React.Component { public static Instance: CollectionDockingView; - public static makeDocumentConfig(document: Doc) { + public static makeDocumentConfig(document: Doc, width?: number) { return { type: 'react-component', component: 'DocumentFrameRenderer', title: document.title, + width: width, props: { documentId: document[Id], //collectionDockingView: CollectionDockingView.Instance @@ -37,7 +39,6 @@ export class CollectionDockingView extends React.Component(); - private _fullScreen: any = null; private _flush: boolean = false; private _ignoreStateChange = ""; @@ -67,20 +68,9 @@ export class CollectionDockingView extends React.Component { - _mainCont = React.createRef(); @observable private _panelWidth = 0; @observable private _panelHeight = 0; @@ -347,10 +336,7 @@ export class DockedFrameRenderer extends React.Component { const nativeH = this.nativeHeight(); const nativeW = this.nativeWidth(); let wscale = this._panelWidth / nativeW; - if (wscale * nativeH > this._panelHeight) { - return this._panelHeight / nativeH; - } - return wscale; + return wscale * nativeH > this._panelHeight ? this._panelHeight / nativeH : wscale; } ScreenToLocalTransform = () => { @@ -364,6 +350,8 @@ export class DockedFrameRenderer extends React.Component { get previewPanelCenteringOffset() { return (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2; } get content() { + if (!this._document) + return (null); return (
      @@ -385,9 +373,10 @@ export class DockedFrameRenderer extends React.Component { } render() { + let theContent = this.content; return !this._document ? (null) : { this._panelWidth = r.entry.width; this._panelHeight = r.entry.height; })}> - {({ measureRef }) =>
      {this.content}
      } + {({ measureRef }) =>
      {theContent}
      }
      ; } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index 95df5edb9..ecb5f78bb 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -41,7 +41,6 @@ .coll-title { font-size: 24px; - margin-bottom: 20px; } .docContainer { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index f148c2b2f..0520e0f88 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -9,10 +9,15 @@ import { CollectionSubView } from "./CollectionSubView"; import "./CollectionTreeView.scss"; import React = require("react"); import { Document, listSpec } from '../../../new_fields/Schema'; -import { Cast, StrCast, BoolCast } from '../../../new_fields/Types'; +import { Cast, StrCast, BoolCast, FieldValue } from '../../../new_fields/Types'; import { Doc } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/RefField'; import { Utils } from '../../../Utils'; +import { JSXElement } from 'babel-types'; +import { ContextMenu } from '../ContextMenu'; +import { undoBatch } from '../../util/UndoManager'; +import { Main } from '../Main'; +import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils'; export interface TreeViewProps { @@ -42,11 +47,14 @@ class TreeView extends React.Component { delete = () => this.props.deleteDoc(this.props.document); + get children() { + return Cast(this.props.document.data, listSpec(Doc), []).filter(doc => FieldValue(doc)); + } + @action remove = (document: Document) => { - var children = Cast(this.props.document.data, listSpec(Doc)); - if (children) { - children.splice(children.indexOf(document), 1); + if (this.children) { + this.children.splice(this.children.indexOf(document), 1); } } @@ -94,27 +102,38 @@ class TreeView extends React.Component {
      ); } + onWorkspaceContextMenu = (e: React.MouseEvent): void => { + if (!e.isPropagationStopped() && this.props.document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 + if (!ContextMenu.Instance.getItems().some(item => item.description === "Open as Workspace")) { + ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => Main.Instance.openWorkspace(this.props.document)) }); + } + } + } render() { let bulletType = BulletType.List; - let childElements: JSX.Element | undefined = undefined; + let contentElement: JSX.Element | null = (null); var children = Cast(this.props.document.data, listSpec(Doc)); if (children) { // add children for a collection if (!this._collapsed) { bulletType = BulletType.Collapsible; - childElements =
        - {children.map(value => )} + contentElement =
          + {TreeView.GetChildElements(children, this.remove, this.move, this.props.copyOnDrag)}
        ; } else bulletType = BulletType.Collapsed; } - return
        + return
      • {this.renderBullet(bulletType)} {this.renderTitle()} - {childElements ? childElements : (null)} + {contentElement}
      • ; } + public static GetChildElements(docs: Doc[], remove: ((doc: Doc) => void), move: DragManager.MoveFunction, copyOnDrag: boolean) { + return docs.filter(child => !child.excludeFromLibrary).map(child => + ); + } } @observer @@ -128,21 +147,30 @@ export class CollectionTreeView extends CollectionSubView(Document) { } } + onContextMenu = (e: React.MouseEvent): void => { + if (!e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 + ContextMenu.Instance.addItem({ description: "Create Workspace", event: undoBatch(() => Main.Instance.createNewWorkspace()) }); + } + } render() { const children = this.children; let copyOnDrag = BoolCast(this.props.Document.copyDraggedItems, false); - let childrenElement = !children ? (null) : - (children.map(value => - )); + if (!children) { + return (null); + } + let testForLibrary = children && children.length === 1 && children[0] === CurrentUserUtils.UserDocument; + var subchildren = testForLibrary ? Cast(children[0].data, listSpec(Doc), children) : children; + let childElements = TreeView.GetChildElements(subchildren, this.remove, this.props.moveDocument, copyOnDrag); return (
        e.stopPropagation()} onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget}>
        StrCast(this.props.Document.title)} @@ -151,9 +179,8 @@ export class CollectionTreeView extends CollectionSubView(Document) { return true; }} />
        -
          - {childrenElement} + {childElements}
        ); diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index c2049a09a..8c1442d38 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -11,6 +11,7 @@ import { observer } from 'mobx-react'; import { undoBatch } from '../../util/UndoManager'; import { trace } from 'mobx'; import { Id } from '../../../new_fields/RefField'; +import { Main } from '../Main'; @observer export class CollectionView extends React.Component { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 5588fa1ac..86d34725d 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -205,16 +205,8 @@ export class DocumentView extends DocComponent(Docu CollectionDockingView.Instance.OpenFullScreen(doc); } ContextMenu.Instance.clearItems(); - ContextMenu.Instance.addItem({ description: "Close Full Screen", event: this.closeFullScreenClicked }); - ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); - } - closeFullScreenClicked = (e: React.MouseEvent): void => { - CollectionDockingView.Instance.CloseFullScreen(); - ContextMenu.Instance.clearItems(); - ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked }); - ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); + SelectionManager.DeselectAll(); } - @undoBatch @action drop = async (e: Event, de: DragManager.DropEvent) => { diff --git a/src/server/authentication/controllers/WorkspacesMenu.css b/src/server/authentication/controllers/WorkspacesMenu.css deleted file mode 100644 index b89039965..000000000 --- a/src/server/authentication/controllers/WorkspacesMenu.css +++ /dev/null @@ -1,3 +0,0 @@ -.ids:hover { - color: darkblue; -} \ No newline at end of file diff --git a/src/server/authentication/controllers/WorkspacesMenu.tsx b/src/server/authentication/controllers/WorkspacesMenu.tsx deleted file mode 100644 index 91756315d..000000000 --- a/src/server/authentication/controllers/WorkspacesMenu.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import * as React from 'react'; -import { observable, action, configure, reaction, computed, ObservableMap, runInAction } from 'mobx'; -import { observer } from "mobx-react"; -import './WorkspacesMenu.css'; -import { EditableView } from '../../../client/views/EditableView'; -import { Doc } from '../../../new_fields/Doc'; -import { StrCast } from '../../../new_fields/Types'; -import { Id } from '../../../new_fields/RefField'; - -export interface WorkspaceMenuProps { - active: Doc | undefined; - open: (workspace: Doc) => void; - new: () => void; - allWorkspaces: Doc[]; - isShown: () => boolean; - toggle: () => void; -} - -@observer -export class WorkspacesMenu extends React.Component { - constructor(props: WorkspaceMenuProps) { - super(props); - this.addNewWorkspace = this.addNewWorkspace.bind(this); - } - - @action - addNewWorkspace() { - this.props.new(); - this.props.toggle(); - } - - render() { - return ( -
        - - {this.props.allWorkspaces.map((s, i) => -
        { - e.preventDefault(); - this.props.open(s); - }} - style={{ - marginTop: 10, - color: s === this.props.active ? "red" : "black" - }} - > - {i + 1} - - StrCast(s.title)} - SetValue={(title: string): boolean => { - s.title = title; - return true; - }} - contents={s.Title} - height={20} - /> -
        - )} -
        - ); - } -} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 1ece3904b6b3fbf4ddde29e93e31a30f3898e720 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 2 May 2019 13:20:27 -0400 Subject: cleaned up drag code - userDropAction takes precedence over dropAction. default is alias --- src/client/documents/Documents.ts | 3 ++- src/client/util/DragManager.ts | 19 ++++++++++------ src/client/views/DocumentDecorations.tsx | 1 - src/client/views/EditableView.tsx | 2 +- src/client/views/Main.tsx | 14 +++++------- src/client/views/collections/CollectionSubView.tsx | 6 ++--- .../views/collections/CollectionTreeView.scss | 3 ++- .../views/collections/CollectionTreeView.tsx | 26 +++++++++++----------- src/client/views/nodes/DocumentView.tsx | 12 +++++----- src/client/views/nodes/FormattedTextBox.tsx | 6 +++-- 10 files changed, 49 insertions(+), 43 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 2de389a3c..8706359e4 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -32,6 +32,7 @@ import { IconField } from "../../new_fields/IconField"; import { listSpec } from "../../new_fields/Schema"; import { DocServer } from "../DocServer"; import { StrokeData, InkField } from "../../new_fields/InkField"; +import { dropActionType } from "../util/DragManager"; export interface DocumentOptions { x?: number; @@ -51,7 +52,7 @@ export interface DocumentOptions { templates?: List; viewType?: number; backgroundColor?: string; - copyDraggedItems?: boolean; + dropAction?: dropActionType; backgroundLayout?: string; curPage?: number; documentText?: string; diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 4acf609f4..a3dbe6e43 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -1,14 +1,14 @@ import { action } from "mobx"; import { emptyFunction } from "../../Utils"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; -import { DocumentDecorations } from "../views/DocumentDecorations"; import * as globalCssVariables from "../views/globalCssVariables.scss"; import { MainOverlayTextBox } from "../views/MainOverlayTextBox"; import { Doc } from "../../new_fields/Doc"; import { Cast } from "../../new_fields/Types"; import { listSpec } from "../../new_fields/Schema"; -export function SetupDrag(_reference: React.RefObject, docFunc: () => Doc, moveFunc?: DragManager.MoveFunction, copyOnDrop: boolean = false) { +export type dropActionType = "alias" | "copy" | undefined; +export function SetupDrag(_reference: React.RefObject, docFunc: () => Doc, moveFunc?: DragManager.MoveFunction, dropAction?: dropActionType) { let onRowMove = action((e: PointerEvent): void => { e.stopPropagation(); e.preventDefault(); @@ -16,7 +16,7 @@ export function SetupDrag(_reference: React.RefObject, docFunc: document.removeEventListener("pointermove", onRowMove); document.removeEventListener('pointerup', onRowUp); var dragData = new DragManager.DocumentDragData([docFunc()]); - dragData.copyOnDrop = copyOnDrop; + dragData.dropAction = dropAction; dragData.moveDocument = moveFunc; DragManager.StartDocumentDrag([_reference.current!], dragData, e.x, e.y); }); @@ -146,15 +146,20 @@ export namespace DragManager { droppedDocuments: Doc[]; xOffset: number; yOffset: number; - aliasOnDrop?: boolean; - copyOnDrop?: boolean; + dropAction: dropActionType; + userDropAction: dropActionType; moveDocument?: MoveFunction; [id: string]: any; } export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { StartDrag(eles, dragData, downX, downY, options, - (dropData: { [id: string]: any }) => (dropData.droppedDocuments = dragData.aliasOnDrop ? dragData.draggedDocuments.map(d => Doc.MakeAlias(d)) : dragData.copyOnDrop ? dragData.draggedDocuments.map(d => Doc.MakeCopy(d, true)) : dragData.draggedDocuments)); + (dropData: { [id: string]: any }) => + (dropData.droppedDocuments = dragData.userDropAction == "alias" || (!dragData.userDropAction && dragData.dropAction == "alias") ? + dragData.draggedDocuments.map(d => Doc.MakeAlias(d)) : + dragData.userDropAction == "copy" || (!dragData.userDropAction && dragData.dropAction == "copy") ? + dragData.draggedDocuments.map(d => Doc.MakeCopy(d, true)) : + dragData.draggedDocuments)); } export class LinkDragData { @@ -252,7 +257,7 @@ export namespace DragManager { e.stopPropagation(); e.preventDefault(); if (dragData instanceof DocumentDragData) { - dragData.aliasOnDrop = e.ctrlKey || e.altKey; + dragData.userDropAction = e.ctrlKey || e.altKey ? "alias" : undefined; } if (e.shiftKey) { AbortDrag(); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 7dcd71495..d07adcb71 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -144,7 +144,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> let dragData = new DragManager.DocumentDragData(SelectionManager.SelectedDocuments().map(dv => dv.props.Document)); dragData.xOffset = xoff; dragData.yOffset = yoff; - dragData.aliasOnDrop = false; dragData.moveDocument = SelectionManager.SelectedDocuments()[0].props.moveDocument; this.Interacting = true; this._hidden = true; diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index 2f17c6c51..73467eb9d 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -60,7 +60,7 @@ export class EditableView extends React.Component { return (
        this.editing = true)} > - {this.props.contents} + {this.props.contents}
        ); } diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index b2ca4a196..a07a2d5b1 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -8,9 +8,7 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; import Measure from 'react-measure'; import * as request from 'request'; -import { WorkspacesMenu } from '../../server/authentication/controllers/WorkspacesMenu'; import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; -import { MessageStore } from '../../server/Message'; import { RouteStore } from '../../server/RouteStore'; import { emptyFunction, returnTrue, Utils, returnOne, returnZero } from '../../Utils'; import { Docs } from '../documents/Documents'; @@ -86,11 +84,11 @@ export class Main extends React.Component { this.initEventListeners(); this.initAuthenticationRouters(); - try { - this.initializeNorthstar(); - } catch (e) { + // try { + // this.initializeNorthstar(); + // } catch (e) { - } + // } } componentDidMount() { window.onpopstate = this.onHistory; } @@ -219,8 +217,8 @@ export class Main extends React.Component { let addTextNode = action(() => Docs.TextDocument({ borderRounding: -1, width: 200, height: 200, title: "a text note" })); let addColNode = action(() => Docs.FreeformDocument([], { width: 200, height: 200, title: "a freeform collection" })); let addSchemaNode = action(() => Docs.SchemaDocument([], { width: 200, height: 200, title: "a schema collection" })); - let addTreeNode = action(() => Docs.TreeDocument([CurrentUserUtils.UserDocument], { width: 250, height: 400, title: "Library:" + CurrentUserUtils.email, copyDraggedItems: true })); - // let addTreeNode = action(() => Docs.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", copyDraggedItems: true })); + let addTreeNode = action(() => Docs.TreeDocument([CurrentUserUtils.UserDocument], { width: 250, height: 400, title: "Library:" + CurrentUserUtils.email, dropAction: "alias" })); + // let addTreeNode = action(() => Docs.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", dropAction: "copy" })); let addVideoNode = action(() => Docs.VideoDocument(videourl, { width: 200, title: "video node" })); let addPDFNode = action(() => Docs.PdfDocument(pdfurl, { width: 200, height: 200, title: "a pdf doc" })); let addImageNode = action(() => Docs.ImageDocument(imgurl, { width: 200, title: "an image of a cat" })); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index bcbde24f0..a59bc196e 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -83,13 +83,13 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { @action protected drop(e: Event, de: DragManager.DropEvent): boolean { if (de.data instanceof DragManager.DocumentDragData) { - if (de.data.aliasOnDrop || de.data.copyOnDrop) { + if (de.data.dropAction || de.data.userDropAction) { ["width", "height", "curPage"].map(key => de.data.draggedDocuments.map((draggedDocument: Doc, i: number) => PromiseValue(Cast(draggedDocument[key], "number")).then(f => f && (de.data.droppedDocuments[i][key] = f)))); } let added = false; - if (de.data.aliasOnDrop || de.data.copyOnDrop) { + if (de.data.dropAction || de.data.userDropAction) { added = de.data.droppedDocuments.reduce((added: boolean, d) => { let moved = this.props.addDocument(d); return moved || added; @@ -129,7 +129,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { } if (type.indexOf("excel") !== -1) { ctor = Docs.DBDocument; - options.copyDraggedItems = true; + options.dropAction = "copy"; } if (type.indexOf("html") !== -1) { if (path.includes('localhost')) { diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index ecb5f78bb..d00785879 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -45,7 +45,8 @@ .docContainer { margin-left: 10px; - display: inline-table; + display: block; + width: max-content; } .docContainer:hover { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 0520e0f88..037703c46 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -3,7 +3,7 @@ import { faCaretDown, faCaretRight, faTrashAlt } from '@fortawesome/free-solid-s import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, observable } from "mobx"; import { observer } from "mobx-react"; -import { DragManager, SetupDrag } from "../../util/DragManager"; +import { DragManager, SetupDrag, dropActionType } from "../../util/DragManager"; import { EditableView } from "../EditableView"; import { CollectionSubView } from "./CollectionSubView"; import "./CollectionTreeView.scss"; @@ -12,8 +12,6 @@ import { Document, listSpec } from '../../../new_fields/Schema'; import { Cast, StrCast, BoolCast, FieldValue } from '../../../new_fields/Types'; import { Doc } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/RefField'; -import { Utils } from '../../../Utils'; -import { JSXElement } from 'babel-types'; import { ContextMenu } from '../ContextMenu'; import { undoBatch } from '../../util/UndoManager'; import { Main } from '../Main'; @@ -24,7 +22,7 @@ export interface TreeViewProps { document: Doc; deleteDoc: (doc: Doc) => void; moveDocument: DragManager.MoveFunction; - copyOnDrag: boolean; + dropAction: "alias" | "copy" | undefined; } export enum BulletType { @@ -83,7 +81,7 @@ class TreeView extends React.Component { */ renderTitle() { let reference = React.createRef(); - let onItemDown = SetupDrag(reference, () => this.props.document, this.props.moveDocument, this.props.copyOnDrag); + let onItemDown = SetupDrag(reference, () => this.props.document, this.props.moveDocument, this.props.dropAction); let editableView = (titleString: string) => ( { height={36} GetValue={() => StrCast(this.props.document.title)} SetValue={(value: string) => { - this.props.document.title = value; + let target = this.props.document.proto ? this.props.document.proto : this.props.document; + target.title = value; return true; }} />); return (
        {editableView(StrCast(this.props.document.title))} -
        + {/*
        */}
        ); } @@ -117,7 +116,7 @@ class TreeView extends React.Component { if (!this._collapsed) { bulletType = BulletType.Collapsible; contentElement =
          - {TreeView.GetChildElements(children, this.remove, this.move, this.props.copyOnDrag)} + {TreeView.GetChildElements(children, this.remove, this.move, this.props.dropAction)}
        ; } else bulletType = BulletType.Collapsed; @@ -130,9 +129,9 @@ class TreeView extends React.Component {
        ; } - public static GetChildElements(docs: Doc[], remove: ((doc: Doc) => void), move: DragManager.MoveFunction, copyOnDrag: boolean) { + public static GetChildElements(docs: Doc[], remove: ((doc: Doc) => void), move: DragManager.MoveFunction, dropAction: dropActionType) { return docs.filter(child => !child.excludeFromLibrary).map(child => - ); + ); } } @@ -154,13 +153,13 @@ export class CollectionTreeView extends CollectionSubView(Document) { } render() { const children = this.children; - let copyOnDrag = BoolCast(this.props.Document.copyDraggedItems, false); + let dropAction = StrCast(this.props.Document.dropAction, "alias") as dropActionType; if (!children) { return (null); } let testForLibrary = children && children.length === 1 && children[0] === CurrentUserUtils.UserDocument; var subchildren = testForLibrary ? Cast(children[0].data, listSpec(Doc), children) : children; - let childElements = TreeView.GetChildElements(subchildren, this.remove, this.props.moveDocument, copyOnDrag); + let childElements = TreeView.GetChildElements(subchildren, this.remove, this.props.moveDocument, dropAction); return (
        StrCast(this.props.Document.title)} SetValue={(value: string) => { - this.props.Document.title = value; + let target = this.props.Document.proto ? this.props.Document.proto : this.props.Document; + target.title = value; return true; }} />
        diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 86d34725d..b151657e1 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -3,7 +3,7 @@ import { observer } from "mobx-react"; import { emptyFunction, Utils } from "../../../Utils"; import { Docs } from "../../documents/Documents"; import { DocumentManager } from "../../util/DocumentManager"; -import { DragManager } from "../../util/DragManager"; +import { DragManager, dropActionType } from "../../util/DragManager"; import { SelectionManager } from "../../util/SelectionManager"; import { Transform } from "../../util/Transform"; import { undoBatch, UndoManager } from "../../util/UndoManager"; @@ -129,12 +129,12 @@ export class DocumentView extends DocComponent(Docu e.stopPropagation(); } - startDragging(x: number, y: number, dropAliasOfDraggedDoc: boolean) { + startDragging(x: number, y: number, dropAction: dropActionType) { if (this._mainCont.current) { const [left, top] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(0, 0); let dragData = new DragManager.DocumentDragData([this.props.Document]); const [xoff, yoff] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top); - dragData.aliasOnDrop = dropAliasOfDraggedDoc; + dragData.dropAction = dropAction; dragData.xOffset = xoff; dragData.yOffset = yoff; dragData.moveDocument = this.props.moveDocument; @@ -142,7 +142,7 @@ export class DocumentView extends DocComponent(Docu handlers: { dragComplete: action(emptyFunction) }, - hideSource: !dropAliasOfDraggedDoc + hideSource: !dropAction }); } } @@ -162,7 +162,7 @@ export class DocumentView extends DocComponent(Docu } if (e.shiftKey && e.buttons === 1) { if (this.props.isTopMost) { - this.startDragging(e.pageX, e.pageY, e.altKey || e.ctrlKey); + this.startDragging(e.pageX, e.pageY, e.altKey || e.ctrlKey ? "alias" : undefined); } else { CollectionDockingView.Instance.StartOtherDrag([this.props.Document], e); } @@ -180,7 +180,7 @@ export class DocumentView extends DocComponent(Docu document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); if (!e.altKey && !this.topMost && (!CollectionFreeFormView.RIGHT_BTN_DRAG && e.buttons === 1) || (CollectionFreeFormView.RIGHT_BTN_DRAG && e.buttons === 2)) { - this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey); + this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined); } } e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 7f269a505..1d362bdaa 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -89,7 +89,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (title && title.startsWith("-") && this._editorView) { let str = this._editorView.state.doc.textContent; let titlestr = str.substr(0, Math.min(40, str.length)); - this.props.Document.title = "-" + titlestr + (str.length > 40 ? "..." : ""); + let target = this.props.Document.proto ? this.props.Document.proto : this.props.Document; + target.title = "-" + titlestr + (str.length > 40 ? "..." : ""); }; } } @@ -275,7 +276,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (StrCast(this.props.Document.title).startsWith("-") && this._editorView) { let str = this._editorView.state.doc.textContent; let titlestr = str.substr(0, Math.min(40, str.length)); - this.props.Document.title = "-" + titlestr + (str.length > 40 ? "..." : ""); + let target = this.props.Document.proto ? this.props.Document.proto : this.props.Document; + target.title = "-" + titlestr + (str.length > 40 ? "..." : ""); } } render() { -- cgit v1.2.3-70-g09d2 From 7dcd9dac57f848271ef6fa96f66064382331ee13 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 2 May 2019 14:17:15 -0400 Subject: added some hover highlighting. --- .../views/collections/CollectionTreeView.tsx | 28 ++++++++++++++++------ .../views/nodes/CollectionFreeFormDocumentView.tsx | 9 ++++++- src/client/views/nodes/DocumentView.tsx | 5 ++-- 3 files changed, 32 insertions(+), 10 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 037703c46..3c8cfcffd 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -46,13 +46,14 @@ class TreeView extends React.Component { delete = () => this.props.deleteDoc(this.props.document); get children() { - return Cast(this.props.document.data, listSpec(Doc), []).filter(doc => FieldValue(doc)); + return Cast(this.props.document.data, listSpec(Doc), []); // bcz: needed? .filter(doc => FieldValue(doc)); } @action remove = (document: Document) => { - if (this.children) { - this.children.splice(this.children.indexOf(document), 1); + let children = Cast(this.props.document.data, listSpec(Doc), []); + if (children) { + children.splice(children.indexOf(document), 1); } } @@ -106,8 +107,15 @@ class TreeView extends React.Component { if (!ContextMenu.Instance.getItems().some(item => item.description === "Open as Workspace")) { ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => Main.Instance.openWorkspace(this.props.document)) }); } + if (!ContextMenu.Instance.getItems().some(item => item.description === "Delete")) { + ContextMenu.Instance.addItem({ description: "Delete", event: undoBatch(() => this.props.deleteDoc(this.props.document)) }); + } } } + + onPointerEnter = (e: React.PointerEvent): void => { this.props.document.libraryBrush = true; } + onPointerLeave = (e: React.PointerEvent): void => { this.props.document.libraryBrush = false; } + render() { let bulletType = BulletType.List; let contentElement: JSX.Element | null = (null); @@ -121,7 +129,12 @@ class TreeView extends React.Component { } else bulletType = BulletType.Collapsed; } - return
        + return
      • {this.renderBullet(bulletType)} {this.renderTitle()} @@ -137,19 +150,20 @@ class TreeView extends React.Component { @observer export class CollectionTreeView extends CollectionSubView(Document) { - @action remove = (document: Document) => { - const children = this.children; + let children = Cast(this.props.Document.data, listSpec(Doc), []); if (children) { children.splice(children.indexOf(document), 1); } } - onContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 ContextMenu.Instance.addItem({ description: "Create Workspace", event: undoBatch(() => Main.Instance.createNewWorkspace()) }); } + if (!ContextMenu.Instance.getItems().some(item => item.description === "Delete")) { + ContextMenu.Instance.addItem({ description: "Delete", event: undoBatch(() => this.remove(this.props.Document)) }); + } } render() { const children = this.children; diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 27928d00b..09f9c8516 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -6,7 +6,7 @@ import "./DocumentView.scss"; import React = require("react"); import { DocComponent } from "../DocComponent"; import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema"; -import { FieldValue, Cast, NumCast } from "../../../new_fields/Types"; +import { FieldValue, Cast, NumCast, BoolCast } from "../../../new_fields/Types"; import { OmitKeys, Utils } from "../../../Utils"; import { SelectionManager } from "../../util/SelectionManager"; import { matchedData } from "express-validator/filter"; @@ -155,6 +155,9 @@ export class CollectionFreeFormDocumentView extends DocComponent { this.props.Document.libraryBrush = true; } + onPointerLeave = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = false; } + borderRounding = () => { let br = NumCast(this.props.Document.borderRounding); return br >= 0 ? br : @@ -180,8 +183,12 @@ export class CollectionFreeFormDocumentView extends DocComponent(Docu ContextMenu.Instance.addItem({ description: "Copy URL", event: () => Utils.CopyText(DocServer.prepend("/doc/" + this.props.Document[Id])) }); ContextMenu.Instance.addItem({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document[Id]) }); //ContextMenu.Instance.addItem({ description: "Docking", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Docking) }) - ContextMenu.Instance.addItem({ description: "Delete", event: this.deleteClicked }); + ContextMenu.Instance.addItem({ description: "Delete" + this.props.Document.title, event: this.deleteClicked }); ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); if (!SelectionManager.IsSelected(this)) { SelectionManager.SelectDoc(this, false); } } + isSelected = () => SelectionManager.IsSelected(this); select = (ctrlPressed: boolean) => SelectionManager.SelectDoc(this, ctrlPressed); -- cgit v1.2.3-70-g09d2 From a79bda7e5c7e77e398a33b8262f861155c5f1462 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 2 May 2019 14:34:33 -0400 Subject: fixed some issues with contextmenus for tree view --- src/client/views/collections/CollectionTreeView.tsx | 18 ++++++++++-------- src/client/views/nodes/DocumentView.tsx | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 3c8cfcffd..48b226615 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -16,6 +16,8 @@ import { ContextMenu } from '../ContextMenu'; import { undoBatch } from '../../util/UndoManager'; import { Main } from '../Main'; import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils'; +import { CollectionDockingView } from './CollectionDockingView'; +import { DocumentManager } from '../../util/DocumentManager'; export interface TreeViewProps { @@ -104,12 +106,14 @@ class TreeView extends React.Component { onWorkspaceContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped() && this.props.document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 - if (!ContextMenu.Instance.getItems().some(item => item.description === "Open as Workspace")) { - ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => Main.Instance.openWorkspace(this.props.document)) }); - } - if (!ContextMenu.Instance.getItems().some(item => item.description === "Delete")) { - ContextMenu.Instance.addItem({ description: "Delete", event: undoBatch(() => this.props.deleteDoc(this.props.document)) }); + ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => Main.Instance.openWorkspace(this.props.document)) }); + ContextMenu.Instance.addItem({ description: "Open Right", event: () => CollectionDockingView.Instance.AddRightSplit(this.props.document) }); + if (DocumentManager.Instance.getDocumentViews(this.props.document).length) { + ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.props.document).map(view => view.props.focus(this.props.document)) }); } + ContextMenu.Instance.addItem({ description: "Delete", event: undoBatch(() => this.props.deleteDoc(this.props.document)) }); + ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); + e.stopPropagation(); } } @@ -130,9 +134,7 @@ class TreeView extends React.Component { else bulletType = BulletType.Collapsed; } return
      • diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 3c8804415..8c245ab3a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -268,7 +268,7 @@ export class DocumentView extends DocComponent(Docu ContextMenu.Instance.addItem({ description: "Copy URL", event: () => Utils.CopyText(DocServer.prepend("/doc/" + this.props.Document[Id])) }); ContextMenu.Instance.addItem({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document[Id]) }); //ContextMenu.Instance.addItem({ description: "Docking", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Docking) }) - ContextMenu.Instance.addItem({ description: "Delete" + this.props.Document.title, event: this.deleteClicked }); + ContextMenu.Instance.addItem({ description: "Delete", event: this.deleteClicked }); ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); if (!SelectionManager.IsSelected(this)) { SelectionManager.SelectDoc(this, false); -- cgit v1.2.3-70-g09d2 From a78282cdf7fbb99386484640e1fb89d4b9b0cbee Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 2 May 2019 16:19:07 -0400 Subject: fixed some things with trees and summaries. --- src/client/views/Main.tsx | 3 +-- src/client/views/Templates.tsx | 12 +++++++++++- src/client/views/collections/CollectionTreeView.tsx | 10 +++++++--- .../views/collections/collectionFreeForm/MarqueeView.tsx | 4 +++- src/client/views/nodes/DocumentContentsView.tsx | 3 ++- src/client/views/nodes/FieldView.tsx | 11 ++++------- src/client/views/nodes/ImageBox.scss | 2 ++ src/new_fields/List.ts | 6 +++--- src/server/authentication/models/current_user_utils.ts | 2 ++ 9 files changed, 35 insertions(+), 18 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index a07a2d5b1..97eb73d7f 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -144,8 +144,7 @@ export class Main extends React.Component { createNewWorkspace = async (id?: string) => { const list = Cast(CurrentUserUtils.UserDocument.data, listSpec(Doc)); if (list) { - let libraryDoc = Docs.TreeDocument([CurrentUserUtils.UserDocument], { x: 0, y: 400, title: `Library: ${CurrentUserUtils.email} ${list.length + 1}` }); - libraryDoc.excludeFromLibrary = true; + let libraryDoc = await (CurrentUserUtils.UserDocument.library as Doc); let freeformDoc = Docs.FreeformDocument([], { x: 0, y: 400, title: `WS collection ${list.length + 1}` }); var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(libraryDoc, 150), CollectionDockingView.makeDocumentConfig(freeformDoc, 600)] }] }; let mainDoc = Docs.DockDocument([libraryDoc, freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${list.length + 1}` }); diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx index e17dd2354..668ae5312 100644 --- a/src/client/views/Templates.tsx +++ b/src/client/views/Templates.tsx @@ -54,7 +54,17 @@ export namespace Templates { `
        {layout}
        {props.Document.title}
        ` ); export const Summary = new Template("Title", TemplatePosition.InnerTop, - `
        {layout}
        {props.Document.doc1.title}
        ` + `
        +
        + {layout} +
        +
        + +
        +
        + +
        +
        ` ); export const TemplateList: Template[] = [Title, OuterCaption, InnerCaption, SideCaption]; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 48b226615..2cef1462b 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -1,7 +1,7 @@ import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; import { faCaretDown, faCaretRight, faTrashAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, observable } from "mobx"; +import { action, observable, trace } from "mobx"; import { observer } from "mobx-react"; import { DragManager, SetupDrag, dropActionType } from "../../util/DragManager"; import { EditableView } from "../EditableView"; @@ -145,8 +145,11 @@ class TreeView extends React.Component {
      • ; } public static GetChildElements(docs: Doc[], remove: ((doc: Doc) => void), move: DragManager.MoveFunction, dropAction: dropActionType) { - return docs.filter(child => !child.excludeFromLibrary).map(child => - ); + return docs.filter(child => !child.excludeFromLibrary).filter(doc => FieldValue(doc)).map(child => { + console.log("child = " + child[Id]); + return + } + ); } } @@ -168,6 +171,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { } } render() { + trace(); const children = this.children; let dropAction = StrCast(this.props.Document.dropAction, "alias") as dropActionType; if (!children) { diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index c58e7780c..82027a6f2 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -180,7 +180,9 @@ export class MarqueeView extends React.Component // SelectionManager.DeselectAll(); if (e.key === "r") { let summary = Docs.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "yellow", title: "-summary-" }); - summary.doc1 = newCollection.proto!; + summary.doc1 = selected[0]; + if (selected.length > 1) + summary.doc2 = selected[1]; summary.templates = new List([Templates.Summary.Layout]); this.props.addLiveTextDocument(summary); e.preventDefault(); diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 24e8a36ae..ddfe79a5c 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -15,6 +15,7 @@ import { IconBox } from "./IconBox"; import { KeyValueBox } from "./KeyValueBox"; import { PDFBox } from "./PDFBox"; import { VideoBox } from "./VideoBox"; +import { FieldView } from "./FieldView"; import { WebBox } from "./WebBox"; import { HistogramBox } from "../../northstar/dash-nodes/HistogramBox"; import React = require("react"); @@ -63,7 +64,7 @@ export class DocumentContentsView extends React.Component { } render() { const field = this.field; - if (!field) { + if (field === undefined) { return

        {''}

        ; } - if (typeof field === "string") { - return

        {field}

        ; - } + // if (typeof field === "string") { + // return

        {field}

        ; + // } else if (field instanceof RichTextField) { return ; } @@ -108,9 +108,6 @@ export class FieldView extends React.Component { // else if (field instanceof HtmlField) { // return // } - else if (typeof field === "number") { - return

        {field}

        ; - } else if (!(field instanceof Promise)) { return

        {JSON.stringify(field)}

        ; } diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss index 9fe211df0..2316a050e 100644 --- a/src/client/views/nodes/ImageBox.scss +++ b/src/client/views/nodes/ImageBox.scss @@ -10,6 +10,8 @@ } .imageBox-cont-interactive { pointer-events: all; + width:100%; + height:auto; } .imageBox-dot { diff --git a/src/new_fields/List.ts b/src/new_fields/List.ts index 1c4b96c81..db7932cec 100644 --- a/src/new_fields/List.ts +++ b/src/new_fields/List.ts @@ -2,7 +2,7 @@ import { Deserializable, autoObject } from "../client/util/SerializationHelper"; import { Field, Update, Self, FieldResult } from "./Doc"; import { setter, getter, deleteProperty } from "./util"; import { serializable, alias, list } from "serializr"; -import { observable } from "mobx"; +import { observable, action } from "mobx"; import { ObjectField, OnUpdate, Copy } from "./ObjectField"; import { RefField } from "./RefField"; import { ProxyField } from "./Proxy"; @@ -25,12 +25,12 @@ const listHandlers: any = { this[Update](); return field; }, - push(...items: any[]) { + push: action(function (this: any, ...items: any[]) { items = items.map(toObjectField); const res = this[Self].__fields.push(...items); this[Update](); return res; - }, + }), reverse() { const res = this[Self].__fields.reverse(); this[Update](); diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 9db470ca0..93c2afb1d 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -26,6 +26,8 @@ export class CurrentUserUtils { doc.title = this.email; doc.data = new List(); doc.optionalRightCollection = Docs.SchemaDocument([], { title: "Pending documents" }); + doc.library = Docs.TreeDocument([doc], { title: `Library: ${CurrentUserUtils.email}` }); + (doc.library as Doc).excludeFromLibrary = true; return doc; } -- cgit v1.2.3-70-g09d2 From ac9b42dba420139c0a7fad5685425b3cf9c1570e Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 2 May 2019 16:58:14 -0400 Subject: clean up --- src/client/views/Main.tsx | 5 ++--- src/client/views/collections/CollectionTreeView.scss | 2 ++ src/client/views/collections/CollectionTreeView.tsx | 11 +++-------- src/new_fields/Doc.ts | 5 +++-- src/server/authentication/models/current_user_utils.ts | 10 ++++++++-- 5 files changed, 18 insertions(+), 15 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 97eb73d7f..90339aea2 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -144,10 +144,9 @@ export class Main extends React.Component { createNewWorkspace = async (id?: string) => { const list = Cast(CurrentUserUtils.UserDocument.data, listSpec(Doc)); if (list) { - let libraryDoc = await (CurrentUserUtils.UserDocument.library as Doc); let freeformDoc = Docs.FreeformDocument([], { x: 0, y: 400, title: `WS collection ${list.length + 1}` }); - var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(libraryDoc, 150), CollectionDockingView.makeDocumentConfig(freeformDoc, 600)] }] }; - let mainDoc = Docs.DockDocument([libraryDoc, freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${list.length + 1}` }); + var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(CurrentUserUtils.UserDocument, 150), CollectionDockingView.makeDocumentConfig(freeformDoc, 600)] }] }; + let mainDoc = Docs.DockDocument([CurrentUserUtils.UserDocument, freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${list.length + 1}` }); list.push(mainDoc); // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container) setTimeout(() => { diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index ce7da5767..19d4abc05 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -37,6 +37,8 @@ width: 1.5em; display: inline-block; color: $intermediate-color; + margin-top: 3px; + transform: scale(1.3,1.3); } .coll-title { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 2cef1462b..b67d6f965 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -145,11 +145,8 @@ class TreeView extends React.Component {
        ; } public static GetChildElements(docs: Doc[], remove: ((doc: Doc) => void), move: DragManager.MoveFunction, dropAction: dropActionType) { - return docs.filter(child => !child.excludeFromLibrary).filter(doc => FieldValue(doc)).map(child => { - console.log("child = " + child[Id]); - return - } - ); + return docs.filter(child => !child.excludeFromLibrary).filter(doc => FieldValue(doc)).map(child => + ); } } @@ -177,9 +174,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { if (!children) { return (null); } - let testForLibrary = children && children.length === 1 && children[0] === CurrentUserUtils.UserDocument; - var subchildren = testForLibrary ? Cast(children[0].data, listSpec(Doc), children) : children; - let childElements = TreeView.GetChildElements(subchildren, this.remove, this.props.moveDocument, dropAction); + let childElements = TreeView.GetChildElements(children, this.remove, this.props.moveDocument, dropAction); return (
        { - // let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 }); - let linkDoc = new Doc; + let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 }); + //let linkDoc = new Doc; linkDoc.title = "-link name-"; linkDoc.linkDescription = ""; linkDoc.linkTags = "Default"; diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 93c2afb1d..5f45d7bcc 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -7,6 +7,9 @@ import { RouteStore } from "../../RouteStore"; import { DocServer } from "../../../client/DocServer"; import { Doc } from "../../../new_fields/Doc"; import { List } from "../../../new_fields/List"; +import { CollectionViewType } from "../../../client/views/collections/CollectionBaseView"; +import { CollectionTreeView } from "../../../client/views/collections/CollectionTreeView"; +import { CollectionView } from "../../../client/views/collections/CollectionView"; export class CurrentUserUtils { private static curr_email: string; @@ -23,11 +26,14 @@ export class CurrentUserUtils { private static createUserDocument(id: string): Doc { let doc = new Doc(id, true); + doc.viewType = CollectionViewType.Tree; + doc.layout = CollectionView.LayoutString(); doc.title = this.email; doc.data = new List(); + doc.excludeFromLibrary = true; doc.optionalRightCollection = Docs.SchemaDocument([], { title: "Pending documents" }); - doc.library = Docs.TreeDocument([doc], { title: `Library: ${CurrentUserUtils.email}` }); - (doc.library as Doc).excludeFromLibrary = true; + // doc.library = Docs.TreeDocument([doc], { title: `Library: ${CurrentUserUtils.email}` }); + // (doc.library as Doc).excludeFromLibrary = true; return doc; } -- cgit v1.2.3-70-g09d2 From c9f3a1934548169ebe3c2e97968680ff339e304e Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Sat, 4 May 2019 18:59:30 -0400 Subject: initial commit - todo: use cursor field rather than generic tuple field --- .../northstar/operations/HistogramOperation.ts | 5 +- src/client/util/DragManager.ts | 8 +-- src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/PresentationView.tsx | 4 +- src/client/views/PreviewCursor.tsx | 4 +- .../views/collections/CollectionDockingView.tsx | 6 ++- src/client/views/collections/CollectionSubView.tsx | 9 ++-- .../views/collections/CollectionTreeView.tsx | 4 +- .../views/collections/CollectionVideoView.tsx | 2 +- .../collections/collectionFreeForm/MarqueeView.tsx | 7 +-- .../views/nodes/CollectionFreeFormDocumentView.tsx | 9 ++-- src/client/views/nodes/FormattedTextBox.tsx | 8 +-- src/client/views/nodes/PDFBox.tsx | 5 +- src/new_fields/InkField.ts | 2 +- src/new_fields/ObjectField.ts | 2 +- src/new_fields/TupleField.ts | 63 ++++++++++++++++++++++ 16 files changed, 106 insertions(+), 34 deletions(-) create mode 100644 src/new_fields/TupleField.ts (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/northstar/operations/HistogramOperation.ts b/src/client/northstar/operations/HistogramOperation.ts index 5c9c832c0..78b206bdc 100644 --- a/src/client/northstar/operations/HistogramOperation.ts +++ b/src/client/northstar/operations/HistogramOperation.ts @@ -65,7 +65,7 @@ export class HistogramOperation extends BaseOperation implements IBaseFilterCons @computed public get FilterString(): string { if (this.OverridingFilters.length > 0) { - return "(" + this.OverridingFilters.filter(fm => fm != null).map(fm => fm.ToPythonString()).join(" || ") + ")"; + return "(" + this.OverridingFilters.filter(fm => fm !== null).map(fm => fm.ToPythonString()).join(" || ") + ")"; } let filterModels: FilterModel[] = []; return FilterModel.GetFilterModelsRecursive(this, new Set(), filterModels, true); @@ -89,8 +89,9 @@ export class HistogramOperation extends BaseOperation implements IBaseFilterCons @action public DrillDown(up: boolean) { if (!up) { - if (!this.BarFilterModels.length) + if (!this.BarFilterModels.length) { return; + } this._stackedFilters.push(this.BarFilterModels.map(f => f)); this.OverridingFilters.length = 0; this.OverridingFilters.push(...this._stackedFilters[this._stackedFilters.length - 1]); diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index a3dbe6e43..8f27f5b21 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -155,9 +155,9 @@ export namespace DragManager { export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { StartDrag(eles, dragData, downX, downY, options, (dropData: { [id: string]: any }) => - (dropData.droppedDocuments = dragData.userDropAction == "alias" || (!dragData.userDropAction && dragData.dropAction == "alias") ? + (dropData.droppedDocuments = dragData.userDropAction === "alias" || (!dragData.userDropAction && dragData.dropAction === "alias") ? dragData.draggedDocuments.map(d => Doc.MakeAlias(d)) : - dragData.userDropAction == "copy" || (!dragData.userDropAction && dragData.dropAction == "copy") ? + dragData.userDropAction === "copy" || (!dragData.userDropAction && dragData.dropAction === "copy") ? dragData.draggedDocuments.map(d => Doc.MakeCopy(d, true)) : dragData.draggedDocuments)); } @@ -280,7 +280,7 @@ export namespace DragManager { }; let hideDragElements = () => { - dragElements.map(dragElement => dragElement.parentNode == dragDiv && dragDiv.removeChild(dragElement)); + dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement)); eles.map(ele => (ele.hidden = false)); }; let endDrag = () => { @@ -289,7 +289,7 @@ export namespace DragManager { if (options) { options.handlers.dragComplete({}); } - } + }; AbortDrag = () => { hideDragElements(); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 693d6ec55..16e5b6b48 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -278,7 +278,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> iconDoc = this.createIcon([docView], layout); } if (SelectionManager.SelectedDocuments()[0].props.addDocument !== undefined) { - SelectionManager.SelectedDocuments()[0].props.addDocument!(iconDoc!); + SelectionManager.SelectedDocuments()[0].props.addDocument!(iconDoc); } return iconDoc; } diff --git a/src/client/views/PresentationView.tsx b/src/client/views/PresentationView.tsx index b4c12d057..d8bdbacca 100644 --- a/src/client/views/PresentationView.tsx +++ b/src/client/views/PresentationView.tsx @@ -1,7 +1,7 @@ import { observer } from "mobx-react"; -import React = require("react") +import React = require("react"); import { observable, action } from "mobx"; -import "./PresentationView.scss" +import "./PresentationView.scss"; import "./Main.tsx"; import { DocumentManager } from "../util/DocumentManager"; import { Utils } from "../../Utils"; diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index 4359ba093..9520f489c 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -17,7 +17,7 @@ export class PreviewCursor extends React.Component<{}> { constructor(props: any) { super(props); - document.addEventListener("keydown", this.onKeyPress) + document.addEventListener("keydown", this.onKeyPress); } @action @@ -27,7 +27,7 @@ export class PreviewCursor extends React.Component<{}> { // the keyPress here. //if not these keys, make a textbox if preview cursor is active! if (e.key.startsWith("F") && !e.key.endsWith("F")) { - } else if (e.key != "Escape" && e.key != "Alt" && e.key != "Shift" && e.key != "Meta" && e.key != "Control" && !e.defaultPrevented && !(e as any).DASHFormattedTextBoxHandled) { + } else if (e.key !== "Escape" && e.key !== "Alt" && e.key !== "Shift" && e.key !== "Meta" && e.key !== "Control" && !e.defaultPrevented && !(e as any).DASHFormattedTextBoxHandled) { if ((!e.ctrlKey && !e.metaKey) || e.key === "v") { PreviewCursor.Visible && PreviewCursor._onKeyPress && PreviewCursor._onKeyPress(e); PreviewCursor.Visible = false; diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index cfb1aef7d..725f0ab51 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -350,13 +350,15 @@ export class DockedFrameRenderer extends React.Component { get previewPanelCenteringOffset() { return (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2; } get content() { - if (!this._document) + if (!this._document) { return (null); + } return (
        - boolean; @@ -66,12 +67,12 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { if (!proto) { return; } - let cursors = await Cast(proto.cursors, listSpec(ObjectField)); + let cursors = await Cast(proto!.cursors, listSpec(TupleField)); if (!cursors) { - proto.cursors = cursors = new List(); + proto!.cursors = cursors = new List>(); } - if (cursors.length > 0 && (ind = cursors.findIndex(entry => entry.Data[0][0] === id)) > -1) { - cursors[ind].Data[1] = position; + if (cursors!.length > 0 && (ind = cursors!.findIndex(entry => entry.data[0][0] === id)) > -1) { + cursors![ind].data[1] = position; } else { let entry = new TupleField<[string, string], [number, number]>([textInfo, position]); cursors.push(entry); diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index b67d6f965..7898d74ce 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -117,8 +117,8 @@ class TreeView extends React.Component { } } - onPointerEnter = (e: React.PointerEvent): void => { this.props.document.libraryBrush = true; } - onPointerLeave = (e: React.PointerEvent): void => { this.props.document.libraryBrush = false; } + onPointerEnter = (e: React.PointerEvent): void => { this.props.document.libraryBrush = true; }; + onPointerLeave = (e: React.PointerEvent): void => { this.props.document.libraryBrush = false; }; render() { let bulletType = BulletType.List; diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx index 9dee217cb..cb3fd1ba4 100644 --- a/src/client/views/collections/CollectionVideoView.tsx +++ b/src/client/views/collections/CollectionVideoView.tsx @@ -90,7 +90,7 @@ export class CollectionVideoView extends React.Component { } } - setVideoBox = (player: VideoBox) => { this._videoBox = player; } + setVideoBox = (player: VideoBox) => { this._videoBox = player; }; private subView = (_type: CollectionViewType, renderProps: CollectionRenderProps) => { let props = { ...this.props, ...renderProps }; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 8c81f6990..a9e627188 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -139,7 +139,7 @@ export class MarqueeView extends React.Component if (this._commandExecuted) { return; } - if (e.key === "Backspace" || e.key === "Delete" || e.key == "d") { + if (e.key === "Backspace" || e.key === "Delete" || e.key === "d") { this._commandExecuted = true; this.marqueeSelect().map(d => this.props.removeDocument(d)); let ink = Cast(this.props.container.props.Document.ink, InkField); @@ -154,8 +154,9 @@ export class MarqueeView extends React.Component e.stopPropagation(); let bounds = this.Bounds; let selected = this.marqueeSelect().map(d => { - if (e.key !== "r") + if (e.key !== "r") { this.props.removeDocument(d); + } d.x = NumCast(d.x) - bounds.left - bounds.width / 2; d.y = NumCast(d.y) - bounds.top - bounds.height / 2; d.page = -1; @@ -194,7 +195,7 @@ export class MarqueeView extends React.Component let maxy = NumCast(maximizedDoc.y, undefined); let maxw = NumCast(maximizedDoc.width, undefined); let maxh = NumCast(maximizedDoc.height, undefined); - maximizedDoc.isIconAnimating = new List([scrpt[0], scrpt[1], maxx, maxy, maxw, maxh, Date.now(), 0]) + maximizedDoc.isIconAnimating = new List([scrpt[0], scrpt[1], maxx, maxy, maxw, maxh, Date.now(), 0]); }); } else { diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 2ba0458f5..6186cf348 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -148,7 +148,7 @@ export class CollectionFreeFormDocumentView extends DocComponent([scrpt[0], scrpt[1], maxx, maxy, maxw, maxh, Date.now(), isMinimized ? 1 : 0]) + maximizedDoc.isIconAnimating = new List([scrpt[0], scrpt[1], maxx, maxy, maxw, maxh, Date.now(), isMinimized ? 1 : 0]); } } }); @@ -167,16 +167,17 @@ export class CollectionFreeFormDocumentView extends DocComponent d instanceof Doc).map(maxDoc => this.props.addDocument!(maxDoc, false)); + } this.toggleIcon(); } } } } - onPointerEnter = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = true; } - onPointerLeave = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = false; } + onPointerEnter = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = true; }; + onPointerLeave = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = false; }; borderRounding = () => { let br = NumCast(this.props.Document.borderRounding); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index eeb60cb3d..3873dfd62 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -91,7 +91,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe let titlestr = str.substr(0, Math.min(40, str.length)); let target = this.props.Document.proto ? this.props.Document.proto : this.props.Document; target.title = "-" + titlestr + (str.length > 40 ? "..." : ""); - }; + } } } @@ -176,8 +176,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe onPointerDown = (e: React.PointerEvent): void => { if (e.button === 0 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) { e.stopPropagation(); - if (this._toolTipTextMenu && this._toolTipTextMenu.tooltip) + if (this._toolTipTextMenu && this._toolTipTextMenu.tooltip) { this._toolTipTextMenu.tooltip.style.opacity = "0"; + } } if (e.button === 2 || (e.button === 0 && e.ctrlKey)) { this._gotDown = true; @@ -185,8 +186,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } } onPointerUp = (e: React.PointerEvent): void => { - if (this._toolTipTextMenu && this._toolTipTextMenu.tooltip) + if (this._toolTipTextMenu && this._toolTipTextMenu.tooltip) { this._toolTipTextMenu.tooltip.style.opacity = "1"; + } if (e.buttons === 1 && this.props.isSelected() && !e.altKey) { e.stopPropagation(); } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index eb45ea273..caa66cbeb 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -215,8 +215,9 @@ export class PDFBox extends DocComponent(PdfDocumen if (e.altKey) { this._alt = true; } else { - if (e.metaKey) + if (e.metaKey) { e.stopPropagation(); + } } document.removeEventListener("pointerup", this.onPointerUp); document.addEventListener("pointerup", this.onPointerUp); @@ -286,7 +287,7 @@ export class PDFBox extends DocComponent(PdfDocumen renderHeight = 2400; @computed get pdfPage() { - return + return ; } @computed get pdfContent() { diff --git a/src/new_fields/InkField.ts b/src/new_fields/InkField.ts index 86a8bd18a..a3157857f 100644 --- a/src/new_fields/InkField.ts +++ b/src/new_fields/InkField.ts @@ -39,6 +39,6 @@ export class InkField extends ObjectField { } [Copy]() { - return new InkField(deepCopy(this.inkData)) + return new InkField(deepCopy(this.inkData)); } } diff --git a/src/new_fields/ObjectField.ts b/src/new_fields/ObjectField.ts index 0f3777af6..715c6a924 100644 --- a/src/new_fields/ObjectField.ts +++ b/src/new_fields/ObjectField.ts @@ -5,7 +5,7 @@ export const Parent = Symbol("Parent"); export const Copy = Symbol("Copy"); export abstract class ObjectField { - protected [OnUpdate]?: (diff?: any) => void; + protected [OnUpdate](diff?: any) { }; private [Parent]?: Doc; abstract [Copy](): ObjectField; } diff --git a/src/new_fields/TupleField.ts b/src/new_fields/TupleField.ts new file mode 100644 index 000000000..1ff57fefc --- /dev/null +++ b/src/new_fields/TupleField.ts @@ -0,0 +1,63 @@ +import { ObjectField, Copy } from "./ObjectField"; +import { IObservableArray, IArrayChange, IArraySplice, observe, Lambda, observable } from "mobx"; +import { UndoManager } from "../client/util/UndoManager"; +import { Field } from "./Doc"; +import { Deserializable } from "../client/util/SerializationHelper"; +import { serializable, createSimpleSchema, list, object } from "serializr"; +import { array } from "prop-types"; + +const tupleSchema = createSimpleSchema({ + +}); + +@Deserializable("tuple") +export class TupleField extends ObjectField { + + + @serializable(list(object(tupleSchema))) + private Data: [T, U]; + + public get data() { + return this.Data; + } + + constructor(data: [T, U]) { + super(); + this.Data = data; + this.observeTuple(); + } + + private observeDisposer: Lambda | undefined; + private observeTuple(): void { + this.observeDisposer = observe(this.Data as (T | U)[] as IObservableArray, (change: IArrayChange | IArraySplice) => { + if (change.type === "update") { + UndoManager.AddEvent({ + undo: () => this.Data[change.index] = change.oldValue, + redo: () => this.Data[change.index] = change.newValue + }); + } else { + throw new Error("Why are you messing with the length of a tuple, huh?"); + } + }); + } + + protected setData(value: [T, U]) { + if (this.observeDisposer) { + this.observeDisposer(); + } + this.Data = observable(value) as (T | U)[] as [T, U]; + this.observeTuple(); + } + + UpdateFromServer(values: [T, U]) { + this.setData(values); + } + + ToScriptString(): string { + return `new TupleField([${this.Data[0], this.Data[1]}])`; + } + + [Copy]() { + return new TupleField(this.Data); + } +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From af2e5dbf49e0e82d76f267c681761968d4bafc62 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 4 May 2019 23:03:49 -0400 Subject: fixed tree view. added non-data keys. --- src/client/views/Main.scss | 1 + .../views/collections/CollectionTreeView.scss | 17 +++++++--- .../views/collections/CollectionTreeView.tsx | 37 ++++++++++++++-------- src/new_fields/Doc.ts | 2 +- 4 files changed, 38 insertions(+), 19 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss index cbf920793..5c5c252e9 100644 --- a/src/client/views/Main.scss +++ b/src/client/views/Main.scss @@ -182,6 +182,7 @@ button:hover { top: 0; left: 0; overflow: scroll; + z-index: 1; } #mainContent-div { width: 100%; diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index 19d4abc05..6ce13cf56 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -33,9 +33,10 @@ } .bullet { - position: absolute; - width: 1.5em; - display: inline-block; + float:left; + position: relative; + width: 15px; + display: block; color: $intermediate-color; margin-top: 3px; transform: scale(1.3,1.3); @@ -50,7 +51,7 @@ .docContainer { margin-left: 10px; display: block; - width: max-content; + width:100%;//width: max-content; } .docContainer:hover { @@ -59,6 +60,9 @@ // width: auto; } } + .editableView-container { + font-weight: bold; + } .delete-button { color: $intermediate-color; @@ -67,4 +71,9 @@ // margin-top: 3px; display: inline; } + + .collectionTreeView-keyHeader { + font-style: italic; + font-size: 8pt; + } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index b67d6f965..17109508d 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -18,6 +18,8 @@ import { Main } from '../Main'; import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils'; import { CollectionDockingView } from './CollectionDockingView'; import { DocumentManager } from '../../util/DocumentManager'; +import { Utils } from '../../../Utils'; +import { List } from '../../../new_fields/List'; export interface TreeViewProps { @@ -122,17 +124,25 @@ class TreeView extends React.Component { render() { let bulletType = BulletType.List; - let contentElement: JSX.Element | null = (null); - var children = Cast(this.props.document.data, listSpec(Doc)); - if (children) { // add children for a collection - if (!this._collapsed) { - bulletType = BulletType.Collapsible; - contentElement =
          - {TreeView.GetChildElements(children, this.remove, this.move, this.props.dropAction)} -
        ; - } - else bulletType = BulletType.Collapsed; + let contentElement: (JSX.Element | null)[] = []; + let keys = Array.from(Object.keys(this.props.document)); + if (this.props.document.proto instanceof Doc) { + keys.push(...Array.from(Object.keys(this.props.document.proto))); } + keys.map(key => { + let docList = Cast(this.props.document[key], listSpec(Doc)); + if (docList instanceof List && docList.length && docList[0] instanceof Doc) { + if (!this._collapsed) { + bulletType = BulletType.Collapsible; + contentElement.push(
          + {(key === "data") ? (null) : + {key}} + {TreeView.GetChildElements(docList, key !== "data", this.remove, this.move, this.props.dropAction)} +
        ); + } else + bulletType = BulletType.Collapsed; + } + }); return
        {
        ; } - public static GetChildElements(docs: Doc[], remove: ((doc: Doc) => void), move: DragManager.MoveFunction, dropAction: dropActionType) { - return docs.filter(child => !child.excludeFromLibrary).filter(doc => FieldValue(doc)).map(child => + public static GetChildElements(docs: Doc[], allowMinimized: boolean, remove: ((doc: Doc) => void), move: DragManager.MoveFunction, dropAction: dropActionType) { + return docs.filter(child => !child.excludeFromLibrary && (allowMinimized || !child.isMinimized)).filter(doc => FieldValue(doc)).map(child => ); } } @@ -168,13 +178,12 @@ export class CollectionTreeView extends CollectionSubView(Document) { } } render() { - trace(); const children = this.children; let dropAction = StrCast(this.props.Document.dropAction, "alias") as dropActionType; if (!children) { return (null); } - let childElements = TreeView.GetChildElements(children, this.remove, this.props.moveDocument, dropAction); + let childElements = TreeView.GetChildElements(children, false, this.remove, this.props.moveDocument, dropAction); return (
        { let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 }); //let linkDoc = new Doc; - linkDoc.title = "-link name-"; + linkDoc.proto!.title = "-link name-"; linkDoc.linkDescription = ""; linkDoc.linkTags = "Default"; -- cgit v1.2.3-70-g09d2 From 9830eb15041fa8e99ef8f5bb6fdcf6b3c28c058d Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 6 May 2019 01:10:14 -0400 Subject: added start of "minimap" support via better buttons and summaries. --- .../views/collections/CollectionDockingView.tsx | 36 +++++++++++++++++--- .../views/collections/CollectionTreeView.scss | 39 +++++++++++++--------- .../views/collections/CollectionTreeView.tsx | 13 +++++--- .../collections/collectionFreeForm/MarqueeView.tsx | 37 ++++++++++++-------- .../views/nodes/CollectionFreeFormDocumentView.tsx | 28 +++++++++++++--- src/client/views/nodes/DocumentView.tsx | 9 ++--- src/client/views/nodes/FormattedTextBox.tsx | 4 +-- 7 files changed, 118 insertions(+), 48 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 278065479..e4f02a4bc 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -72,6 +72,37 @@ export class CollectionDockingView extends React.Component { + if (child.contentItems.length === 1 && child.contentItems[0].config.component === "DocumentFrameRenderer" && + child.contentItems[0].config.props.documentId == document[Id]) { + child.contentItems[0].remove(); + //this._goldenLayout.root.contentItems[0].contentItems.splice(i, 1); + this.layoutChanged(document); + } else + child.contentItems.map((tab: any, j: number) => { + if (tab.config.component === "DocumentFrameRenderer" && tab.config.props.documentId === document[Id]) { + child.contentItems[j].remove(); + let docs = Cast(this.props.Document.data, listSpec(Doc)); + docs && docs.indexOf(document) !== -1 && docs.splice(docs.indexOf(document), 1); + } + }); + }) + } + } + + @action + layoutChanged(removed?: Doc) { + this._goldenLayout.root.callDownwards('setSize', [this._goldenLayout.width, this._goldenLayout.height]); + this._goldenLayout.emit('sbcreteChanged'); + this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig()); + if (removed) CollectionDockingView.Instance._removedDocs.push(removed); + this.stateChanged(); + } + // // Creates a vertical split on the right side of the docking view, and then adds the Document to that split // @@ -107,10 +138,7 @@ export class CollectionDockingView extends React.Component { @observable _collapsed: boolean = true; delete = () => this.props.deleteDoc(this.props.document); + openRight = () => CollectionDockingView.Instance.AddRightSplit(this.props.document); get children() { return Cast(this.props.document.data, listSpec(Doc), []); // bcz: needed? .filter(doc => FieldValue(doc)); @@ -100,9 +102,11 @@ class TreeView extends React.Component { }} />); return ( -
        +
        {editableView(StrCast(this.props.document.title))} - {/*
        */} +
        + {/* {
        } */}
        ); } @@ -145,8 +149,7 @@ class TreeView extends React.Component { }); return
        + onContextMenu={this.onWorkspaceContextMenu}>
      • {this.renderBullet(bulletType)} {this.renderTitle()} diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 8c81f6990..805921ad4 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -15,6 +15,8 @@ import { NumCast, Cast } from "../../../../new_fields/Types"; import { InkField, StrokeData } from "../../../../new_fields/InkField"; import { Templates } from "../../Templates"; import { List } from "../../../../new_fields/List"; +import { emitKeypressEvents } from "readline"; +import { listSpec } from "../../../../new_fields/Schema"; interface MarqueeViewProps { getContainerTransform: () => Transform; @@ -149,16 +151,17 @@ export class MarqueeView extends React.Component this.cleanupInteractions(false); e.stopPropagation(); } - if (e.key === "c" || e.key === "r" || e.key === "e") { + if (e.key === "c" || e.key === "r" || e.key === "R" || e.key === "e") { this._commandExecuted = true; e.stopPropagation(); let bounds = this.Bounds; let selected = this.marqueeSelect().map(d => { - if (e.key !== "r") + if (e.key !== "R") { this.props.removeDocument(d); - d.x = NumCast(d.x) - bounds.left - bounds.width / 2; - d.y = NumCast(d.y) - bounds.top - bounds.height / 2; - d.page = -1; + d.x = NumCast(d.x) - bounds.left - bounds.width / 2; + d.y = NumCast(d.y) - bounds.top - bounds.height / 2; + d.page = -1; + } return d; }); let ink = Cast(this.props.container.props.Document.ink, InkField); @@ -179,16 +182,23 @@ export class MarqueeView extends React.Component this.marqueeInkDelete(inkData); // SelectionManager.DeselectAll(); - if (e.key === "r") { - let summary = Docs.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "yellow", title: "-summary-" }); - summary.maximizedDocs = new List(selected); - // summary.doc1 = selected[0]; - // if (selected.length > 1) - // summary.doc2 = selected[1]; - // summary.templates = new List([Templates.Summary.Layout]); - this.props.addLiveTextDocument(summary); + if (e.key === "r" || e.key === "R") { e.preventDefault(); let scrpt = this.props.getTransform().inverse().transformPoint(bounds.left, bounds.top); + let summary = Docs.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "yellow", title: "-summary-" }); + + if (e.key === "r") { + summary.proto!.maximizeOnRight = true; + let list = Cast(newCollection.data, listSpec(Doc)); + if (list && list.length === 1) { + selected = list; + } else { + selected = [newCollection]; + this.props.addDocument(newCollection, false); + } + } + summary.proto!.maximizedDocs = new List(selected); + summary.proto!.isButton = true; selected.map(maximizedDoc => { let maxx = NumCast(maximizedDoc.x, undefined); let maxy = NumCast(maximizedDoc.y, undefined); @@ -196,6 +206,7 @@ export class MarqueeView extends React.Component let maxh = NumCast(maximizedDoc.height, undefined); maximizedDoc.isIconAnimating = new List([scrpt[0], scrpt[1], maxx, maxy, maxw, maxh, Date.now(), 0]) }); + this.props.addLiveTextDocument(summary); } else { this.props.addDocument(newCollection, false); diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 2ba0458f5..cf08c1bc4 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -6,11 +6,12 @@ import "./DocumentView.scss"; import React = require("react"); import { DocComponent } from "../DocComponent"; import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema"; -import { FieldValue, Cast, NumCast, BoolCast } from "../../../new_fields/Types"; +import { FieldValue, Cast, NumCast, BoolCast, PromiseValue } from "../../../new_fields/Types"; import { OmitKeys, Utils } from "../../../Utils"; import { SelectionManager } from "../../util/SelectionManager"; import { Doc } from "../../../new_fields/Doc"; import { List } from "../../../new_fields/List"; +import { CollectionDockingView } from "../collections/CollectionDockingView"; export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { } @@ -86,7 +87,7 @@ export class CollectionFreeFormDocumentView extends DocComponent { e.stopPropagation(); let ctrlKey = e.ctrlKey; + let metaKey = e.metaKey; if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) { if (await BoolCast(this.props.Document.isButton, false)) { let maximizedDocs = await Cast(this.props.Document.maximizedDocs, listSpec(Doc)); if (maximizedDocs) { // bcz: need a better way to associate behaviors with click events on widget-documents - if (ctrlKey) - this.props.addDocument && maximizedDocs.filter(d => d instanceof Doc).map(maxDoc => this.props.addDocument!(maxDoc, false)); - this.toggleIcon(); + if ((metaKey && !this.props.Document.maximizeOnRight) || (!metaKey && this.props.Document.maximizeOnRight)) { + SelectionManager.DeselectAll(); + maximizedDocs.map(async mdoc => { + let maxDoc = await mdoc; + let dataDocs = await Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc)); + if (dataDocs) { + Promise.all(dataDocs.map(async doc => await doc)).then(docs => { + if (!docs || docs.indexOf(maxDoc) == -1) { + CollectionDockingView.Instance.AddRightSplit(maxDoc); + } else { + CollectionDockingView.Instance.CloseRightSplit(maxDoc); + } + }) + } + }); + } else { + this.props.addDocument && maximizedDocs.map(async maxDoc => this.props.addDocument!(await maxDoc, false)); + this.toggleIcon(); + } } } } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 096a02d9b..9d356cc30 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -202,10 +202,11 @@ export class DocumentView extends DocComponent(Docu CollectionDockingView.Instance.AddRightSplit(kvp); } makeButton = (e: React.MouseEvent): void => { - this.props.Document.isButton = !BoolCast(this.props.Document.isButton, false); - if (this.props.Document.isButton && !this.props.Document.nativeWidth) { - this.props.Document.nativeWidth = this.props.Document[WidthSym](); - this.props.Document.nativeHeight = this.props.Document[HeightSym](); + let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document; + doc.isButton = !BoolCast(doc.isButton, false); + if (doc.isButton && !doc.nativeWidth) { + doc.nativeWidth = doc[WidthSym](); + doc.nativeHeight = doc[HeightSym](); } } fullScreenClicked = (e: React.MouseEvent): void => { diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index eeb60cb3d..65b8b805f 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -128,7 +128,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe ); } else { this._proxyReactionDisposer = reaction(() => this.props.isSelected(), - () => this.props.isSelected() && !BoolCast(this.props.Document.isButton, false) && MainOverlayTextBox.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)); } @@ -310,7 +310,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe // tfs: do we need this event handler onWheel={this.onPointerWheel} > -
        +
        ); } -- cgit v1.2.3-70-g09d2 From c631db5ff9cc8f376588c856a71c9ad09e309f1d Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 6 May 2019 12:50:32 -0400 Subject: added goldenlayout as a file, changed list / type stuff a bit. --- src/client/goldenLayout.js | 5359 ++++++++++++++++++++ .../views/collections/CollectionDockingView.tsx | 48 +- .../views/collections/CollectionTreeView.tsx | 12 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 39 +- src/new_fields/List.ts | 2 +- 5 files changed, 5413 insertions(+), 47 deletions(-) create mode 100644 src/client/goldenLayout.js (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js new file mode 100644 index 000000000..56a71f1ac --- /dev/null +++ b/src/client/goldenLayout.js @@ -0,0 +1,5359 @@ +(function ($) { + var lm = { "config": {}, "container": {}, "controls": {}, "errors": {}, "items": {}, "utils": {} }; + lm.utils.F = function () { + }; + + lm.utils.extend = function (subClass, superClass) { + subClass.prototype = lm.utils.createObject(superClass.prototype); + subClass.prototype.contructor = subClass; + }; + + lm.utils.createObject = function (prototype) { + if (typeof Object.create === 'function') { + return Object.create(prototype); + } else { + lm.utils.F.prototype = prototype; + return new lm.utils.F(); + } + }; + + lm.utils.objectKeys = function (object) { + var keys, key; + + if (typeof Object.keys === 'function') { + return Object.keys(object); + } else { + keys = []; + for (key in object) { + keys.push(key); + } + return keys; + } + }; + + lm.utils.getHashValue = function (key) { + var matches = location.hash.match(new RegExp(key + '=([^&]*)')); + return matches ? matches[1] : null; + }; + + lm.utils.getQueryStringParam = function (param) { + if (window.location.hash) { + return lm.utils.getHashValue(param); + } else if (!window.location.search) { + return null; + } + + var keyValuePairs = window.location.search.substr(1).split('&'), + params = {}, + pair, + i; + + for (i = 0; i < keyValuePairs.length; i++) { + pair = keyValuePairs[i].split('='); + params[pair[0]] = pair[1]; + } + + return params[param] || null; + }; + + lm.utils.copy = function (target, source) { + for (var key in source) { + target[key] = source[key]; + } + return target; + }; + + /** + * This is based on Paul Irish's shim, but looks quite odd in comparison. Why? + * Because + * a) it shouldn't affect the global requestAnimationFrame function + * b) it shouldn't pass on the time that has passed + * + * @param {Function} fn + * + * @returns {void} + */ + lm.utils.animFrame = function (fn) { + return (window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + })(function () { + fn(); + }); + }; + + lm.utils.indexOf = function (needle, haystack) { + if (!(haystack instanceof Array)) { + throw new Error('Haystack is not an Array'); + } + + if (haystack.indexOf) { + return haystack.indexOf(needle); + } else { + for (var i = 0; i < haystack.length; i++) { + if (haystack[i] === needle) { + return i; + } + } + return -1; + } + }; + + if (typeof /./ != 'function' && typeof Int8Array != 'object') { + lm.utils.isFunction = function (obj) { + return typeof obj == 'function' || false; + }; + } else { + lm.utils.isFunction = function (obj) { + return toString.call(obj) === '[object Function]'; + }; + } + + lm.utils.fnBind = function (fn, context, boundArgs) { + + if (Function.prototype.bind !== undefined) { + return Function.prototype.bind.apply(fn, [context].concat(boundArgs || [])); + } + + var bound = function () { + + // Join the already applied arguments to the now called ones (after converting to an array again). + var args = (boundArgs || []).concat(Array.prototype.slice.call(arguments, 0)); + + // If not being called as a constructor + if (!(this instanceof bound)) { + // return the result of the function called bound to target and partially applied. + return fn.apply(context, args); + } + // If being called as a constructor, apply the function bound to self. + fn.apply(this, args); + }; + // Attach the prototype of the function to our newly created function. + bound.prototype = fn.prototype; + return bound; + }; + + lm.utils.removeFromArray = function (item, array) { + var index = lm.utils.indexOf(item, array); + + if (index === -1) { + throw new Error('Can\'t remove item from array. Item is not in the array'); + } + + array.splice(index, 1); + }; + + lm.utils.now = function () { + if (typeof Date.now === 'function') { + return Date.now(); + } else { + return (new Date()).getTime(); + } + }; + + lm.utils.getUniqueId = function () { + return (Math.random() * 1000000000000000) + .toString(36) + .replace('.', ''); + }; + + /** + * A basic XSS filter. It is ultimately up to the + * implementing developer to make sure their particular + * applications and usecases are save from cross site scripting attacks + * + * @param {String} input + * @param {Boolean} keepTags + * + * @returns {String} filtered input + */ + lm.utils.filterXss = function (input, keepTags) { + + var output = input + .replace(/javascript/gi, 'javascript') + .replace(/expression/gi, 'expression') + .replace(/onload/gi, 'onload') + .replace(/script/gi, 'script') + .replace(/onerror/gi, 'onerror'); + + if (keepTags === true) { + return output; + } else { + return output + .replace(/>/g, '>') + .replace(/]+)>)/ig, '')); + }; + /** + * A generic and very fast EventEmitter + * implementation. On top of emitting the + * actual event it emits an + * + * lm.utils.EventEmitter.ALL_EVENT + * + * event for every event triggered. This allows + * to hook into it and proxy events forwards + * + * @constructor + */ + lm.utils.EventEmitter = function () { + this._mSubscriptions = {}; + this._mSubscriptions[lm.utils.EventEmitter.ALL_EVENT] = []; + + /** + * Listen for events + * + * @param {String} sEvent The name of the event to listen to + * @param {Function} fCallback The callback to execute when the event occurs + * @param {[Object]} oContext The value of the this pointer within the callback function + * + * @returns {void} + */ + this.on = function (sEvent, fCallback, oContext) { + if (!lm.utils.isFunction(fCallback)) { + throw new Error('Tried to listen to event ' + sEvent + ' with non-function callback ' + fCallback); + } + + if (!this._mSubscriptions[sEvent]) { + this._mSubscriptions[sEvent] = []; + } + + this._mSubscriptions[sEvent].push({ fn: fCallback, ctx: oContext }); + }; + + /** + * Emit an event and notify listeners + * + * @param {String} sEvent The name of the event + * @param {Mixed} various additional arguments that will be passed to the listener + * + * @returns {void} + */ + this.emit = function (sEvent) { + var i, ctx, args; + + args = Array.prototype.slice.call(arguments, 1); + + var subs = this._mSubscriptions[sEvent]; + + if (subs) { + subs = subs.slice(); + for (i = 0; i < subs.length; i++) { + ctx = subs[i].ctx || {}; + subs[i].fn.apply(ctx, args); + } + } + + args.unshift(sEvent); + + var allEventSubs = this._mSubscriptions[lm.utils.EventEmitter.ALL_EVENT].slice() + + for (i = 0; i < allEventSubs.length; i++) { + ctx = allEventSubs[i].ctx || {}; + allEventSubs[i].fn.apply(ctx, args); + } + }; + + /** + * Removes a listener for an event, or all listeners if no callback and context is provided. + * + * @param {String} sEvent The name of the event + * @param {Function} fCallback The previously registered callback method (optional) + * @param {Object} oContext The previously registered context (optional) + * + * @returns {void} + */ + this.unbind = function (sEvent, fCallback, oContext) { + if (!this._mSubscriptions[sEvent]) { + throw new Error('No subscribtions to unsubscribe for event ' + sEvent); + } + + var i, bUnbound = false; + + for (i = 0; i < this._mSubscriptions[sEvent].length; i++) { + if + ( + (!fCallback || this._mSubscriptions[sEvent][i].fn === fCallback) && + (!oContext || oContext === this._mSubscriptions[sEvent][i].ctx) + ) { + this._mSubscriptions[sEvent].splice(i, 1); + bUnbound = true; + } + } + + if (bUnbound === false) { + throw new Error('Nothing to unbind for ' + sEvent); + } + }; + + /** + * Alias for unbind + */ + this.off = this.unbind; + + /** + * Alias for emit + */ + this.trigger = this.emit; + }; + + /** + * The name of the event that's triggered for every other event + * + * usage + * + * myEmitter.on( lm.utils.EventEmitter.ALL_EVENT, function( eventName, argsArray ){ + * //do stuff + * }); + * + * @type {String} + */ + lm.utils.EventEmitter.ALL_EVENT = '__all'; + lm.utils.DragListener = function (eElement, nButtonCode) { + lm.utils.EventEmitter.call(this); + + this._eElement = $(eElement); + this._oDocument = $(document); + this._eBody = $(document.body); + this._nButtonCode = nButtonCode || 0; + + /** + * The delay after which to start the drag in milliseconds + */ + this._nDelay = 200; + + /** + * The distance the mouse needs to be moved to qualify as a drag + */ + this._nDistance = 10;//TODO - works better with delay only + + this._nX = 0; + this._nY = 0; + + this._nOriginalX = 0; + this._nOriginalY = 0; + + this._bDragging = false; + + this._fMove = lm.utils.fnBind(this.onMouseMove, this); + this._fUp = lm.utils.fnBind(this.onMouseUp, this); + this._fDown = lm.utils.fnBind(this.onMouseDown, this); + + + this._eElement.on('mousedown touchstart', this._fDown); + }; + + lm.utils.DragListener.timeout = null; + + lm.utils.copy(lm.utils.DragListener.prototype, { + destroy: function () { + this._eElement.unbind('mousedown touchstart', this._fDown); + this._oDocument.unbind('mouseup touchend', this._fUp); + this._eElement = null; + this._oDocument = null; + this._eBody = null; + }, + + onMouseDown: function (oEvent) { + oEvent.preventDefault(); + + if (oEvent.button == 0 || oEvent.type === "touchstart") { + var coordinates = this._getCoordinates(oEvent); + + this._nOriginalX = coordinates.x; + this._nOriginalY = coordinates.y; + + this._oDocument.on('mousemove touchmove', this._fMove); + this._oDocument.one('mouseup touchend', this._fUp); + + this._timeout = setTimeout(lm.utils.fnBind(this._startDrag, this), this._nDelay); + } + }, + + onMouseMove: function (oEvent) { + if (this._timeout != null) { + oEvent.preventDefault(); + + var coordinates = this._getCoordinates(oEvent); + + this._nX = coordinates.x - this._nOriginalX; + this._nY = coordinates.y - this._nOriginalY; + + if (this._bDragging === false) { + if ( + Math.abs(this._nX) > this._nDistance || + Math.abs(this._nY) > this._nDistance + ) { + clearTimeout(this._timeout); + this._startDrag(); + } + } + + if (this._bDragging) { + this.emit('drag', this._nX, this._nY, oEvent); + } + } + }, + + onMouseUp: function (oEvent) { + if (this._timeout != null) { + clearTimeout(this._timeout); + this._eBody.removeClass('lm_dragging'); + this._eElement.removeClass('lm_dragging'); + this._oDocument.find('iframe').css('pointer-events', ''); + this._oDocument.unbind('mousemove touchmove', this._fMove); + this._oDocument.unbind('mouseup touchend', this._fUp); + + if (this._bDragging === true) { + this._bDragging = false; + this.emit('dragStop', oEvent, this._nOriginalX + this._nX); + } + } + }, + + _startDrag: function () { + this._bDragging = true; + this._eBody.addClass('lm_dragging'); + this._eElement.addClass('lm_dragging'); + this._oDocument.find('iframe').css('pointer-events', 'none'); + this.emit('dragStart', this._nOriginalX, this._nOriginalY); + }, + + _getCoordinates: function (event) { + event = event.originalEvent && event.originalEvent.touches ? event.originalEvent.touches[0] : event; + return { + x: event.pageX, + y: event.pageY + }; + } + }); + /** + * The main class that will be exposed as GoldenLayout. + * + * @public + * @constructor + * @param {GoldenLayout config} config + * @param {[DOM element container]} container Can be a jQuery selector string or a Dom element. Defaults to body + * + * @returns {VOID} + */ + lm.LayoutManager = function (config, container) { + + if (!$ || typeof $.noConflict !== 'function') { + var errorMsg = 'jQuery is missing as dependency for GoldenLayout. '; + errorMsg += 'Please either expose $ on GoldenLayout\'s scope (e.g. window) or add "jquery" to '; + errorMsg += 'your paths when using RequireJS/AMD'; + throw new Error(errorMsg); + } + lm.utils.EventEmitter.call(this); + + this.isInitialised = false; + this._isFullPage = false; + this._resizeTimeoutId = null; + this._components = { 'lm-react-component': lm.utils.ReactComponentHandler }; + this._itemAreas = []; + this._resizeFunction = lm.utils.fnBind(this._onResize, this); + this._unloadFunction = lm.utils.fnBind(this._onUnload, this); + this._maximisedItem = null; + this._maximisePlaceholder = $('
        '); + this._creationTimeoutPassed = false; + this._subWindowsCreated = false; + this._dragSources = []; + this._updatingColumnsResponsive = false; + this._firstLoad = true; + + this.width = null; + this.height = null; + this.root = null; + this.openPopouts = []; + this.selectedItem = null; + this.isSubWindow = false; + this.eventHub = new lm.utils.EventHub(this); + this.config = this._createConfig(config); + this.container = container; + this.dropTargetIndicator = null; + this.transitionIndicator = null; + this.tabDropPlaceholder = $('
        '); + + if (this.isSubWindow === true) { + $('body').css('visibility', 'hidden'); + } + + this._typeToItem = { + 'column': lm.utils.fnBind(lm.items.RowOrColumn, this, [true]), + 'row': lm.utils.fnBind(lm.items.RowOrColumn, this, [false]), + 'stack': lm.items.Stack, + 'component': lm.items.Component + }; + }; + + /** + * Hook that allows to access private classes + */ + lm.LayoutManager.__lm = lm; + + /** + * Takes a GoldenLayout configuration object and + * replaces its keys and values recursively with + * one letter codes + * + * @static + * @public + * @param {Object} config A GoldenLayout config object + * + * @returns {Object} minified config + */ + lm.LayoutManager.minifyConfig = function (config) { + return (new lm.utils.ConfigMinifier()).minifyConfig(config); + }; + + /** + * Takes a configuration Object that was previously minified + * using minifyConfig and returns its original version + * + * @static + * @public + * @param {Object} minifiedConfig + * + * @returns {Object} the original configuration + */ + lm.LayoutManager.unminifyConfig = function (config) { + return (new lm.utils.ConfigMinifier()).unminifyConfig(config); + }; + + lm.utils.copy(lm.LayoutManager.prototype, { + + /** + * Register a component with the layout manager. If a configuration node + * of type component is reached it will look up componentName and create the + * associated component + * + * { + * type: "component", + * componentName: "EquityNewsFeed", + * componentState: { "feedTopic": "us-bluechips" } + * } + * + * @public + * @param {String} name + * @param {Function} constructor + * + * @returns {void} + */ + registerComponent: function (name, constructor) { + if (typeof constructor !== 'function') { + throw new Error('Please register a constructor function'); + } + + if (this._components[name] !== undefined) { + throw new Error('Component ' + name + ' is already registered'); + } + + this._components[name] = constructor; + }, + + /** + * Creates a layout configuration object based on the the current state + * + * @public + * @returns {Object} GoldenLayout configuration + */ + toConfig: function (root) { + var config, next, i; + + if (this.isInitialised === false) { + throw new Error('Can\'t create config, layout not yet initialised'); + } + + if (root && !(root instanceof lm.items.AbstractContentItem)) { + throw new Error('Root must be a ContentItem'); + } + + /* + * settings & labels + */ + config = { + settings: lm.utils.copy({}, this.config.settings), + dimensions: lm.utils.copy({}, this.config.dimensions), + labels: lm.utils.copy({}, this.config.labels) + }; + + /* + * Content + */ + config.content = []; + next = function (configNode, item) { + var key, i; + + for (key in item.config) { + if (key !== 'content') { + configNode[key] = item.config[key]; + } + } + + if (item.contentItems.length) { + configNode.content = []; + + for (i = 0; i < item.contentItems.length; i++) { + configNode.content[i] = {}; + next(configNode.content[i], item.contentItems[i]); + } + } + }; + + if (root) { + next(config, { contentItems: [root] }); + } else { + next(config, this.root); + } + + /* + * Retrieve config for subwindows + */ + this._$reconcilePopoutWindows(); + config.openPopouts = []; + for (i = 0; i < this.openPopouts.length; i++) { + config.openPopouts.push(this.openPopouts[i].toConfig()); + } + + /* + * Add maximised item + */ + config.maximisedItemId = this._maximisedItem ? '__glMaximised' : null; + return config; + }, + + /** + * Returns a previously registered component + * + * @public + * @param {String} name The name used + * + * @returns {Function} + */ + getComponent: function (name) { + if (this._components[name] === undefined) { + throw new lm.errors.ConfigurationError('Unknown component "' + name + '"'); + } + + return this._components[name]; + }, + + /** + * Creates the actual layout. Must be called after all initial components + * are registered. Recurses through the configuration and sets up + * the item tree. + * + * If called before the document is ready it adds itself as a listener + * to the document.ready event + * + * @public + * + * @returns {void} + */ + init: function () { + + /** + * Create the popout windows straight away. If popouts are blocked + * an error is thrown on the same 'thread' rather than a timeout and can + * be caught. This also prevents any further initilisation from taking place. + */ + if (this._subWindowsCreated === false) { + this._createSubWindows(); + this._subWindowsCreated = true; + } + + + /** + * If the document isn't ready yet, wait for it. + */ + if (document.readyState === 'loading' || document.body === null) { + $(document).ready(lm.utils.fnBind(this.init, this)); + return; + } + + /** + * If this is a subwindow, wait a few milliseconds for the original + * page's js calls to be executed, then replace the bodies content + * with GoldenLayout + */ + if (this.isSubWindow === true && this._creationTimeoutPassed === false) { + setTimeout(lm.utils.fnBind(this.init, this), 7); + this._creationTimeoutPassed = true; + return; + } + + if (this.isSubWindow === true) { + this._adjustToWindowMode(); + } + + this._setContainer(); + this.dropTargetIndicator = new lm.controls.DropTargetIndicator(this.container); + this.transitionIndicator = new lm.controls.TransitionIndicator(); + this.updateSize(); + this._create(this.config); + this._bindEvents(); + this.isInitialised = true; + this._adjustColumnsResponsive(); + this.emit('initialised'); + }, + + /** + * Updates the layout managers size + * + * @public + * @param {[int]} width height in pixels + * @param {[int]} height width in pixels + * + * @returns {void} + */ + updateSize: function (width, height) { + if (arguments.length === 2) { + this.width = width; + this.height = height; + } else { + this.width = this.container.width(); + this.height = this.container.height(); + } + + if (this.isInitialised === true) { + this.root.callDownwards('setSize', [this.width, this.height]); + + if (this._maximisedItem) { + this._maximisedItem.element.width(this.container.width()); + this._maximisedItem.element.height(this.container.height()); + this._maximisedItem.callDownwards('setSize'); + } + + this._adjustColumnsResponsive(); + } + }, + + /** + * Destroys the LayoutManager instance itself as well as every ContentItem + * within it. After this is called nothing should be left of the LayoutManager. + * + * @public + * @returns {void} + */ + destroy: function () { + if (this.isInitialised === false) { + return; + } + this._onUnload(); + $(window).off('resize', this._resizeFunction); + $(window).off('unload beforeunload', this._unloadFunction); + this.root.callDownwards('_$destroy', [], true); + this.root.contentItems = []; + this.tabDropPlaceholder.remove(); + this.dropTargetIndicator.destroy(); + this.transitionIndicator.destroy(); + this.eventHub.destroy(); + + this._dragSources.forEach(function (dragSource) { + dragSource._dragListener.destroy(); + dragSource._element = null; + dragSource._itemConfig = null; + dragSource._dragListener = null; + }); + this._dragSources = []; + }, + + /** + * Recursively creates new item tree structures based on a provided + * ItemConfiguration object + * + * @public + * @param {Object} config ItemConfig + * @param {[ContentItem]} parent The item the newly created item should be a child of + * + * @returns {lm.items.ContentItem} + */ + createContentItem: function (config, parent) { + var typeErrorMsg, contentItem; + + if (typeof config.type !== 'string') { + throw new lm.errors.ConfigurationError('Missing parameter \'type\'', config); + } + + if (config.type === 'react-component') { + config.type = 'component'; + config.componentName = 'lm-react-component'; + } + + if (!this._typeToItem[config.type]) { + typeErrorMsg = 'Unknown type \'' + config.type + '\'. ' + + 'Valid types are ' + lm.utils.objectKeys(this._typeToItem).join(','); + + throw new lm.errors.ConfigurationError(typeErrorMsg); + } + + + /** + * We add an additional stack around every component that's not within a stack anyways. + */ + if ( + // If this is a component + config.type === 'component' && + + // and it's not already within a stack + !(parent instanceof lm.items.Stack) && + + // and we have a parent + !!parent && + + // and it's not the topmost item in a new window + !(this.isSubWindow === true && parent instanceof lm.items.Root) + ) { + config = { + type: 'stack', + width: config.width, + height: config.height, + content: [config] + }; + } + + contentItem = new this._typeToItem[config.type](this, config, parent); + return contentItem; + }, + + /** + * Creates a popout window with the specified content and dimensions + * + * @param {Object|lm.itemsAbstractContentItem} configOrContentItem + * @param {[Object]} dimensions A map with width, height, left and top + * @param {[String]} parentId the id of the element this item will be appended to + * when popIn is called + * @param {[Number]} indexInParent The position of this item within its parent element + + * @returns {lm.controls.BrowserPopout} + */ + createPopout: function (configOrContentItem, dimensions, parentId, indexInParent) { + var config = configOrContentItem, + isItem = configOrContentItem instanceof lm.items.AbstractContentItem, + self = this, + windowLeft, + windowTop, + offset, + parent, + child, + browserPopout; + + parentId = parentId || null; + + if (isItem) { + config = this.toConfig(configOrContentItem).content; + parentId = lm.utils.getUniqueId(); + + /** + * If the item is the only component within a stack or for some + * other reason the only child of its parent the parent will be destroyed + * when the child is removed. + * + * In order to support this we move up the tree until we find something + * that will remain after the item is being popped out + */ + parent = configOrContentItem.parent; + child = configOrContentItem; + while (parent.contentItems.length === 1 && !parent.isRoot) { + parent = parent.parent; + child = child.parent; + } + + parent.addId(parentId); + if (isNaN(indexInParent)) { + indexInParent = lm.utils.indexOf(child, parent.contentItems); + } + } else { + if (!(config instanceof Array)) { + config = [config]; + } + } + + + if (!dimensions && isItem) { + windowLeft = window.screenX || window.screenLeft; + windowTop = window.screenY || window.screenTop; + offset = configOrContentItem.element.offset(); + + dimensions = { + left: windowLeft + offset.left, + top: windowTop + offset.top, + width: configOrContentItem.element.width(), + height: configOrContentItem.element.height() + }; + } + + if (!dimensions && !isItem) { + dimensions = { + left: window.screenX || window.screenLeft + 20, + top: window.screenY || window.screenTop + 20, + width: 500, + height: 309 + }; + } + + if (isItem) { + configOrContentItem.remove(); + } + + browserPopout = new lm.controls.BrowserPopout(config, dimensions, parentId, indexInParent, this); + + browserPopout.on('initialised', function () { + self.emit('windowOpened', browserPopout); + }); + + browserPopout.on('closed', function () { + self._$reconcilePopoutWindows(); + }); + + this.openPopouts.push(browserPopout); + + return browserPopout; + }, + + /** + * Attaches DragListener to any given DOM element + * and turns it into a way of creating new ContentItems + * by 'dragging' the DOM element into the layout + * + * @param {jQuery DOM element} element + * @param {Object|Function} itemConfig for the new item to be created, or a function which will provide it + * + * @returns {void} + */ + createDragSource: function (element, itemConfig) { + this.config.settings.constrainDragToContainer = false; + var dragSource = new lm.controls.DragSource($(element), itemConfig, this); + this._dragSources.push(dragSource); + + return dragSource; + }, + + /** + * Programmatically selects an item. This deselects + * the currently selected item, selects the specified item + * and emits a selectionChanged event + * + * @param {lm.item.AbstractContentItem} item# + * @param {[Boolean]} _$silent Wheather to notify the item of its selection + * @event selectionChanged + * + * @returns {VOID} + */ + selectItem: function (item, _$silent) { + + if (this.config.settings.selectionEnabled !== true) { + throw new Error('Please set selectionEnabled to true to use this feature'); + } + + if (item === this.selectedItem) { + return; + } + + if (this.selectedItem !== null) { + this.selectedItem.deselect(); + } + + if (item && _$silent !== true) { + item.select(); + } + + this.selectedItem = item; + + this.emit('selectionChanged', item); + }, + + /************************* + * PACKAGE PRIVATE + *************************/ + _$maximiseItem: function (contentItem) { + if (this._maximisedItem !== null) { + this._$minimiseItem(this._maximisedItem); + } + this._maximisedItem = contentItem; + this._maximisedItem.addId('__glMaximised'); + contentItem.element.addClass('lm_maximised'); + contentItem.element.after(this._maximisePlaceholder); + this.root.element.prepend(contentItem.element); + contentItem.element.width(this.container.width()); + contentItem.element.height(this.container.height()); + contentItem.callDownwards('setSize'); + this._maximisedItem.emit('maximised'); + this.emit('stateChanged'); + }, + + _$minimiseItem: function (contentItem) { + contentItem.element.removeClass('lm_maximised'); + contentItem.removeId('__glMaximised'); + this._maximisePlaceholder.after(contentItem.element); + this._maximisePlaceholder.remove(); + contentItem.parent.callDownwards('setSize'); + this._maximisedItem = null; + contentItem.emit('minimised'); + this.emit('stateChanged'); + }, + + /** + * This method is used to get around sandboxed iframe restrictions. + * If 'allow-top-navigation' is not specified in the iframe's 'sandbox' attribute + * (as is the case with codepens) the parent window is forbidden from calling certain + * methods on the child, such as window.close() or setting document.location.href. + * + * This prevented GoldenLayout popouts from popping in in codepens. The fix is to call + * _$closeWindow on the child window's gl instance which (after a timeout to disconnect + * the invoking method from the close call) closes itself. + * + * @packagePrivate + * + * @returns {void} + */ + _$closeWindow: function () { + window.setTimeout(function () { + window.close(); + }, 1); + }, + + _$getArea: function (x, y) { + var i, area, smallestSurface = Infinity, mathingArea = null; + + for (i = 0; i < this._itemAreas.length; i++) { + area = this._itemAreas[i]; + + if ( + x > area.x1 && + x < area.x2 && + y > area.y1 && + y < area.y2 && + smallestSurface > area.surface + ) { + smallestSurface = area.surface; + mathingArea = area; + } + } + + return mathingArea; + }, + + _$createRootItemAreas: function () { + var areaSize = 50; + var sides = { y2: 0, x2: 0, y1: 'y2', x1: 'x2' }; + for (var side in sides) { + var area = this.root._$getArea(); + area.side = side; + if (sides[side]) + area[side] = area[sides[side]] - areaSize; + else + area[side] = areaSize; + area.surface = (area.x2 - area.x1) * (area.y2 - area.y1); + this._itemAreas.push(area); + } + }, + + _$calculateItemAreas: function () { + var i, area, allContentItems = this._getAllContentItems(); + this._itemAreas = []; + + /** + * If the last item is dragged out, highlight the entire container size to + * allow to re-drop it. allContentItems[ 0 ] === this.root at this point + * + * Don't include root into the possible drop areas though otherwise since it + * will used for every gap in the layout, e.g. splitters + */ + if (allContentItems.length === 1) { + this._itemAreas.push(this.root._$getArea()); + return; + } + this._$createRootItemAreas(); + + for (i = 0; i < allContentItems.length; i++) { + + if (!(allContentItems[i].isStack)) { + continue; + } + + area = allContentItems[i]._$getArea(); + + if (area === null) { + continue; + } else if (area instanceof Array) { + this._itemAreas = this._itemAreas.concat(area); + } else { + this._itemAreas.push(area); + var header = {}; + lm.utils.copy(header, area); + lm.utils.copy(header, area.contentItem._contentAreaDimensions.header.highlightArea); + header.surface = (header.x2 - header.x1) * (header.y2 - header.y1); + this._itemAreas.push(header); + } + } + }, + + /** + * Takes a contentItem or a configuration and optionally a parent + * item and returns an initialised instance of the contentItem. + * If the contentItem is a function, it is first called + * + * @packagePrivate + * + * @param {lm.items.AbtractContentItem|Object|Function} contentItemOrConfig + * @param {lm.items.AbtractContentItem} parent Only necessary when passing in config + * + * @returns {lm.items.AbtractContentItem} + */ + _$normalizeContentItem: function (contentItemOrConfig, parent) { + if (!contentItemOrConfig) { + throw new Error('No content item defined'); + } + + if (lm.utils.isFunction(contentItemOrConfig)) { + contentItemOrConfig = contentItemOrConfig(); + } + + if (contentItemOrConfig instanceof lm.items.AbstractContentItem) { + return contentItemOrConfig; + } + + if ($.isPlainObject(contentItemOrConfig) && contentItemOrConfig.type) { + var newContentItem = this.createContentItem(contentItemOrConfig, parent); + newContentItem.callDownwards('_$init'); + return newContentItem; + } else { + throw new Error('Invalid contentItem'); + } + }, + + /** + * Iterates through the array of open popout windows and removes the ones + * that are effectively closed. This is necessary due to the lack of reliably + * listening for window.close / unload events in a cross browser compatible fashion. + * + * @packagePrivate + * + * @returns {void} + */ + _$reconcilePopoutWindows: function () { + var openPopouts = [], i; + + for (i = 0; i < this.openPopouts.length; i++) { + if (this.openPopouts[i].getWindow().closed === false) { + openPopouts.push(this.openPopouts[i]); + } else { + this.emit('windowClosed', this.openPopouts[i]); + } + } + + if (this.openPopouts.length !== openPopouts.length) { + this.emit('stateChanged'); + this.openPopouts = openPopouts; + } + + }, + + /*************************** + * PRIVATE + ***************************/ + /** + * Returns a flattened array of all content items, + * regardles of level or type + * + * @private + * + * @returns {void} + */ + _getAllContentItems: function () { + var allContentItems = []; + + var addChildren = function (contentItem) { + allContentItems.push(contentItem); + + if (contentItem.contentItems instanceof Array) { + for (var i = 0; i < contentItem.contentItems.length; i++) { + addChildren(contentItem.contentItems[i]); + } + } + }; + + addChildren(this.root); + + return allContentItems; + }, + + /** + * Binds to DOM/BOM events on init + * + * @private + * + * @returns {void} + */ + _bindEvents: function () { + if (this._isFullPage) { + $(window).resize(this._resizeFunction); + } + $(window).on('unload beforeunload', this._unloadFunction); + }, + + /** + * Debounces resize events + * + * @private + * + * @returns {void} + */ + _onResize: function () { + clearTimeout(this._resizeTimeoutId); + this._resizeTimeoutId = setTimeout(lm.utils.fnBind(this.updateSize, this), 100); + }, + + /** + * Extends the default config with the user specific settings and applies + * derivations. Please note that there's a seperate method (AbstractContentItem._extendItemNode) + * that deals with the extension of item configs + * + * @param {Object} config + * @static + * @returns {Object} config + */ + _createConfig: function (config) { + var windowConfigKey = lm.utils.getQueryStringParam('gl-window'); + + if (windowConfigKey) { + this.isSubWindow = true; + config = localStorage.getItem(windowConfigKey); + config = JSON.parse(config); + config = (new lm.utils.ConfigMinifier()).unminifyConfig(config); + localStorage.removeItem(windowConfigKey); + } + + config = $.extend(true, {}, lm.config.defaultConfig, config); + + var nextNode = function (node) { + for (var key in node) { + if (key !== 'props' && typeof node[key] === 'object') { + nextNode(node[key]); + } + else if (key === 'type' && node[key] === 'react-component') { + node.type = 'component'; + node.componentName = 'lm-react-component'; + } + } + } + + nextNode(config); + + if (config.settings.hasHeaders === false) { + config.dimensions.headerHeight = 0; + } + + return config; + }, + + /** + * This is executed when GoldenLayout detects that it is run + * within a previously opened popout window. + * + * @private + * + * @returns {void} + */ + _adjustToWindowMode: function () { + var popInButton = $('
        ' + + '
        ' + + '
        ' + + '
        '); + + popInButton.click(lm.utils.fnBind(function () { + this.emit('popIn'); + }, this)); + + document.title = lm.utils.stripTags(this.config.content[0].title); + + $('head').append($('body link, body style, template, .gl_keep')); + + this.container = $('body') + .html('') + .css('visibility', 'visible') + .append(popInButton); + + /* + * This seems a bit pointless, but actually causes a reflow/re-evaluation getting around + * slickgrid's "Cannot find stylesheet." bug in chrome + */ + var x = document.body.offsetHeight; // jshint ignore:line + + /* + * Expose this instance on the window object + * to allow the opening window to interact with + * it + */ + window.__glInstance = this; + }, + + /** + * Creates Subwindows (if there are any). Throws an error + * if popouts are blocked. + * + * @returns {void} + */ + _createSubWindows: function () { + var i, popout; + + for (i = 0; i < this.config.openPopouts.length; i++) { + popout = this.config.openPopouts[i]; + + this.createPopout( + popout.content, + popout.dimensions, + popout.parentId, + popout.indexInParent + ); + } + }, + + /** + * Determines what element the layout will be created in + * + * @private + * + * @returns {void} + */ + _setContainer: function () { + var container = $(this.container || 'body'); + + if (container.length === 0) { + throw new Error('GoldenLayout container not found'); + } + + if (container.length > 1) { + throw new Error('GoldenLayout more than one container element specified'); + } + + if (container[0] === document.body) { + this._isFullPage = true; + + $('html, body').css({ + height: '100%', + margin: 0, + padding: 0, + overflow: 'hidden' + }); + } + + this.container = container; + }, + + /** + * Kicks of the initial, recursive creation chain + * + * @param {Object} config GoldenLayout Config + * + * @returns {void} + */ + _create: function (config) { + var errorMsg; + + if (!(config.content instanceof Array)) { + if (config.content === undefined) { + errorMsg = 'Missing setting \'content\' on top level of configuration'; + } else { + errorMsg = 'Configuration parameter \'content\' must be an array'; + } + + throw new lm.errors.ConfigurationError(errorMsg, config); + } + + if (config.content.length > 1) { + errorMsg = 'Top level content can\'t contain more then one element.'; + throw new lm.errors.ConfigurationError(errorMsg, config); + } + + this.root = new lm.items.Root(this, { content: config.content }, this.container); + this.root.callDownwards('_$init'); + + if (config.maximisedItemId === '__glMaximised') { + this.root.getItemsById(config.maximisedItemId)[0].toggleMaximise(); + } + }, + + /** + * Called when the window is closed or the user navigates away + * from the page + * + * @returns {void} + */ + _onUnload: function () { + if (this.config.settings.closePopoutsOnUnload === true) { + for (var i = 0; i < this.openPopouts.length; i++) { + this.openPopouts[i].close(); + } + } + }, + + /** + * Adjusts the number of columns to be lower to fit the screen and still maintain minItemWidth. + * + * @returns {void} + */ + _adjustColumnsResponsive: function () { + + // If there is no min width set, or not content items, do nothing. + if (!this._useResponsiveLayout() || this._updatingColumnsResponsive || !this.config.dimensions || !this.config.dimensions.minItemWidth || this.root.contentItems.length === 0 || !this.root.contentItems[0].isRow) { + this._firstLoad = false; + return; + } + + this._firstLoad = false; + + // If there is only one column, do nothing. + var columnCount = this.root.contentItems[0].contentItems.length; + if (columnCount <= 1) { + return; + } + + // If they all still fit, do nothing. + var minItemWidth = this.config.dimensions.minItemWidth; + var totalMinWidth = columnCount * minItemWidth; + if (totalMinWidth <= this.width) { + return; + } + + // Prevent updates while it is already happening. + this._updatingColumnsResponsive = true; + + // Figure out how many columns to stack, and put them all in the first stack container. + var finalColumnCount = Math.max(Math.floor(this.width / minItemWidth), 1); + var stackColumnCount = columnCount - finalColumnCount; + + var rootContentItem = this.root.contentItems[0]; + var firstStackContainer = this._findAllStackContainers()[0]; + for (var i = 0; i < stackColumnCount; i++) { + // Stack from right. + var column = rootContentItem.contentItems[rootContentItem.contentItems.length - 1]; + this._addChildContentItemsToContainer(firstStackContainer, column); + } + + this._updatingColumnsResponsive = false; + }, + + /** + * Determines if responsive layout should be used. + * + * @returns {bool} - True if responsive layout should be used; otherwise false. + */ + _useResponsiveLayout: function () { + return this.config.settings && (this.config.settings.responsiveMode == 'always' || (this.config.settings.responsiveMode == 'onload' && this._firstLoad)); + }, + + /** + * Adds all children of a node to another container recursively. + * @param {object} container - Container to add child content items to. + * @param {object} node - Node to search for content items. + * @returns {void} + */ + _addChildContentItemsToContainer: function (container, node) { + if (node.type === 'stack') { + node.contentItems.forEach(function (item) { + container.addChild(item); + node.removeChild(item, true); + }); + } + else { + node.contentItems.forEach(lm.utils.fnBind(function (item) { + this._addChildContentItemsToContainer(container, item); + }, this)); + } + }, + + /** + * Finds all the stack containers. + * @returns {array} - The found stack containers. + */ + _findAllStackContainers: function () { + var stackContainers = []; + this._findAllStackContainersRecursive(stackContainers, this.root); + + return stackContainers; + }, + + /** + * Finds all the stack containers. + * + * @param {array} - Set of containers to populate. + * @param {object} - Current node to process. + * + * @returns {void} + */ + _findAllStackContainersRecursive: function (stackContainers, node) { + node.contentItems.forEach(lm.utils.fnBind(function (item) { + if (item.type == 'stack') { + stackContainers.push(item); + } + else if (!item.isComponent) { + this._findAllStackContainersRecursive(stackContainers, item); + } + }, this)); + } + }); + + /** + * Expose the Layoutmanager as the single entrypoint using UMD + */ + (function () { + /* global define */ + if (typeof define === 'function' && define.amd) { + define(['jquery'], function (jquery) { + $ = jquery; + return lm.LayoutManager; + }); // jshint ignore:line + } else if (typeof exports === 'object') { + module.exports = lm.LayoutManager; + } else { + window.GoldenLayout = lm.LayoutManager; + } + })(); + + lm.config.itemDefaultConfig = { + isClosable: true, + reorderEnabled: true, + title: '' + }; + lm.config.defaultConfig = { + openPopouts: [], + settings: { + hasHeaders: true, + constrainDragToContainer: true, + reorderEnabled: true, + selectionEnabled: false, + popoutWholeStack: false, + blockedPopoutsThrowError: true, + closePopoutsOnUnload: true, + showPopoutIcon: true, + showMaximiseIcon: true, + showCloseIcon: true, + responsiveMode: 'onload', // Can be onload, always, or none. + tabOverlapAllowance: 0, // maximum pixel overlap per tab + reorderOnTabMenuClick: true, + tabControlOffset: 10 + }, + dimensions: { + borderWidth: 5, + borderGrabWidth: 15, + minItemHeight: 10, + minItemWidth: 10, + headerHeight: 20, + dragProxyWidth: 300, + dragProxyHeight: 200 + }, + labels: { + close: 'close', + maximise: 'maximise', + minimise: 'minimise', + popout: 'open in new window', + popin: 'pop in', + tabDropdown: 'additional tabs' + } + }; + + lm.container.ItemContainer = function (config, parent, layoutManager) { + lm.utils.EventEmitter.call(this); + + this.width = null; + this.height = null; + this.title = config.componentName; + this.parent = parent; + this.layoutManager = layoutManager; + this.isHidden = false; + + this._config = config; + this._element = $([ + '
        ', + '
        ', + '
        ' + ].join('')); + + this._contentElement = this._element.find('.lm_content'); + }; + + lm.utils.copy(lm.container.ItemContainer.prototype, { + + /** + * Get the inner DOM element the container's content + * is intended to live in + * + * @returns {DOM element} + */ + getElement: function () { + return this._contentElement; + }, + + /** + * Hide the container. Notifies the containers content first + * and then hides the DOM node. If the container is already hidden + * this should have no effect + * + * @returns {void} + */ + hide: function () { + this.emit('hide'); + this.isHidden = true; + this._element.hide(); + }, + + /** + * Shows a previously hidden container. Notifies the + * containers content first and then shows the DOM element. + * If the container is already visible this has no effect. + * + * @returns {void} + */ + show: function () { + this.emit('show'); + this.isHidden = false; + this._element.show(); + // call shown only if the container has a valid size + if (this.height != 0 || this.width != 0) { + this.emit('shown'); + } + }, + + /** + * Set the size from within the container. Traverses up + * the item tree until it finds a row or column element + * and resizes its items accordingly. + * + * If this container isn't a descendant of a row or column + * it returns false + * @todo Rework!!! + * @param {Number} width The new width in pixel + * @param {Number} height The new height in pixel + * + * @returns {Boolean} resizeSuccesful + */ + setSize: function (width, height) { + var rowOrColumn = this.parent, + rowOrColumnChild = this, + totalPixel, + percentage, + direction, + newSize, + delta, + i; + + while (!rowOrColumn.isColumn && !rowOrColumn.isRow) { + rowOrColumnChild = rowOrColumn; + rowOrColumn = rowOrColumn.parent; + + + /** + * No row or column has been found + */ + if (rowOrColumn.isRoot) { + return false; + } + } + + direction = rowOrColumn.isColumn ? "height" : "width"; + newSize = direction === "height" ? height : width; + + totalPixel = this[direction] * (1 / (rowOrColumnChild.config[direction] / 100)); + percentage = (newSize / totalPixel) * 100; + delta = (rowOrColumnChild.config[direction] - percentage) / (rowOrColumn.contentItems.length - 1); + + for (i = 0; i < rowOrColumn.contentItems.length; i++) { + if (rowOrColumn.contentItems[i] === rowOrColumnChild) { + rowOrColumn.contentItems[i].config[direction] = percentage; + } else { + rowOrColumn.contentItems[i].config[direction] += delta; + } + } + + rowOrColumn.callDownwards('setSize'); + + return true; + }, + + /** + * Closes the container if it is closable. Can be called by + * both the component within at as well as the contentItem containing + * it. Emits a close event before the container itself is closed. + * + * @returns {void} + */ + close: function () { + if (this._config.isClosable) { + this.emit('close'); + this.parent.close(); + } + }, + + /** + * Returns the current state object + * + * @returns {Object} state + */ + getState: function () { + return this._config.componentState; + }, + + /** + * Merges the provided state into the current one + * + * @param {Object} state + * + * @returns {void} + */ + extendState: function (state) { + this.setState($.extend(true, this.getState(), state)); + }, + + /** + * Notifies the layout manager of a stateupdate + * + * @param {serialisable} state + */ + setState: function (state) { + this._config.componentState = state; + this.parent.emitBubblingEvent('stateChanged'); + }, + + /** + * Set's the components title + * + * @param {String} title + */ + setTitle: function (title) { + this.parent.setTitle(title); + }, + + /** + * Set's the containers size. Called by the container's component. + * To set the size programmatically from within the container please + * use the public setSize method + * + * @param {[Int]} width in px + * @param {[Int]} height in px + * + * @returns {void} + */ + _$setSize: function (width, height) { + if (width !== this.width || height !== this.height) { + this.width = width; + this.height = height; + var cl = this._contentElement[0]; + var hdelta = cl.offsetWidth - cl.clientWidth; + var vdelta = cl.offsetHeight - cl.clientHeight; + this._contentElement.width(this.width - hdelta) + .height(this.height - vdelta); + this.emit('resize'); + } + } + }); + + /** + * Pops a content item out into a new browser window. + * This is achieved by + * + * - Creating a new configuration with the content item as root element + * - Serializing and minifying the configuration + * - Opening the current window's URL with the configuration as a GET parameter + * - GoldenLayout when opened in the new window will look for the GET parameter + * and use it instead of the provided configuration + * + * @param {Object} config GoldenLayout item config + * @param {Object} dimensions A map with width, height, top and left + * @param {String} parentId The id of the element the item will be appended to on popIn + * @param {Number} indexInParent The position of this element within its parent + * @param {lm.LayoutManager} layoutManager + */ + lm.controls.BrowserPopout = function (config, dimensions, parentId, indexInParent, layoutManager) { + lm.utils.EventEmitter.call(this); + this.isInitialised = false; + + this._config = config; + this._dimensions = dimensions; + this._parentId = parentId; + this._indexInParent = indexInParent; + this._layoutManager = layoutManager; + this._popoutWindow = null; + this._id = null; + this._createWindow(); + }; + + lm.utils.copy(lm.controls.BrowserPopout.prototype, { + + toConfig: function () { + if (this.isInitialised === false) { + throw new Error('Can\'t create config, layout not yet initialised'); + return; + } + return { + dimensions: { + width: this.getGlInstance().width, + height: this.getGlInstance().height, + left: this._popoutWindow.screenX || this._popoutWindow.screenLeft, + top: this._popoutWindow.screenY || this._popoutWindow.screenTop + }, + content: this.getGlInstance().toConfig().content, + parentId: this._parentId, + indexInParent: this._indexInParent + }; + }, + + getGlInstance: function () { + return this._popoutWindow.__glInstance; + }, + + getWindow: function () { + return this._popoutWindow; + }, + + close: function () { + if (this.getGlInstance()) { + this.getGlInstance()._$closeWindow(); + } else { + try { + this.getWindow().close(); + } catch (e) { + } + } + }, + + /** + * Returns the popped out item to its original position. If the original + * parent isn't available anymore it falls back to the layout's topmost element + */ + popIn: function () { + var childConfig, + parentItem, + index = this._indexInParent; + + if (this._parentId) { + + /* + * The $.extend call seems a bit pointless, but it's crucial to + * copy the config returned by this.getGlInstance().toConfig() + * onto a new object. Internet Explorer keeps the references + * to objects on the child window, resulting in the following error + * once the child window is closed: + * + * The callee (server [not server application]) is not available and disappeared + */ + childConfig = $.extend(true, {}, this.getGlInstance().toConfig()).content[0]; + parentItem = this._layoutManager.root.getItemsById(this._parentId)[0]; + + /* + * Fallback if parentItem is not available. Either add it to the topmost + * item or make it the topmost item if the layout is empty + */ + if (!parentItem) { + if (this._layoutManager.root.contentItems.length > 0) { + parentItem = this._layoutManager.root.contentItems[0]; + } else { + parentItem = this._layoutManager.root; + } + index = 0; + } + } + + parentItem.addChild(childConfig, this._indexInParent); + this.close(); + }, + + /** + * Creates the URL and window parameter + * and opens a new window + * + * @private + * + * @returns {void} + */ + _createWindow: function () { + var checkReadyInterval, + url = this._createUrl(), + + /** + * Bogus title to prevent re-usage of existing window with the + * same title. The actual title will be set by the new window's + * GoldenLayout instance if it detects that it is in subWindowMode + */ + title = Math.floor(Math.random() * 1000000).toString(36), + + /** + * The options as used in the window.open string + */ + options = this._serializeWindowOptions({ + width: this._dimensions.width, + height: this._dimensions.height, + innerWidth: this._dimensions.width, + innerHeight: this._dimensions.height, + menubar: 'no', + toolbar: 'no', + location: 'no', + personalbar: 'no', + resizable: 'yes', + scrollbars: 'no', + status: 'no' + }); + + this._popoutWindow = window.open(url, title, options); + + if (!this._popoutWindow) { + if (this._layoutManager.config.settings.blockedPopoutsThrowError === true) { + var error = new Error('Popout blocked'); + error.type = 'popoutBlocked'; + throw error; + } else { + return; + } + } + + $(this._popoutWindow) + .on('load', lm.utils.fnBind(this._positionWindow, this)) + .on('unload beforeunload', lm.utils.fnBind(this._onClose, this)); + + /** + * Polling the childwindow to find out if GoldenLayout has been initialised + * doesn't seem optimal, but the alternatives - adding a callback to the parent + * window or raising an event on the window object - both would introduce knowledge + * about the parent to the child window which we'd rather avoid + */ + checkReadyInterval = setInterval(lm.utils.fnBind(function () { + if (this._popoutWindow.__glInstance && this._popoutWindow.__glInstance.isInitialised) { + this._onInitialised(); + clearInterval(checkReadyInterval); + } + }, this), 10); + }, + + /** + * Serialises a map of key:values to a window options string + * + * @param {Object} windowOptions + * + * @returns {String} serialised window options + */ + _serializeWindowOptions: function (windowOptions) { + var windowOptionsString = [], key; + + for (key in windowOptions) { + windowOptionsString.push(key + '=' + windowOptions[key]); + } + + return windowOptionsString.join(','); + }, + + /** + * Creates the URL for the new window, including the + * config GET parameter + * + * @returns {String} URL + */ + _createUrl: function () { + var config = { content: this._config }, + storageKey = 'gl-window-config-' + lm.utils.getUniqueId(), + urlParts; + + config = (new lm.utils.ConfigMinifier()).minifyConfig(config); + + try { + localStorage.setItem(storageKey, JSON.stringify(config)); + } catch (e) { + throw new Error('Error while writing to localStorage ' + e.toString()); + } + + urlParts = document.location.href.split('?'); + + // URL doesn't contain GET-parameters + if (urlParts.length === 1) { + return urlParts[0] + '?gl-window=' + storageKey; + + // URL contains GET-parameters + } else { + return document.location.href + '&gl-window=' + storageKey; + } + }, + + /** + * Move the newly created window roughly to + * where the component used to be. + * + * @private + * + * @returns {void} + */ + _positionWindow: function () { + this._popoutWindow.moveTo(this._dimensions.left, this._dimensions.top); + this._popoutWindow.focus(); + }, + + /** + * Callback when the new window is opened and the GoldenLayout instance + * within it is initialised + * + * @returns {void} + */ + _onInitialised: function () { + this.isInitialised = true; + this.getGlInstance().on('popIn', this.popIn, this); + this.emit('initialised'); + }, + + /** + * Invoked 50ms after the window unload event + * + * @private + * + * @returns {void} + */ + _onClose: function () { + setTimeout(lm.utils.fnBind(this.emit, this, ['closed']), 50); + } + }); + /** + * This class creates a temporary container + * for the component whilst it is being dragged + * and handles drag events + * + * @constructor + * @private + * + * @param {Number} x The initial x position + * @param {Number} y The initial y position + * @param {lm.utils.DragListener} dragListener + * @param {lm.LayoutManager} layoutManager + * @param {lm.item.AbstractContentItem} contentItem + * @param {lm.item.AbstractContentItem} originalParent + */ + lm.controls.DragProxy = function (x, y, dragListener, layoutManager, contentItem, originalParent) { + + lm.utils.EventEmitter.call(this); + + this._dragListener = dragListener; + this._layoutManager = layoutManager; + this._contentItem = contentItem; + this._originalParent = originalParent; + + this._area = null; + this._lastValidArea = null; + + this._dragListener.on('drag', this._onDrag, this); + this._dragListener.on('dragStop', this._onDrop, this); + + this.element = $(lm.controls.DragProxy._template); + if (originalParent && originalParent._side) { + this._sided = originalParent._sided; + this.element.addClass('lm_' + originalParent._side); + if (['right', 'bottom'].indexOf(originalParent._side) >= 0) + this.element.find('.lm_content').after(this.element.find('.lm_header')); + } + this.element.css({ left: x, top: y }); + this.element.find('.lm_tab').attr('title', lm.utils.stripTags(this._contentItem.config.title)); + this.element.find('.lm_title').html(this._contentItem.config.title); + this.childElementContainer = this.element.find('.lm_content'); + this.childElementContainer.append(contentItem.element); + + this._updateTree(); + this._layoutManager._$calculateItemAreas(); + this._setDimensions(); + + $(document.body).append(this.element); + + var offset = this._layoutManager.container.offset(); + + this._minX = offset.left; + this._minY = offset.top; + this._maxX = this._layoutManager.container.width() + this._minX; + this._maxY = this._layoutManager.container.height() + this._minY; + this._width = this.element.width(); + this._height = this.element.height(); + + this._setDropPosition(x, y); + }; + + lm.controls.DragProxy._template = '
        ' + + '
        ' + + '
          ' + + '
        • ' + + '' + + '
        • ' + + '
        ' + + '
        ' + + '
        ' + + '
        '; + + lm.utils.copy(lm.controls.DragProxy.prototype, { + + /** + * Callback on every mouseMove event during a drag. Determines if the drag is + * still within the valid drag area and calls the layoutManager to highlight the + * current drop area + * + * @param {Number} offsetX The difference from the original x position in px + * @param {Number} offsetY The difference from the original y position in px + * @param {jQuery DOM event} event + * + * @private + * + * @returns {void} + */ + _onDrag: function (offsetX, offsetY, event) { + + event = event.originalEvent && event.originalEvent.touches ? event.originalEvent.touches[0] : event; + + var x = event.pageX, + y = event.pageY, + isWithinContainer = x > this._minX && x < this._maxX && y > this._minY && y < this._maxY; + + if (!isWithinContainer && this._layoutManager.config.settings.constrainDragToContainer === true) { + return; + } + + this._setDropPosition(x, y); + }, + + /** + * Sets the target position, highlighting the appropriate area + * + * @param {Number} x The x position in px + * @param {Number} y The y position in px + * + * @private + * + * @returns {void} + */ + _setDropPosition: function (x, y) { + this.element.css({ left: x, top: y }); + this._area = this._layoutManager._$getArea(x, y); + + if (this._area !== null) { + this._lastValidArea = this._area; + this._area.contentItem._$highlightDropZone(x, y, this._area); + } + }, + + /** + * Callback when the drag has finished. Determines the drop area + * and adds the child to it + * + * @private + * + * @returns {void} + */ + _onDrop: function () { + this._layoutManager.dropTargetIndicator.hide(); + + /* + * Valid drop area found + */ + if (this._area !== null) { + this._area.contentItem._$onDrop(this._contentItem, this._area); + + /** + * No valid drop area available at present, but one has been found before. + * Use it + */ + } else if (this._lastValidArea !== null) { + this._lastValidArea.contentItem._$onDrop(this._contentItem, this._lastValidArea); + + /** + * No valid drop area found during the duration of the drag. Return + * content item to its original position if a original parent is provided. + * (Which is not the case if the drag had been initiated by createDragSource) + */ + } else if (this._originalParent) { + this._originalParent.addChild(this._contentItem); + + /** + * The drag didn't ultimately end up with adding the content item to + * any container. In order to ensure clean up happens, destroy the + * content item. + */ + } else { + this._contentItem._$destroy(); + } + + this.element.remove(); + + this._layoutManager.emit('itemDropped', this._contentItem); + }, + + /** + * Removes the item from its original position within the tree + * + * @private + * + * @returns {void} + */ + _updateTree: function () { + + /** + * parent is null if the drag had been initiated by a external drag source + */ + if (this._contentItem.parent) { + this._contentItem.parent.removeChild(this._contentItem, true); + } + + this._contentItem._$setParent(this); + }, + + /** + * Updates the Drag Proxie's dimensions + * + * @private + * + * @returns {void} + */ + _setDimensions: function () { + var dimensions = this._layoutManager.config.dimensions, + width = dimensions.dragProxyWidth, + height = dimensions.dragProxyHeight; + + this.element.width(width); + this.element.height(height); + width -= (this._sided ? dimensions.headerHeight : 0); + height -= (!this._sided ? dimensions.headerHeight : 0); + this.childElementContainer.width(width); + this.childElementContainer.height(height); + this._contentItem.element.width(width); + this._contentItem.element.height(height); + this._contentItem.callDownwards('_$show'); + this._contentItem.callDownwards('setSize'); + } + }); + + /** + * Allows for any DOM item to create a component on drag + * start tobe dragged into the Layout + * + * @param {jQuery element} element + * @param {Object} itemConfig the configuration for the contentItem that will be created + * @param {LayoutManager} layoutManager + * + * @constructor + */ + lm.controls.DragSource = function (element, itemConfig, layoutManager) { + this._element = element; + this._itemConfig = itemConfig; + this._layoutManager = layoutManager; + this._dragListener = null; + + this._createDragListener(); + }; + + lm.utils.copy(lm.controls.DragSource.prototype, { + + /** + * Called initially and after every drag + * + * @returns {void} + */ + _createDragListener: function () { + if (this._dragListener !== null) { + this._dragListener.destroy(); + } + + this._dragListener = new lm.utils.DragListener(this._element); + this._dragListener.on('dragStart', this._onDragStart, this); + this._dragListener.on('dragStop', this._createDragListener, this); + }, + + /** + * Callback for the DragListener's dragStart event + * + * @param {int} x the x position of the mouse on dragStart + * @param {int} y the x position of the mouse on dragStart + * + * @returns {void} + */ + _onDragStart: function (x, y) { + var itemConfig = this._itemConfig; + if (lm.utils.isFunction(itemConfig)) { + itemConfig = itemConfig(); + } + var contentItem = this._layoutManager._$normalizeContentItem($.extend(true, {}, itemConfig)), + dragProxy = new lm.controls.DragProxy(x, y, this._dragListener, this._layoutManager, contentItem, null); + + this._layoutManager.transitionIndicator.transitionElements(this._element, dragProxy.element); + } + }); + + lm.controls.DropTargetIndicator = function () { + this.element = $(lm.controls.DropTargetIndicator._template); + $(document.body).append(this.element); + }; + + lm.controls.DropTargetIndicator._template = '
        '; + + lm.utils.copy(lm.controls.DropTargetIndicator.prototype, { + destroy: function () { + this.element.remove(); + }, + + highlight: function (x1, y1, x2, y2) { + this.highlightArea({ x1: x1, y1: y1, x2: x2, y2: y2 }); + }, + + highlightArea: function (area) { + this.element.css({ + left: area.x1, + top: area.y1, + width: area.x2 - area.x1, + height: area.y2 - area.y1 + }).show(); + }, + + hide: function () { + this.element.hide(); + } + }); + /** + * This class represents a header above a Stack ContentItem. + * + * @param {lm.LayoutManager} layoutManager + * @param {lm.item.AbstractContentItem} parent + */ + lm.controls.Header = function (layoutManager, parent) { + lm.utils.EventEmitter.call(this); + + this.layoutManager = layoutManager; + this.element = $(lm.controls.Header._template); + + if (this.layoutManager.config.settings.selectionEnabled === true) { + this.element.addClass('lm_selectable'); + this.element.on('click touchstart', lm.utils.fnBind(this._onHeaderClick, this)); + } + + this.tabsContainer = this.element.find('.lm_tabs'); + this.tabDropdownContainer = this.element.find('.lm_tabdropdown_list'); + this.tabDropdownContainer.hide(); + this.controlsContainer = this.element.find('.lm_controls'); + this.parent = parent; + this.parent.on('resize', this._updateTabSizes, this); + this.tabs = []; + this.activeContentItem = null; + this.closeButton = null; + this.tabDropdownButton = null; + this.hideAdditionalTabsDropdown = lm.utils.fnBind(this._hideAdditionalTabsDropdown, this); + $(document).mouseup(this.hideAdditionalTabsDropdown); + + this._lastVisibleTabIndex = -1; + this._tabControlOffset = this.layoutManager.config.settings.tabControlOffset; + this._createControls(); + }; + + lm.controls.Header._template = [ + '
        ', + '
          ', + '
            ', + '
              ', + '
              ' + ].join(''); + + lm.utils.copy(lm.controls.Header.prototype, { + + /** + * Creates a new tab and associates it with a contentItem + * + * @param {lm.item.AbstractContentItem} contentItem + * @param {Integer} index The position of the tab + * + * @returns {void} + */ + createTab: function (contentItem, index) { + var tab, i; + + //If there's already a tab relating to the + //content item, don't do anything + for (i = 0; i < this.tabs.length; i++) { + if (this.tabs[i].contentItem === contentItem) { + return; + } + } + + tab = new lm.controls.Tab(this, contentItem); + + if (this.tabs.length === 0) { + this.tabs.push(tab); + this.tabsContainer.append(tab.element); + return; + } + + if (index === undefined) { + index = this.tabs.length; + } + + if (index > 0) { + this.tabs[index - 1].element.after(tab.element); + } else { + this.tabs[0].element.before(tab.element); + } + + this.tabs.splice(index, 0, tab); + this._updateTabSizes(); + }, + + /** + * Finds a tab based on the contentItem its associated with and removes it. + * + * @param {lm.item.AbstractContentItem} contentItem + * + * @returns {void} + */ + removeTab: function (contentItem) { + for (var i = 0; i < this.tabs.length; i++) { + if (this.tabs[i].contentItem === contentItem) { + this.tabs[i]._$destroy(); + this.tabs.splice(i, 1); + return; + } + } + + throw new Error('contentItem is not controlled by this header'); + }, + + /** + * The programmatical equivalent of clicking a Tab. + * + * @param {lm.item.AbstractContentItem} contentItem + */ + setActiveContentItem: function (contentItem) { + var i, j, isActive, activeTab; + + for (i = 0; i < this.tabs.length; i++) { + isActive = this.tabs[i].contentItem === contentItem; + this.tabs[i].setActive(isActive); + if (isActive === true) { + this.activeContentItem = contentItem; + this.parent.config.activeItemIndex = i; + } + } + + if (this.layoutManager.config.settings.reorderOnTabMenuClick) { + /** + * If the tab selected was in the dropdown, move everything down one to make way for this one to be the first. + * This will make sure the most used tabs stay visible. + */ + if (this._lastVisibleTabIndex !== -1 && this.parent.config.activeItemIndex > this._lastVisibleTabIndex) { + activeTab = this.tabs[this.parent.config.activeItemIndex]; + for (j = this.parent.config.activeItemIndex; j > 0; j--) { + this.tabs[j] = this.tabs[j - 1]; + } + this.tabs[0] = activeTab; + this.parent.config.activeItemIndex = 0; + } + } + + this._updateTabSizes(); + this.parent.emitBubblingEvent('stateChanged'); + }, + + /** + * Programmatically operate with header position. + * + * @param {string} position one of ('top','left','right','bottom') to set or empty to get it. + * + * @returns {string} previous header position + */ + position: function (position) { + var previous = this.parent._header.show; + if (previous && !this.parent._side) + previous = 'top'; + if (position !== undefined && this.parent._header.show != position) { + this.parent._header.show = position; + this.parent._setupHeaderPosition(); + } + return previous; + }, + + /** + * Programmatically set closability. + * + * @package private + * @param {Boolean} isClosable Whether to enable/disable closability. + * + * @returns {Boolean} Whether the action was successful + */ + _$setClosable: function (isClosable) { + if (this.closeButton && this._isClosable()) { + this.closeButton.element[isClosable ? "show" : "hide"](); + return true; + } + + return false; + }, + + /** + * Destroys the entire header + * + * @package private + * + * @returns {void} + */ + _$destroy: function () { + this.emit('destroy', this); + + for (var i = 0; i < this.tabs.length; i++) { + this.tabs[i]._$destroy(); + } + $(document).off('mouseup', this.hideAdditionalTabsDropdown); + this.element.remove(); + }, + + /** + * get settings from header + * + * @returns {string} when exists + */ + _getHeaderSetting: function (name) { + if (name in this.parent._header) + return this.parent._header[name]; + }, + /** + * Creates the popout, maximise and close buttons in the header's top right corner + * + * @returns {void} + */ + _createControls: function () { + var closeStack, + popout, + label, + maximiseLabel, + minimiseLabel, + maximise, + maximiseButton, + tabDropdownLabel, + showTabDropdown; + + /** + * Dropdown to show additional tabs. + */ + showTabDropdown = lm.utils.fnBind(this._showAdditionalTabsDropdown, this); + tabDropdownLabel = this.layoutManager.config.labels.tabDropdown; + this.tabDropdownButton = new lm.controls.HeaderButton(this, tabDropdownLabel, 'lm_tabdropdown', showTabDropdown); + this.tabDropdownButton.element.hide(); + + /** + * Popout control to launch component in new window. + */ + if (this._getHeaderSetting('popout')) { + popout = lm.utils.fnBind(this._onPopoutClick, this); + label = this._getHeaderSetting('popout'); + new lm.controls.HeaderButton(this, label, 'lm_popout', popout); + } + + /** + * Maximise control - set the component to the full size of the layout + */ + if (this._getHeaderSetting('maximise')) { + maximise = lm.utils.fnBind(this.parent.toggleMaximise, this.parent); + maximiseLabel = this._getHeaderSetting('maximise'); + minimiseLabel = this._getHeaderSetting('minimise'); + maximiseButton = new lm.controls.HeaderButton(this, maximiseLabel, 'lm_maximise', maximise); + + this.parent.on('maximised', function () { + maximiseButton.element.attr('title', minimiseLabel); + }); + + this.parent.on('minimised', function () { + maximiseButton.element.attr('title', maximiseLabel); + }); + } + + /** + * Close button + */ + if (this._isClosable()) { + closeStack = lm.utils.fnBind(this.parent.remove, this.parent); + label = this._getHeaderSetting('close'); + this.closeButton = new lm.controls.HeaderButton(this, label, 'lm_close', closeStack); + } + }, + + /** + * Shows drop down for additional tabs when there are too many to display. + * + * @returns {void} + */ + _showAdditionalTabsDropdown: function () { + this.tabDropdownContainer.show(); + }, + + /** + * Hides drop down for additional tabs when there are too many to display. + * + * @returns {void} + */ + _hideAdditionalTabsDropdown: function (e) { + this.tabDropdownContainer.hide(); + }, + + /** + * Checks whether the header is closable based on the parent config and + * the global config. + * + * @returns {Boolean} Whether the header is closable. + */ + _isClosable: function () { + return this.parent.config.isClosable && this.layoutManager.config.settings.showCloseIcon; + }, + + _onPopoutClick: function () { + if (this.layoutManager.config.settings.popoutWholeStack === true) { + this.parent.popout(); + } else { + this.activeContentItem.popout(); + } + }, + + + /** + * Invoked when the header's background is clicked (not it's tabs or controls) + * + * @param {jQuery DOM event} event + * + * @returns {void} + */ + _onHeaderClick: function (event) { + if (event.target === this.element[0]) { + this.parent.select(); + } + }, + + /** + * Pushes the tabs to the tab dropdown if the available space is not sufficient + * + * @returns {void} + */ + _updateTabSizes: function (showTabMenu) { + if (this.tabs.length === 0) { + return; + } + + //Show the menu based on function argument + this.tabDropdownButton.element.toggle(showTabMenu === true); + + var size = function (val) { + return val ? 'width' : 'height'; + }; + this.element.css(size(!this.parent._sided), ''); + this.element[size(this.parent._sided)](this.layoutManager.config.dimensions.headerHeight); + var availableWidth = this.element.outerWidth() - this.controlsContainer.outerWidth() - this._tabControlOffset, + cumulativeTabWidth = 0, + visibleTabWidth = 0, + tabElement, + i, + j, + marginLeft, + overlap = 0, + tabWidth, + tabOverlapAllowance = this.layoutManager.config.settings.tabOverlapAllowance, + tabOverlapAllowanceExceeded = false, + activeIndex = (this.activeContentItem ? this.tabs.indexOf(this.activeContentItem.tab) : 0), + activeTab = this.tabs[activeIndex]; + if (this.parent._sided) + availableWidth = this.element.outerHeight() - this.controlsContainer.outerHeight() - this._tabControlOffset; + this._lastVisibleTabIndex = -1; + + for (i = 0; i < this.tabs.length; i++) { + tabElement = this.tabs[i].element; + + //Put the tab in the tabContainer so its true width can be checked + this.tabsContainer.append(tabElement); + tabWidth = tabElement.outerWidth() + parseInt(tabElement.css('margin-right'), 10); + + cumulativeTabWidth += tabWidth; + + //Include the active tab's width if it isn't already + //This is to ensure there is room to show the active tab + if (activeIndex <= i) { + visibleTabWidth = cumulativeTabWidth; + } else { + visibleTabWidth = cumulativeTabWidth + activeTab.element.outerWidth() + parseInt(activeTab.element.css('margin-right'), 10); + } + + // If the tabs won't fit, check the overlap allowance. + if (visibleTabWidth > availableWidth) { + + //Once allowance is exceeded, all remaining tabs go to menu. + if (!tabOverlapAllowanceExceeded) { + + //No overlap for first tab or active tab + //Overlap spreads among non-active, non-first tabs + if (activeIndex > 0 && activeIndex <= i) { + overlap = (visibleTabWidth - availableWidth) / (i - 1); + } else { + overlap = (visibleTabWidth - availableWidth) / i; + } + + //Check overlap against allowance. + if (overlap < tabOverlapAllowance) { + for (j = 0; j <= i; j++) { + marginLeft = (j !== activeIndex && j !== 0) ? '-' + overlap + 'px' : ''; + this.tabs[j].element.css({ 'z-index': i - j, 'margin-left': marginLeft }); + } + this._lastVisibleTabIndex = i; + this.tabsContainer.append(tabElement); + } else { + tabOverlapAllowanceExceeded = true; + } + + } else if (i === activeIndex) { + //Active tab should show even if allowance exceeded. (We left room.) + tabElement.css({ 'z-index': 'auto', 'margin-left': '' }); + this.tabsContainer.append(tabElement); + } + + if (tabOverlapAllowanceExceeded && i !== activeIndex) { + if (showTabMenu) { + //Tab menu already shown, so we just add to it. + tabElement.css({ 'z-index': 'auto', 'margin-left': '' }); + this.tabDropdownContainer.append(tabElement); + } else { + //We now know the tab menu must be shown, so we have to recalculate everything. + this._updateTabSizes(true); + return; + } + } + + } + else { + this._lastVisibleTabIndex = i; + tabElement.css({ 'z-index': 'auto', 'margin-left': '' }); + this.tabsContainer.append(tabElement); + } + } + + } + }); + + + lm.controls.HeaderButton = function (header, label, cssClass, action) { + this._header = header; + this.element = $('
            • '); + this._header.on('destroy', this._$destroy, this); + this._action = action; + this.element.on('click touchstart', this._action); + this._header.controlsContainer.append(this.element); + }; + + lm.utils.copy(lm.controls.HeaderButton.prototype, { + _$destroy: function () { + this.element.off(); + this.element.remove(); + } + }); + lm.controls.Splitter = function (isVertical, size, grabSize) { + this._isVertical = isVertical; + this._size = size; + this._grabSize = grabSize < size ? size : grabSize; + + this.element = this._createElement(); + this._dragListener = new lm.utils.DragListener(this.element); + }; + + lm.utils.copy(lm.controls.Splitter.prototype, { + on: function (event, callback, context) { + this._dragListener.on(event, callback, context); + }, + + _$destroy: function () { + this.element.remove(); + }, + + _createElement: function () { + var dragHandle = $('
              '); + var element = $('
              '); + element.append(dragHandle); + + var handleExcessSize = this._grabSize - this._size; + var handleExcessPos = handleExcessSize / 2; + + if (this._isVertical) { + dragHandle.css('top', -handleExcessPos); + dragHandle.css('height', this._size + handleExcessSize); + element.addClass('lm_vertical'); + element['height'](this._size); + } else { + dragHandle.css('left', -handleExcessPos); + dragHandle.css('width', this._size + handleExcessSize); + element.addClass('lm_horizontal'); + element['width'](this._size); + } + + return element; + } + }); + + /** + * Represents an individual tab within a Stack's header + * + * @param {lm.controls.Header} header + * @param {lm.items.AbstractContentItem} contentItem + * + * @constructor + */ + lm.controls.Tab = function (header, contentItem) { + this.header = header; + this.contentItem = contentItem; + this.element = $(lm.controls.Tab._template); + this.titleElement = this.element.find('.lm_title'); + this.closeElement = this.element.find('.lm_close_tab'); + this.closeElement[contentItem.config.isClosable ? 'show' : 'hide'](); + this.isActive = false; + + this.setTitle(contentItem.config.title); + this.contentItem.on('titleChanged', this.setTitle, this); + + this._layoutManager = this.contentItem.layoutManager; + + if ( + this._layoutManager.config.settings.reorderEnabled === true && + contentItem.config.reorderEnabled === true + ) { + this._dragListener = new lm.utils.DragListener(this.element); + this._dragListener.on('dragStart', this._onDragStart, this); + this.contentItem.on('destroy', this._dragListener.destroy, this._dragListener); + } + + this._onTabClickFn = lm.utils.fnBind(this._onTabClick, this); + this._onCloseClickFn = lm.utils.fnBind(this._onCloseClick, this); + + this.element.on('mousedown touchstart', this._onTabClickFn); + + if (this.contentItem.config.isClosable) { + this.closeElement.on('click touchstart', this._onCloseClickFn); + this.closeElement.on('mousedown', this._onCloseMousedown); + } else { + this.closeElement.remove(); + } + + this.contentItem.tab = this; + this.contentItem.emit('tab', this); + this.contentItem.layoutManager.emit('tabCreated', this); + + if (this.contentItem.isComponent) { + this.contentItem.container.tab = this; + this.contentItem.container.emit('tab', this); + } + }; + + /** + * The tab's html template + * + * @type {String} + */ + lm.controls.Tab._template = '
            • ' + + '
              ' + + '
            • '; + + lm.utils.copy(lm.controls.Tab.prototype, { + + /** + * Sets the tab's title to the provided string and sets + * its title attribute to a pure text representation (without + * html tags) of the same string. + * + * @public + * @param {String} title can contain html + */ + setTitle: function (title) { + this.element.attr('title', lm.utils.stripTags(title)); + this.titleElement.html(title); + }, + + /** + * Sets this tab's active state. To programmatically + * switch tabs, use header.setActiveContentItem( item ) instead. + * + * @public + * @param {Boolean} isActive + */ + setActive: function (isActive) { + if (isActive === this.isActive) { + return; + } + this.isActive = isActive; + + if (isActive) { + this.element.addClass('lm_active'); + } else { + this.element.removeClass('lm_active'); + } + }, + + /** + * Destroys the tab + * + * @private + * @returns {void} + */ + _$destroy: function () { + this.element.off('mousedown touchstart', this._onTabClickFn); + this.closeElement.off('click touchstart', this._onCloseClickFn); + if (this._dragListener) { + this.contentItem.off('destroy', this._dragListener.destroy, this._dragListener); + this._dragListener.off('dragStart', this._onDragStart); + this._dragListener = null; + } + this.element.remove(); + }, + + /** + * Callback for the DragListener + * + * @param {Number} x The tabs absolute x position + * @param {Number} y The tabs absolute y position + * + * @private + * @returns {void} + */ + _onDragStart: function (x, y) { + if (this.contentItem.parent.isMaximised === true) { + this.contentItem.parent.toggleMaximise(); + } + new lm.controls.DragProxy( + x, + y, + this._dragListener, + this._layoutManager, + this.contentItem, + this.header.parent + ); + }, + + /** + * Callback when the tab is clicked + * + * @param {jQuery DOM event} event + * + * @private + * @returns {void} + */ + _onTabClick: function (event) { + // left mouse button or tap + if (event.button === 0 || event.type === 'touchstart') { + var activeContentItem = this.header.parent.getActiveContentItem(); + if (this.contentItem !== activeContentItem) { + this.header.parent.setActiveContentItem(this.contentItem); + } + + // middle mouse button + } else if (event.button === 1 && this.contentItem.config.isClosable) { + this._onCloseClick(event); + } + }, + + /** + * Callback when the tab's close button is + * clicked + * + * @param {jQuery DOM event} event + * + * @private + * @returns {void} + */ + _onCloseClick: function (event) { + event.stopPropagation(); + this.header.parent.removeChild(this.contentItem); + }, + + + /** + * Callback to capture tab close button mousedown + * to prevent tab from activating. + * + * @param (jQuery DOM event) event + * + * @private + * @returns {void} + */ + _onCloseMousedown: function (event) { + event.stopPropagation(); + } + }); + + lm.controls.TransitionIndicator = function () { + this._element = $('
              '); + $(document.body).append(this._element); + + this._toElement = null; + this._fromDimensions = null; + this._totalAnimationDuration = 200; + this._animationStartTime = null; + }; + + lm.utils.copy(lm.controls.TransitionIndicator.prototype, { + destroy: function () { + this._element.remove(); + }, + + transitionElements: function (fromElement, toElement) { + /** + * TODO - This is not quite as cool as expected. Review. + */ + return; + this._toElement = toElement; + this._animationStartTime = lm.utils.now(); + this._fromDimensions = this._measure(fromElement); + this._fromDimensions.opacity = 0.8; + this._element.show().css(this._fromDimensions); + lm.utils.animFrame(lm.utils.fnBind(this._nextAnimationFrame, this)); + }, + + _nextAnimationFrame: function () { + var toDimensions = this._measure(this._toElement), + animationProgress = (lm.utils.now() - this._animationStartTime) / this._totalAnimationDuration, + currentFrameStyles = {}, + cssProperty; + + if (animationProgress >= 1) { + this._element.hide(); + return; + } + + toDimensions.opacity = 0; + + for (cssProperty in this._fromDimensions) { + currentFrameStyles[cssProperty] = this._fromDimensions[cssProperty] + + (toDimensions[cssProperty] - this._fromDimensions[cssProperty]) * + animationProgress; + } + + this._element.css(currentFrameStyles); + lm.utils.animFrame(lm.utils.fnBind(this._nextAnimationFrame, this)); + }, + + _measure: function (element) { + var offset = element.offset(); + + return { + left: offset.left, + top: offset.top, + width: element.outerWidth(), + height: element.outerHeight() + }; + } + }); + lm.errors.ConfigurationError = function (message, node) { + Error.call(this); + + this.name = 'Configuration Error'; + this.message = message; + this.node = node; + }; + + lm.errors.ConfigurationError.prototype = new Error(); + + /** + * This is the baseclass that all content items inherit from. + * Most methods provide a subset of what the sub-classes do. + * + * It also provides a number of functions for tree traversal + * + * @param {lm.LayoutManager} layoutManager + * @param {item node configuration} config + * @param {lm.item} parent + * + * @event stateChanged + * @event beforeItemDestroyed + * @event itemDestroyed + * @event itemCreated + * @event componentCreated + * @event rowCreated + * @event columnCreated + * @event stackCreated + * + * @constructor + */ + lm.items.AbstractContentItem = function (layoutManager, config, parent) { + lm.utils.EventEmitter.call(this); + + this.config = this._extendItemNode(config); + this.type = config.type; + this.contentItems = []; + this.parent = parent; + + this.isInitialised = false; + this.isMaximised = false; + this.isRoot = false; + this.isRow = false; + this.isColumn = false; + this.isStack = false; + this.isComponent = false; + + this.layoutManager = layoutManager; + this._pendingEventPropagations = {}; + this._throttledEvents = ['stateChanged']; + + this.on(lm.utils.EventEmitter.ALL_EVENT, this._propagateEvent, this); + + if (config.content) { + this._createContentItems(config); + } + }; + + lm.utils.copy(lm.items.AbstractContentItem.prototype, { + + /** + * Set the size of the component and its children, called recursively + * + * @abstract + * @returns void + */ + setSize: function () { + throw new Error('Abstract Method'); + }, + + /** + * Calls a method recursively downwards on the tree + * + * @param {String} functionName the name of the function to be called + * @param {[Array]}functionArguments optional arguments that are passed to every function + * @param {[bool]} bottomUp Call methods from bottom to top, defaults to false + * @param {[bool]} skipSelf Don't invoke the method on the class that calls it, defaults to false + * + * @returns {void} + */ + callDownwards: function (functionName, functionArguments, bottomUp, skipSelf) { + var i; + + if (bottomUp !== true && skipSelf !== true) { + this[functionName].apply(this, functionArguments || []); + } + for (i = 0; i < this.contentItems.length; i++) { + this.contentItems[i].callDownwards(functionName, functionArguments, bottomUp); + } + if (bottomUp === true && skipSelf !== true) { + this[functionName].apply(this, functionArguments || []); + } + }, + + /** + * Removes a child node (and its children) from the tree + * + * @param {lm.items.ContentItem} contentItem + * + * @returns {void} + */ + removeChild: function (contentItem, keepChild) { + + /* + * Get the position of the item that's to be removed within all content items this node contains + */ + var index = lm.utils.indexOf(contentItem, this.contentItems); + + /* + * Make sure the content item to be removed is actually a child of this item + */ + if (index === -1) { + throw new Error('Can\'t remove child item. Unknown content item'); + } + + /** + * Call ._$destroy on the content item. This also calls ._$destroy on all its children + */ + if (keepChild !== true) { + this.contentItems[index]._$destroy(); + } + + /** + * Remove the content item from this nodes array of children + */ + this.contentItems.splice(index, 1); + + /** + * Remove the item from the configuration + */ + this.config.content.splice(index, 1); + + /** + * If this node still contains other content items, adjust their size + */ + if (this.contentItems.length > 0) { + this.callDownwards('setSize'); + + /** + * If this was the last content item, remove this node as well + */ + } else if (!(this instanceof lm.items.Root) && this.config.isClosable === true) { + this.parent.removeChild(this); + } + }, + + /** + * Sets up the tree structure for the newly added child + * The responsibility for the actual DOM manipulations lies + * with the concrete item + * + * @param {lm.items.AbstractContentItem} contentItem + * @param {[Int]} index If omitted item will be appended + */ + addChild: function (contentItem, index) { + if (index === undefined) { + index = this.contentItems.length; + } + + this.contentItems.splice(index, 0, contentItem); + + if (this.config.content === undefined) { + this.config.content = []; + } + + this.config.content.splice(index, 0, contentItem.config); + contentItem.parent = this; + + if (contentItem.parent.isInitialised === true && contentItem.isInitialised === false) { + contentItem._$init(); + } + }, + + /** + * Replaces oldChild with newChild. This used to use jQuery.replaceWith... which for + * some reason removes all event listeners, so isn't really an option. + * + * @param {lm.item.AbstractContentItem} oldChild + * @param {lm.item.AbstractContentItem} newChild + * + * @returns {void} + */ + replaceChild: function (oldChild, newChild, _$destroyOldChild) { + + newChild = this.layoutManager._$normalizeContentItem(newChild); + + var index = lm.utils.indexOf(oldChild, this.contentItems), + parentNode = oldChild.element[0].parentNode; + + if (index === -1) { + throw new Error('Can\'t replace child. oldChild is not child of this'); + } + + parentNode.replaceChild(newChild.element[0], oldChild.element[0]); + + /* + * Optionally destroy the old content item + */ + if (_$destroyOldChild === true) { + oldChild.parent = null; + oldChild._$destroy(); + } + + /* + * Wire the new contentItem into the tree + */ + this.contentItems[index] = newChild; + newChild.parent = this; + + /* + * Update tab reference + */ + if (this.isStack) { + this.header.tabs[index].contentItem = newChild; + } + + //TODO This doesn't update the config... refactor to leave item nodes untouched after creation + if (newChild.parent.isInitialised === true && newChild.isInitialised === false) { + newChild._$init(); + } + + this.callDownwards('setSize'); + }, + + /** + * Convenience method. + * Shorthand for this.parent.removeChild( this ) + * + * @returns {void} + */ + remove: function () { + this.parent.removeChild(this); + }, + + /** + * Removes the component from the layout and creates a new + * browser window with the component and its children inside + * + * @returns {lm.controls.BrowserPopout} + */ + popout: function () { + var browserPopout = this.layoutManager.createPopout(this); + this.emitBubblingEvent('stateChanged'); + return browserPopout; + }, + + /** + * Maximises the Item or minimises it if it is already maximised + * + * @returns {void} + */ + toggleMaximise: function (e) { + e && e.preventDefault(); + if (this.isMaximised === true) { + this.layoutManager._$minimiseItem(this); + } else { + this.layoutManager._$maximiseItem(this); + } + + this.isMaximised = !this.isMaximised; + this.emitBubblingEvent('stateChanged'); + }, + + /** + * Selects the item if it is not already selected + * + * @returns {void} + */ + select: function () { + if (this.layoutManager.selectedItem !== this) { + this.layoutManager.selectItem(this, true); + this.element.addClass('lm_selected'); + } + }, + + /** + * De-selects the item if it is selected + * + * @returns {void} + */ + deselect: function () { + if (this.layoutManager.selectedItem === this) { + this.layoutManager.selectedItem = null; + this.element.removeClass('lm_selected'); + } + }, + + /** + * Set this component's title + * + * @public + * @param {String} title + * + * @returns {void} + */ + setTitle: function (title) { + this.config.title = title; + this.emit('titleChanged', title); + this.emit('stateChanged'); + }, + + /** + * Checks whether a provided id is present + * + * @public + * @param {String} id + * + * @returns {Boolean} isPresent + */ + hasId: function (id) { + if (!this.config.id) { + return false; + } else if (typeof this.config.id === 'string') { + return this.config.id === id; + } else if (this.config.id instanceof Array) { + return lm.utils.indexOf(id, this.config.id) !== -1; + } + }, + + /** + * Adds an id. Adds it as a string if the component doesn't + * have an id yet or creates/uses an array + * + * @public + * @param {String} id + * + * @returns {void} + */ + addId: function (id) { + if (this.hasId(id)) { + return; + } + + if (!this.config.id) { + this.config.id = id; + } else if (typeof this.config.id === 'string') { + this.config.id = [this.config.id, id]; + } else if (this.config.id instanceof Array) { + this.config.id.push(id); + } + }, + + /** + * Removes an existing id. Throws an error + * if the id is not present + * + * @public + * @param {String} id + * + * @returns {void} + */ + removeId: function (id) { + if (!this.hasId(id)) { + throw new Error('Id not found'); + } + + if (typeof this.config.id === 'string') { + delete this.config.id; + } else if (this.config.id instanceof Array) { + var index = lm.utils.indexOf(id, this.config.id); + this.config.id.splice(index, 1); + } + }, + + /**************************************** + * SELECTOR + ****************************************/ + getItemsByFilter: function (filter) { + var result = [], + next = function (contentItem) { + for (var i = 0; i < contentItem.contentItems.length; i++) { + + if (filter(contentItem.contentItems[i]) === true) { + result.push(contentItem.contentItems[i]); + } + + next(contentItem.contentItems[i]); + } + }; + + next(this); + return result; + }, + + getItemsById: function (id) { + return this.getItemsByFilter(function (item) { + if (item.config.id instanceof Array) { + return lm.utils.indexOf(id, item.config.id) !== -1; + } else { + return item.config.id === id; + } + }); + }, + + getItemsByType: function (type) { + return this._$getItemsByProperty('type', type); + }, + + getComponentsByName: function (componentName) { + var components = this._$getItemsByProperty('componentName', componentName), + instances = [], + i; + + for (i = 0; i < components.length; i++) { + instances.push(components[i].instance); + } + + return instances; + }, + + /**************************************** + * PACKAGE PRIVATE + ****************************************/ + _$getItemsByProperty: function (key, value) { + return this.getItemsByFilter(function (item) { + return item[key] === value; + }); + }, + + _$setParent: function (parent) { + this.parent = parent; + }, + + _$highlightDropZone: function (x, y, area) { + this.layoutManager.dropTargetIndicator.highlightArea(area); + }, + + _$onDrop: function (contentItem) { + this.addChild(contentItem); + }, + + _$hide: function () { + this._callOnActiveComponents('hide'); + this.element.hide(); + this.layoutManager.updateSize(); + }, + + _$show: function () { + this._callOnActiveComponents('show'); + this.element.show(); + this.layoutManager.updateSize(); + }, + + _callOnActiveComponents: function (methodName) { + var stacks = this.getItemsByType('stack'), + activeContentItem, + i; + + for (i = 0; i < stacks.length; i++) { + activeContentItem = stacks[i].getActiveContentItem(); + + if (activeContentItem && activeContentItem.isComponent) { + activeContentItem.container[methodName](); + } + } + }, + + /** + * Destroys this item ands its children + * + * @returns {void} + */ + _$destroy: function () { + this.emitBubblingEvent('beforeItemDestroyed'); + this.callDownwards('_$destroy', [], true, true); + this.element.remove(); + this.emitBubblingEvent('itemDestroyed'); + }, + + /** + * Returns the area the component currently occupies in the format + * + * { + * x1: int + * xy: int + * y1: int + * y2: int + * contentItem: contentItem + * } + */ + _$getArea: function (element) { + element = element || this.element; + + var offset = element.offset(), + width = element.width(), + height = element.height(); + + return { + x1: offset.left, + y1: offset.top, + x2: offset.left + width, + y2: offset.top + height, + surface: width * height, + contentItem: this + }; + }, + + /** + * The tree of content items is created in two steps: First all content items are instantiated, + * then init is called recursively from top to bottem. This is the basic init function, + * it can be used, extended or overwritten by the content items + * + * Its behaviour depends on the content item + * + * @package private + * + * @returns {void} + */ + _$init: function () { + var i; + this.setSize(); + + for (i = 0; i < this.contentItems.length; i++) { + this.childElementContainer.append(this.contentItems[i].element); + } + + this.isInitialised = true; + this.emitBubblingEvent('itemCreated'); + this.emitBubblingEvent(this.type + 'Created'); + }, + + /** + * Emit an event that bubbles up the item tree. + * + * @param {String} name The name of the event + * + * @returns {void} + */ + emitBubblingEvent: function (name) { + var event = new lm.utils.BubblingEvent(name, this); + this.emit(name, event); + }, + + /** + * Private method, creates all content items for this node at initialisation time + * PLEASE NOTE, please see addChild for adding contentItems add runtime + * @private + * @param {configuration item node} config + * + * @returns {void} + */ + _createContentItems: function (config) { + var oContentItem, i; + + if (!(config.content instanceof Array)) { + throw new lm.errors.ConfigurationError('content must be an Array', config); + } + + for (i = 0; i < config.content.length; i++) { + oContentItem = this.layoutManager.createContentItem(config.content[i], this); + this.contentItems.push(oContentItem); + } + }, + + /** + * Extends an item configuration node with default settings + * @private + * @param {configuration item node} config + * + * @returns {configuration item node} extended config + */ + _extendItemNode: function (config) { + + for (var key in lm.config.itemDefaultConfig) { + if (config[key] === undefined) { + config[key] = lm.config.itemDefaultConfig[key]; + } + } + + return config; + }, + + /** + * Called for every event on the item tree. Decides whether the event is a bubbling + * event and propagates it to its parent + * + * @param {String} name the name of the event + * @param {lm.utils.BubblingEvent} event + * + * @returns {void} + */ + _propagateEvent: function (name, event) { + if (event instanceof lm.utils.BubblingEvent && + event.isPropagationStopped === false && + this.isInitialised === true) { + + /** + * In some cases (e.g. if an element is created from a DragSource) it + * doesn't have a parent and is not below root. If that's the case + * propagate the bubbling event from the top level of the substree directly + * to the layoutManager + */ + if (this.isRoot === false && this.parent) { + this.parent.emit.apply(this.parent, Array.prototype.slice.call(arguments, 0)); + } else { + this._scheduleEventPropagationToLayoutManager(name, event); + } + } + }, + + /** + * All raw events bubble up to the root element. Some events that + * are propagated to - and emitted by - the layoutManager however are + * only string-based, batched and sanitized to make them more usable + * + * @param {String} name the name of the event + * + * @private + * @returns {void} + */ + _scheduleEventPropagationToLayoutManager: function (name, event) { + if (lm.utils.indexOf(name, this._throttledEvents) === -1) { + this.layoutManager.emit(name, event.origin); + } else { + if (this._pendingEventPropagations[name] !== true) { + this._pendingEventPropagations[name] = true; + lm.utils.animFrame(lm.utils.fnBind(this._propagateEventToLayoutManager, this, [name, event])); + } + } + + }, + + /** + * Callback for events scheduled by _scheduleEventPropagationToLayoutManager + * + * @param {String} name the name of the event + * + * @private + * @returns {void} + */ + _propagateEventToLayoutManager: function (name, event) { + this._pendingEventPropagations[name] = false; + this.layoutManager.emit(name, event); + } + }); + + /** + * @param {[type]} layoutManager [description] + * @param {[type]} config [description] + * @param {[type]} parent [description] + */ + lm.items.Component = function (layoutManager, config, parent) { + lm.items.AbstractContentItem.call(this, layoutManager, config, parent); + + var ComponentConstructor = layoutManager.getComponent(this.config.componentName), + componentConfig = $.extend(true, {}, this.config.componentState || {}); + + componentConfig.componentName = this.config.componentName; + this.componentName = this.config.componentName; + + if (this.config.title === '') { + this.config.title = this.config.componentName; + } + + this.isComponent = true; + this.container = new lm.container.ItemContainer(this.config, this, layoutManager); + this.instance = new ComponentConstructor(this.container, componentConfig); + this.element = this.container._element; + }; + + lm.utils.extend(lm.items.Component, lm.items.AbstractContentItem); + + lm.utils.copy(lm.items.Component.prototype, { + + close: function () { + this.parent.removeChild(this); + }, + + setSize: function () { + if (this.element.is(':visible')) { + // Do not update size of hidden components to prevent unwanted reflows + this.container._$setSize(this.element.width(), this.element.height()); + } + }, + + _$init: function () { + lm.items.AbstractContentItem.prototype._$init.call(this); + this.container.emit('open'); + }, + + _$hide: function () { + this.container.hide(); + lm.items.AbstractContentItem.prototype._$hide.call(this); + }, + + _$show: function () { + this.container.show(); + lm.items.AbstractContentItem.prototype._$show.call(this); + }, + + _$shown: function () { + this.container.shown(); + lm.items.AbstractContentItem.prototype._$shown.call(this); + }, + + _$destroy: function () { + this.container.emit('destroy', this); + lm.items.AbstractContentItem.prototype._$destroy.call(this); + }, + + /** + * Dragging onto a component directly is not an option + * + * @returns null + */ + _$getArea: function () { + return null; + } + }); + + lm.items.Root = function (layoutManager, config, containerElement) { + lm.items.AbstractContentItem.call(this, layoutManager, config, null); + this.isRoot = true; + this.type = 'root'; + this.element = $('
              '); + this.childElementContainer = this.element; + this._containerElement = containerElement; + this._containerElement.append(this.element); + }; + + lm.utils.extend(lm.items.Root, lm.items.AbstractContentItem); + + lm.utils.copy(lm.items.Root.prototype, { + addChild: function (contentItem) { + if (this.contentItems.length > 0) { + throw new Error('Root node can only have a single child'); + } + + contentItem = this.layoutManager._$normalizeContentItem(contentItem, this); + this.childElementContainer.append(contentItem.element); + lm.items.AbstractContentItem.prototype.addChild.call(this, contentItem); + + this.callDownwards('setSize'); + this.emitBubblingEvent('stateChanged'); + }, + + setSize: function (width, height) { + width = (typeof width === 'undefined') ? this._containerElement.width() : width; + height = (typeof height === 'undefined') ? this._containerElement.height() : height; + + this.element.width(width); + this.element.height(height); + + /* + * Root can be empty + */ + if (this.contentItems[0]) { + this.contentItems[0].element.width(width); + this.contentItems[0].element.height(height); + } + }, + _$highlightDropZone: function (x, y, area) { + this.layoutManager.tabDropPlaceholder.remove(); + lm.items.AbstractContentItem.prototype._$highlightDropZone.apply(this, arguments); + }, + + _$onDrop: function (contentItem, area) { + var stack; + + if (contentItem.isComponent) { + stack = this.layoutManager.createContentItem({ + type: 'stack', + header: contentItem.config.header || {} + }, this); + stack._$init(); + stack.addChild(contentItem); + contentItem = stack; + } + + if (!this.contentItems.length) { + this.addChild(contentItem); + } else { + var type = area.side[0] == 'x' ? 'row' : 'column'; + var dimension = area.side[0] == 'x' ? 'width' : 'height'; + var insertBefore = area.side[1] == '2'; + var column = this.contentItems[0]; + if (!column instanceof lm.items.RowOrColumn || column.type != type) { + var rowOrColumn = this.layoutManager.createContentItem({ type: type }, this); + this.replaceChild(column, rowOrColumn); + rowOrColumn.addChild(contentItem, insertBefore ? 0 : undefined, true); + rowOrColumn.addChild(column, insertBefore ? undefined : 0, true); + column.config[dimension] = 50; + contentItem.config[dimension] = 50; + rowOrColumn.callDownwards('setSize'); + } else { + var sibbling = column.contentItems[insertBefore ? 0 : column.contentItems.length - 1] + column.addChild(contentItem, insertBefore ? 0 : undefined, true); + sibbling.config[dimension] *= 0.5; + contentItem.config[dimension] = sibbling.config[dimension]; + column.callDownwards('setSize'); + } + } + } + }); + + + + lm.items.RowOrColumn = function (isColumn, layoutManager, config, parent) { + lm.items.AbstractContentItem.call(this, layoutManager, config, parent); + + this.isRow = !isColumn; + this.isColumn = isColumn; + + this.element = $('
              '); + this.childElementContainer = this.element; + this._splitterSize = layoutManager.config.dimensions.borderWidth; + this._splitterGrabSize = layoutManager.config.dimensions.borderGrabWidth; + this._isColumn = isColumn; + this._dimension = isColumn ? 'height' : 'width'; + this._splitter = []; + this._splitterPosition = null; + this._splitterMinPosition = null; + this._splitterMaxPosition = null; + }; + + lm.utils.extend(lm.items.RowOrColumn, lm.items.AbstractContentItem); + + lm.utils.copy(lm.items.RowOrColumn.prototype, { + + /** + * Add a new contentItem to the Row or Column + * + * @param {lm.item.AbstractContentItem} contentItem + * @param {[int]} index The position of the new item within the Row or Column. + * If no index is provided the item will be added to the end + * @param {[bool]} _$suspendResize If true the items won't be resized. This will leave the item in + * an inconsistent state and is only intended to be used if multiple + * children need to be added in one go and resize is called afterwards + * + * @returns {void} + */ + addChild: function (contentItem, index, _$suspendResize) { + + var newItemSize, itemSize, i, splitterElement; + + contentItem = this.layoutManager._$normalizeContentItem(contentItem, this); + + if (index === undefined) { + index = this.contentItems.length; + } + + if (this.contentItems.length > 0) { + splitterElement = this._createSplitter(Math.max(0, index - 1)).element; + + if (index > 0) { + this.contentItems[index - 1].element.after(splitterElement); + splitterElement.after(contentItem.element); + } else { + this.contentItems[0].element.before(splitterElement); + splitterElement.before(contentItem.element); + } + } else { + this.childElementContainer.append(contentItem.element); + } + + lm.items.AbstractContentItem.prototype.addChild.call(this, contentItem, index); + + let fixedItemSize = 0; + let variableItemCount = 0; + for (i = 0; i < this.contentItems.length; i++) { + if (this.contentItems[i].config.fixed) + fixedItemSize += this.contentItems[i].config[this._dimension]; + else variableItemCount++; + } + + newItemSize = (1 / variableItemCount) * (100 - fixedItemSize); + + if (_$suspendResize === true) { + this.emitBubblingEvent('stateChanged'); + return; + } + + for (i = 0; i < this.contentItems.length; i++) { + if (this.contentItems[i].config.fixed) + ; + else if (this.contentItems[i] === contentItem) { + contentItem.config[this._dimension] = newItemSize; + } else { + itemSize = this.contentItems[i].config[this._dimension] *= (100 - newItemSize - fixedItemSize) / (100 - fixedItemSize); + this.contentItems[i].config[this._dimension] = itemSize; + } + } + + this.callDownwards('setSize'); + this.emitBubblingEvent('stateChanged'); + + }, + + /** + * Removes a child of this element + * + * @param {lm.items.AbstractContentItem} contentItem + * @param {boolean} keepChild If true the child will be removed, but not destroyed + * + * @returns {void} + */ + removeChild: function (contentItem, keepChild) { + var removedItemSize = contentItem.config[this._dimension], + index = lm.utils.indexOf(contentItem, this.contentItems), + splitterIndex = Math.max(index - 1, 0), + i, + childItem; + + if (index === -1) { + throw new Error('Can\'t remove child. ContentItem is not child of this Row or Column'); + } + + /** + * Remove the splitter before the item or after if the item happens + * to be the first in the row/column + */ + if (this._splitter[splitterIndex]) { + this._splitter[splitterIndex]._$destroy(); + this._splitter.splice(splitterIndex, 1); + } + + let fixedItemSize = 0; + for (i = 0; i < this.contentItems.length; i++) { + if (this.contentItems[i].config.fixed) + fixedItemSize += this.contentItems[i].config[this._dimension]; + } + /** + * Allocate the space that the removed item occupied to the remaining items + */ + for (i = 0; i < this.contentItems.length; i++) { + if (this.contentItems[i].config.fixed) + ; + else if (this.contentItems[i] !== contentItem) { + this.contentItems[i].config[this._dimension] *= (100 - fixedItemSize) / (100 - removedItemSize - fixedItemSize); + } + } + + lm.items.AbstractContentItem.prototype.removeChild.call(this, contentItem, keepChild); + + if (this.contentItems.length === 1 && this.config.isClosable === true) { + childItem = this.contentItems[0]; + this.contentItems = []; + this.parent.replaceChild(this, childItem, true); + } else { + this.callDownwards('setSize'); + this.emitBubblingEvent('stateChanged'); + } + }, + + /** + * Replaces a child of this Row or Column with another contentItem + * + * @param {lm.items.AbstractContentItem} oldChild + * @param {lm.items.AbstractContentItem} newChild + * + * @returns {void} + */ + replaceChild: function (oldChild, newChild) { + var size = oldChild.config[this._dimension]; + lm.items.AbstractContentItem.prototype.replaceChild.call(this, oldChild, newChild); + newChild.config[this._dimension] = size; + this.callDownwards('setSize'); + this.emitBubblingEvent('stateChanged'); + }, + + /** + * Called whenever the dimensions of this item or one of its parents change + * + * @returns {void} + */ + setSize: function () { + if (this.contentItems.length > 0) { + this._calculateRelativeSizes(); + this._setAbsoluteSizes(); + } + this.emitBubblingEvent('stateChanged'); + this.emit('resize'); + }, + + /** + * Invoked recursively by the layout manager. AbstractContentItem.init appends + * the contentItem's DOM elements to the container, RowOrColumn init adds splitters + * in between them + * + * @package private + * @override AbstractContentItem._$init + * @returns {void} + */ + _$init: function () { + if (this.isInitialised === true) return; + + var i; + + lm.items.AbstractContentItem.prototype._$init.call(this); + + for (i = 0; i < this.contentItems.length - 1; i++) { + this.contentItems[i].element.after(this._createSplitter(i).element); + } + }, + + /** + * Turns the relative sizes calculated by _calculateRelativeSizes into + * absolute pixel values and applies them to the children's DOM elements + * + * Assigns additional pixels to counteract Math.floor + * + * @private + * @returns {void} + */ + _setAbsoluteSizes: function () { + var i, + sizeData = this._calculateAbsoluteSizes(); + + for (i = 0; i < this.contentItems.length; i++) { + if (sizeData.additionalPixel - i > 0) { + sizeData.itemSizes[i]++; + } + + if (this._isColumn) { + this.contentItems[i].element.width(sizeData.totalWidth); + this.contentItems[i].element.height(sizeData.itemSizes[i]); + } else { + this.contentItems[i].element.width(sizeData.itemSizes[i]); + this.contentItems[i].element.height(sizeData.totalHeight); + } + } + }, + + /** + * Calculates the absolute sizes of all of the children of this Item. + * @returns {object} - Set with absolute sizes and additional pixels. + */ + _calculateAbsoluteSizes: function () { + var i, + totalSplitterSize = (this.contentItems.length - 1) * this._splitterSize, + totalWidth = this.element.width(), + totalHeight = this.element.height(), + totalAssigned = 0, + additionalPixel, + itemSize, + itemSizes = []; + + if (this._isColumn) { + totalHeight -= totalSplitterSize; + } else { + totalWidth -= totalSplitterSize; + } + + for (i = 0; i < this.contentItems.length; i++) { + if (this._isColumn) { + itemSize = Math.floor(totalHeight * (this.contentItems[i].config.height / 100)); + } else { + itemSize = Math.floor(totalWidth * (this.contentItems[i].config.width / 100)); + } + + totalAssigned += itemSize; + itemSizes.push(itemSize); + } + + additionalPixel = Math.floor((this._isColumn ? totalHeight : totalWidth) - totalAssigned); + + return { + itemSizes: itemSizes, + additionalPixel: additionalPixel, + totalWidth: totalWidth, + totalHeight: totalHeight + }; + }, + + /** + * Calculates the relative sizes of all children of this Item. The logic + * is as follows: + * + * - Add up the total size of all items that have a configured size + * + * - If the total == 100 (check for floating point errors) + * Excellent, job done + * + * - If the total is > 100, + * set the size of items without set dimensions to 1/3 and add this to the total + * set the size off all items so that the total is hundred relative to their original size + * + * - If the total is < 100 + * If there are items without set dimensions, distribute the remainder to 100 evenly between them + * If there are no items without set dimensions, increase all items sizes relative to + * their original size so that they add up to 100 + * + * @private + * @returns {void} + */ + _calculateRelativeSizes: function () { + + var i, + total = 0, + itemsWithoutSetDimension = [], + dimension = this._isColumn ? 'height' : 'width'; + + for (i = 0; i < this.contentItems.length; i++) { + if (this.contentItems[i].config[dimension] !== undefined) { + total += this.contentItems[i].config[dimension]; + } else { + itemsWithoutSetDimension.push(this.contentItems[i]); + } + } + + /** + * Everything adds up to hundred, all good :-) + */ + if (Math.round(total) === 100) { + this._respectMinItemWidth(); + return; + } + + /** + * Allocate the remaining size to the items without a set dimension + */ + if (Math.round(total) < 100 && itemsWithoutSetDimension.length > 0) { + for (i = 0; i < itemsWithoutSetDimension.length; i++) { + itemsWithoutSetDimension[i].config[dimension] = (100 - total) / itemsWithoutSetDimension.length; + } + this._respectMinItemWidth(); + return; + } + + /** + * If the total is > 100, but there are also items without a set dimension left, assing 50 + * as their dimension and add it to the total + * + * This will be reset in the next step + */ + if (Math.round(total) > 100) { + for (i = 0; i < itemsWithoutSetDimension.length; i++) { + itemsWithoutSetDimension[i].config[dimension] = 50; + total += 50; + } + } + + /** + * Set every items size relative to 100 relative to its size to total + */ + for (i = 0; i < this.contentItems.length; i++) { + this.contentItems[i].config[dimension] = (this.contentItems[i].config[dimension] / total) * 100; + } + + this._respectMinItemWidth(); + }, + + /** + * Adjusts the column widths to respect the dimensions minItemWidth if set. + * @returns {} + */ + _respectMinItemWidth: function () { + var minItemWidth = this.layoutManager.config.dimensions ? (this.layoutManager.config.dimensions.minItemWidth || 0) : 0, + sizeData = null, + entriesOverMin = [], + totalOverMin = 0, + totalUnderMin = 0, + remainingWidth = 0, + itemSize = 0, + contentItem = null, + reducePercent, + reducedWidth, + allEntries = [], + entry; + + if (this._isColumn || !minItemWidth || this.contentItems.length <= 1) { + return; + } + + sizeData = this._calculateAbsoluteSizes(); + + /** + * Figure out how much we are under the min item size total and how much room we have to use. + */ + for (var i = 0; i < this.contentItems.length; i++) { + + contentItem = this.contentItems[i]; + itemSize = sizeData.itemSizes[i]; + + if (itemSize < minItemWidth) { + totalUnderMin += minItemWidth - itemSize; + entry = { width: minItemWidth }; + + } + else { + totalOverMin += itemSize - minItemWidth; + entry = { width: itemSize }; + entriesOverMin.push(entry); + } + + allEntries.push(entry); + } + + /** + * If there is nothing under min, or there is not enough over to make up the difference, do nothing. + */ + if (totalUnderMin === 0 || totalUnderMin > totalOverMin) { + return; + } + + /** + * Evenly reduce all columns that are over the min item width to make up the difference. + */ + reducePercent = totalUnderMin / totalOverMin; + remainingWidth = totalUnderMin; + for (i = 0; i < entriesOverMin.length; i++) { + entry = entriesOverMin[i]; + reducedWidth = Math.round((entry.width - minItemWidth) * reducePercent); + remainingWidth -= reducedWidth; + entry.width -= reducedWidth; + } + + /** + * Take anything remaining from the last item. + */ + if (remainingWidth !== 0) { + allEntries[allEntries.length - 1].width -= remainingWidth; + } + + /** + * Set every items size relative to 100 relative to its size to total + */ + for (i = 0; i < this.contentItems.length; i++) { + this.contentItems[i].config.width = (allEntries[i].width / sizeData.totalWidth) * 100; + } + }, + + /** + * Instantiates a new lm.controls.Splitter, binds events to it and adds + * it to the array of splitters at the position specified as the index argument + * + * What it doesn't do though is append the splitter to the DOM + * + * @param {Int} index The position of the splitter + * + * @returns {lm.controls.Splitter} + */ + _createSplitter: function (index) { + var splitter; + splitter = new lm.controls.Splitter(this._isColumn, this._splitterSize, this._splitterGrabSize); + splitter.on('drag', lm.utils.fnBind(this._onSplitterDrag, this, [splitter]), this); + splitter.on('dragStop', lm.utils.fnBind(this._onSplitterDragStop, this, [splitter]), this); + splitter.on('dragStart', lm.utils.fnBind(this._onSplitterDragStart, this, [splitter]), this); + this._splitter.splice(index, 0, splitter); + return splitter; + }, + + /** + * Locates the instance of lm.controls.Splitter in the array of + * registered splitters and returns a map containing the contentItem + * before and after the splitters, both of which are affected if the + * splitter is moved + * + * @param {lm.controls.Splitter} splitter + * + * @returns {Object} A map of contentItems that the splitter affects + */ + _getItemsForSplitter: function (splitter) { + var index = lm.utils.indexOf(splitter, this._splitter); + + return { + before: this.contentItems[index], + after: this.contentItems[index + 1] + }; + }, + + /** + * Gets the minimum dimensions for the given item configuration array + * @param item + * @private + */ + _getMinimumDimensions: function (arr) { + var minWidth = 0, minHeight = 0; + + for (var i = 0; i < arr.length; ++i) { + minWidth = Math.max(arr[i].minWidth || 0, minWidth); + minHeight = Math.max(arr[i].minHeight || 0, minHeight); + } + + return { horizontal: minWidth, vertical: minHeight }; + }, + + /** + * Invoked when a splitter's dragListener fires dragStart. Calculates the splitters + * movement area once (so that it doesn't need calculating on every mousemove event) + * + * @param {lm.controls.Splitter} splitter + * + * @returns {void} + */ + _onSplitterDragStart: function (splitter) { + var items = this._getItemsForSplitter(splitter), + minSize = this.layoutManager.config.dimensions[this._isColumn ? 'minItemHeight' : 'minItemWidth']; + + var beforeMinDim = this._getMinimumDimensions(items.before.config.content); + var beforeMinSize = this._isColumn ? beforeMinDim.vertical : beforeMinDim.horizontal; + + var afterMinDim = this._getMinimumDimensions(items.after.config.content); + var afterMinSize = this._isColumn ? afterMinDim.vertical : afterMinDim.horizontal; + + this._splitterPosition = 0; + this._splitterMinPosition = -1 * (items.before.element[this._dimension]() - (beforeMinSize || minSize)); + this._splitterMaxPosition = items.after.element[this._dimension]() - (afterMinSize || minSize); + }, + + /** + * Invoked when a splitter's DragListener fires drag. Updates the splitters DOM position, + * but not the sizes of the elements the splitter controls in order to minimize resize events + * + * @param {lm.controls.Splitter} splitter + * @param {Int} offsetX Relative pixel values to the splitters original position. Can be negative + * @param {Int} offsetY Relative pixel values to the splitters original position. Can be negative + * + * @returns {void} + */ + _onSplitterDrag: function (splitter, offsetX, offsetY) { + var offset = this._isColumn ? offsetY : offsetX; + + if (offset > this._splitterMinPosition && offset < this._splitterMaxPosition) { + this._splitterPosition = offset; + splitter.element.css(this._isColumn ? 'top' : 'left', offset); + } + }, + + /** + * Invoked when a splitter's DragListener fires dragStop. Resets the splitters DOM position, + * and applies the new sizes to the elements before and after the splitter and their children + * on the next animation frame + * + * @param {lm.controls.Splitter} splitter + * + * @returns {void} + */ + _onSplitterDragStop: function (splitter) { + + var items = this._getItemsForSplitter(splitter), + sizeBefore = items.before.element[this._dimension](), + sizeAfter = items.after.element[this._dimension](), + splitterPositionInRange = (this._splitterPosition + sizeBefore) / (sizeBefore + sizeAfter), + totalRelativeSize = items.before.config[this._dimension] + items.after.config[this._dimension]; + + items.before.config[this._dimension] = splitterPositionInRange * totalRelativeSize; + items.after.config[this._dimension] = (1 - splitterPositionInRange) * totalRelativeSize; + + splitter.element.css({ + 'top': 0, + 'left': 0 + }); + + lm.utils.animFrame(lm.utils.fnBind(this.callDownwards, this, ['setSize'])); + } + }); + + lm.items.Stack = function (layoutManager, config, parent) { + lm.items.AbstractContentItem.call(this, layoutManager, config, parent); + + this.element = $('
              '); + this._activeContentItem = null; + var cfg = layoutManager.config; + this._header = { // defaults' reconstruction from old configuration style + show: cfg.settings.hasHeaders === true && config.hasHeaders !== false, + popout: cfg.settings.showPopoutIcon && cfg.labels.popout, + maximise: cfg.settings.showMaximiseIcon && cfg.labels.maximise, + close: cfg.settings.showCloseIcon && cfg.labels.close, + minimise: cfg.labels.minimise, + }; + if (cfg.header) // load simplified version of header configuration (https://github.com/deepstreamIO/golden-layout/pull/245) + lm.utils.copy(this._header, cfg.header); + if (config.header) // load from stack + lm.utils.copy(this._header, config.header); + if (config.content && config.content[0] && config.content[0].header) // load from component if stack omitted + lm.utils.copy(this._header, config.content[0].header); + + this._dropZones = {}; + this._dropSegment = null; + this._contentAreaDimensions = null; + this._dropIndex = null; + + this.isStack = true; + + this.childElementContainer = $('
              '); + this.header = new lm.controls.Header(layoutManager, this); + + this.element.append(this.header.element); + this.element.append(this.childElementContainer); + this._setupHeaderPosition(); + this._$validateClosability(); + }; + + lm.utils.extend(lm.items.Stack, lm.items.AbstractContentItem); + + lm.utils.copy(lm.items.Stack.prototype, { + + setSize: function () { + var i, + headerSize = this._header.show ? this.layoutManager.config.dimensions.headerHeight : 0, + contentWidth = this.element.width() - (this._sided ? headerSize : 0), + contentHeight = this.element.height() - (!this._sided ? headerSize : 0); + + this.childElementContainer.width(contentWidth); + this.childElementContainer.height(contentHeight); + + for (i = 0; i < this.contentItems.length; i++) { + this.contentItems[i].element.width(contentWidth).height(contentHeight); + } + this.emit('resize'); + this.emitBubblingEvent('stateChanged'); + }, + + _$init: function () { + var i, initialItem; + + if (this.isInitialised === true) return; + + lm.items.AbstractContentItem.prototype._$init.call(this); + + for (i = 0; i < this.contentItems.length; i++) { + this.header.createTab(this.contentItems[i]); + this.contentItems[i]._$hide(); + } + + if (this.contentItems.length > 0) { + initialItem = this.contentItems[this.config.activeItemIndex || 0]; + + if (!initialItem) { + throw new Error('Configured activeItemIndex out of bounds'); + } + + this.setActiveContentItem(initialItem); + } + }, + + setActiveContentItem: function (contentItem) { + if (lm.utils.indexOf(contentItem, this.contentItems) === -1) { + throw new Error('contentItem is not a child of this stack'); + } + + if (this._activeContentItem !== null) { + this._activeContentItem._$hide(); + } + + this._activeContentItem = contentItem; + this.header.setActiveContentItem(contentItem); + contentItem._$show(); + this.emit('activeContentItemChanged', contentItem); + this.layoutManager.emit('activeContentItemChanged', contentItem); + this.emitBubblingEvent('stateChanged'); + }, + + getActiveContentItem: function () { + return this.header.activeContentItem; + }, + + addChild: function (contentItem, index) { + contentItem = this.layoutManager._$normalizeContentItem(contentItem, this); + lm.items.AbstractContentItem.prototype.addChild.call(this, contentItem, index); + this.childElementContainer.append(contentItem.element); + this.header.createTab(contentItem, index); + this.setActiveContentItem(contentItem); + this.callDownwards('setSize'); + this._$validateClosability(); + this.emitBubblingEvent('stateChanged'); + }, + + removeChild: function (contentItem, keepChild) { + var index = lm.utils.indexOf(contentItem, this.contentItems); + lm.items.AbstractContentItem.prototype.removeChild.call(this, contentItem, keepChild); + this.header.removeTab(contentItem); + if (this.header.activeContentItem === contentItem) { + if (this.contentItems.length > 0) { + this.setActiveContentItem(this.contentItems[Math.max(index - 1, 0)]); + } else { + this._activeContentItem = null; + } + } + + this._$validateClosability(); + this.emitBubblingEvent('stateChanged'); + }, + + /** + * Validates that the stack is still closable or not. If a stack is able + * to close, but has a non closable component added to it, the stack is no + * longer closable until all components are closable. + * + * @returns {void} + */ + _$validateClosability: function () { + var contentItem, + isClosable, + len, + i; + + isClosable = this.header._isClosable(); + + for (i = 0, len = this.contentItems.length; i < len; i++) { + if (!isClosable) { + break; + } + + isClosable = this.contentItems[i].config.isClosable; + } + + this.header._$setClosable(isClosable); + }, + + _$destroy: function () { + lm.items.AbstractContentItem.prototype._$destroy.call(this); + this.header._$destroy(); + }, + + + /** + * Ok, this one is going to be the tricky one: The user has dropped {contentItem} onto this stack. + * + * It was dropped on either the stacks header or the top, right, bottom or left bit of the content area + * (which one of those is stored in this._dropSegment). Now, if the user has dropped on the header the case + * is relatively clear: We add the item to the existing stack... job done (might be good to have + * tab reordering at some point, but lets not sweat it right now) + * + * If the item was dropped on the content part things are a bit more complicated. If it was dropped on either the + * top or bottom region we need to create a new column and place the items accordingly. + * Unless, of course if the stack is already within a column... in which case we want + * to add the newly created item to the existing column... + * either prepend or append it, depending on wether its top or bottom. + * + * Same thing for rows and left / right drop segments... so in total there are 9 things that can potentially happen + * (left, top, right, bottom) * is child of the right parent (row, column) + header drop + * + * @param {lm.item} contentItem + * + * @returns {void} + */ + _$onDrop: function (contentItem) { + + /* + * The item was dropped on the header area. Just add it as a child of this stack and + * get the hell out of this logic + */ + if (this._dropSegment === 'header') { + this._resetHeaderDropZone(); + this.addChild(contentItem, this._dropIndex); + return; + } + + /* + * The stack is empty. Let's just add the element. + */ + if (this._dropSegment === 'body') { + this.addChild(contentItem); + return; + } + + /* + * The item was dropped on the top-, left-, bottom- or right- part of the content. Let's + * aggregate some conditions to make the if statements later on more readable + */ + var isVertical = this._dropSegment === 'top' || this._dropSegment === 'bottom', + isHorizontal = this._dropSegment === 'left' || this._dropSegment === 'right', + insertBefore = this._dropSegment === 'top' || this._dropSegment === 'left', + hasCorrectParent = (isVertical && this.parent.isColumn) || (isHorizontal && this.parent.isRow), + type = isVertical ? 'column' : 'row', + dimension = isVertical ? 'height' : 'width', + index, + stack, + rowOrColumn; + + /* + * The content item can be either a component or a stack. If it is a component, wrap it into a stack + */ + if (contentItem.isComponent) { + stack = this.layoutManager.createContentItem({ + type: 'stack', + header: contentItem.config.header || {} + }, this); + stack._$init(); + stack.addChild(contentItem); + contentItem = stack; + } + + /* + * If the item is dropped on top or bottom of a column or left and right of a row, it's already + * layd out in the correct way. Just add it as a child + */ + if (hasCorrectParent) { + index = lm.utils.indexOf(this, this.parent.contentItems); + this.parent.addChild(contentItem, insertBefore ? index : index + 1, true); + this.config[dimension] *= 0.5; + contentItem.config[dimension] = this.config[dimension]; + this.parent.callDownwards('setSize'); + /* + * This handles items that are dropped on top or bottom of a row or left / right of a column. We need + * to create the appropriate contentItem for them to live in + */ + } else { + type = isVertical ? 'column' : 'row'; + rowOrColumn = this.layoutManager.createContentItem({ type: type }, this); + this.parent.replaceChild(this, rowOrColumn); + + rowOrColumn.addChild(contentItem, insertBefore ? 0 : undefined, true); + rowOrColumn.addChild(this, insertBefore ? undefined : 0, true); + + this.config[dimension] = 50; + contentItem.config[dimension] = 50; + rowOrColumn.callDownwards('setSize'); + } + }, + + /** + * If the user hovers above the header part of the stack, indicate drop positions for tabs. + * otherwise indicate which segment of the body the dragged item would be dropped on + * + * @param {Int} x Absolute Screen X + * @param {Int} y Absolute Screen Y + * + * @returns {void} + */ + _$highlightDropZone: function (x, y) { + var segment, area; + + for (segment in this._contentAreaDimensions) { + area = this._contentAreaDimensions[segment].hoverArea; + + if (area.x1 < x && area.x2 > x && area.y1 < y && area.y2 > y) { + + if (segment === 'header') { + this._dropSegment = 'header'; + this._highlightHeaderDropZone(this._sided ? y : x); + } else { + this._resetHeaderDropZone(); + this._highlightBodyDropZone(segment); + } + + return; + } + } + }, + + _$getArea: function () { + if (this.element.is(':visible') === false) { + return null; + } + + var getArea = lm.items.AbstractContentItem.prototype._$getArea, + headerArea = getArea.call(this, this.header.element), + contentArea = getArea.call(this, this.childElementContainer), + contentWidth = contentArea.x2 - contentArea.x1, + contentHeight = contentArea.y2 - contentArea.y1; + + this._contentAreaDimensions = { + header: { + hoverArea: { + x1: headerArea.x1, + y1: headerArea.y1, + x2: headerArea.x2, + y2: headerArea.y2 + }, + highlightArea: { + x1: headerArea.x1, + y1: headerArea.y1, + x2: headerArea.x2, + y2: headerArea.y2 + } + } + }; + + /** + * If this Stack is a parent to rows, columns or other stacks only its + * header is a valid dropzone. + */ + if (this._activeContentItem && this._activeContentItem.isComponent === false) { + return headerArea; + } + + /** + * Highlight the entire body if the stack is empty + */ + if (this.contentItems.length === 0) { + + this._contentAreaDimensions.body = { + hoverArea: { + x1: contentArea.x1, + y1: contentArea.y1, + x2: contentArea.x2, + y2: contentArea.y2 + }, + highlightArea: { + x1: contentArea.x1, + y1: contentArea.y1, + x2: contentArea.x2, + y2: contentArea.y2 + } + }; + + return getArea.call(this, this.element); + } + + this._contentAreaDimensions.left = { + hoverArea: { + x1: contentArea.x1, + y1: contentArea.y1, + x2: contentArea.x1 + contentWidth * 0.25, + y2: contentArea.y2 + }, + highlightArea: { + x1: contentArea.x1, + y1: contentArea.y1, + x2: contentArea.x1 + contentWidth * 0.5, + y2: contentArea.y2 + } + }; + + this._contentAreaDimensions.top = { + hoverArea: { + x1: contentArea.x1 + contentWidth * 0.25, + y1: contentArea.y1, + x2: contentArea.x1 + contentWidth * 0.75, + y2: contentArea.y1 + contentHeight * 0.5 + }, + highlightArea: { + x1: contentArea.x1, + y1: contentArea.y1, + x2: contentArea.x2, + y2: contentArea.y1 + contentHeight * 0.5 + } + }; + + this._contentAreaDimensions.right = { + hoverArea: { + x1: contentArea.x1 + contentWidth * 0.75, + y1: contentArea.y1, + x2: contentArea.x2, + y2: contentArea.y2 + }, + highlightArea: { + x1: contentArea.x1 + contentWidth * 0.5, + y1: contentArea.y1, + x2: contentArea.x2, + y2: contentArea.y2 + } + }; + + this._contentAreaDimensions.bottom = { + hoverArea: { + x1: contentArea.x1 + contentWidth * 0.25, + y1: contentArea.y1 + contentHeight * 0.5, + x2: contentArea.x1 + contentWidth * 0.75, + y2: contentArea.y2 + }, + highlightArea: { + x1: contentArea.x1, + y1: contentArea.y1 + contentHeight * 0.5, + x2: contentArea.x2, + y2: contentArea.y2 + } + }; + + return getArea.call(this, this.element); + }, + + _highlightHeaderDropZone: function (x) { + var i, + tabElement, + tabsLength = this.header.tabs.length, + isAboveTab = false, + tabTop, + tabLeft, + offset, + placeHolderLeft, + headerOffset, + tabWidth, + halfX; + + // Empty stack + if (tabsLength === 0) { + headerOffset = this.header.element.offset(); + + this.layoutManager.dropTargetIndicator.highlightArea({ + x1: headerOffset.left, + x2: headerOffset.left + 100, + y1: headerOffset.top + this.header.element.height() - 20, + y2: headerOffset.top + this.header.element.height() + }); + + return; + } + + for (i = 0; i < tabsLength; i++) { + tabElement = this.header.tabs[i].element; + offset = tabElement.offset(); + if (this._sided) { + tabLeft = offset.top; + tabTop = offset.left; + tabWidth = tabElement.height(); + } else { + tabLeft = offset.left; + tabTop = offset.top; + tabWidth = tabElement.width(); + } + + if (x > tabLeft && x < tabLeft + tabWidth) { + isAboveTab = true; + break; + } + } + + if (isAboveTab === false && x < tabLeft) { + return; + } + + halfX = tabLeft + tabWidth / 2; + + if (x < halfX) { + this._dropIndex = i; + tabElement.before(this.layoutManager.tabDropPlaceholder); + } else { + this._dropIndex = Math.min(i + 1, tabsLength); + tabElement.after(this.layoutManager.tabDropPlaceholder); + } + + + if (this._sided) { + placeHolderTop = this.layoutManager.tabDropPlaceholder.offset().top; + this.layoutManager.dropTargetIndicator.highlightArea({ + x1: tabTop, + x2: tabTop + tabElement.innerHeight(), + y1: placeHolderTop, + y2: placeHolderTop + this.layoutManager.tabDropPlaceholder.width() + }); + return; + } + placeHolderLeft = this.layoutManager.tabDropPlaceholder.offset().left; + + this.layoutManager.dropTargetIndicator.highlightArea({ + x1: placeHolderLeft, + x2: placeHolderLeft + this.layoutManager.tabDropPlaceholder.width(), + y1: tabTop, + y2: tabTop + tabElement.innerHeight() + }); + }, + + _resetHeaderDropZone: function () { + this.layoutManager.tabDropPlaceholder.remove(); + }, + + _setupHeaderPosition: function () { + var side = ['right', 'left', 'bottom'].indexOf(this._header.show) >= 0 && this._header.show; + this.header.element.toggle(!!this._header.show); + this._side = side; + this._sided = ['right', 'left'].indexOf(this._side) >= 0; + this.element.removeClass('lm_left lm_right lm_bottom'); + if (this._side) + this.element.addClass('lm_' + this._side); + if (this.element.find('.lm_header').length && this.childElementContainer) { + var headerPosition = ['right', 'bottom'].indexOf(this._side) >= 0 ? 'before' : 'after'; + this.header.element[headerPosition](this.childElementContainer); + this.callDownwards('setSize'); + } + }, + + _highlightBodyDropZone: function (segment) { + var highlightArea = this._contentAreaDimensions[segment].highlightArea; + this.layoutManager.dropTargetIndicator.highlightArea(highlightArea); + this._dropSegment = segment; + } + }); + + lm.utils.BubblingEvent = function (name, origin) { + this.name = name; + this.origin = origin; + this.isPropagationStopped = false; + }; + + lm.utils.BubblingEvent.prototype.stopPropagation = function () { + this.isPropagationStopped = true; + }; + /** + * Minifies and unminifies configs by replacing frequent keys + * and values with one letter substitutes. Config options must + * retain array position/index, add new options at the end. + * + * @constructor + */ + lm.utils.ConfigMinifier = function () { + this._keys = [ + 'settings', + 'hasHeaders', + 'constrainDragToContainer', + 'selectionEnabled', + 'dimensions', + 'borderWidth', + 'minItemHeight', + 'minItemWidth', + 'headerHeight', + 'dragProxyWidth', + 'dragProxyHeight', + 'labels', + 'close', + 'maximise', + 'minimise', + 'popout', + 'content', + 'componentName', + 'componentState', + 'id', + 'width', + 'type', + 'height', + 'isClosable', + 'title', + 'popoutWholeStack', + 'openPopouts', + 'parentId', + 'activeItemIndex', + 'reorderEnabled', + 'borderGrabWidth', + + + + + //Maximum 36 entries, do not cross this line! + ]; + if (this._keys.length > 36) { + throw new Error('Too many keys in config minifier map'); + } + + this._values = [ + true, + false, + 'row', + 'column', + 'stack', + 'component', + 'close', + 'maximise', + 'minimise', + 'open in new window' + ]; + }; + + lm.utils.copy(lm.utils.ConfigMinifier.prototype, { + + /** + * Takes a GoldenLayout configuration object and + * replaces its keys and values recursively with + * one letter counterparts + * + * @param {Object} config A GoldenLayout config object + * + * @returns {Object} minified config + */ + minifyConfig: function (config) { + var min = {}; + this._nextLevel(config, min, '_min'); + return min; + }, + + /** + * Takes a configuration Object that was previously minified + * using minifyConfig and returns its original version + * + * @param {Object} minifiedConfig + * + * @returns {Object} the original configuration + */ + unminifyConfig: function (minifiedConfig) { + var orig = {}; + this._nextLevel(minifiedConfig, orig, '_max'); + return orig; + }, + + /** + * Recursive function, called for every level of the config structure + * + * @param {Array|Object} orig + * @param {Array|Object} min + * @param {String} translationFn + * + * @returns {void} + */ + _nextLevel: function (from, to, translationFn) { + var key, minKey; + + for (key in from) { + + /** + * For in returns array indices as keys, so let's cast them to numbers + */ + if (from instanceof Array) key = parseInt(key, 10); + + /** + * In case something has extended Object prototypes + */ + if (!from.hasOwnProperty(key)) continue; + + /** + * Translate the key to a one letter substitute + */ + minKey = this[translationFn](key, this._keys); + + /** + * For Arrays and Objects, create a new Array/Object + * on the minified object and recurse into it + */ + if (typeof from[key] === 'object') { + to[minKey] = from[key] instanceof Array ? [] : {}; + this._nextLevel(from[key], to[minKey], translationFn); + + /** + * For primitive values (Strings, Numbers, Boolean etc.) + * minify the value + */ + } else { + to[minKey] = this[translationFn](from[key], this._values); + } + } + }, + + /** + * Minifies value based on a dictionary + * + * @param {String|Boolean} value + * @param {Array} dictionary + * + * @returns {String} The minified version + */ + _min: function (value, dictionary) { + /** + * If a value actually is a single character, prefix it + * with ___ to avoid mistaking it for a minification code + */ + if (typeof value === 'string' && value.length === 1) { + return '___' + value; + } + + var index = lm.utils.indexOf(value, dictionary); + + /** + * value not found in the dictionary, return it unmodified + */ + if (index === -1) { + return value; + + /** + * value found in dictionary, return its base36 counterpart + */ + } else { + return index.toString(36); + } + }, + + _max: function (value, dictionary) { + /** + * value is a single character. Assume that it's a translation + * and return the original value from the dictionary + */ + if (typeof value === 'string' && value.length === 1) { + return dictionary[parseInt(value, 36)]; + } + + /** + * value originally was a single character and was prefixed with ___ + * to avoid mistaking it for a translation. Remove the prefix + * and return the original character + */ + if (typeof value === 'string' && value.substr(0, 3) === '___') { + return value[3]; + } + /** + * value was not minified + */ + return value; + } + }); + + /** + * An EventEmitter singleton that propagates events + * across multiple windows. This is a little bit trickier since + * windows are allowed to open childWindows in their own right + * + * This means that we deal with a tree of windows. Hence the rules for event propagation are: + * + * - Propagate events from this layout to both parents and children + * - Propagate events from parent to this and children + * - Propagate events from children to the other children (but not the emitting one) and the parent + * + * @constructor + * + * @param {lm.LayoutManager} layoutManager + */ + lm.utils.EventHub = function (layoutManager) { + lm.utils.EventEmitter.call(this); + this._layoutManager = layoutManager; + this._dontPropagateToParent = null; + this._childEventSource = null; + this.on(lm.utils.EventEmitter.ALL_EVENT, lm.utils.fnBind(this._onEventFromThis, this)); + this._boundOnEventFromChild = lm.utils.fnBind(this._onEventFromChild, this); + $(window).on('gl_child_event', this._boundOnEventFromChild); + }; + + /** + * Called on every event emitted on this eventHub, regardles of origin. + * + * @private + * + * @param {Mixed} + * + * @returns {void} + */ + lm.utils.EventHub.prototype._onEventFromThis = function () { + var args = Array.prototype.slice.call(arguments); + + if (this._layoutManager.isSubWindow && args[0] !== this._dontPropagateToParent) { + this._propagateToParent(args); + } + this._propagateToChildren(args); + + //Reset + this._dontPropagateToParent = null; + this._childEventSource = null; + }; + + /** + * Called by the parent layout. + * + * @param {Array} args Event name + arguments + * + * @returns {void} + */ + lm.utils.EventHub.prototype._$onEventFromParent = function (args) { + this._dontPropagateToParent = args[0]; + this.emit.apply(this, args); + }; + + /** + * Callback for child events raised on the window + * + * @param {DOMEvent} event + * @private + * + * @returns {void} + */ + lm.utils.EventHub.prototype._onEventFromChild = function (event) { + this._childEventSource = event.originalEvent.__gl; + this.emit.apply(this, event.originalEvent.__glArgs); + }; + + /** + * Propagates the event to the parent by emitting + * it on the parent's DOM window + * + * @param {Array} args Event name + arguments + * @private + * + * @returns {void} + */ + lm.utils.EventHub.prototype._propagateToParent = function (args) { + var event, + eventName = 'gl_child_event'; + + if (document.createEvent) { + event = window.opener.document.createEvent('HTMLEvents'); + event.initEvent(eventName, true, true); + } else { + event = window.opener.document.createEventObject(); + event.eventType = eventName; + } + + event.eventName = eventName; + event.__glArgs = args; + event.__gl = this._layoutManager; + + if (document.createEvent) { + window.opener.dispatchEvent(event); + } else { + window.opener.fireEvent('on' + event.eventType, event); + } + }; + + /** + * Propagate events to children + * + * @param {Array} args Event name + arguments + * @private + * + * @returns {void} + */ + lm.utils.EventHub.prototype._propagateToChildren = function (args) { + var childGl, i; + + for (i = 0; i < this._layoutManager.openPopouts.length; i++) { + childGl = this._layoutManager.openPopouts[i].getGlInstance(); + + if (childGl && childGl !== this._childEventSource) { + childGl.eventHub._$onEventFromParent(args); + } + } + }; + + + /** + * Destroys the EventHub + * + * @public + * @returns {void} + */ + + lm.utils.EventHub.prototype.destroy = function () { + $(window).off('gl_child_event', this._boundOnEventFromChild); + }; + /** + * A specialised GoldenLayout component that binds GoldenLayout container + * lifecycle events to react components + * + * @constructor + * + * @param {lm.container.ItemContainer} container + * @param {Object} state state is not required for react components + */ + lm.utils.ReactComponentHandler = function (container, state) { + this._reactComponent = null; + this._originalComponentWillUpdate = null; + this._container = container; + this._initialState = state; + this._reactClass = this._getReactClass(); + this._container.on('open', this._render, this); + this._container.on('destroy', this._destroy, this); + }; + + lm.utils.copy(lm.utils.ReactComponentHandler.prototype, { + + /** + * Creates the react class and component and hydrates it with + * the initial state - if one is present + * + * By default, react's getInitialState will be used + * + * @private + * @returns {void} + */ + _render: function () { + this._reactComponent = ReactDOM.render(this._getReactComponent(), this._container.getElement()[0]); + this._originalComponentWillUpdate = this._reactComponent.componentWillUpdate || function () { + }; + this._reactComponent.componentWillUpdate = this._onUpdate.bind(this); + if (this._container.getState()) { + this._reactComponent.setState(this._container.getState()); + } + }, + + /** + * Removes the component from the DOM and thus invokes React's unmount lifecycle + * + * @private + * @returns {void} + */ + _destroy: function () { + ReactDOM.unmountComponentAtNode(this._container.getElement()[0]); + this._container.off('open', this._render, this); + this._container.off('destroy', this._destroy, this); + }, + + /** + * Hooks into React's state management and applies the componentstate + * to GoldenLayout + * + * @private + * @returns {void} + */ + _onUpdate: function (nextProps, nextState) { + this._container.setState(nextState); + this._originalComponentWillUpdate.call(this._reactComponent, nextProps, nextState); + }, + + /** + * Retrieves the react class from GoldenLayout's registry + * + * @private + * @returns {React.Class} + */ + _getReactClass: function () { + var componentName = this._container._config.component; + var reactClass; + + if (!componentName) { + throw new Error('No react component name. type: react-component needs a field `component`'); + } + + reactClass = this._container.layoutManager.getComponent(componentName); + + if (!reactClass) { + throw new Error('React component "' + componentName + '" not found. ' + + 'Please register all components with GoldenLayout using `registerComponent(name, component)`'); + } + + return reactClass; + }, + + /** + * Copies and extends the properties array and returns the React element + * + * @private + * @returns {React.Element} + */ + _getReactComponent: function () { + var defaultProps = { + glEventHub: this._container.layoutManager.eventHub, + glContainer: this._container, + }; + var props = $.extend(defaultProps, this._container._config.props); + return React.createElement(this._reactClass, props); + } + }); +})(window.$); \ No newline at end of file diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 21ca08471..6f721a0c8 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -1,25 +1,23 @@ -import * as GoldenLayout from "golden-layout"; import 'golden-layout/src/css/goldenlayout-base.css'; import 'golden-layout/src/css/goldenlayout-dark-theme.css'; -import { action, observable, reaction, trace, runInAction } from "mobx"; +import { action, observable, reaction } from "mobx"; import { observer } from "mobx-react"; import * as ReactDOM from 'react-dom'; import Measure from "react-measure"; -import { Utils, returnTrue, emptyFunction, returnOne, returnZero } from "../../../Utils"; +import * as GoldenLayout from "../../../client/goldenLayout"; +import { Doc, Field, Opt } from "../../../new_fields/Doc"; +import { FieldId, Id } from "../../../new_fields/RefField"; +import { listSpec } from "../../../new_fields/Schema"; +import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; +import { emptyFunction, returnTrue, Utils } from "../../../Utils"; +import { DocServer } from "../../DocServer"; +import { DragLinksAsDocuments, DragManager } from "../../util/DragManager"; +import { Transform } from '../../util/Transform'; import { undoBatch, UndoManager } from "../../util/UndoManager"; import { DocumentView } from "../nodes/DocumentView"; import "./CollectionDockingView.scss"; -import React = require("react"); import { SubCollectionViewProps } from "./CollectionSubView"; -import { DragManager, DragLinksAsDocuments } from "../../util/DragManager"; -import { Transform } from '../../util/Transform'; -import { Doc, Opt, Field } from "../../../new_fields/Doc"; -import { Cast, NumCast, StrCast, PromiseValue } from "../../../new_fields/Types"; -import { List } from "../../../new_fields/List"; -import { DocServer } from "../../DocServer"; -import { listSpec } from "../../../new_fields/Schema"; -import { Id, FieldId } from "../../../new_fields/RefField"; -import { faSignInAlt } from "@fortawesome/free-solid-svg-icons"; +import React = require("react"); @observer export class CollectionDockingView extends React.Component { @@ -286,6 +284,9 @@ export class CollectionDockingView extends React.Component { if (tab.hasOwnProperty("contentItem") && tab.contentItem.config.type !== "stack") { + if (tab.contentItem.config.fixed) { + tab.contentItem.parent.config.fixed = true; + } DocServer.GetRefField(tab.contentItem.config.props.documentId).then(async doc => { if (doc instanceof Doc) { let counter: any = this.htmlToElement(`
              0
              `); @@ -325,16 +326,6 @@ export class CollectionDockingView extends React.Component { let doc = await DocServer.GetRefField(contentItem.config.props.documentId); if (doc instanceof Doc) { @@ -342,7 +333,15 @@ export class CollectionDockingView extends React.Component ); } + } interface DockedFrameProps { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 947a066d9..429d0f047 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -20,6 +20,7 @@ import { CollectionDockingView } from './CollectionDockingView'; import { DocumentManager } from '../../util/DocumentManager'; import { Utils } from '../../../Utils'; import { List } from '../../../new_fields/List'; +import { indexOf } from 'typescript-collections/dist/lib/arrays'; export interface TreeViewProps { @@ -48,8 +49,15 @@ class TreeView extends React.Component { @observable _collapsed: boolean = true; - delete = () => this.props.deleteDoc(this.props.document); - openRight = () => CollectionDockingView.Instance.AddRightSplit(this.props.document); + @undoBatch delete = () => this.props.deleteDoc(this.props.document); + + @undoBatch openRight = () => { + if (this.props.document.dockingConfig) { + Main.Instance.openWorkspace(this.props.document); + } else { + CollectionDockingView.Instance.AddRightSplit(this.props.document); + } + }; get children() { return Cast(this.props.document.data, listSpec(Doc), []); // bcz: needed? .filter(doc => FieldValue(doc)); diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index cf08c1bc4..2c8e6aef3 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -125,15 +125,15 @@ export class CollectionFreeFormDocumentView extends DocComponent => { SelectionManager.DeselectAll(); let isMinimized: boolean | undefined; - let maximizedDocs = await Cast(this.props.Document.maximizedDocs, listSpec(Doc)); + let maximizedDocs = Cast(this.props.Document.maximizedDocs, listSpec(Doc)); let minimizedDoc: Doc | undefined = this.props.Document; if (!maximizedDocs) { minimizedDoc = await Cast(this.props.Document.minimizedDoc, Doc); - if (minimizedDoc) maximizedDocs = await Cast(minimizedDoc.maximizedDocs, listSpec(Doc)); + if (minimizedDoc) maximizedDocs = Cast(minimizedDoc.maximizedDocs, listSpec(Doc)); } - if (minimizedDoc && maximizedDocs && maximizedDocs instanceof List) { + if (minimizedDoc && maximizedDocs) { let minimizedTarget = minimizedDoc; - maximizedDocs.map(maximizedDoc => { + (await Promise.all(maximizedDocs)).forEach(maximizedDoc => { let iconAnimating = Cast(maximizedDoc.isIconAnimating, List); if (!iconAnimating || (Date.now() - iconAnimating[6] > 1000)) { if (isMinimized === undefined) { @@ -166,26 +166,25 @@ export class CollectionFreeFormDocumentView extends DocComponent { - let maxDoc = await mdoc; - let dataDocs = await Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc)); - if (dataDocs) { - Promise.all(dataDocs.map(async doc => await doc)).then(docs => { - if (!docs || docs.indexOf(maxDoc) == -1) { - CollectionDockingView.Instance.AddRightSplit(maxDoc); - } else { - CollectionDockingView.Instance.CloseRightSplit(maxDoc); - } - }) - } - }); + let dataDocs = Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc)); + if (dataDocs) { + const docs = await Promise.all(dataDocs); + const maxDocs = await Promise.all(maximizedDocs); + SelectionManager.DeselectAll(); + maxDocs.forEach(maxDoc => { + if (!docs || docs.indexOf(maxDoc) == -1) { + CollectionDockingView.Instance.AddRightSplit(maxDoc); + } else { + CollectionDockingView.Instance.CloseRightSplit(maxDoc); + } + }); + } } else { - this.props.addDocument && maximizedDocs.map(async maxDoc => this.props.addDocument!(await maxDoc, false)); + this.props.addDocument && maximizedDocs.forEach(async maxDoc => this.props.addDocument!(await maxDoc, false)); this.toggleIcon(); } } diff --git a/src/new_fields/List.ts b/src/new_fields/List.ts index 96018dafa..213ed21aa 100644 --- a/src/new_fields/List.ts +++ b/src/new_fields/List.ts @@ -206,7 +206,7 @@ class ListImpl extends ObjectField { return list; } - [key: number]: FieldResult; + [key: number]: T | (T extends RefField ? Promise : never); @serializable(alias("fields", list(autoObject()))) private get __fields() { -- cgit v1.2.3-70-g09d2 From 684c8e190098dee8c285665ebf1e2c598bd5cf4c Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 6 May 2019 15:28:59 -0400 Subject: fixed a few golden layout and tree view issues. --- src/client/views/collections/CollectionDockingView.tsx | 4 +++- src/client/views/collections/CollectionTreeView.tsx | 18 ++++++++++++++---- src/client/views/nodes/IconBox.tsx | 14 +++++++++++++- src/new_fields/Doc.ts | 1 - 4 files changed, 30 insertions(+), 7 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 6f721a0c8..05c467763 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -78,14 +78,16 @@ export class CollectionDockingView extends React.Component { if (tab.config.component === "DocumentFrameRenderer" && tab.config.props.documentId === document[Id]) { child.contentItems[j].remove(); + child.config.activeItemIndex = Math.max(child.contentItems.length - 1, 0); let docs = Cast(this.props.Document.data, listSpec(Doc)); docs && docs.indexOf(document) !== -1 && docs.splice(docs.indexOf(document), 1); + this.stateChanged(); } }); }) diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 429d0f047..6fa374464 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -10,7 +10,7 @@ import "./CollectionTreeView.scss"; import React = require("react"); import { Document, listSpec } from '../../../new_fields/Schema'; import { Cast, StrCast, BoolCast, FieldValue } from '../../../new_fields/Types'; -import { Doc } from '../../../new_fields/Doc'; +import { Doc, DocListCast } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/RefField'; import { ContextMenu } from '../ContextMenu'; import { undoBatch } from '../../util/UndoManager'; @@ -51,7 +51,7 @@ class TreeView extends React.Component { @undoBatch delete = () => this.props.deleteDoc(this.props.document); - @undoBatch openRight = () => { + @undoBatch openRight = async () => { if (this.props.document.dockingConfig) { Main.Instance.openWorkspace(this.props.document); } else { @@ -63,6 +63,10 @@ class TreeView extends React.Component { return Cast(this.props.document.data, listSpec(Doc), []); // bcz: needed? .filter(doc => FieldValue(doc)); } + onPointerDown = (e: React.PointerEvent) => { + e.stopPropagation(); + } + @action remove = (document: Document) => { let children = Cast(this.props.document.data, listSpec(Doc), []); @@ -109,11 +113,18 @@ class TreeView extends React.Component { return true; }} />); + let dataDocs = Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc), []); + let openRight = dataDocs && dataDocs.indexOf(this.props.document) !== -1 ? (null) : ( +
              + + +
              ); return (
              {editableView(StrCast(this.props.document.title))} -
              + {openRight} {/* {
              } */}
              ); } @@ -156,7 +167,6 @@ class TreeView extends React.Component { } }); return
            • {this.renderBullet(bulletType)} diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx index 3fab10df4..b521d5ce6 100644 --- a/src/client/views/nodes/IconBox.tsx +++ b/src/client/views/nodes/IconBox.tsx @@ -2,7 +2,7 @@ import React = require("react"); import { library } from '@fortawesome/fontawesome-svg-core'; import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, computed, observable, runInAction } from "mobx"; +import { action, computed, observable, runInAction, reaction, IReactionDisposer } from "mobx"; import { observer } from "mobx-react"; import { SelectionManager } from "../../util/SelectionManager"; import { FieldView, FieldViewProps } from './FieldView'; @@ -25,6 +25,17 @@ library.add(faFilm); @observer export class IconBox extends React.Component { public static LayoutString() { return FieldView.LayoutString(IconBox); } + _reactionDisposer?: IReactionDisposer; + componentDidMount() { + this._reactionDisposer = reaction(() => [this.props.Document.maximizedDocs], + () => { + let maxDoc = Cast(this.props.Document.maximizedDocs, listSpec(Doc), []); + this.props.Document.title = maxDoc && (maxDoc.length === 1 ? maxDoc[0].title + ".icon" : ""); + }, { fireImmediately: true }); + } + componentWillUnmount() { + if (this._reactionDisposer) this._reactionDisposer(); + } @computed get layout(): string { const field = Cast(this.props.Document[this.props.fieldKey], IconField); return field ? field.icon : "

              Error loading icon data

              "; } @computed get minimizedIcon() { return IconBox.DocumentIcon(this.layout); } @@ -52,6 +63,7 @@ export class IconBox extends React.Component { @observable _panelWidth: number = 0; @observable _panelHeight: number = 0; render() { + let title = this._title; let labelField = StrCast(this.props.Document.labelField); let hideLabel = BoolCast(this.props.Document.hideLabel); let maxDoc = Cast(this.props.Document.maximizedDocs, listSpec(Doc), []); diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 70dd8361b..38c220bc8 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -220,7 +220,6 @@ export namespace Doc { return undefined; } const delegate = new Doc(); - //TODO Does this need to be doc[Self]? delegate.proto = doc; return delegate; } -- cgit v1.2.3-70-g09d2 From a573867d2443a806f174db58e2a920db3405934c Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 8 May 2019 10:23:28 -0400 Subject: added delete from non-data keys in tree view. fixed undo iconanimating problem --- src/client/views/collections/CollectionTreeView.tsx | 20 ++++++++++++-------- .../views/nodes/CollectionFreeFormDocumentView.tsx | 4 +++- 2 files changed, 15 insertions(+), 9 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 6fa374464..6e690236f 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -68,8 +68,8 @@ class TreeView extends React.Component { } @action - remove = (document: Document) => { - let children = Cast(this.props.document.data, listSpec(Doc), []); + remove = (document: Document, key: string) => { + let children = Cast(this.props.document[key], listSpec(Doc), []); if (children) { children.splice(children.indexOf(document), 1); } @@ -81,7 +81,7 @@ class TreeView extends React.Component { return true; } //TODO This should check if it was removed - this.remove(document); + this.remove(document, "data"); return addDoc(document); } @@ -136,7 +136,11 @@ class TreeView extends React.Component { if (DocumentManager.Instance.getDocumentViews(this.props.document).length) { ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.props.document).map(view => view.props.focus(this.props.document)) }); } - ContextMenu.Instance.addItem({ description: "Delete", event: undoBatch(() => this.props.deleteDoc(this.props.document)) }); + ContextMenu.Instance.addItem({ + description: "Delete", event: undoBatch(() => { + this.props.deleteDoc(this.props.document); + }) + }); ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); e.stopPropagation(); } @@ -160,7 +164,7 @@ class TreeView extends React.Component { contentElement.push(
                {(key === "data") ? (null) : {key}} - {TreeView.GetChildElements(docList, key !== "data", this.remove, this.move, this.props.dropAction)} + {TreeView.GetChildElements(docList, key !== "data", (doc: Doc) => this.remove(doc, key), this.move, this.props.dropAction)}
              ); } else bulletType = BulletType.Collapsed; @@ -175,9 +179,9 @@ class TreeView extends React.Component {
            • ; } - public static GetChildElements(docs: Doc[], allowMinimized: boolean, remove: ((doc: Doc) => void), move: DragManager.MoveFunction, dropAction: dropActionType) { - return docs.filter(child => !child.excludeFromLibrary && (allowMinimized || !child.isMinimized)).filter(doc => FieldValue(doc)).map(child => - ); + public static GetChildElements(docs: (Doc | Promise)[], allowMinimized: boolean, remove: ((doc: Doc) => void), move: DragManager.MoveFunction, dropAction: dropActionType) { + return docs.filter(child => child instanceof Doc && !child.excludeFromLibrary && (allowMinimized || !child.isMinimized)).filter(doc => FieldValue(doc)).map(child => + ); } } diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 0e34cb626..5efc75793 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -138,7 +138,9 @@ export class CollectionFreeFormDocumentView extends DocComponent { let iconAnimating = Cast(maximizedDoc.isIconAnimating, List); if (!iconAnimating || (Date.now() - iconAnimating[6] > 1000)) { -- cgit v1.2.3-70-g09d2 From 7da76d2538ebde21d7a878b5096d5a673e5d6375 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 10 May 2019 23:01:08 -0400 Subject: added summary screen grab icon as a template view of a text document. converted Cast(...ListSpec(Doc)) pattern to DocListCast --- src/client/northstar/dash-nodes/HistogramBox.tsx | 10 +++---- src/client/util/DocumentManager.ts | 6 ++-- src/client/util/DragManager.ts | 6 ++-- src/client/views/PresentationView.tsx | 11 +++++-- src/client/views/Templates.tsx | 35 +++++++++++++++------- .../views/collections/CollectionBaseView.tsx | 8 ++--- .../views/collections/CollectionSchemaView.tsx | 6 ++-- src/client/views/collections/CollectionSubView.tsx | 4 +-- .../views/collections/CollectionTreeView.tsx | 22 +++++++------- .../CollectionFreeFormLinksView.tsx | 12 ++++---- .../collections/collectionFreeForm/MarqueeView.tsx | 24 +++++++++++---- .../views/nodes/CollectionFreeFormDocumentView.tsx | 28 +++++++++-------- src/client/views/nodes/DocumentView.tsx | 23 +++++++------- src/client/views/nodes/IconBox.tsx | 4 +-- src/client/views/nodes/ImageBox.tsx | 14 ++++++--- src/client/views/nodes/LinkMenu.tsx | 6 ++-- src/new_fields/Doc.ts | 10 +++++-- 17 files changed, 137 insertions(+), 92 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx index 5e7b867b3..ed556cf45 100644 --- a/src/client/northstar/dash-nodes/HistogramBox.tsx +++ b/src/client/northstar/dash-nodes/HistogramBox.tsx @@ -19,7 +19,7 @@ import { HistogramLabelPrimitives } from "./HistogramLabelPrimitives"; import { StyleConstants } from "../utils/StyleContants"; import { NumCast, Cast } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; -import { Doc } from "../../../new_fields/Doc"; +import { Doc, DocListCast } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/RefField"; @@ -117,15 +117,15 @@ export class HistogramBox extends React.Component { runInAction(() => { this.HistoOp = histoOp ? histoOp.HistoOp : HistogramOperation.Empty; if (this.HistoOp !== HistogramOperation.Empty) { - reaction(() => Cast(this.props.Document.linkedFromDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc), (docs) => this.HistoOp.Links.splice(0, this.HistoOp.Links.length, ...docs), { fireImmediately: true }); - reaction(() => Cast(this.props.Document.brushingDocs, listSpec(Doc), []).length, + reaction(() => DocListCast(this.props.Document.linkedFromDocs), (docs) => this.HistoOp.Links.splice(0, this.HistoOp.Links.length, ...docs), { fireImmediately: true }); + reaction(() => DocListCast(this.props.Document.brushingDocs).length, () => { - let brushingDocs = Cast(this.props.Document.brushingDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc); + let brushingDocs = DocListCast(this.props.Document.brushingDocs); const proto = this.props.Document.proto; if (proto) { this.HistoOp.BrushLinks.splice(0, this.HistoOp.BrushLinks.length, ...brushingDocs.map((brush, i) => { brush.backgroundColor = StyleConstants.BRUSH_COLORS[i % StyleConstants.BRUSH_COLORS.length]; - let brushed = Cast(brush.brushingDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc); + let brushed = DocListCast(brush.brushingDocs); return { l: brush, b: brushed[0][Id] === proto[Id] ? brushed[1] : brushed[0] }; })); } diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index a8b643d4d..9a7a94228 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,6 +1,6 @@ import { computed, observable } from 'mobx'; import { DocumentView } from '../views/nodes/DocumentView'; -import { Doc } from '../../new_fields/Doc'; +import { Doc, DocListCast } from '../../new_fields/Doc'; import { FieldValue, Cast, NumCast, BoolCast } from '../../new_fields/Types'; import { listSpec } from '../../new_fields/Schema'; import { undoBatch } from './UndoManager'; @@ -73,7 +73,7 @@ export class DocumentManager { @computed public get LinkedDocumentViews() { return DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)).reduce((pairs, dv) => { - let linksList = Cast(dv.props.Document.linkedToDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc); + let linksList = DocListCast(dv.props.Document.linkedToDocs); if (linksList && linksList.length) { pairs.push(...linksList.reduce((pairs, link) => { if (link) { @@ -86,7 +86,7 @@ export class DocumentManager { return pairs; }, [] as { a: DocumentView, b: DocumentView, l: Doc }[])); } - linksList = Cast(dv.props.Document.linkedFromDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc); + linksList = DocListCast(dv.props.Document.linkedFromDocs); if (linksList && linksList.length) { pairs.push(...linksList.reduce((pairs, link) => { if (link) { diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index c0402f0c9..2da0d5b51 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -1,5 +1,5 @@ import { action, runInAction } from "mobx"; -import { Doc, DocListCast } from "../../new_fields/Doc"; +import { Doc, DocListCastAsync } from "../../new_fields/Doc"; import { Cast } from "../../new_fields/Types"; import { emptyFunction } from "../../Utils"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; @@ -43,8 +43,8 @@ export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: n let draggedDocs: Doc[] = []; let draggedFromDocs: Doc[] = [] if (srcTarg) { - let linkToDocs = await DocListCast(srcTarg.linkedToDocs); - let linkFromDocs = await DocListCast(srcTarg.linkedFromDocs); + let linkToDocs = await DocListCastAsync(srcTarg.linkedToDocs); + let linkFromDocs = await DocListCastAsync(srcTarg.linkedFromDocs); if (linkToDocs) draggedDocs = linkToDocs.map(linkDoc => Cast(linkDoc.linkedTo, Doc) as Doc); if (linkFromDocs) draggedFromDocs = linkFromDocs.map(linkDoc => Cast(linkDoc.linkedFrom, Doc) as Doc); } diff --git a/src/client/views/PresentationView.tsx b/src/client/views/PresentationView.tsx index 4853eb151..3fb24a339 100644 --- a/src/client/views/PresentationView.tsx +++ b/src/client/views/PresentationView.tsx @@ -141,8 +141,15 @@ export class PresentationView extends React.Component { (activeW) => { if (activeW && activeW instanceof Doc) { PromiseValue(Cast(activeW.presentationView, Doc)). - then(pv => runInAction(() => - self.Document = pv ? pv : (activeW.presentationView = new Doc()))) + then(pv => runInAction(() => { + if (pv) self.Document = pv; + else { + pv = new Doc(); + pv.title = "Presentation Doc"; + activeW.presentationView = pv; + self.Document = pv; + } + })) } }, { fireImmediately: true }); diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx index bbedc95f1..a98870b04 100644 --- a/src/client/views/Templates.tsx +++ b/src/client/views/Templates.tsx @@ -5,6 +5,7 @@ export enum TemplatePosition { InnerBottom, InnerRight, InnerLeft, + TopRight, OutterTop, OutterBottom, OutterRight, @@ -39,30 +40,42 @@ export namespace Templates { // export const BasicLayout = new Template("Basic layout", "{layout}"); export const Caption = new Template("Caption", TemplatePosition.OutterBottom, - `
              ` - ); + `
              + +
              ` ); + export const TitleOverlay = new Template("TitleOverlay", TemplatePosition.InnerTop, - `
              {layout}
              -
              - {props.Document.title} -
              ` - ); + `
              +
              {layout}
              +
              + {props.Document.title} +
              +
              ` ); + export const Title = new Template("Title", TemplatePosition.InnerTop, `
              {layout}
              {props.Document.title} -
              ` - ); +
              ` ); export const Bullet = new Template("Bullet", TemplatePosition.InnerTop, `
              {layout}
              -
              - +
              ` ); + export function ImageOverlay(width: number, height: number, field: string = "thumbnail") { + return (`
              +
              {layout}
              +
              + +
              +
              `); + } + export const TemplateList: Template[] = [Title, TitleOverlay, Caption, Bullet]; export function sortTemplates(a: Template, b: Template) { diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index 2b1f7bb37..645296d27 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import { ContextMenu } from '../ContextMenu'; import { FieldViewProps } from '../nodes/FieldView'; import { Cast, FieldValue, PromiseValue, NumCast } from '../../../new_fields/Types'; -import { Doc, FieldResult, Opt } from '../../../new_fields/Doc'; +import { Doc, FieldResult, Opt, DocListCast } from '../../../new_fields/Doc'; import { listSpec } from '../../../new_fields/Schema'; import { List } from '../../../new_fields/List'; import { Id } from '../../../new_fields/RefField'; @@ -63,13 +63,13 @@ export class CollectionBaseView extends React.Component { if (!(documentToAdd instanceof Doc)) { return false; } - let data = Cast(documentToAdd.data, listSpec(Doc), []).filter(d => d).map(d => d as Doc); - for (const doc of data.filter(d => d instanceof Document)) { + let data = DocListCast(documentToAdd.data); + for (const doc of data) { if (this.createsCycle(doc, containerDocument)) { return true; } } - let annots = Cast(documentToAdd.annotations, listSpec(Doc), []).filter(d => d).map(d => d as Doc); + let annots = DocListCast(documentToAdd.annotations); for (const annot of annots) { if (this.createsCycle(annot, containerDocument)) { return true; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index ae949b2ed..506e60a65 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -19,7 +19,7 @@ import { DocumentView } from "../nodes/DocumentView"; import { FieldView, FieldViewProps } from "../nodes/FieldView"; import "./CollectionSchemaView.scss"; import { CollectionSubView } from "./CollectionSubView"; -import { Opt, Field, Doc, DocListCast } from "../../../new_fields/Doc"; +import { Opt, Field, Doc, DocListCastAsync, DocListCast } from "../../../new_fields/Doc"; import { Cast, FieldValue, NumCast } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; import { List } from "../../../new_fields/List"; @@ -118,7 +118,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { } const run = script.run; //TODO This should be able to be refactored to compile the script once - const val = await DocListCast(this.props.Document[this.props.fieldKey]) + const val = await DocListCastAsync(this.props.Document[this.props.fieldKey]) val && val.forEach(doc => applyToDoc(doc, run)); }}> @@ -276,7 +276,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { } get documentKeysCheckList() { - const docs = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []).filter(d => d).map(d => d as Doc); + const docs = DocListCast(this.props.Document[this.props.fieldKey]); let keys: { [key: string]: boolean } = {}; // bcz: ugh. this is untracked since otherwise a large collection of documents will blast the server for all their fields. // then as each document's fields come back, we update the documents _proxies. Each time we do this, the whole schema will be diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 0b08e150a..a86d250bd 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -10,7 +10,7 @@ import * as rp from 'request-promise'; import { CollectionView } from "./CollectionView"; import { CollectionPDFView } from "./CollectionPDFView"; import { CollectionVideoView } from "./CollectionVideoView"; -import { Doc, Opt, FieldResult } from "../../../new_fields/Doc"; +import { Doc, Opt, FieldResult, DocListCast } from "../../../new_fields/Doc"; import { DocComponent } from "../DocComponent"; import { listSpec } from "../../../new_fields/Schema"; import { Cast, PromiseValue, FieldValue, ListSpec } from "../../../new_fields/Types"; @@ -49,7 +49,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { get children() { //TODO tfs: This might not be what we want? //This linter error can't be fixed because of how js arguments work, so don't switch this to filter(FieldValue) - return Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []).filter(doc => FieldValue(doc)).map(doc => doc as Doc); + return DocListCast(this.props.Document[this.props.fieldKey]); } @action diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 33787f06b..78c84cc89 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -18,9 +18,7 @@ import { Main } from '../Main'; import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils'; import { CollectionDockingView } from './CollectionDockingView'; import { DocumentManager } from '../../util/DocumentManager'; -import { Utils } from '../../../Utils'; import { List } from '../../../new_fields/List'; -import { indexOf } from 'typescript-collections/dist/lib/arrays'; export interface TreeViewProps { @@ -155,16 +153,21 @@ class TreeView extends React.Component { let keys = Array.from(Object.keys(this.props.document)); if (this.props.document.proto instanceof Doc) { keys.push(...Array.from(Object.keys(this.props.document.proto))); + while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1); } keys.map(key => { - let docList = Cast(this.props.document[key], listSpec(Doc)); - if (docList instanceof List && docList.length && docList[0] instanceof Doc) { + let docList = DocListCast(this.props.document[key]); + let doc = Cast(this.props.document[key], Doc); + if (doc instanceof Doc || docList.length) { if (!this._collapsed) { bulletType = BulletType.Collapsible; + let spacing = (key === "data") ? 0 : -10; contentElement.push(
                {(key === "data") ? (null) : - {key}} - {TreeView.GetChildElements(docList, key !== "data", (doc: Doc) => this.remove(doc, key), this.move, this.props.dropAction)} + {key}} +
                + {TreeView.GetChildElements(doc instanceof Doc ? [doc] : docList, key !== "data", (doc: Doc) => this.remove(doc, key), this.move, this.props.dropAction)} +
              ); } else bulletType = BulletType.Collapsed; @@ -179,7 +182,7 @@ class TreeView extends React.Component {
              ; } - public static GetChildElements(docs: (Doc | Promise)[], allowMinimized: boolean, remove: ((doc: Doc) => void), move: DragManager.MoveFunction, dropAction: dropActionType) { + public static GetChildElements(docs: Doc[], allowMinimized: boolean, remove: ((doc: Doc) => void), move: DragManager.MoveFunction, dropAction: dropActionType) { return docs.filter(child => child instanceof Doc && !child.excludeFromLibrary && (allowMinimized || !child.isMinimized)).filter(doc => FieldValue(doc)).map(child => ); } @@ -203,12 +206,11 @@ export class CollectionTreeView extends CollectionSubView(Document) { } } render() { - const children = this.children; let dropAction = StrCast(this.props.Document.dropAction, "alias") as dropActionType; - if (!children) { + if (!this.children) { return (null); } - let childElements = TreeView.GetChildElements(children, false, this.remove, this.props.moveDocument, dropAction); + let childElements = TreeView.GetChildElements(this.children, false, this.remove, this.props.moveDocument, dropAction); return (
              { - let doclist = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []); - return { doclist: doclist ? doclist : [], xs: doclist instanceof List ? doclist.map(d => d instanceof Doc && d.x) : [] }; + let doclist = DocListCast(this.props.Document[this.props.fieldKey]); + return { doclist: doclist ? doclist : [], xs: doclist.map(d => d.x) }; }, - async () => { - let doclist = await DocListCast(this.props.Document[this.props.fieldKey]); + () => { + let doclist = DocListCast(this.props.Document[this.props.fieldKey]); let views = doclist ? doclist.filter(doc => StrCast(doc.backgroundLayout).indexOf("istogram") !== -1) : []; views.forEach((dstDoc, i) => { views.forEach((srcDoc, j) => { @@ -84,7 +84,7 @@ export class CollectionFreeFormLinksView extends React.Component d).map(d => d as Doc). + DocListCast(this.props.Document[this.props.fieldKey]). filter(child => child[Id] === collid).map(view => DocumentManager.Instance.getDocumentViews(view).map(view => diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 1bf39e335..9ace0272a 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -1,3 +1,4 @@ +import * as htmlToImage from "html-to-image"; import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import { Docs } from "../../../documents/Documents"; @@ -14,6 +15,8 @@ import { Doc } from "../../../../new_fields/Doc"; import { NumCast, Cast } from "../../../../new_fields/Types"; import { InkField, StrokeData } from "../../../../new_fields/InkField"; import { List } from "../../../../new_fields/List"; +import { ImageField } from "../../../../new_fields/URLField"; +import { Template, Templates } from "../../Templates"; interface MarqueeViewProps { getContainerTransform: () => Transform; @@ -30,6 +33,7 @@ interface MarqueeViewProps { @observer export class MarqueeView extends React.Component { + private _mainCont = React.createRef(); @observable _lastX: number = 0; @observable _lastY: number = 0; @observable _downX: number = 0; @@ -166,7 +170,7 @@ export class MarqueeView extends React.Component @undoBatch @action - marqueeCommand = (e: KeyboardEvent) => { + marqueeCommand = async (e: KeyboardEvent) => { if (this._commandExecuted) { return; } @@ -224,13 +228,17 @@ export class MarqueeView extends React.Component let scrpt = this.props.getTransform().inverse().transformPoint(bounds.left, bounds.top); let summary = Docs.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "yellow", title: "-summary-" }); + let dataUrl = await htmlToImage.toPng(this._mainCont.current!, { width: bounds.width, height: bounds.height, quality: 1 }); + summary.proto!.thumbnail = new ImageField(new URL(dataUrl)); + + summary.proto!.templates = new List([Templates.ImageOverlay(Math.min(50, bounds.width), bounds.height * Math.min(50, bounds.width) / bounds.width, "thumbnail")]); if (e.key === "s" || e.key === "p") { summary.proto!.maximizeOnRight = true; newCollection.proto!.summaryDoc = summary; selected = [newCollection]; } summary.proto!.summarizedDocs = new List(selected); - summary.proto!.isButton = true; + //summary.proto!.isButton = true; selected.map(summarizedDoc => { let maxx = NumCast(summarizedDoc.x, undefined); let maxy = NumCast(summarizedDoc.y, undefined); @@ -313,17 +321,21 @@ export class MarqueeView extends React.Component @computed get marqueeDiv() { - let p = this.props.getContainerTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY); let v = this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY); - return
              + return
              ; } render() { + let p = this.props.getContainerTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY); return
              - {this.props.children} - {!this._visible ? (null) : this.marqueeDiv} +
              +
              + {this.props.children} +
              + {!this._visible ? null : this.marqueeDiv} +
              ; } } \ No newline at end of file diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index b05f2eea2..39d216da0 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -9,10 +9,10 @@ import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schem import { FieldValue, Cast, NumCast, BoolCast, StrCast } from "../../../new_fields/Types"; import { OmitKeys, Utils } from "../../../Utils"; import { SelectionManager } from "../../util/SelectionManager"; -import { Doc, DocListCast, HeightSym } from "../../../new_fields/Doc"; +import { Doc, DocListCastAsync, DocListCast, } from "../../../new_fields/Doc"; import { List } from "../../../new_fields/List"; import { CollectionDockingView } from "../collections/CollectionDockingView"; -import { undoBatch, UndoManager } from "../../util/UndoManager"; +import { UndoManager } from "../../util/UndoManager"; export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { } @@ -65,7 +65,7 @@ export class CollectionFreeFormDocumentView extends DocComponent this.nativeWidth > 0 ? this.width / this.nativeWidth : 1; panelWidth = () => this.props.PanelWidth(); panelHeight = () => this.props.PanelHeight(); - toggleMinimized = async () => this.toggleIcon(await DocListCast(this.props.Document.maximizedDocs)); + toggleMinimized = async () => this.toggleIcon(await DocListCastAsync(this.props.Document.maximizedDocs)); getTransform = (): Transform => this.props.ScreenToLocalTransform() .translate(-this.X, -this.Y) .scale(1 / this.contentScaling()).scale(1 / this.zoom) @@ -132,7 +132,7 @@ export class CollectionFreeFormDocumentView extends DocComponent { + expandedDocs.forEach(maxDoc => { maxDoc.isMinimized = false; if (!dataDocs || dataDocs.indexOf(maxDoc) == -1) { CollectionDockingView.Instance.AddRightSplit(maxDoc); @@ -195,8 +197,8 @@ export class CollectionFreeFormDocumentView extends DocComponent this.props.addDocument!(await maxDoc, false)); - this.toggleIcon(maximizedDocs); + this.props.addDocument && expandedDocs.forEach(async maxDoc => this.props.addDocument!(await maxDoc, false)); + this.toggleIcon(expandedDocs); } } } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 5aa74c703..90f67db7c 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -12,13 +12,13 @@ import { CollectionPDFView } from "../collections/CollectionPDFView"; import { CollectionVideoView } from "../collections/CollectionVideoView"; import { CollectionView } from "../collections/CollectionView"; import { ContextMenu } from "../ContextMenu"; -import { Template, Templates } from "./../Templates"; +import { Template } from "./../Templates"; import { DocumentContentsView } from "./DocumentContentsView"; import "./DocumentView.scss"; import React = require("react"); -import { Opt, Doc, WidthSym, HeightSym, DocListCast } from "../../../new_fields/Doc"; +import { Opt, Doc, WidthSym, HeightSym, DocListCastAsync, DocListCast } from "../../../new_fields/Doc"; import { DocComponent } from "../DocComponent"; -import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema"; +import { createSchema, makeInterface } from "../../../new_fields/Schema"; import { FieldValue, StrCast, BoolCast, Cast } from "../../../new_fields/Types"; import { List } from "../../../new_fields/List"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; @@ -109,8 +109,8 @@ export class DocumentView extends DocComponent(Docu } // bcz: kind of ugly .. setup a reaction to update the title of a summary document's target (maximizedDocs) whenver the summary doc's title changes this._reactionDisposer = reaction(() => [this.props.Document.maximizedDocs, this.props.Document.summaryDoc, this.props.Document.summaryDoc instanceof Doc ? this.props.Document.summaryDoc.title : ""], - async () => { - let maxDoc = await DocListCast(this.props.Document.maximizedDocs); + () => { + let maxDoc = DocListCast(this.props.Document.maximizedDocs); if (maxDoc && StrCast(this.props.Document.layout).indexOf("IconBox") !== -1) { this.props.Document.title = (maxDoc && maxDoc.length === 1 ? maxDoc[0].title + ".icon" : ""); } @@ -145,7 +145,7 @@ export class DocumentView extends DocComponent(Docu startDragging(x: number, y: number, dropAction: dropActionType, dragSubBullets: boolean) { if (this._mainCont.current) { - let allConnected = dragSubBullets ? [this.props.Document, ...Cast(this.props.Document.subBulletDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc)] : [this.props.Document]; + let allConnected = [this.props.Document, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])]; const [left, top] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(0, 0); let dragData = new DragManager.DocumentDragData(allConnected); const [xoff, yoff] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top); @@ -169,17 +169,17 @@ export class DocumentView extends DocComponent(Docu SelectionManager.SelectDoc(this, e.ctrlKey); } } - _hitIsBullet = false; + _hitExpander = false; onPointerDown = (e: React.PointerEvent): void => { this._downX = e.clientX; this._downY = e.clientY; if (CollectionFreeFormView.RIGHT_BTN_DRAG && (e.button === 2 || (e.button === 0 && e.altKey)) && !this.isSelected()) { return; } - this._hitIsBullet = (e.target && (e.target as any).id === "isBullet") || Cast(this.props.Document.subBulletDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc).length > 0; + this._hitExpander = DocListCast(this.props.Document.subBulletDocs).length > 0; if (e.shiftKey && e.buttons === 1) { if (this.props.isTopMost) { - this.startDragging(e.pageX, e.pageY, e.altKey || e.ctrlKey ? "alias" : undefined, this._hitIsBullet); + this.startDragging(e.pageX, e.pageY, e.altKey || e.ctrlKey ? "alias" : undefined, this._hitExpander); } else if (this.props.Document) { CollectionDockingView.Instance.StartOtherDrag([Doc.MakeAlias(this.props.Document)], e); } @@ -197,7 +197,7 @@ export class DocumentView extends DocComponent(Docu document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); if (!e.altKey && !this.topMost && (!CollectionFreeFormView.RIGHT_BTN_DRAG && e.buttons === 1) || (CollectionFreeFormView.RIGHT_BTN_DRAG && e.buttons === 2)) { - this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined, this._hitIsBullet); + this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined, this._hitExpander); } } e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers @@ -232,6 +232,7 @@ export class DocumentView extends DocComponent(Docu ContextMenu.Instance.clearItems(); SelectionManager.DeselectAll(); } + @undoBatch @action drop = async (e: Event, de: DragManager.DropEvent) => { @@ -259,7 +260,6 @@ export class DocumentView extends DocComponent(Docu } } - @action addTemplate = (template: Template) => { this.templates.push(template.Layout); @@ -307,7 +307,6 @@ export class DocumentView extends DocComponent(Docu } } - isSelected = () => SelectionManager.IsSelected(this); select = (ctrlPressed: boolean) => SelectionManager.SelectDoc(this, ctrlPressed); diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx index 4bcb4c636..48aecdcd0 100644 --- a/src/client/views/nodes/IconBox.tsx +++ b/src/client/views/nodes/IconBox.tsx @@ -2,12 +2,12 @@ import React = require("react"); import { library } from '@fortawesome/fontawesome-svg-core'; import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { computed, observable, runInAction, reaction, IReactionDisposer } from "mobx"; +import { computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; import { FieldView, FieldViewProps } from './FieldView'; import "./IconBox.scss"; import { Cast, StrCast, BoolCast } from "../../../new_fields/Types"; -import { Doc, DocListCast } from "../../../new_fields/Doc"; +import { Doc } from "../../../new_fields/Doc"; import { IconField } from "../../../new_fields/IconField"; import { ContextMenu } from "../ContextMenu"; import Measure from "react-measure"; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 0e9e904a8..f9659a4b2 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -1,5 +1,5 @@ -import { action, observable } from 'mobx'; +import { action, observable, trace } from 'mobx'; import { observer } from "mobx-react"; import Lightbox from 'react-image-lightbox'; import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app @@ -157,15 +157,21 @@ export class ImageBox extends DocComponent(ImageD } render() { + trace(); let field = this.Document[this.props.fieldKey]; let paths: string[] = ["http://www.cs.brown.edu/~bcz/face.gif"]; if (field instanceof ImageField) paths = [field.url.href]; else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => (p as ImageField).url.href); - let nativeWidth = FieldValue(this.Document.nativeWidth, 1); + let nativeWidth = FieldValue(this.Document.nativeWidth, (this.props.PanelWidth as any) as string ? Number((this.props.PanelWidth as any) as string) : 50); let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive"; + let id = this.props.id; return ( -
              - Image not found +
              + {paths.length > 1 ? this.dots(paths) : (null)} {this.lightbox(paths)}
              ); diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index 24901913d..7bf13d5f9 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -5,7 +5,7 @@ import { LinkBox } from "./LinkBox"; import { LinkEditor } from "./LinkEditor"; import './LinkMenu.scss'; import React = require("react"); -import { Doc } from "../../../new_fields/Doc"; +import { Doc, DocListCast } from "../../../new_fields/Doc"; import { Cast, FieldValue } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; import { Id } from "../../../new_fields/RefField"; @@ -31,8 +31,8 @@ export class LinkMenu extends React.Component { render() { //get list of links from document - let linkFrom = Cast(this.props.docView.props.Document.linkedFromDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc); - let linkTo = Cast(this.props.docView.props.Document.linkedToDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc); + let linkFrom = DocListCast(this.props.docView.props.Document.linkedFromDocs); + let linkTo = DocListCast(this.props.docView.props.Document.linkedToDoc); if (this._editingLink === undefined) { return (
              diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 2ab145fa3..46ccb3e90 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -29,13 +29,17 @@ export const SelfProxy = Symbol("SelfProxy"); export const WidthSym = Symbol("Width"); export const HeightSym = Symbol("Height"); -export function DocListCast(field: FieldResult): Promise; -export function DocListCast(field: FieldResult, defaultValue: Doc[]): Promise; -export function DocListCast(field: FieldResult, defaultValue?: Doc[]) { +export function DocListCastAsync(field: FieldResult): Promise; +export function DocListCastAsync(field: FieldResult, defaultValue: Doc[]): Promise; +export function DocListCastAsync(field: FieldResult, defaultValue?: Doc[]) { const list = Cast(field, listSpec(Doc)); return list ? Promise.all(list) : Promise.resolve(defaultValue); } +export function DocListCast(field: FieldResult) { + return Cast(field, listSpec(Doc), []).filter(d => d && d instanceof Doc).map(d => d as Doc) +} + @Deserializable("doc").withFields(["id"]) export class Doc extends RefField { constructor(id?: FieldId, forceSave?: boolean) { -- cgit v1.2.3-70-g09d2 From 0e457c6f8268c70408d3740984f3976437535f51 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 12 May 2019 09:12:50 -0400 Subject: adjustments to treeview and ctrl-q pasting. --- src/client/views/DocumentDecorations.scss | 2 +- src/client/views/EditableView.scss | 17 ++++++++++++++++- src/client/views/EditableView.tsx | 7 ++++--- src/client/views/collections/CollectionTreeView.tsx | 14 ++++++++++++-- .../collections/collectionFreeForm/MarqueeView.tsx | 8 ++------ src/client/views/globalCssVariables.scss | 6 ++++-- 6 files changed, 39 insertions(+), 15 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index 6a2e33836..ba9f32d7d 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -65,7 +65,7 @@ $linkGap : 3px; cursor: ew-resize; } .title{ - background: lightblue; + background: $alt-accent; grid-column-start: 3; grid-column-end: 4; pointer-events: auto; diff --git a/src/client/views/EditableView.scss b/src/client/views/EditableView.scss index ea401eaf9..dfa110f8d 100644 --- a/src/client/views/EditableView.scss +++ b/src/client/views/EditableView.scss @@ -1,5 +1,20 @@ -.editableView-container-editing { +.editableView-container-editing, .editableView-container-editing-oneLine { overflow-wrap: break-word; word-wrap: break-word; hyphens: auto; + overflow: hidden; +} +.editableView-container-editing-oneLine { + span { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + display:block; + } + input { + display:block; + } +} +.editableView-input { + width: 100%; } \ No newline at end of file diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index 73467eb9d..78143ccda 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -24,6 +24,7 @@ export interface EditableProps { contents: any; height: number; display?: string; + oneLine?: boolean; } /** @@ -54,11 +55,11 @@ export class EditableView extends React.Component { render() { if (this.editing) { - return this.editing = false)} - style={{ display: this.props.display }}>; + return this.editing = false)} + style={{ display: this.props.display }} />; } else { return ( -
              this.editing = true)} > {this.props.contents}
              diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 78c84cc89..9bd32a89f 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -93,6 +93,15 @@ class TreeView extends React.Component { return
              {bullet ? : ""}
              ; } + @action + onMouseEnter = () => { + this._isOver = true; + } + @observable _isOver: boolean = false; + @action + onMouseLeave = () => { + this._isOver = false; + } /** * Renders the EditableView title element for placement into the tree. */ @@ -101,7 +110,8 @@ class TreeView extends React.Component { let onItemDown = SetupDrag(reference, () => this.props.document, this.props.moveDocument, this.props.dropAction); let editableView = (titleString: string) => ( StrCast(this.props.document.title)} @@ -118,7 +128,7 @@ class TreeView extends React.Component {
              ); return ( -
              {editableView(StrCast(this.props.document.title))} diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 9ace0272a..718a75770 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -59,13 +59,9 @@ export class MarqueeView extends React.Component if (e.key === "q" && e.ctrlKey) { e.preventDefault(); (async () => { - let text = await navigator.clipboard.readText(); - let ns = text.split("\n").filter(t => t != "\r"); + let text: string = await navigator.clipboard.readText(); + let ns = text.split("\n").filter(t => t.trim() != "\r" && t.trim() != ""); for (let i = 0; i < ns.length - 1; i++) { - if (ns[i].trim() === "") { - ns.splice(i, 1); - continue; - } while (!(ns[i].trim() === "" || ns[i].endsWith("-\r") || ns[i].endsWith("-") || ns[i].endsWith(";\r") || ns[i].endsWith(";") || ns[i].endsWith(".\r") || ns[i].endsWith(".") || diff --git a/src/client/views/globalCssVariables.scss b/src/client/views/globalCssVariables.scss index cb4d1ad87..838d4d9ac 100644 --- a/src/client/views/globalCssVariables.scss +++ b/src/client/views/globalCssVariables.scss @@ -2,10 +2,12 @@ // colors $light-color: #fcfbf7; $light-color-secondary:#f1efeb; -$main-accent: #61aaa3; +//$main-accent: #61aaa3; +$main-accent: #aaaaa3; // $alt-accent: #cdd5ec; // $alt-accent: #cdeceb; -$alt-accent: #59dff7; +//$alt-accent: #59dff7; +$alt-accent: #c2c2c5; $lighter-alt-accent: rgb(207, 220, 240); $intermediate-color: #9c9396; $dark-color: #121721; -- cgit v1.2.3-70-g09d2 From b9d32e8e07137484c57f3d1ab89090b27d56cfe6 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 12 May 2019 21:52:04 -0400 Subject: finally fixed disappearing videobox and changed marquee div structure --- src/client/views/collections/CollectionTreeView.tsx | 5 +++++ src/client/views/collections/collectionFreeForm/MarqueeView.tsx | 8 ++++---- src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 9bd32a89f..d22418b2c 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -19,6 +19,7 @@ import { CurrentUserUtils } from '../../../server/authentication/models/current_ import { CollectionDockingView } from './CollectionDockingView'; import { DocumentManager } from '../../util/DocumentManager'; import { List } from '../../../new_fields/List'; +import { Docs } from '../../documents/Documents'; export interface TreeViewProps { @@ -141,6 +142,10 @@ class TreeView extends React.Component { if (!e.isPropagationStopped() && this.props.document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => Main.Instance.openWorkspace(this.props.document)) }); ContextMenu.Instance.addItem({ description: "Open Right", event: () => CollectionDockingView.Instance.AddRightSplit(this.props.document) }); + ContextMenu.Instance.addItem({ + description: "Open Fields", event: () => CollectionDockingView.Instance.AddRightSplit(Docs.KVPDocument(this.props.document, + { title: this.props.document.title + ".kvp", width: 300, height: 300 })) + }); if (DocumentManager.Instance.getDocumentViews(this.props.document).length) { ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.props.document).map(view => view.props.focus(this.props.document)) }); } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 611ee7fd6..080c484f4 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -358,7 +358,7 @@ export class MarqueeView extends React.Component @computed get marqueeDiv() { let v = this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY); - return
              + return
              ; } @@ -366,11 +366,11 @@ export class MarqueeView extends React.Component render() { let p = this.props.getContainerTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY); return
              -
              -
              +
              + {!this._visible ? null : this.marqueeDiv} +
              {this.props.children}
              - {!this._visible ? null : this.marqueeDiv}
              ; } diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 39d216da0..817a23ce8 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -228,7 +228,7 @@ export class CollectionFreeFormDocumentView extends DocComponent fadeUp */ ? Math.max(0.1, Math.min(1, 2 - (w < fadeDown ? fadeDown / w : w / fadeUp))) : 1; + zoomFade = w < fadeDown /* || w > fadeUp */ ? Math.max(0.1, Math.min(1, 2 - (w < fadeDown ? Math.sqrt(Math.sqrt(fadeDown / w)) : w / fadeUp))) : 1; return (
              Date: Wed, 15 May 2019 09:10:41 -0400 Subject: set hover highlight for search results --- src/client/views/SearchItem.tsx | 12 +++++++++++- src/client/views/collections/CollectionTreeView.tsx | 2 +- src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 4 +++- src/new_fields/Doc.ts | 2 +- 4 files changed, 16 insertions(+), 4 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/views/SearchItem.tsx b/src/client/views/SearchItem.tsx index 0da0bdae8..01c7316d6 100644 --- a/src/client/views/SearchItem.tsx +++ b/src/client/views/SearchItem.tsx @@ -40,6 +40,14 @@ export class SearchItem extends React.Component { faCaretUp; return ; } + onPointerEnter = (e: React.PointerEvent) => { + this.props.doc.libraryBrush = true; + Doc.SetOnPrototype(this.props.doc, "protoBrush", true); + } + onPointerLeave = (e: React.PointerEvent) => { + this.props.doc.libraryBrush = false; + Doc.SetOnPrototype(this.props.doc, "protoBrush", false); + } collectionRef = React.createRef(); startDocDrag = () => { @@ -53,7 +61,9 @@ export class SearchItem extends React.Component { } render() { return ( -
              +
              title: {this.props.doc.title}
              {/*
              Type: {this.props.doc.layout}
              */} {/*
              {SearchItem.DocumentIcon(this.layout)}
              */} diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index d22418b2c..70c09d97c 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -130,7 +130,7 @@ class TreeView extends React.Component {
              ); return (
              {editableView(StrCast(this.props.document.title))} {openRight} diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index fc1dc2b1e..925945b17 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -247,7 +247,9 @@ export class CollectionFreeFormDocumentView extends DocComponent; } export async function SetOnPrototype(doc: Doc, key: string, value: Field) { - const proto = Object.getOwnPropertyNames(doc).indexOf("isProto") == -1 ? doc.proto : doc; + const proto = Object.getOwnPropertyNames(doc).indexOf("isPrototype") == -1 ? doc.proto : doc; if (proto) { proto[key] = value; -- cgit v1.2.3-70-g09d2 From b29a0d1cef60b55f609fcd94ad38232ae557e681 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Thu, 16 May 2019 18:32:57 -0400 Subject: Fixed linter errors --- src/client/goldenLayout.d.ts | 3 +++ src/client/northstar/dash-fields/HistogramField.ts | 2 +- src/client/northstar/model/ModelHelpers.ts | 9 +++---- src/client/util/DragManager.ts | 2 +- src/client/util/RichTextSchema.tsx | 16 +++++------ src/client/util/TooltipTextMenu.tsx | 13 ++++----- src/client/views/DocumentDecorations.tsx | 31 +++++++++++----------- src/client/views/PresentationView.tsx | 4 +-- src/client/views/TemplateMenu.tsx | 4 +-- .../views/collections/CollectionDockingView.tsx | 5 ++-- .../views/collections/CollectionSchemaView.tsx | 10 +++---- .../views/collections/CollectionTreeView.tsx | 9 ++++--- .../collections/collectionFreeForm/MarqueeView.tsx | 13 ++++----- .../views/nodes/CollectionFreeFormDocumentView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 8 +++--- src/client/views/nodes/ImageBox.tsx | 2 +- src/client/views/nodes/KeyValueBox.tsx | 2 +- src/client/views/nodes/KeyValuePair.tsx | 3 ++- src/client/views/nodes/VideoBox.tsx | 22 +++++++-------- src/new_fields/CursorField.ts | 6 ++--- src/new_fields/Doc.ts | 4 +-- src/new_fields/ObjectField.ts | 2 +- .../authentication/models/current_user_utils.ts | 2 +- 24 files changed, 91 insertions(+), 85 deletions(-) create mode 100644 src/client/goldenLayout.d.ts (limited to 'src/client/views/collections/CollectionTreeView.tsx') diff --git a/src/client/goldenLayout.d.ts b/src/client/goldenLayout.d.ts new file mode 100644 index 000000000..b50240563 --- /dev/null +++ b/src/client/goldenLayout.d.ts @@ -0,0 +1,3 @@ + +declare const GoldenLayout: any; +export = GoldenLayout; \ No newline at end of file diff --git a/src/client/northstar/dash-fields/HistogramField.ts b/src/client/northstar/dash-fields/HistogramField.ts index aabc77bb2..1ee2189b9 100644 --- a/src/client/northstar/dash-fields/HistogramField.ts +++ b/src/client/northstar/dash-fields/HistogramField.ts @@ -52,7 +52,7 @@ export class HistogramField extends ObjectField { [Copy]() { let y = this.HistoOp; - let z = this.HistoOp["Copy"]; + let z = this.HistoOp.Copy; return new HistogramField(HistogramOperation.Duplicate(this.HistoOp)); } } \ No newline at end of file diff --git a/src/client/northstar/model/ModelHelpers.ts b/src/client/northstar/model/ModelHelpers.ts index 80bb71224..88e6e72b8 100644 --- a/src/client/northstar/model/ModelHelpers.ts +++ b/src/client/northstar/model/ModelHelpers.ts @@ -32,12 +32,9 @@ export class ModelHelpers { public static GetAggregateParametersIndex(histogramResult: HistogramResult, aggParameters?: AggregateParameters): number { return Array.from(histogramResult.aggregateParameters!).findIndex((value, i, set) => { - if (set[i] instanceof CountAggregateParameters && value instanceof CountAggregateParameters) - return true; - if (set[i] instanceof MarginAggregateParameters && value instanceof MarginAggregateParameters) - return true; - if (set[i] instanceof SumAggregateParameters && value instanceof SumAggregateParameters) - return true; + if (set[i] instanceof CountAggregateParameters && value instanceof CountAggregateParameters) return true; + if (set[i] instanceof MarginAggregateParameters && value instanceof MarginAggregateParameters) return true; + if (set[i] instanceof SumAggregateParameters && value instanceof SumAggregateParameters) return true; return false; }); } diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 26da34e67..05eb5c38a 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -41,7 +41,7 @@ export function SetupDrag(_reference: React.RefObject, docFunc: () export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: number, sourceDoc: Doc) { let srcTarg = sourceDoc.proto; let draggedDocs: Doc[] = []; - let draggedFromDocs: Doc[] = [] + let draggedFromDocs: Doc[] = []; if (srcTarg) { let linkToDocs = await DocListCastAsync(srcTarg.linkedToDocs); let linkFromDocs = await DocListCastAsync(srcTarg.linkedFromDocs); diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index c0e6f7899..3e3e98206 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -97,13 +97,13 @@ export const nodes: { [index: string]: NodeSpec } = { title: dom.getAttribute("title"), alt: dom.getAttribute("alt"), width: Math.min(100, Number(dom.getAttribute("width"))), - } + }; } }], // TODO if we don't define toDom, something weird happens: dragging the image will not move it but clone it. Why? toDOM(node) { - const attrs = { style: `width: ${node.attrs.width}` } - return ["img", { ...node.attrs, ...attrs }] + const attrs = { style: `width: ${node.attrs.width}` }; + return ["img", { ...node.attrs, ...attrs }]; } }, @@ -375,7 +375,7 @@ export class ImageResizeView { const currentX = e.pageX; const diffInPx = currentX - startX; self._outer.style.width = `${startWidth + diffInPx}`; - } + }; const onpointerup = () => { document.removeEventListener("pointermove", onpointermove); @@ -384,11 +384,11 @@ export class ImageResizeView { view.state.tr.setNodeMarkup(getPos(), null, { src: node.attrs.src, width: self._outer.style.width }) .setSelection(view.state.selection)); - } + }; - document.addEventListener("pointermove", onpointermove) - document.addEventListener("pointerup", onpointerup) - } + document.addEventListener("pointermove", onpointermove); + document.addEventListener("pointerup", onpointerup); + }; this._outer.appendChild(this._handle); this._outer.appendChild(this._img); diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 6eb654319..223921428 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -175,7 +175,7 @@ export class TooltipTextMenu { this.linkText.style.width = "150px"; this.linkText.style.overflow = "hidden"; this.linkText.style.color = "white"; - this.linkText.onpointerdown = (e: PointerEvent) => { e.stopPropagation(); } + this.linkText.onpointerdown = (e: PointerEvent) => { e.stopPropagation(); }; let linkBtn = document.createElement("div"); linkBtn.textContent = ">>"; linkBtn.style.width = "20px"; @@ -192,8 +192,9 @@ export class TooltipTextMenu { let docid = href.replace(DocServer.prepend("/doc/"), ""); DocServer.GetRefField(docid).then(action((f: Opt) => { if (f instanceof Doc) { - if (DocumentManager.Instance.getDocumentView(f)) + if (DocumentManager.Instance.getDocumentView(f)) { DocumentManager.Instance.getDocumentView(f)!.props.focus(f); + } else CollectionDockingView.Instance.AddRightSplit(f); } })); @@ -201,7 +202,7 @@ export class TooltipTextMenu { e.stopPropagation(); e.preventDefault(); } - } + }; this.linkDrag = document.createElement("img"); this.linkDrag.src = "https://seogurusnyc.com/wp-content/uploads/2016/12/link-1.png"; this.linkDrag.style.width = "20px"; @@ -216,12 +217,12 @@ export class TooltipTextMenu { { handlers: { dragComplete: action(() => { - let m = dragData.droppedDocuments as Doc[]; + let m = dragData.droppedDocuments; this.makeLink(DocServer.prepend("/doc/" + m[0][Id])); }), }, hideSource: false - }) + }); }; this.linkEditor.appendChild(this.linkDrag); this.linkEditor.appendChild(this.linkText); @@ -239,7 +240,7 @@ export class TooltipTextMenu { e.stopPropagation(); e.preventDefault(); } - } + }; this.tooltip.appendChild(this.linkEditor); } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index b2c65a31f..7083b1003 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -250,22 +250,21 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> selectedDocs[0].props.removeDocument && selectedDocs[0].props.removeDocument(this._iconDoc); } if (!this._removeIcon) { - if (selectedDocs.length === 1) + if (selectedDocs.length === 1) { this.getIconDoc(selectedDocs[0]).then(icon => selectedDocs[0].props.toggleMinimized()); - else - if (Math.abs(e.pageX - this._downX) < Utils.DRAG_THRESHOLD && - Math.abs(e.pageY - this._downY) < Utils.DRAG_THRESHOLD) { - let docViews = SelectionManager.ViewsSortedVertically(); - let topDocView = docViews[0]; - let ind = topDocView.templates.indexOf(Templates.Bullet.Layout); - if (ind !== -1) { - topDocView.templates.splice(ind, 1); - topDocView.props.Document.subBulletDocs = undefined; - } else { - topDocView.addTemplate(Templates.Bullet); - topDocView.props.Document.subBulletDocs = new List(docViews.filter(v => v !== topDocView).map(v => v.props.Document.proto!)); - } + } else if (Math.abs(e.pageX - this._downX) < Utils.DRAG_THRESHOLD && + Math.abs(e.pageY - this._downY) < Utils.DRAG_THRESHOLD) { + let docViews = SelectionManager.ViewsSortedVertically(); + let topDocView = docViews[0]; + let ind = topDocView.templates.indexOf(Templates.Bullet.Layout); + if (ind !== -1) { + topDocView.templates.splice(ind, 1); + topDocView.props.Document.subBulletDocs = undefined; + } else { + topDocView.addTemplate(Templates.Bullet); + topDocView.props.Document.subBulletDocs = new List(docViews.filter(v => v !== topDocView).map(v => v.props.Document.proto!)); } + } } this._removeIcon = false; } @@ -537,9 +536,9 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> if (temp !== Templates.Bullet.Layout || i === 0) { res.push(temp); } - }) + }); } - return res + return res; }, [] as string[]); let checked = false; docTemps.forEach(temp => { diff --git a/src/client/views/PresentationView.tsx b/src/client/views/PresentationView.tsx index 098e725c7..7d0dc2913 100644 --- a/src/client/views/PresentationView.tsx +++ b/src/client/views/PresentationView.tsx @@ -1,7 +1,7 @@ import { observer } from "mobx-react"; -import React = require("react") +import React = require("react"); import { observable, action, runInAction, reaction } from "mobx"; -import "./PresentationView.scss" +import "./PresentationView.scss"; import "./Main.tsx"; import { DocumentManager } from "../util/DocumentManager"; import { Utils } from "../../Utils"; diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index cfe1b0663..e5b679e24 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -43,7 +43,7 @@ export class TemplateMenu extends React.Component { @action toggleTemplate = (event: React.ChangeEvent, template: Template): void => { if (event.target.checked) { - if (template.Name == "Bullet") { + if (template.Name === "Bullet") { let topDocView = this.props.docs[0]; topDocView.addTemplate(template); topDocView.props.Document.subBulletDocs = new List(this.props.docs.filter(v => v !== topDocView).map(v => v.props.Document.proto!)); @@ -52,7 +52,7 @@ export class TemplateMenu extends React.Component { } this.props.templates.set(template, true); } else { - if (template.Name == "Bullet") { + if (template.Name === "Bullet") { let topDocView = this.props.docs[0]; topDocView.removeTemplate(template); topDocView.props.Document.subBulletDocs = undefined; diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 58f1e33a1..deec64225 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -83,7 +83,7 @@ export class CollectionDockingView extends React.Component tab.config.component === "DocumentFrameRenderer").some((tab: any, j: number) => { if (Doc.AreProtosEqual(DocumentManager.Instance.getDocumentViewById(tab.config.props.documentId)!.Document, document)) { child.contentItems[j].remove(); @@ -94,8 +94,9 @@ export class CollectionDockingView extends React.Component doc) { let fieldContentView = ; let reference = React.createRef(); let onItemDown = (e: React.PointerEvent) => - (this.props.CollectionView!.props.isSelected() ? + (this.props.CollectionView.props.isSelected() ? SetupDrag(reference, () => props.Document, this.props.moveDocument)(e) : undefined); let applyToDoc = (doc: Doc, run: (args?: { [name: string]: any }) => any) => { const res = run({ this: doc }); @@ -127,7 +127,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { } const run = script.run; //TODO This should be able to be refactored to compile the script once - const val = await DocListCastAsync(this.props.Document[this.props.fieldKey]) + const val = await DocListCastAsync(this.props.Document[this.props.fieldKey]); val && val.forEach(doc => applyToDoc(doc, run)); }}> @@ -230,14 +230,14 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { DocListCast(this.props.Document.data).map(doc => { csv += self.columns.reduce((val, col) => val + (doc[col] ? doc[col]!.toString() : "") + ",", ""); csv = csv.substr(0, csv.length - 1) + "\n"; - }) + }); csv.substring(0, csv.length - 1); let dbName = StrCast(this.props.Document.title); let res = await Gateway.Instance.PostSchema(csv, dbName); if (self.props.CollectionView.props.addDocument) { let schemaDoc = await Docs.DBDocument("https://www.cs.brown.edu/" + dbName, { title: dbName }); if (schemaDoc) { - self.props.CollectionView.props.addDocument(schemaDoc, false); + self.props.CollectionView.props.addDocument(schemaDoc, false); } } } @@ -263,7 +263,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { get previewDocument(): Doc | undefined { const children = DocListCast(this.props.Document[this.props.fieldKey]); const selected = children.length > this._selectedIndex ? FieldValue(children[this._selectedIndex]) : undefined; - return selected ? (this.previewScript && this.previewScript != "this" ? FieldValue(Cast(selected[this.previewScript], Doc)) : selected) : undefined; + return selected ? (this.previewScript && this.previewScript !== "this" ? FieldValue(Cast(selected[this.previewScript], Doc)) : selected) : undefined; } get tableWidth() { return (this.props.PanelWidth() - 2 * this.borderWidth - this.DIVIDER_WIDTH) * (1 - this.splitPercentage / 100); } get previewRegionHeight() { return this.props.PanelHeight() - 2 * this.borderWidth; } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 70c09d97c..6acef434e 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -56,7 +56,7 @@ class TreeView extends React.Component { } else { CollectionDockingView.Instance.AddRightSplit(this.props.document); } - }; + } get children() { return Cast(this.props.document.data, listSpec(Doc), []); // bcz: needed? .filter(doc => FieldValue(doc)); @@ -184,8 +184,9 @@ class TreeView extends React.Component { {TreeView.GetChildElements(doc instanceof Doc ? [doc] : docList, key !== "data", (doc: Doc) => this.remove(doc, key), this.move, this.props.dropAction)}
            ); - } else + } else { bulletType = BulletType.Collapsed; + } } }); return
            {
            ; } public static GetChildElements(docs: Doc[], allowMinimized: boolean, remove: ((doc: Doc) => void), move: DragManager.MoveFunction, dropAction: dropActionType) { - return docs.filter(child => child instanceof Doc && !child.excludeFromLibrary && (allowMinimized || !child.isMinimized)).filter(doc => FieldValue(doc)).map(child => - ); + return docs.filter(child => !child.excludeFromLibrary && (allowMinimized || !child.isMinimized)).map(child => + ); } } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 12edb2c2a..c3c4115b8 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -63,7 +63,7 @@ export class MarqueeView extends React.Component e.preventDefault(); (async () => { let text: string = await navigator.clipboard.readText(); - let ns = text.split("\n").filter(t => t.trim() != "\r" && t.trim() != ""); + let ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== ""); for (let i = 0; i < ns.length - 1; i++) { while (!(ns[i].trim() === "" || ns[i].endsWith("-\r") || ns[i].endsWith("-") || ns[i].endsWith(";\r") || ns[i].endsWith(";") || @@ -80,7 +80,7 @@ export class MarqueeView extends React.Component let newBox = Docs.TextDocument({ width: 200, height: 35, x: x + indent / 3 * 10, y: y, documentText: "@@@" + line, title: line }); this.props.addDocument(newBox, false); y += 40 * this.props.getTransform().Scale; - }) + }); })(); } else if (e.key === "b" && e.ctrlKey) { //heuristically converts pasted text into a table. @@ -93,9 +93,10 @@ export class MarqueeView extends React.Component e.preventDefault(); (async () => { let text: string = await navigator.clipboard.readText(); - let ns = text.split("\n").filter(t => t.trim() != "\r" && t.trim() != ""); - while (ns.length > 0 && ns[0].split("\t").length < 2) + let ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== ""); + while (ns.length > 0 && ns[0].split("\t").length < 2) { ns.splice(0, 1); + } if (ns.length > 0) { let columns = ns[0].split("\t"); let docList: Doc[] = []; @@ -109,7 +110,7 @@ export class MarqueeView extends React.Component let doc = new Doc(); columns.forEach((col, i) => doc[columns[i]] = (values.length > i ? ((values[i].indexOf(Number(values[i]).toString()) !== -1) ? Number(values[i]) : values[i]) : undefined)); if (groupAttr) { - doc["_group"] = groupAttr; + doc._group = groupAttr; } doc.title = i.toString(); docList.push(doc); @@ -284,7 +285,7 @@ export class MarqueeView extends React.Component // summarizedDoc.isIconAnimating = new List([scrpt[0], scrpt[1], maxx, maxy, maxw, maxh, Date.now(), 0]); // }); this.props.addLiveTextDocument(summary); - }) + }); } else { this.props.addDocument(newCollection, false); diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 7c7ca9e25..d4f660b3f 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -149,7 +149,7 @@ export class CollectionFreeFormDocumentView extends DocComponent([scrpt[0], scrpt[1], Date.now(), isMinimized ? 1 : 0]) + maximizedDoc.isIconAnimating = new List([scrpt[0], scrpt[1], Date.now(), isMinimized ? 1 : 0]); } } }); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index cf8bcbb42..428dd9b36 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -244,7 +244,7 @@ export class DocumentView extends DocComponent(Docu const protoDest = destDoc.proto; const protoSrc = sourceDoc.proto; - if (de.mods == "Control") { + if (de.mods === "Control") { let src = protoSrc ? protoSrc : sourceDoc; let dst = protoDest ? protoDest : destDoc; dst.data = src; diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index f24d4ae88..f30022508 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -184,7 +184,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe state: field && field.Data ? EditorState.fromJSON(config, JSON.parse(field.Data)) : EditorState.create(config), dispatchTransaction: this.dispatchTransaction, nodeViews: { - image(node, view, getPos) { return new ImageResizeView(node, view, getPos) } + image(node, view, getPos) { return new ImageResizeView(node, view, getPos); } } }); let text = StrCast(this.props.Document.documentText); @@ -232,9 +232,11 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe let docid = href.replace(DocServer.prepend("/doc/"), "").split("?")[0]; DocServer.GetRefField(docid).then(action((f: Opt) => { if (f instanceof Doc) { - if (DocumentManager.Instance.getDocumentView(f)) + if (DocumentManager.Instance.getDocumentView(f)) { DocumentManager.Instance.getDocumentView(f)!.props.focus(f); - else CollectionDockingView.Instance.AddRightSplit(f); + } else { + CollectionDockingView.Instance.AddRightSplit(f); + } } })); } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 6472ae711..3cc60a6c5 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -165,7 +165,7 @@ export class ImageBox extends DocComponent(ImageD else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => (p as ImageField).url.href); let nativeWidth = FieldValue(this.Document.nativeWidth, (this.props.PanelWidth as any) as string ? Number((this.props.PanelWidth as any) as string) : 50); let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive"; - let id = this.props.id; // bcz: used to set id = "isExpander" in templates.tsx + let id = (this.props as any).id; // bcz: used to set id = "isExpander" in templates.tsx return (
            { @observable private _keyInput: string = ""; @observable private _valueInput: string = ""; @computed get splitPercentage() { return NumCast(this.props.Document.schemaSplitPercentage, 50); } - get fieldDocToLayout() { return this.props.fieldKey ? FieldValue(Cast(this.props.Document[this.props.fieldKey], Doc)) : this.props.Document } + get fieldDocToLayout() { return this.props.fieldKey ? FieldValue(Cast(this.props.Document[this.props.fieldKey], Doc)) : this.props.Document; } constructor(props: FieldViewProps) { super(props); diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index 5de660d57..4f7919f50 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -46,8 +46,9 @@ export class KeyValuePair extends React.Component {