diff options
Diffstat (limited to 'src/client/views/collections/CollectionTreeView.tsx')
-rw-r--r-- | src/client/views/collections/CollectionTreeView.tsx | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx new file mode 100644 index 000000000..8b06d9ac4 --- /dev/null +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -0,0 +1,175 @@ +import { observer } from "mobx-react"; +import { CollectionViewBase } from "./CollectionViewBase"; +import { Document } from "../../../fields/Document"; +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 } from "./CollectionView"; + +export interface TreeViewProps { + document: Document; + deleteDoc: (doc: Document) => void; +} + +export enum BulletType { + Collapsed, + Collapsible, + List +} + +@observer +/** + * Component that takes in a document prop and a boolean whether it's collapsed or not. + */ +class TreeView extends React.Component<TreeViewProps> { + + @observable + collapsed: boolean = false; + + delete = () => { + this.props.deleteDoc(this.props.document); + } + + + @action + remove = (document: Document) => { + var children = this.props.document.GetT<ListField<Document>>(KeyStore.Data, ListField); + if (children && children !== FieldWaiting) { + children.Data.splice(children.Data.indexOf(document), 1); + } + } + + renderBullet(type: BulletType) { + let onClicked = action(() => this.collapsed = !this.collapsed); + + switch (type) { + case BulletType.Collapsed: + return <div className="bullet" onClick={onClicked}>▶</div> + case BulletType.Collapsible: + return <div className="bullet" onClick={onClicked}>▼</div> + case BulletType.List: + return <div className="bullet">—</div> + } + } + + /** + * Renders the EditableView title element for placement into the tree. + */ + renderTitle() { + let title = this.props.document.GetT<TextField>(KeyStore.Title, TextField); + + // if the title hasn't loaded, immediately return the div + if (!title || title === "<Waiting>") { + return <div key={this.props.document.Id}></div>; + } + + return <div className="docContainer"> <EditableView contents={title.Data} + height={36} GetValue={() => { + let title = this.props.document.GetT<TextField>(KeyStore.Title, TextField); + if (title && title !== "<Waiting>") + return title.Data; + return ""; + }} SetValue={(value: string) => { + this.props.document.SetData(KeyStore.Title, value, TextField); + return true; + }} /> + <div className="delete-button" onClick={this.delete}>x</div> + </div > + } + + render() { + var children = this.props.document.GetT<ListField<Document>>(KeyStore.Data, ListField); + + let reference = React.createRef<HTMLDivElement>(); + let onItemDown = setupDrag(reference, () => this.props.document); + let titleElement = this.renderTitle(); + + // 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 => + <TreeView document={value} deleteDoc={this.remove} />) + ) + subView = + <li key={this.props.document.Id} > + {this.renderBullet(BulletType.Collapsible)} + {titleElement} + <ul key={this.props.document.Id}> + {childrenElement} + </ul> + </li> + } else { + subView = <li key={this.props.document.Id}> + {this.renderBullet(BulletType.Collapsed)} + {titleElement} + </li> + } + + return <div className="treeViewItem-container" onPointerDown={onItemDown} ref={reference}> + {subView} + </div> + } + + // otherwise this is a normal leaf node + else { + return <li key={this.props.document.Id}> + {this.renderBullet(BulletType.List)} + {titleElement} + </li>; + } + } +} + + +@observer +export class CollectionTreeView extends CollectionViewBase { + + @action + remove = (document: Document) => { + var children = this.props.Document.GetT<ListField<Document>>(KeyStore.Data, ListField); + if (children && children !== FieldWaiting) { + children.Data.splice(children.Data.indexOf(document), 1); + } + } + + render() { + let titleStr = ""; + let title = this.props.Document.GetT<TextField>(KeyStore.Title, TextField); + if (title && title !== FieldWaiting) { + titleStr = title.Data; + } + + var children = this.props.Document.GetT<ListField<Document>>(KeyStore.Data, ListField); + let childrenElement = !children || children === FieldWaiting ? (null) : + (children.Data.map(value => + <TreeView document={value} key={value.Id} deleteDoc={this.remove} />) + ) + + return ( + <div id="body" className="collectionTreeView-dropTarget" onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }}> + <h3> + <EditableView contents={titleStr} + height={72} GetValue={() => { + return this.props.Document.Title; + }} SetValue={(value: string) => { + this.props.Document.SetData(KeyStore.Title, value, TextField); + return true; + }} /> + </h3> + <ul className="no-indent"> + {childrenElement} + </ul> + </div > + ); + } +}
\ No newline at end of file |