UNPKG

v-tree-select

Version:
544 lines (508 loc) 17.3 kB
/* eslint-disable */ // import React from 'react' export function getValuePropValue(child) { const props = child.props if ('value' in props) { return props.value } if (child.key) { return child.key } throw new Error(`no key or value for ${child}`) } export function getPropValue(child, prop) { if (prop === 'value') { return getValuePropValue(child) } return child.props[prop] } export function isCombobox(props) { return props.combobox } export function isMultipleOrTags(props) { return !!(props.multiple || props.tags || props.treeCheckable) } export function isMultipleOrTagsOrCombobox(props) { return isMultipleOrTags(props) || isCombobox(props) } export function isSingleMode(props) { return !isMultipleOrTagsOrCombobox(props) } export function toArray(value) { let ret = value if (value === undefined) { ret = [] } else if (!Array.isArray(value)) { ret = [value] } return ret } export function preventDefaultEvent(e) { e.preventDefault() } export const UNSELECTABLE_STYLE = { userSelect: 'none', WebkitUserSelect: 'none' } export const UNSELECTABLE_ATTRIBUTE = { unselectable: 'unselectable' } export function labelCompatible(prop) { let newProp = prop if (newProp === 'label') { newProp = 'title' } return newProp } export function isInclude(smallArray, bigArray) { // attention: [0,0,1] [0,0,10] return smallArray.every((ii, i) => { return ii === bigArray[i] }) } /* export function getCheckedKeys(node, checkedKeys, allCheckedNodesKeys) { const nodeKey = node.props.eventKey; let newCks = [...checkedKeys]; let nodePos; const unCheck = allCheckedNodesKeys.some(item => { if (item.key === nodeKey) { nodePos = item.pos; return true; } }); if (unCheck) { const nArr = nodePos.split('-'); newCks = []; allCheckedNodesKeys.forEach(item => { const iArr = item.pos.split('-'); if (item.pos === nodePos || nArr.length > iArr.length && isInclude(iArr, nArr) || nArr.length < iArr.length && isInclude(nArr, iArr)) { return; } newCks.push(item.key); }); } else { newCks.push(nodeKey); } return newCks; } */ function getChildrenlength(children) { let len = 1 if (Array.isArray(children)) { len = children.length } return len } function getSiblingPosition(index, len, siblingPosition) { if (len === 1) { siblingPosition.first = true siblingPosition.last = true } else { siblingPosition.first = index === 0 siblingPosition.last = index === len - 1 } return siblingPosition } // export function loopAllChildren (childs, callback, parent) { // const loop = (children, level, _parent) => { // const len = getChildrenlength(children) // React.Children.forEach(children, function handler(item, index) { // eslint-disable-line // const pos = `${level}-${index}` // if (item && item.props.children && item.type) { // loop(item.props.children, pos, { node: item, pos }) // } // if (item) { // callback(item, index, pos, item.key || pos, getSiblingPosition(index, len, {}), _parent) // } // }) // } // loop(childs, 0, parent) // } export function loopAllChildren(data, callback) { const loop = (children, level) => { children.forEach((item, index) => { const pos = `${level}-${index}` if (item && item.children) { loop(item.children, pos) } if (item) { callback(item, index, pos) } }) } loop(data, 0) } // export function loopAllChildren(childs, callback) { // const loop = (children, level) => { // React.Children.forEach(children, (item, index) => { // const pos = `${level}-${index}`; // if (item && item.props.children) { // loop(item.props.children, pos); // } // if (item) { // callback(item, index, pos, getValuePropValue(item)); // } // }); // }; // loop(childs, 0); // } export function flatToHierarchy(arr) { if (!arr.length) { return arr } const hierarchyNodes = [] const levelObj = {} arr.forEach((item) => { if (!item.pos) { return } const posLen = item.pos.split('-').length if (!levelObj[posLen]) { levelObj[posLen] = [] } levelObj[posLen].push(item) }) const levelArr = Object.keys(levelObj).sort((a, b) => b - a) // const s = Date.now(); // todo: there are performance issues! levelArr.reduce((pre, cur) => { if (cur && cur !== pre) { levelObj[pre].forEach((item) => { let haveParent = false levelObj[cur].forEach((ii) => { if (isInclude(ii.pos.split('-'), item.pos.split('-'))) { haveParent = true if (!ii.children) { ii.children = [] } ii.children.push(item) } }) if (!haveParent) { hierarchyNodes.push(item) } }) } return cur }) // console.log(Date.now() - s); return levelObj[levelArr[levelArr.length - 1]].concat(hierarchyNodes) } // arr.length === 628, use time: ~20ms export function filterParentPosition(arr) { const levelObj = {} arr.forEach((item) => { const posLen = item.split('-').length if (!levelObj[posLen]) { levelObj[posLen] = [] } levelObj[posLen].push(item) }) const levelArr = Object.keys(levelObj).sort() for (let i = 0; i < levelArr.length; i++) { if (levelArr[i + 1]) { levelObj[levelArr[i]].forEach(ii => { for (let j = i + 1; j < levelArr.length; j++) { levelObj[levelArr[j]].forEach((_i, index) => { if (isInclude(ii.split('-'), _i.split('-'))) { levelObj[levelArr[j]][index] = null } }) levelObj[levelArr[j]] = levelObj[levelArr[j]].filter(p => p) } }) } } let nArr = [] levelArr.forEach(i => { nArr = nArr.concat(levelObj[i]) }) return nArr } // console.log(filterParentPosition( // ['0-2', '0-3-3', '0-10', '0-10-0', '0-0-1', '0-0', '0-1-1', '0-1'] // )); function stripTail(str) { const arr = str.match(/(.+)(-[^-]+)$/) let st = '' if (arr && arr.length === 3) { st = arr[1] } return st } function splitPosition(pos) { return pos.split('-') } // todo: do optimization. export function handleCheckState(obj, checkedPositionArr, checkIt) { // console.log(stripTail('0-101-000')); // let s = Date.now(); let objKeys = Object.keys(obj) objKeys.forEach((i, index) => { const iArr = splitPosition(i) let saved = false checkedPositionArr.forEach((_pos) => { const _posArr = splitPosition(_pos) if (iArr.length > _posArr.length && isInclude(_posArr, iArr)) { obj[i].halfChecked = false obj[i].checked = checkIt objKeys[index] = null } if (iArr[0] === _posArr[0] && iArr[1] === _posArr[1]) { saved = true } }) if (!saved) { objKeys[index] = null } }) objKeys = objKeys.filter(i => i) // filter non null; for (let pIndex = 0; pIndex < checkedPositionArr.length; pIndex++) { // loop to set ancestral nodes's `checked` or `halfChecked` const loop = (__pos) => { const _posLen = splitPosition(__pos).length if (_posLen <= 2) { // e.g. '0-0', '0-1' return } let sibling = 0 let siblingChecked = 0 const parentPosition = stripTail(__pos) objKeys.forEach((i /* , index */) => { const iArr = splitPosition(i) if (iArr.length === _posLen && isInclude(splitPosition(parentPosition), iArr)) { sibling++ if (obj[i].checked) { siblingChecked++ const _i = checkedPositionArr.indexOf(i) if (_i > -1) { checkedPositionArr.splice(_i, 1) if (_i <= pIndex) { pIndex-- } } } else if (obj[i].halfChecked) { siblingChecked += 0.5 } // objKeys[index] = null; } }) // objKeys = objKeys.filter(i => i); // filter non null; const parent = obj[parentPosition] // not check, checked, halfChecked if (siblingChecked === 0) { parent.checked = false parent.halfChecked = false } else if (siblingChecked === sibling) { parent.checked = true parent.halfChecked = false } else { parent.halfChecked = true parent.checked = false } loop(parentPosition) } loop(checkedPositionArr[pIndex], pIndex) } // console.log(Date.now()-s, objKeys.length, checkIt); } function getCheck(treeNodesStates, checkedPositions) { const halfCheckedKeys = [] const checkedKeys = [] const checkedNodes = [] Object.keys(treeNodesStates).forEach((item) => { const itemObj = treeNodesStates[item] if (itemObj.checked) { checkedKeys.push(itemObj.key) // checkedNodes.push(getValuePropValue(itemObj.node)); checkedNodes.push({ ...itemObj, pos: item }) } else if (itemObj.halfChecked) { halfCheckedKeys.push(itemObj.key) } }) return { halfCheckedKeys, checkedKeys, checkedNodes, treeNodesStates, checkedPositions } } export function getTreeNodesStates(children, values) { const checkedPositions = [] const treeNodesStates = {} loopAllChildren(children, (item, index, pos, keyOrPos, siblingPosition) => { treeNodesStates[pos] = { node: item, key: keyOrPos, checked: false, halfChecked: false, siblingPosition } if (values.indexOf(getValuePropValue(item)) !== -1) { treeNodesStates[pos].checked = true checkedPositions.push(pos) } }) handleCheckState(treeNodesStates, filterParentPosition(checkedPositions.sort()), true) return getCheck(treeNodesStates, checkedPositions) } // can add extra prop to every node. export function recursiveCloneChildren(children, cb = ch => ch) { // return React.Children.map(children, child => { return Array.from(children).map(child => { const newChild = cb(child) if (newChild && newChild.props && newChild.props.children) { return React.cloneElement(newChild, {}, recursiveCloneChildren(newChild.props.children, cb)) } return newChild }) } // const newChildren = recursiveCloneChildren(children, child => { // const extraProps = {}; // if (child && child.type && child.type.xxx) { // extraProps._prop = true; // return React.cloneElement(child, extraProps); // } // return child; // }); function recursiveGen(children, level = 0) { return React.Children.map(children, (child, index) => { const pos = `${level}-${index}` const o = { title: child.props.title, label: child.props.label || child.props.title, value: child.props.value, key: child.key, _pos: pos } if (child.props.children) { o.children = recursiveGen(child.props.children, pos) } return o }) } function recursive(children, cb) { children.forEach(item => { cb(item) if (item.children) { recursive(item.children, cb) } }) } // Get the tree's checkedNodes (todo: can merge to the `handleCheckState` function) // If one node checked, it's all children nodes checked. // If sibling nodes all checked, the parent checked. export function filterAllCheckedData(vs, treeNodes) { const vals = [...vs] if (!vals.length) { return vals } const data = recursiveGen(treeNodes) const checkedNodesPositions = [] function checkChildren(children) { children.forEach(item => { if (item.__checked) { return } const ci = vals.indexOf(item.value) const childs = item.children if (ci > -1) { item.__checked = true checkedNodesPositions.push({ node: item, pos: item._pos }) vals.splice(ci, 1) if (childs) { recursive(childs, child => { child.__checked = true checkedNodesPositions.push({ node: child, pos: child._pos }) }) } } else { if (childs) { checkChildren(childs) } } }) } function checkParent(children, parent = { root: true }) { let siblingChecked = 0 children.forEach(item => { const childs = item.children if (childs && !item.__checked && !item.__halfChecked) { const p = checkParent(childs, item) if (p.__checked) { siblingChecked++ } else if (p.__halfChecked) { siblingChecked += 0.5 } } else if (item.__checked) { siblingChecked++ } else if (item.__halfChecked) { siblingChecked += 0.5 } }) const len = children.length if (siblingChecked === len) { parent.__checked = true checkedNodesPositions.push({ node: parent, pos: parent._pos }) } else if (siblingChecked < len && siblingChecked > 0) { parent.__halfChecked = true } if (parent.root) { return children } return parent } checkChildren(data) checkParent(data) checkedNodesPositions.forEach((i, index) => { // clear private metadata delete checkedNodesPositions[index].node.__checked delete checkedNodesPositions[index].node._pos // create the same structure of `onCheck`'s return. checkedNodesPositions[index].node.props = { title: checkedNodesPositions[index].node.title, label: checkedNodesPositions[index].node.label || checkedNodesPositions[index].node.title, value: checkedNodesPositions[index].node.value } if (checkedNodesPositions[index].node.children) { checkedNodesPositions[index].node.props.children = checkedNodesPositions[index].node.children } delete checkedNodesPositions[index].node.title delete checkedNodesPositions[index].node.label delete checkedNodesPositions[index].node.value delete checkedNodesPositions[index].node.children }) return checkedNodesPositions } export function processSimpleTreeData(treeData, format) { function unflatten2(array, parent = { [format.id]: format.rootPId }) { const children = [] for (let i = 0; i < array.length; i++) { array[i] = { ...array[i] } // copy, can not corrupts original data if (array[i][format.pId] === parent[format.id]) { array[i].key = array[i][format.id] children.push(array[i]) array.splice(i--, 1) } } if (children.length) { parent.children = children children.forEach(child => unflatten2(array, child)) } if (parent[format.id] === format.rootPId) { return children } } return unflatten2(treeData) } export function deepCopyArray(array) { let ret = [] array.forEach(item => { let obj = {} obj = Object.assign({}, item) if (item.children) { obj.children = deepCopyArray(item.children) } ret.push(obj) }) return ret }