UNPKG

9.13 kBPlain TextView Raw
1import { IEyzyNodeAPI, APIOpts, APIResult, APIBooleanResult } from '../types/Api'
2import { TreeComponent, TreeProps, TreeAPI } from '../types/Tree'
3import { TreeNode } from '../types/Node'
4import { State } from '../types/State'
5import { InsertOptions } from '../types/Core'
6
7import { hasChild, isArray, toArray } from './utils'
8
9export default class EyzyNode implements IEyzyNodeAPI {
10 _tree: TreeComponent
11 _props: TreeProps
12 _state: State
13 _api: TreeAPI
14 _nodes: TreeNode[] | TreeNode | null
15 _opts: APIOpts
16
17 constructor(nodes: TreeNode[] | TreeNode | null, api: TreeAPI, opts: APIOpts) {
18 this._api = api
19 this._state = api.state
20 this._tree = api.tree
21 this._props = api.tree.props
22 this._nodes = nodes
23 this._opts = opts
24 }
25
26 get length(): number {
27 const nodes = this._nodes
28
29 return isArray(nodes)
30 ? (nodes as TreeNode[]).length
31 : nodes ? 1 : 0
32 }
33
34 get result(): TreeNode | TreeNode[] | null {
35 return this._nodes
36 }
37
38 private isSilence() {
39 return this._opts && this._opts.silence
40 }
41
42 private _insert(operator: (node: TreeNode, state: State) => any): PromiseLike<IEyzyNodeAPI> {
43 if (!this._nodes) {
44 return Promise.resolve(this)
45 }
46
47 return operator(this._nodes as TreeNode, this._state).then((node: TreeNode) => {
48 return new EyzyNode(
49 node,
50 this._api,
51 this._opts
52 )
53 })
54 }
55
56 private _operate<T>(updateState: boolean, operator: (node: TreeNode, state: State) => any): T | null {
57 const nodes = this._nodes
58
59 if (!nodes || isArray(nodes) && !(nodes as TreeNode[]).length) {
60 return null
61 }
62
63 const tree = this._tree
64 const state = this._state
65
66 if (this.isSilence()) {
67 tree.silence = true
68 }
69
70 let result
71
72 if (isArray(nodes)) {
73 result = (nodes as Array<TreeNode>).map((node: TreeNode) => operator(node, state))
74 } else {
75 result = operator(nodes as TreeNode, state)
76 }
77
78 const arrResult = toArray(result)
79
80 if ('boolean' === typeof arrResult[0] || undefined === arrResult[0]) {
81 result = arrResult.every(res => false !== res)
82 }
83
84 if (updateState) {
85 tree.updateState(state)
86 }
87
88 if (this.isSilence()) {
89 tree.silence = false
90 }
91
92 return result
93 }
94
95 remove(): APIResult {
96 return this._operate<APIResult>(false, (node: TreeNode): APIResult => {
97 return this._api.remove(node.id)
98 })
99 }
100
101 empty(): APIBooleanResult {
102 return this._operate<APIBooleanResult>(true, (node: TreeNode, state: State): any => {
103 if (!hasChild(node)) {
104 return false
105 }
106
107 this._api.core.clearKeys(node, true)
108
109 state.set(node.id, {
110 child: [],
111 indeterminate: false,
112 isBatch: false
113 })
114
115 if (node.parent) {
116 this._tree.refreshDefinite(node.id, !!node.checked, false)
117 }
118 })
119 }
120
121 select(extendSelection?: boolean, expandOnSelect?: boolean): APIBooleanResult {
122 if (undefined === expandOnSelect) {
123 expandOnSelect = this._props.expandOnSelect
124 }
125
126 return this._operate<APIBooleanResult>(false, (node: TreeNode): any => {
127 if (node.selected) {
128 return false
129 }
130
131 if (this._props.multiple) {
132 this._tree.select(node, false, extendSelection)
133 } else {
134 this._tree.select(node)
135 }
136
137 if (expandOnSelect && !node.expanded) {
138 this._tree.expand(node)
139 }
140 })
141 }
142
143 unselect(): APIBooleanResult {
144 return this._operate<APIBooleanResult>(false, (node: TreeNode): any => {
145 if (!node.selected) {
146 return false
147 }
148
149 this._tree.unselect(node)
150 })
151 }
152
153 check(): APIBooleanResult {
154 if (!this._props.checkable) {
155 return false
156 }
157
158 return this._operate<APIBooleanResult>(false, (node: TreeNode): any => {
159 if (node.checked) {
160 return false
161 }
162
163 this._tree.check(node)
164 })
165 }
166
167 uncheck(): APIBooleanResult {
168 if (!this._props.checkable) {
169 return false
170 }
171
172 return this._operate<APIBooleanResult>(false, (node: TreeNode, state: State): any => {
173 if (!node.checked) {
174 return false
175 }
176
177 this._tree.check(node)
178 })
179 }
180
181 disable(): APIBooleanResult {
182 return this._operate<APIBooleanResult>(true, (node: TreeNode, state: State): any => {
183 if (node.disabled) {
184 return false
185 }
186
187 state.set(node.id, 'disabled', true)
188 })
189 }
190
191 enable(): APIBooleanResult {
192 return this._operate<APIBooleanResult>(true, (node: TreeNode, state: State): any => {
193 if (!node.disabled) {
194 return false
195 }
196
197 state.set(node.id, 'disabled', false)
198 })
199 }
200
201 disableCheckbox(): APIBooleanResult {
202 if (!this._props.checkable) {
203 return false
204 }
205
206 return this._operate<APIBooleanResult>(true, (node: TreeNode, state: State): any => {
207 if (node.disabledCheckbox) {
208 return false
209 }
210
211 state.set(node.id, 'disabledCheckbox', true)
212 })
213 }
214
215 enableCheckbox(): APIBooleanResult {
216 if (!this._props.checkable) {
217 return false
218 }
219
220 return this._operate<APIBooleanResult>(true, (node: TreeNode, state: State): any => {
221 if (!node.disabledCheckbox) {
222 return false
223 }
224
225 state.set(node.id, 'disabledCheckbox', false)
226
227 if (hasChild(node) && this._props.useIndeterminateState) {
228 const iNode: TreeNode = node.checked
229 ? node
230 : node.child[0]
231
232 this._tree.refreshDefinite(iNode.id, !!iNode.checked, false)
233 }
234 })
235 }
236
237 expand(includingDisabled?: boolean): APIBooleanResult {
238 return this._operate<APIBooleanResult>(false, (node: TreeNode): any => {
239 if (!hasChild(node) || node.expanded) {
240 return false
241 }
242
243 if (node.disabled && !includingDisabled) {
244 return false
245 }
246
247 this._tree.expand(node, includingDisabled)
248 })
249 }
250
251 collapse(includingDisabled?: boolean): APIBooleanResult {
252 return this._operate<APIBooleanResult>(false, (node: TreeNode): any => {
253 if (!hasChild(node) || !node.expanded) {
254 return false
255 }
256
257 if (node.disabled && !includingDisabled) {
258 return false
259 }
260
261 this._tree.expand(node, includingDisabled)
262 })
263 }
264
265 data(key: any, value?: any): any {
266 if (!this._nodes) {
267 return null
268 }
269
270 const nodes = toArray(this._nodes)
271
272 if (1 === nodes.length) {
273 const result: any = this._api.core.data(nodes[0], key, value)
274
275 if (nodes[0] === result) {
276 return new EyzyNode(nodes[0], this._api, this._opts)
277 }
278
279 return result
280 }
281
282 return nodes.map((node: TreeNode) => this._api.core.data(node, key, value))
283 }
284
285 hasClass(className: string): APIBooleanResult {
286 if (!this._nodes) {
287 return null
288 }
289
290 return toArray(this._nodes).every((node: TreeNode) => this._api.core.hasClass(node, className))
291 }
292
293 addClass(classNames: string | string[]): APIBooleanResult {
294 if (!this._nodes) {
295 return null
296 }
297
298 toArray(this._nodes).forEach((node: TreeNode) => this._api.core.addClass(node, classNames))
299
300 return true
301 }
302
303 removeClass(classNames: string | string[]): APIBooleanResult {
304 return this._operate<APIBooleanResult>(false, (node: TreeNode) => {
305 if (!node.className) {
306 return
307 }
308
309 const oldClassNames = node.className
310 const updatedNode = this._api.core.removeClass(node, classNames)
311
312 return oldClassNames !== updatedNode.className
313 })
314 }
315
316 append(source: any, opts?: InsertOptions): PromiseLike<IEyzyNodeAPI> {
317 return this._insert((node: TreeNode) => {
318 return this._api.core.insert(node, source, opts)
319 })
320 }
321
322 prepend(source: any, opts?: InsertOptions): PromiseLike<IEyzyNodeAPI> {
323 return this._insert((node: TreeNode) => {
324 return this._api.core.insert(node, source, Object.assign({}, opts, {index: 0}))
325 })
326 }
327
328 before(source: any): PromiseLike<IEyzyNodeAPI> {
329 return this._insert((node: TreeNode) => {
330 return this._api.core.beside(node, source, 0)
331 })
332 }
333
334 after(source: any): PromiseLike<IEyzyNodeAPI> {
335 return this._insert((node: TreeNode) => {
336 return this._api.core.beside(node, source, 1)
337 })
338 }
339
340 _find<T>(query: any, multiple: boolean): T | null {
341 const nodes = toArray(this._nodes)
342 .reduce((result: TreeNode[], node: TreeNode) => {
343 if (hasChild(node)) {
344 result.push(...node.child)
345 }
346
347 return result
348 }, [])
349
350 return this._api.core.find<T>(nodes, multiple, query)
351 }
352
353 find(query: any): IEyzyNodeAPI {
354 return new EyzyNode(this._find<TreeNode>(query, false), this._api, this._opts)
355 }
356
357 findAll(query: any): IEyzyNodeAPI {
358 return new EyzyNode(this._find<TreeNode[]>(query, true), this._api, this._opts)
359 }
360}
\No newline at end of file