UNPKG

element3

Version:

A Component Library for Vue3

167 lines (138 loc) 4.06 kB
import { isEqual, capitalize } from '../../src/utils/util' import { isDef } from '../../src/utils/shared' let uid = 0 export default class Node { constructor(data, config, parentNode) { this.data = data this.config = config this.parent = parentNode || null this.level = !this.parent ? 1 : this.parent.level + 1 this.uid = uid++ this.initState() this.initChildren() } initState() { const { value: valueKey, label: labelKey } = this.config this.value = this.data[valueKey] this.label = this.data[labelKey] this.pathNodes = this.calculatePathNodes() this.path = this.pathNodes.map((node) => node.value) this.pathLabels = this.pathNodes.map((node) => node.label) // lazy load this.loading = false this.loaded = false } initChildren() { const { config } = this const childrenKey = config.children const childrenData = this.data[childrenKey] this.hasChildren = Array.isArray(childrenData) this.children = (childrenData || []).map( (child) => new Node(child, config, this) ) } get isDisabled() { const { data, parent, config } = this const disabledKey = config.disabled const { checkStrictly } = config return data[disabledKey] || (!checkStrictly && parent && parent.isDisabled) } get isLeaf() { const { data, loaded, hasChildren, children } = this const { lazy, leaf: leafKey } = this.config if (lazy) { const isLeaf = isDef(data[leafKey]) ? data[leafKey] : loaded ? !children.length : false this.hasChildren = !isLeaf return isLeaf } return !hasChildren } calculatePathNodes() { const nodes = [this] let parent = this.parent while (parent) { nodes.unshift(parent) parent = parent.parent } return nodes } getPath() { return this.path } getValue() { return this.value } getValueByOption() { return this.config.emitPath ? this.getPath() : this.getValue() } getText(allLevels, separator) { return allLevels ? this.pathLabels.join(separator) : this.label } isSameNode(checkedValue) { const value = this.getValueByOption() return this.config.multiple && Array.isArray(checkedValue) ? checkedValue.some((val) => isEqual(val, value)) : isEqual(checkedValue, value) } broadcast(event, ...args) { const handlerName = `onParent${capitalize(event)}` this.children.forEach((child) => { if (child) { // bottom up child.broadcast(event, ...args) child[handlerName] && child[handlerName](...args) } }) } emit(event, ...args) { const { parent } = this const handlerName = `onChild${capitalize(event)}` if (parent) { parent[handlerName] && parent[handlerName](...args) parent.emit(event, ...args) } } onParentCheck(checked) { if (!this.isDisabled) { this.setCheckState(checked) } } onChildCheck() { const { children } = this const validChildren = children.filter((child) => !child.isDisabled) const checked = validChildren.length ? validChildren.every((child) => child.checked) : false this.setCheckState(checked) } setCheckState(checked) { const totalNum = this.children.length const checkedNum = this.children.reduce((c, p) => { const num = p.checked ? 1 : p.indeterminate ? 0.5 : 0 return c + num }, 0) this.checked = checked this.indeterminate = checkedNum !== totalNum && checkedNum > 0 } syncCheckState(checkedValue) { const value = this.getValueByOption() const checked = this.isSameNode(checkedValue, value) this.doCheck(checked) } doCheck(checked) { if (this.checked !== checked) { if (this.config.checkStrictly) { this.checked = checked } else { // bottom up to unify the calculation of the indeterminate state this.broadcast('check', checked) this.setCheckState(checked) this.emit('check') } } } }