From 47ecf8d30f4aa5e25a659fc7f3c0c1487420150e Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Thu, 9 May 2019 20:59:10 -0400 Subject: merge with master, but haven't reconciled internal and external linking --- .../views/collections/CollectionTreeView.tsx | 186 +++++++++++++++------ 1 file changed, 131 insertions(+), 55 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 51a02fc25..33787f06b 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -1,27 +1,33 @@ import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; -import { faCaretDown, faCaretRight, faTrashAlt } from '@fortawesome/free-solid-svg-icons'; +import { faCaretDown, faCaretRight, faTrashAlt, faAngleRight } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, observable, trace } 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, dropActionType } 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'; +import { Document, listSpec } from '../../../new_fields/Schema'; +import { Cast, StrCast, BoolCast, FieldValue } from '../../../new_fields/Types'; +import { Doc, DocListCast } from '../../../new_fields/Doc'; +import { Id } from '../../../new_fields/RefField'; +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'; +import { Utils } from '../../../Utils'; +import { List } from '../../../new_fields/List'; +import { indexOf } from 'typescript-collections/dist/lib/arrays'; export interface TreeViewProps { - document: Document; - deleteDoc: (doc: Document) => void; + document: Doc; + deleteDoc: (doc: Doc) => void; moveDocument: DragManager.MoveFunction; - copyOnDrag: boolean; + dropAction: "alias" | "copy" | undefined; } export enum BulletType { @@ -31,6 +37,7 @@ export enum BulletType { } library.add(faTrashAlt); +library.add(faAngleRight); library.add(faCaretDown); library.add(faCaretRight); @@ -42,13 +49,29 @@ class TreeView extends React.Component { @observable _collapsed: boolean = true; - delete = () => this.props.deleteDoc(this.props.document); + @undoBatch delete = () => this.props.deleteDoc(this.props.document); + + @undoBatch openRight = async () => { + 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)); + } + + onPointerDown = (e: React.PointerEvent) => { + e.stopPropagation(); + } @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); + remove = (document: Document, key: string) => { + let children = Cast(this.props.document[key], listSpec(Doc), []); + if (children) { + children.splice(children.indexOf(document), 1); } } @@ -58,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); } @@ -77,83 +100,136 @@ 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) => ( this.props.document.Title} + GetValue={() => StrCast(this.props.document.title)} SetValue={(value: string) => { - this.props.document.SetText(KeyStore.Title, value); + let target = this.props.document.proto ? this.props.document.proto : this.props.document; + target.title = value; return true; }} />); + let dataDocs = Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc), []); + let openRight = dataDocs && dataDocs.indexOf(this.props.document) !== -1 ? (null) : ( +
+ + +
); return ( -
- {editableView(this.props.document.Title)} -
+
+ {editableView(StrCast(this.props.document.title))} + {openRight} + {/* {
} */}
); } + 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 + 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(); + } + } + + 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 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 => )} -
; - } - 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))); } - return
+ 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", (doc: Doc) => this.remove(doc, key), this.move, this.props.dropAction)} +
); + } else + bulletType = BulletType.Collapsed; + } + }); + return
  • {this.renderBullet(bulletType)} {this.renderTitle()} - {childElements ? childElements : (null)} + {contentElement}
  • ; } + 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 => + ); + } } @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); + 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() { - 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 dropAction = StrCast(this.props.Document.dropAction, "alias") as dropActionType; + if (!children) { + return (null); + } + let childElements = TreeView.GetChildElements(children, false, this.remove, this.props.moveDocument, dropAction); 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}>
    this.props.Document.Title} + GetValue={() => StrCast(this.props.Document.title)} SetValue={(value: string) => { - this.props.Document.SetText(KeyStore.Title, value); + let target = this.props.Document.proto ? this.props.Document.proto : this.props.Document; + target.title = value; return true; }} />
    -
      - {childrenElement} + {childElements}
    ); -- cgit v1.2.3-70-g09d2