1 | import { IEyzyNodeAPI, APIOpts, APIResult, APIBooleanResult } from '../types/Api'
|
2 | import { TreeComponent, TreeProps, TreeAPI } from '../types/Tree'
|
3 | import { TreeNode } from '../types/Node'
|
4 | import { State } from '../types/State'
|
5 | import { InsertOptions } from '../types/Core'
|
6 |
|
7 | import { hasChild, isArray, toArray } from './utils'
|
8 |
|
9 | export 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 |