eyzy-tree
Version:
React tree component
212 lines (162 loc) • 4.54 kB
text/typescript
import { TreeNode } from '../types/Node'
import { IterableValue, State as StateType } from '../types/State'
import { recurseDown, rootElement } from '../utils/traveler'
import {
isRoot,
copyObject,
copyArray,
hasOwnProp,
copy
} from '../utils/index'
function iterable(key: any, value: any): IterableValue[] {
if ('string' === typeof key) {
return [[key, value]]
}
if (!value) {
const res: IterableValue[] = []
for (const i in key) {
if (hasOwnProp.call(key, i)) {
res.push([i, key[i]])
}
}
return res
}
return []
}
function replace(node: TreeNode): TreeNode {
if (!node || !node.child) {
return node
}
node.child = copyArray(node.child)
node.child.forEach((child: TreeNode) => {
const replaced = replace(child)
replaced.parent = node
return replaced
})
return node
}
function itemById(id: string, targetState: any): any {
return Object.keys(targetState)
.find((k: string) => targetState[k].id === id) || null
}
function updateChild(parentNode: TreeNode) {
parentNode.child.forEach((child: TreeNode) => {
child.parent = parentNode
})
return parentNode
}
export default class State implements StateType {
nodes: TreeNode[]
constructor(nodes: TreeNode[]) {
this.nodes = nodes
}
updateRoot(node: TreeNode, iterableValue?: IterableValue[]): TreeNode | void {
const i = itemById(node.id, this.nodes)
const newObj = copyObject(node)
if (iterableValue) {
iterableValue.forEach(([key, value]: IterableValue) => {
node[key] = value
newObj[key] = value
})
}
if (null !== i) {
this.nodes[i] = updateChild(newObj as TreeNode)
return this.nodes[i]
}
}
updateLeaf(node: TreeNode, iterableValue?: IterableValue[]): TreeNode | void {
const root: TreeNode | null = rootElement(node)
const parentNode: TreeNode | null = node.parent
if (!parentNode || !root || !parentNode.child) {
return
}
const index = this.getIndex(node)
if (null === index) {
return
}
if (iterableValue) {
iterableValue.forEach(([key, value]: IterableValue) => {
node[key] = value
parentNode.child[index][key] = value
})
}
this.updateRoot(replace(root))
return node
}
set(id: string, key: any, value?: any): TreeNode | void {
const node = this.byId(id)
if (!node) {
return
}
if (isRoot(node)) {
return this.updateRoot(node, iterable(key, value))
}
return this.updateLeaf(node, iterable(key, value))
}
getIndex(node: TreeNode): number | null {
const parent: TreeNode | null = node.parent
const nodeId: string = node.id
if (parent && parent.child) {
let childIndex: number | null = null
parent.child.some((node: TreeNode, i: number): boolean => {
if (nodeId === node.id) {
childIndex = i
return true
}
return false
})
return childIndex
}
for (let i = 0; i < this.nodes.length; i++ ) {
if (this.nodes[i].id === nodeId) {
return i
}
}
return null
}
insertAt(parent: TreeNode | null, nodes: TreeNode[], index: number): TreeNode[] {
if (parent && parent.child) {
const child: TreeNode[] = copyArray(parent.child)
child.splice(index, 0, ...nodes)
return child
} else {
this.nodes.splice(index, 0, ...nodes)
}
return nodes
}
remove(id: string): TreeNode | null {
const node: TreeNode | null = this.byId(id)
if (!node) {
return null
}
const index: number | null = this.getIndex(node)
if (null === index || !~index) {
return null
}
const parent: TreeNode | null = node.parent
if (!parent) {
this.nodes.splice(index, 1)
} else {
const child = copyArray(parent.child)
child.splice(index, 1)
this.set(parent.id, 'child', child)
}
return node
}
byId(id: string): TreeNode | null {
let node: TreeNode | null = null
recurseDown(this.nodes, (obj: TreeNode): any => {
if (obj.id === id) {
node = obj
return false
}
})
return node
}
get(): TreeNode[] {
return this.nodes
}
toArray(): TreeNode[] {
return copy(this.nodes)
}
}