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}>▶</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 >
);
}
}
|