aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/CollectionTreeView.tsx
blob: 8b06d9ac418b7b4760dbf2949bbd10af0e839687 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
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}>&#9654;</div>
            case BulletType.Collapsible:
                return <div className="bullet" onClick={onClicked}>&#9660;</div>
            case BulletType.List:
                return <div className="bullet">&mdash;</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 >
        );
    }
}