fix:快捷键操作节点状态扩展
This commit is contained in:
parent
e39e1da33f
commit
7513975328
|
@ -133,6 +133,8 @@
|
|||
background-color: rgb(255, 247, 231); }
|
||||
.u-tree li a.u-tree-node-selected .u-tree-title {
|
||||
color: rgb(245, 60, 50); }
|
||||
.u-tree li.u-tree-treenode-focused > a {
|
||||
background-color: rgb(235, 236, 240); }
|
||||
.u-tree li span.u-checkbox {
|
||||
margin: 2px 4px 0 0; }
|
||||
.u-tree li span.u-tree-switcher,
|
||||
|
|
|
@ -57,7 +57,8 @@ var Tree = function (_React$Component) {
|
|||
selectedKeys: _this.getDefaultSelectedKeys(props),
|
||||
dragNodesKeys: '',
|
||||
dragOverNodeKey: '',
|
||||
dropNodeKey: ''
|
||||
dropNodeKey: '',
|
||||
focusKey: '' //上下箭头选择树节点时,用于标识focus状态
|
||||
};
|
||||
return _this;
|
||||
}
|
||||
|
@ -486,7 +487,11 @@ var Tree = function (_React$Component) {
|
|||
var parentEle = (0, _util.closest)(e.target, ".u-tree");
|
||||
var focusEle = parentEle ? parentEle.querySelector(queryInfo) : null;
|
||||
focusEle && focusEle.focus();
|
||||
this.onSelect(nextTreeNode);
|
||||
var eventKey = nextTreeNode.props.eventKey || nextTreeNode.key;
|
||||
this.setState({
|
||||
focusKey: eventKey
|
||||
});
|
||||
// this.onSelect(nextTreeNode);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -535,7 +540,11 @@ var Tree = function (_React$Component) {
|
|||
}
|
||||
}
|
||||
preElement && preElement.focus();
|
||||
this.onSelect(prevTreeNode);
|
||||
var eventKey = prevTreeNode.props.eventKey || prevTreeNode.key;
|
||||
this.setState({
|
||||
focusKey: eventKey
|
||||
});
|
||||
// this.onSelect(prevTreeNode);
|
||||
};
|
||||
// all keyboard events callbacks run from here at first
|
||||
|
||||
|
@ -558,6 +567,7 @@ var Tree = function (_React$Component) {
|
|||
// 展开树节点
|
||||
this.onExpand(treeNode, 'right');
|
||||
} else if (e.keyCode == _tinperBeeCore.KeyCode.SPACE && props.checkable) {
|
||||
this.onSelect(treeNode);
|
||||
// 如果是多选tree则进行选中或者反选该节点
|
||||
this.onCheck(treeNode);
|
||||
} else if (e.keyCode == _tinperBeeCore.KeyCode.ENTER) {
|
||||
|
@ -777,6 +787,7 @@ var Tree = function (_React$Component) {
|
|||
_dropTrigger: this._dropTrigger,
|
||||
expanded: state.expandedKeys.indexOf(key) !== -1,
|
||||
selected: state.selectedKeys.indexOf(key) !== -1,
|
||||
focused: state.focusKey === key,
|
||||
openTransitionName: this.getOpenTransitionName(),
|
||||
openAnimation: props.openAnimation,
|
||||
filterTreeNode: this.filterTreeNode.bind(this),
|
||||
|
|
|
@ -532,10 +532,11 @@ var TreeNode = function (_React$Component) {
|
|||
return _react2["default"].createElement('span', { className: (0, _classnames2["default"])(cls) });
|
||||
};
|
||||
var selectedCls = props.selected ? prefixCls + '-treenode-selected' : '';
|
||||
var focusedCls = props.focused ? prefixCls + '-treenode-focused' : '';
|
||||
return _react2["default"].createElement(
|
||||
'li',
|
||||
_extends({}, liProps, { style: props.style,
|
||||
className: (0, _classnames2["default"])(props.className, disabledCls, dragOverCls, filterCls, selectedCls)
|
||||
className: (0, _classnames2["default"])(props.className, disabledCls, dragOverCls, filterCls, selectedCls, focusedCls)
|
||||
}),
|
||||
canRenderSwitcher ? this.renderSwitcher(props, expandedState) : noopSwitcher(),
|
||||
props.checkable ? this.renderCheckbox(props) : null,
|
||||
|
|
168
demo/index.js
168
demo/index.js
File diff suppressed because one or more lines are too long
|
@ -121,6 +121,8 @@
|
|||
background-color: rgb(255, 247, 231); }
|
||||
.u-tree li a.u-tree-node-selected .u-tree-title {
|
||||
color: rgb(245, 60, 50); }
|
||||
.u-tree li.u-tree-treenode-focused > a {
|
||||
background-color: rgb(235, 236, 240); }
|
||||
.u-tree li span.u-checkbox {
|
||||
margin: 2px 4px 0 0; }
|
||||
.u-tree li span.u-tree-switcher,
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -76,7 +76,7 @@ import 'bee-tree/build/Tree.css';
|
|||
|
||||
| 快捷键 | 类型 |快捷键说明 |
|
||||
| --- | :---: | --- |
|
||||
| focusable | function | 是否开启快捷键 |
|
||||
| focusable | bool | 是否开启快捷键 |
|
||||
| tab | - | tab 进入焦点,且选中第一行。|
|
||||
| ↑、↓ | - | ↑(上箭)、↓(下箭) 选中上一行、选中下一行。 |
|
||||
| ←、→ | - | ←(左箭)、→(右箭) 收起、展开。 |
|
||||
|
|
15
src/Tree.js
15
src/Tree.js
|
@ -34,6 +34,7 @@ class Tree extends React.Component {
|
|||
dragNodesKeys: '',
|
||||
dragOverNodeKey: '',
|
||||
dropNodeKey: '',
|
||||
focusKey: '', //上下箭头选择树节点时,用于标识focus状态
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -461,7 +462,11 @@ onExpand(treeNode,keyType) {
|
|||
const parentEle = closest(e.target,".u-tree")
|
||||
const focusEle = parentEle?parentEle.querySelector(queryInfo):null;
|
||||
focusEle && focusEle.focus()
|
||||
this.onSelect(nextTreeNode);
|
||||
const eventKey = nextTreeNode.props.eventKey || nextTreeNode.key;
|
||||
this.setState({
|
||||
focusKey: eventKey
|
||||
})
|
||||
// this.onSelect(nextTreeNode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -511,7 +516,11 @@ onExpand(treeNode,keyType) {
|
|||
|
||||
}
|
||||
preElement && preElement.focus();
|
||||
this.onSelect(prevTreeNode);
|
||||
const eventKey = prevTreeNode.props.eventKey || prevTreeNode.key;
|
||||
this.setState({
|
||||
focusKey: eventKey
|
||||
})
|
||||
// this.onSelect(prevTreeNode);
|
||||
}
|
||||
// all keyboard events callbacks run from here at first
|
||||
onKeyDown(e,treeNode) {
|
||||
|
@ -532,6 +541,7 @@ onExpand(treeNode,keyType) {
|
|||
// 展开树节点
|
||||
this.onExpand(treeNode,'right');
|
||||
}else if (e.keyCode == KeyCode.SPACE && props.checkable){
|
||||
this.onSelect(treeNode);
|
||||
// 如果是多选tree则进行选中或者反选该节点
|
||||
this.onCheck(treeNode);
|
||||
}else if(e.keyCode == KeyCode.ENTER){
|
||||
|
@ -752,6 +762,7 @@ onExpand(treeNode,keyType) {
|
|||
_dropTrigger: this._dropTrigger,
|
||||
expanded: state.expandedKeys.indexOf(key) !== -1,
|
||||
selected: state.selectedKeys.indexOf(key) !== -1,
|
||||
focused: state.focusKey === key,
|
||||
openTransitionName: this.getOpenTransitionName(),
|
||||
openAnimation: props.openAnimation,
|
||||
filterTreeNode: this.filterTreeNode.bind(this),
|
||||
|
|
|
@ -176,6 +176,9 @@ $treePrefixCls : "u-tree";
|
|||
color: $brand-primary;
|
||||
}
|
||||
}
|
||||
li.u-tree-treenode-focused>a{
|
||||
background-color: $tree-node-bg-color;
|
||||
}
|
||||
li span.u-checkbox {
|
||||
margin: 2px 4px 0 0;
|
||||
}
|
||||
|
|
|
@ -492,9 +492,10 @@ class TreeNode extends React.Component {
|
|||
return <span className={classNames(cls)}></span>;
|
||||
};
|
||||
const selectedCls = props.selected?`${prefixCls}-treenode-selected`:'';
|
||||
const focusedCls = props.focused ? `${prefixCls}-treenode-focused` : '';
|
||||
return (
|
||||
<li {...liProps} style={props.style}
|
||||
className={classNames(props.className, disabledCls, dragOverCls, filterCls,selectedCls) }
|
||||
className={classNames(props.className, disabledCls, dragOverCls, filterCls,selectedCls,focusedCls) }
|
||||
>
|
||||
{canRenderSwitcher ? this.renderSwitcher(props, expandedState) : noopSwitcher()}
|
||||
{props.checkable ? this.renderCheckbox(props) : null}
|
||||
|
|
650
src/util.js
650
src/util.js
|
@ -1,325 +1,325 @@
|
|||
/* eslint no-loop-func: 0*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
export function browser(navigator) {
|
||||
let tem;
|
||||
const ua = navigator.userAgent;
|
||||
let M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
|
||||
if (/trident/i.test(M[1])) {
|
||||
tem = /\brv[ :]+(\d+)/g.exec(ua) || [];
|
||||
return `IE ${tem[1] || ''}`;
|
||||
}
|
||||
if (M[1] === 'Chrome') {
|
||||
tem = ua.match(/\b(OPR|Edge)\/(\d+)/);
|
||||
if (tem) return tem.slice(1).join(' ').replace('OPR', 'Opera');
|
||||
}
|
||||
M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?'];
|
||||
tem = ua.match(/version\/(\d+)/i);
|
||||
if (tem) {
|
||||
M.splice(1, 1, tem[1]);
|
||||
}
|
||||
return M.join(' ');
|
||||
}
|
||||
|
||||
// export function getOffset(el) {
|
||||
// const obj = el.getBoundingClientRect();
|
||||
// return {
|
||||
// left: obj.left + document.body.scrollLeft,
|
||||
// top: obj.top + document.body.scrollTop,
|
||||
// width: obj.width,
|
||||
// height: obj.height
|
||||
// };
|
||||
// }
|
||||
|
||||
// // iscroll offset
|
||||
// offset = function (el) {
|
||||
// var left = -el.offsetLeft,
|
||||
// top = -el.offsetTop;
|
||||
|
||||
// // jshint -W084
|
||||
// while (el = el.offsetParent) {
|
||||
// left -= el.offsetLeft;
|
||||
// top -= el.offsetTop;
|
||||
// }
|
||||
// // jshint +W084
|
||||
|
||||
// return {
|
||||
// left: left,
|
||||
// top: top
|
||||
// };
|
||||
// }
|
||||
|
||||
/* eslint-disable */
|
||||
export function getOffset(ele) {
|
||||
let doc, win, docElem, rect;
|
||||
|
||||
if (!ele.getClientRects().length) {
|
||||
return { top: 0, left: 0 };
|
||||
}
|
||||
|
||||
rect = ele.getBoundingClientRect();
|
||||
|
||||
if (rect.width || rect.height) {
|
||||
doc = ele.ownerDocument;
|
||||
win = doc.defaultView;
|
||||
docElem = doc.documentElement;
|
||||
|
||||
return {
|
||||
top: rect.top + win.pageYOffset - docElem.clientTop,
|
||||
left: rect.left + win.pageXOffset - docElem.clientLeft
|
||||
};
|
||||
}
|
||||
|
||||
return rect;
|
||||
}
|
||||
/* eslint-enable */
|
||||
|
||||
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, (item, index) => {
|
||||
const pos = `${level}-${index}`;
|
||||
if (item.props.children && item.type && item.type.isTreeNode) {
|
||||
loop(item.props.children, pos, { node: item, pos });
|
||||
}
|
||||
callback(item, index, pos, item.key || pos, getSiblingPosition(index, len, {}), _parent);
|
||||
});
|
||||
};
|
||||
loop(childs, 0, parent);
|
||||
}
|
||||
|
||||
export function isInclude(smallArray, bigArray) {
|
||||
return smallArray.every((ii, i) => {
|
||||
return ii === bigArray[i];
|
||||
});
|
||||
}
|
||||
// console.log(isInclude(['0', '1'], ['0', '10', '1']));
|
||||
|
||||
|
||||
// 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('-');
|
||||
}
|
||||
|
||||
export function handleCheckState(obj, checkedPositionArr, checkIt) {
|
||||
// console.log(stripTail('0-101-000'));
|
||||
let objKeys = Object.keys(obj);
|
||||
// let s = Date.now();
|
||||
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;
|
||||
}
|
||||
});
|
||||
// TODO: 循环 2470000 次耗时约 1400 ms。 性能瓶颈!
|
||||
// console.log(Date.now()-s, checkedPositionArr.length * objKeys.length);
|
||||
objKeys = objKeys.filter(i => i); // filter non null;
|
||||
|
||||
for (let pIndex = 0; pIndex < checkedPositionArr.length; pIndex++) {
|
||||
// 循环设置父节点的 选中 或 半选状态
|
||||
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];
|
||||
// sibling 不会等于0
|
||||
// 全不选 - 全选 - 半选
|
||||
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);
|
||||
}
|
||||
|
||||
export function getCheck(treeNodesStates) {
|
||||
const halfCheckedKeys = [];
|
||||
const checkedKeys = [];
|
||||
const checkedNodes = [];
|
||||
const checkedNodesPositions = [];
|
||||
Object.keys(treeNodesStates).forEach((item) => {
|
||||
const itemObj = treeNodesStates[item];
|
||||
if (itemObj.checked) {
|
||||
checkedKeys.push(itemObj.key);
|
||||
checkedNodes.push(itemObj.node);
|
||||
checkedNodesPositions.push({ node: itemObj.node, pos: item });
|
||||
} else if (itemObj.halfChecked) {
|
||||
halfCheckedKeys.push(itemObj.key);
|
||||
}
|
||||
});
|
||||
return {
|
||||
halfCheckedKeys, checkedKeys, checkedNodes, checkedNodesPositions, treeNodesStates,
|
||||
};
|
||||
}
|
||||
|
||||
export function getStrictlyValue(checkedKeys, halfChecked) {
|
||||
if (halfChecked) {
|
||||
return { checked: checkedKeys, halfChecked };
|
||||
}
|
||||
return checkedKeys;
|
||||
}
|
||||
|
||||
export function arraysEqual(a, b) {
|
||||
if (a === b) return true;
|
||||
if (a === null || typeof a === 'undefined' || b === null || typeof b === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
if (a.length !== b.length) return false;
|
||||
|
||||
// If you don't care about the order of the elements inside
|
||||
// the array, you should sort both arrays here.
|
||||
|
||||
for (let i = 0; i < a.length; ++i) {
|
||||
if (a[i] !== b[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
export function closest(el, selector) {
|
||||
const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector;
|
||||
|
||||
while (el) {
|
||||
if (matchesSelector.call(el, selector)) {
|
||||
return el;
|
||||
} else {
|
||||
el = el.parentElement;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function isTreeNode(node) {
|
||||
return node && node.type && node.type.isTreeNode;
|
||||
}
|
||||
|
||||
export function toArray(children) {
|
||||
const ret = [];
|
||||
React.Children.forEach(children, (c) => {
|
||||
ret.push(c);
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function getNodeChildren(children) {
|
||||
return toArray(children).filter(isTreeNode);
|
||||
}
|
||||
|
||||
let onlyTreeNodeWarned = false;
|
||||
|
||||
export function warnOnlyTreeNode() {
|
||||
if (onlyTreeNodeWarned) return;
|
||||
onlyTreeNodeWarned = true;
|
||||
console.warn('Tree only accept TreeNode as children.');
|
||||
}
|
||||
|
||||
/* eslint no-loop-func: 0*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
export function browser(navigator) {
|
||||
let tem;
|
||||
const ua = navigator.userAgent;
|
||||
let M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
|
||||
if (/trident/i.test(M[1])) {
|
||||
tem = /\brv[ :]+(\d+)/g.exec(ua) || [];
|
||||
return `IE ${tem[1] || ''}`;
|
||||
}
|
||||
if (M[1] === 'Chrome') {
|
||||
tem = ua.match(/\b(OPR|Edge)\/(\d+)/);
|
||||
if (tem) return tem.slice(1).join(' ').replace('OPR', 'Opera');
|
||||
}
|
||||
M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?'];
|
||||
tem = ua.match(/version\/(\d+)/i);
|
||||
if (tem) {
|
||||
M.splice(1, 1, tem[1]);
|
||||
}
|
||||
return M.join(' ');
|
||||
}
|
||||
|
||||
// export function getOffset(el) {
|
||||
// const obj = el.getBoundingClientRect();
|
||||
// return {
|
||||
// left: obj.left + document.body.scrollLeft,
|
||||
// top: obj.top + document.body.scrollTop,
|
||||
// width: obj.width,
|
||||
// height: obj.height
|
||||
// };
|
||||
// }
|
||||
|
||||
// // iscroll offset
|
||||
// offset = function (el) {
|
||||
// var left = -el.offsetLeft,
|
||||
// top = -el.offsetTop;
|
||||
|
||||
// // jshint -W084
|
||||
// while (el = el.offsetParent) {
|
||||
// left -= el.offsetLeft;
|
||||
// top -= el.offsetTop;
|
||||
// }
|
||||
// // jshint +W084
|
||||
|
||||
// return {
|
||||
// left: left,
|
||||
// top: top
|
||||
// };
|
||||
// }
|
||||
|
||||
/* eslint-disable */
|
||||
export function getOffset(ele) {
|
||||
let doc, win, docElem, rect;
|
||||
|
||||
if (!ele.getClientRects().length) {
|
||||
return { top: 0, left: 0 };
|
||||
}
|
||||
|
||||
rect = ele.getBoundingClientRect();
|
||||
|
||||
if (rect.width || rect.height) {
|
||||
doc = ele.ownerDocument;
|
||||
win = doc.defaultView;
|
||||
docElem = doc.documentElement;
|
||||
|
||||
return {
|
||||
top: rect.top + win.pageYOffset - docElem.clientTop,
|
||||
left: rect.left + win.pageXOffset - docElem.clientLeft
|
||||
};
|
||||
}
|
||||
|
||||
return rect;
|
||||
}
|
||||
/* eslint-enable */
|
||||
|
||||
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, (item, index) => {
|
||||
const pos = `${level}-${index}`;
|
||||
if (item.props.children && item.type && item.type.isTreeNode) {
|
||||
loop(item.props.children, pos, { node: item, pos });
|
||||
}
|
||||
callback(item, index, pos, item.key || pos, getSiblingPosition(index, len, {}), _parent);
|
||||
});
|
||||
};
|
||||
loop(childs, 0, parent);
|
||||
}
|
||||
|
||||
export function isInclude(smallArray, bigArray) {
|
||||
return smallArray.every((ii, i) => {
|
||||
return ii === bigArray[i];
|
||||
});
|
||||
}
|
||||
// console.log(isInclude(['0', '1'], ['0', '10', '1']));
|
||||
|
||||
|
||||
// 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('-');
|
||||
}
|
||||
|
||||
export function handleCheckState(obj, checkedPositionArr, checkIt) {
|
||||
// console.log(stripTail('0-101-000'));
|
||||
let objKeys = Object.keys(obj);
|
||||
// let s = Date.now();
|
||||
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;
|
||||
}
|
||||
});
|
||||
// TODO: 循环 2470000 次耗时约 1400 ms。 性能瓶颈!
|
||||
// console.log(Date.now()-s, checkedPositionArr.length * objKeys.length);
|
||||
objKeys = objKeys.filter(i => i); // filter non null;
|
||||
|
||||
for (let pIndex = 0; pIndex < checkedPositionArr.length; pIndex++) {
|
||||
// 循环设置父节点的 选中 或 半选状态
|
||||
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];
|
||||
// sibling 不会等于0
|
||||
// 全不选 - 全选 - 半选
|
||||
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);
|
||||
}
|
||||
|
||||
export function getCheck(treeNodesStates) {
|
||||
const halfCheckedKeys = [];
|
||||
const checkedKeys = [];
|
||||
const checkedNodes = [];
|
||||
const checkedNodesPositions = [];
|
||||
Object.keys(treeNodesStates).forEach((item) => {
|
||||
const itemObj = treeNodesStates[item];
|
||||
if (itemObj.checked) {
|
||||
checkedKeys.push(itemObj.key);
|
||||
checkedNodes.push(itemObj.node);
|
||||
checkedNodesPositions.push({ node: itemObj.node, pos: item });
|
||||
} else if (itemObj.halfChecked) {
|
||||
halfCheckedKeys.push(itemObj.key);
|
||||
}
|
||||
});
|
||||
return {
|
||||
halfCheckedKeys, checkedKeys, checkedNodes, checkedNodesPositions, treeNodesStates,
|
||||
};
|
||||
}
|
||||
|
||||
export function getStrictlyValue(checkedKeys, halfChecked) {
|
||||
if (halfChecked) {
|
||||
return { checked: checkedKeys, halfChecked };
|
||||
}
|
||||
return checkedKeys;
|
||||
}
|
||||
|
||||
export function arraysEqual(a, b) {
|
||||
if (a === b) return true;
|
||||
if (a === null || typeof a === 'undefined' || b === null || typeof b === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
if (a.length !== b.length) return false;
|
||||
|
||||
// If you don't care about the order of the elements inside
|
||||
// the array, you should sort both arrays here.
|
||||
|
||||
for (let i = 0; i < a.length; ++i) {
|
||||
if (a[i] !== b[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
export function closest(el, selector) {
|
||||
const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector;
|
||||
|
||||
while (el) {
|
||||
if (matchesSelector.call(el, selector)) {
|
||||
return el;
|
||||
} else {
|
||||
el = el.parentElement;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function isTreeNode(node) {
|
||||
return node && node.type && node.type.isTreeNode;
|
||||
}
|
||||
|
||||
export function toArray(children) {
|
||||
const ret = [];
|
||||
React.Children.forEach(children, (c) => {
|
||||
ret.push(c);
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function getNodeChildren(children) {
|
||||
return toArray(children).filter(isTreeNode);
|
||||
}
|
||||
|
||||
let onlyTreeNodeWarned = false;
|
||||
|
||||
export function warnOnlyTreeNode() {
|
||||
if (onlyTreeNodeWarned) return;
|
||||
onlyTreeNodeWarned = true;
|
||||
console.warn('Tree only accept TreeNode as children.');
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue