UNPKG

5.21 kBPlain TextView Raw
1import { TreeNode } from '../types/Node'
2import { TreeComponent } from '../types/Tree'
3import { State } from '../types/State'
4import { Core, PromiseCallback, PromiseNodes, Resource, InsertOptions } from '../types/Core'
5import { callFetcher, isCallable, isString, remove, has } from '../utils'
6import { parseNode } from '../utils/parser'
7import { recurseDown, walkBreadth, FlatMap, flatMap } from '../utils/traveler'
8import { find } from '../utils/find'
9
10function parseOpts(opts?: InsertOptions): InsertOptions {
11 try {
12 return Object.assign({}, {loading: true}, opts)
13 } catch(e) {
14 return {
15 loading: true
16 }
17 }
18}
19
20export default class CoreTree implements Core {
21 private state: State
22 private tree: TreeComponent
23
24 constructor(tree: TreeComponent, state: State) {
25 this.state = state
26 this.tree = tree
27 }
28
29 flatMap = (collection: TreeNode[], ignoreCollapsed?: boolean): FlatMap => {
30 return flatMap(collection, ignoreCollapsed)
31 }
32
33 find<T>(target: TreeNode[], multiple: boolean, criterias: any): T | null {
34 return find(target, walkBreadth, multiple, criterias)
35 }
36
37 set = (node: TreeNode, key: string, value: any): void => {
38 // TODO: selected, checked should be dublicated in the tree state
39 this.state.set(node.id, key, value)
40 this.tree.updateState()
41 }
42
43 clearKeys = (node: TreeNode, includeSelf: boolean = false): void => {
44 const selected: string[] = this.tree.selected
45 const checked: string[] = this.tree.checked
46 const indeterminate: string[] = this.tree.indeterminate
47
48 recurseDown(node, (child: TreeNode) => {
49 if (child.selected) {
50 remove(selected, child.id)
51 }
52
53 if (child.checked) {
54 remove(checked, child.id)
55 }
56
57 remove(indeterminate, child.id)
58 }, includeSelf)
59 }
60
61 load = (node: TreeNode, resource: Resource, showLoading?: boolean): PromiseNodes => {
62 const result = callFetcher(node, resource)
63
64 if (showLoading) {
65 this.set(node, 'loading', true)
66 }
67
68 return result.then((items: any) => {
69 if (showLoading) {
70 this.state.set(node.id, 'loading', false)
71 }
72
73 return parseNode(items)
74 })
75 }
76
77 insertAt = (node: TreeNode, resource: Resource, insertIndex: number): PromiseNodes | TreeNode[] => {
78 const parent: TreeNode | null = node.parent || null
79 const insert = (nodes: TreeNode[]) => {
80 this.state.insertAt(
81 parent ? parent : null,
82 nodes,
83 insertIndex
84 )
85 this.tree.updateState()
86 return nodes
87 }
88
89 if (isCallable(resource)) {
90 return this.load(node, resource as PromiseCallback, false).then(insert)
91 } else {
92 return insert(parseNode(resource))
93 }
94 }
95
96 addChild = (node: TreeNode, resource: Resource, insertIndex: number | undefined, opts?: InsertOptions): PromiseNodes | TreeNode => {
97 const id: string = node.id
98 const {expand, loading} = parseOpts(opts)
99
100 if (isCallable(resource)) {
101 return this.load(node, resource, loading).then((nodes: TreeNode[]) => {
102 this.tree.addChild(id, nodes, insertIndex)
103
104 const updatedNode: TreeNode | null = this.state.byId(id)
105
106 if (expand && updatedNode && !updatedNode.expanded) {
107 this.tree.expand(updatedNode)
108 }
109
110 this.tree.updateState()
111
112 return node
113 })
114 } else {
115 this.tree.addChild(id, resource)
116
117 if (expand && !node.expanded) {
118 this.tree.expand(node)
119 }
120
121 this.tree.updateState()
122 }
123
124 return node
125 }
126
127 remove = (node: TreeNode): TreeNode | null => {
128 const removedNode: TreeNode | null = this.state.remove(node.id)
129
130 if (removedNode) {
131 removedNode.parent = null
132
133 this.clearKeys(removedNode)
134 this.tree.updateState()
135 this.tree.$emit('onRemove', removedNode)
136 }
137
138 return removedNode
139 }
140
141 data = (node: TreeNode, key: any, value?: any): any => {
142 if (!key && !value) {
143 return node.data
144 }
145
146 if (undefined === value && isString(key)) {
147 return node.data[key]
148 }
149
150 let data
151
152 if (!isString(key)) {
153 data = key
154 } else {
155 node.data[key] = value
156 data = node.data
157 }
158
159 this.state.set(node.id, 'data', data)
160 this.tree.updateState()
161
162 return node
163 }
164
165 hasClass = (node: TreeNode, className: string): boolean => {
166 return !!node.className && new RegExp(className).test(node.className)
167 }
168
169 removeClass(node: TreeNode, classNames: string[]): TreeNode {
170 const className: string = (node.className || "")
171 .split(' ')
172 .filter((klazz: string) => !has(classNames, klazz))
173 .join(' ')
174
175 this.set(node, 'className', className)
176
177 return node
178 }
179
180 addClass(node: TreeNode, classNames: string[]): TreeNode {
181 const className: string[] = node.className ? node.className.split(' ') : []
182
183 classNames.forEach((klazz: string) => {
184 if (!has(className, "" + klazz)) {
185 className.push(klazz)
186 }
187 })
188
189 this.set(node, 'className', className.join(' '))
190
191 return node
192 }
193
194}
\No newline at end of file