UNPKG

4.63 kBPlain TextView Raw
1import { TreeNode } from '../types/Node'
2import { IterableValue, State as StateType } from '../types/State'
3
4import { recurseDown, rootElement } from '../utils/traveler'
5import {
6 isRoot,
7 copyObject,
8 copyArray,
9 hasOwnProp
10} from '../utils/index'
11
12function iterable(key: any, value: any): IterableValue[] {
13 if ('string' === typeof key) {
14 return [[key, value]]
15 }
16
17 if (!value) {
18 const res: IterableValue[] = []
19
20 for (const i in key) {
21 if (hasOwnProp.call(key, i)) {
22 res.push([i, key[i]])
23 }
24 }
25
26 return res
27 }
28
29 return []
30}
31
32function replace(node: TreeNode): TreeNode {
33 if (!node || !node.child) {
34 return node
35 }
36
37 node.child = copyArray(node.child)
38 node.child.forEach((child: TreeNode) => {
39 const replaced = replace(child)
40 replaced.parent = node
41
42 return replaced
43 })
44
45 return node
46}
47
48function itemById(id: string, targetState: any): any {
49 return Object.keys(targetState)
50 .find((k: string) => targetState[k].id === id) || null
51}
52
53function updateChild(parentNode: TreeNode) {
54 parentNode.child.forEach((child: TreeNode) => {
55 child.parent = parentNode
56 })
57
58 return parentNode
59}
60
61export default class State {
62 nodes: TreeNode[]
63
64 constructor(state: TreeNode[]) {
65 this.nodes = state
66 }
67
68 updateRoot(node: TreeNode, iterableValue?: IterableValue[]) {
69 const i = itemById(node.id, this.nodes)
70 const newObj = copyObject(node)
71
72 if (iterableValue) {
73 iterableValue.forEach(([key, value]: IterableValue) => {
74 node[key] = value
75 newObj[key] = value
76 })
77 }
78
79 if (null !== i) {
80 this.nodes[i] = updateChild(newObj as TreeNode)
81 }
82 }
83
84 updateLeaf(node: TreeNode, iterableValue?: IterableValue[]) {
85 const root: TreeNode | null = rootElement(node)
86 const parentNode: TreeNode | null | undefined = node.parent
87
88 if (!parentNode || !root || !parentNode.child) {
89 return
90 }
91
92 const index = this.getIndex(node)
93
94 if (null === index) {
95 return
96 }
97
98 if (iterableValue) {
99 iterableValue.forEach(([key, value]: IterableValue) => {
100 node[key] = value
101 parentNode.child[index][key] = value
102 })
103 }
104
105 this.updateRoot(replace(root))
106 }
107
108 set(id: string, key: any, value?: any): StateType {
109 const node = this.byId(id)
110
111 if (!node) {
112 return this
113 }
114
115 if (isRoot(node)) {
116 this.updateRoot(node, iterable(key, value))
117 } else {
118 this.updateLeaf(node, iterable(key, value))
119 }
120
121 return this
122 }
123
124 getIndex(node: TreeNode): number | null {
125 const parent: TreeNode | null = node.parent
126 const nodeId: string = node.id
127
128 if (parent && parent.child) {
129 let childIndex: number | null = null
130
131 parent.child.some((node: TreeNode, i: number): boolean => {
132 if (nodeId === node.id) {
133 childIndex = i
134 return true
135 }
136
137 return false
138 })
139
140 return childIndex
141 }
142
143 for (let i = 0; i < this.nodes.length; i++ ) {
144 if (this.nodes[i].id === nodeId) {
145 return i
146 }
147 }
148
149 return null
150 }
151
152 insertAt(parent: TreeNode | null, nodes: TreeNode[], index: number): TreeNode[] | TreeNode {
153 if (parent && parent.child) {
154 const child: TreeNode[] = copyArray(parent.child)
155 child.splice(index, 0, ...nodes)
156 } else {
157 this.nodes.splice(index, 0, ...nodes)
158 }
159
160 return nodes
161 }
162
163 remove(id: string): TreeNode | null {
164 const node: TreeNode | null = this.byId(id)
165
166 if (!node) {
167 return null
168 }
169
170 const index: number | null = this.getIndex(node)
171
172 if (null === index || !~index) {
173 return null
174 }
175
176 const parent: TreeNode | null = node.parent
177
178 if (!parent) {
179 this.nodes.splice(index, 1)
180 } else {
181 const child = copyArray(parent.child)
182 child.splice(index, 1)
183 this.set(parent.id, 'child', child)
184 }
185
186 return node
187 }
188
189 byId(id: string): TreeNode | null {
190 let node: TreeNode | null = null
191
192 recurseDown(this.nodes, (obj: TreeNode): any => {
193 if (obj.id === id) {
194 node = obj
195 return false
196 }
197 })
198
199 return node
200 }
201
202 get(): TreeNode[] {
203 return this.nodes
204 }
205
206 toArray(): TreeNode[] {
207 const result: TreeNode[] = []
208 const state = this.nodes
209
210 for (const i in state) {
211 if (hasOwnProp.call(state, i)) {
212 result.push(state[i])
213 }
214 }
215
216 return result
217 }
218}
\No newline at end of file