zent
Version:
一套前端设计语言和基于React的实现
607 lines (528 loc) • 18.9 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _defineProperty2 = require('babel-runtime/helpers/defineProperty');
var _defineProperty3 = _interopRequireDefault(_defineProperty2);
var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray');
var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
var _inherits2 = require('babel-runtime/helpers/inherits');
var _inherits3 = _interopRequireDefault(_inherits2);
var _typeof2 = require('babel-runtime/helpers/typeof');
var _typeof3 = _interopRequireDefault(_typeof2);
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _checkbox = require('../checkbox');
var _checkbox2 = _interopRequireDefault(_checkbox);
var _assign = require('lodash/assign');
var _assign2 = _interopRequireDefault(_assign);
var _clone = require('lodash/clone');
var _clone2 = _interopRequireDefault(_clone);
var _find = require('lodash/find');
var _find2 = _interopRequireDefault(_find);
var _classnames2 = require('classnames');
var _classnames3 = _interopRequireDefault(_classnames2);
var _propTypes = require('prop-types');
var _propTypes2 = _interopRequireDefault(_propTypes);
var _Loading = require('./components/Loading');
var _Loading2 = _interopRequireDefault(_Loading);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
// 记录是否已经触发收起展开逻辑
// 防止出现闪烁的bug
var isTriggerSlide = false;
var deepClone = function deepClone(arr) {
var i = void 0;
var copy = void 0;
if (Array.isArray(arr)) {
copy = arr.slice(0);
for (i = 0; i < copy.length; i += 1) {
copy[i] = deepClone(copy[i]);
}
return copy;
} else if ((typeof arr === 'undefined' ? 'undefined' : (0, _typeof3['default'])(arr)) === 'object') {
return (0, _assign2['default'])({}, arr);
}
return arr;
};
var toggleSlide = function toggleSlide(el, isClose) {
if (!isClose) {
el.style.display = 'block';
el.style.height = 0;
}
var maxDelay = 300;
var height = el.scrollHeight;
var speed = Math.max(height / maxDelay, 0.5); // px/ms
var sum = 0;
var start = null;
var animate = function animate(timestamp) {
if (!start) start = timestamp;
var progress = timestamp - start;
sum = progress * speed;
el.style.height = (isClose ? height - sum : sum) + 'px';
if (height < sum) {
if (isClose) {
el.style.display = 'none';
}
el.style.height = '';
isTriggerSlide = false;
} else {
window.requestAnimationFrame(animate);
}
};
window.requestAnimationFrame(animate);
};
var _ref3 = _react2['default'].createElement(_Loading2['default'], null);
var Tree = function (_ref) {
(0, _inherits3['default'])(Tree, _ref);
function Tree() {
var _ref2;
var _temp, _this, _ret;
(0, _classCallCheck3['default'])(this, Tree);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _ret = (_temp = (_this = (0, _possibleConstructorReturn3['default'])(this, (_ref2 = Tree.__proto__ || Object.getPrototypeOf(Tree)).call.apply(_ref2, [this].concat(args))), _this), _this.isInitial = true, _this.isDataUpdate = false, _this.state = {
checkedTree: {},
loadingNode: []
}, _temp), (0, _possibleConstructorReturn3['default'])(_this, _ret);
}
(0, _createClass3['default'])(Tree, [{
key: 'componentWillMount',
value: function componentWillMount() {
// init checkedTree
var _props = this.props,
data = _props.data,
dataType = _props.dataType;
var formatData = this.formatDataIntoTree(data, dataType);
this.setState({
checkedTree: this.formatDataIntoCheckedTree(formatData)
});
}
}, {
key: 'componentWillReceiveProps',
value: function componentWillReceiveProps(nextProps) {
if (nextProps.data !== this.props.data) {
// update checkTree
this.isDataUpdate = true;
var data = this.formatDataIntoTree(nextProps.data, nextProps.dataType);
this.setState({
checkedTree: this.formatDataIntoCheckedTree(data)
});
}
}
}, {
key: 'formatDataIntoTree',
value: function formatDataIntoTree(data, dataType) {
var roots = [];
if (dataType === 'plain') {
var isRoot = this.props.isRoot;
var map = {};
var orderRecord = [];
data.forEach(function (node, index) {
if (!node.isLeaf) {
node.children = [];
}
map[node.id] = node;
orderRecord[index] = node.id;
});
orderRecord.forEach(function (key) {
var node = map[key];
var isRootNode = isRoot && isRoot(node) || node.parentId === 0 || node.parentId === undefined || node.parentId === '0';
if (isRootNode) {
roots.push(node);
} else if (map[node.parentId]) {
// 防止只删除父节点没有子节点的情况
map[node.parentId].children.push(node);
}
});
} else if (dataType === 'tree') {
roots = data;
} else {
// console.error(`The dataType should be declared as plain/tree, but your dataType is ${dataType}, Please check your config.`)
}
return roots;
}
}, {
key: 'formatDataIntoCheckedTree',
value: function formatDataIntoCheckedTree(data) {
var checkedTree = {};
if (this.isInitial) {
checkedTree = this.initialCheckedTree(data);
this.isInitial = false;
} else if (this.isDataUpdate) {
checkedTree = this.reloadCheckedTree(data);
this.isDataUpdate = false;
}
this.updateWholeCheckedTree(checkedTree);
return checkedTree;
}
}, {
key: 'isSwitcherExpanded',
value: function isSwitcherExpanded(node) {
return !node.parentNode.classList.contains('off');
}
}, {
key: 'handleExpandClick',
value: function handleExpandClick(root, e) {
var _this2 = this;
var loadMore = this.props.loadMore;
var loadingNode = this.state.loadingNode;
if (loadMore) {
if (!root.children || root.children.length === 0) {
e.persist();
this.setState({ loadingNode: [root.id].concat((0, _toConsumableArray3['default'])(loadingNode)) });
loadMore(root).then(function () {
_this2.setState({
loadingNode: loadingNode.filter(function (x) {
return x !== root.id;
})
});
_this2.handleFoldClick(root, e);
})['catch'](function () {});
return;
}
}
this.handleFoldClick(root, e);
}
}, {
key: 'handleFoldClick',
value: function handleFoldClick(root, e) {
if (!isTriggerSlide) {
var onExpand = this.props.onExpand;
var switcher = e.target;
var elp = switcher.parentNode;
elp.classList.toggle('off');
var isClose = !this.isSwitcherExpanded(switcher);
if (onExpand) {
onExpand(root, {
isExpanded: !isClose
});
}
var el = elp.nextSibling;
// no content, unmount switcher
if (!el) {
switcher.remove();
return;
}
isTriggerSlide = true;
toggleSlide(el, isClose);
}
}
}, {
key: 'triggerSwitcherClick',
value: function triggerSwitcherClick(root, e) {
var _props2 = this.props,
autoExpandOnSelect = _props2.autoExpandOnSelect,
onSelect = _props2.onSelect;
var target = e.currentTarget;
if (onSelect) {
onSelect(root, target);
}
if (target && autoExpandOnSelect) {
var switcher = target.parentNode.previousSibling;
if (switcher) {
switcher.click();
}
}
}
}, {
key: 'handleCheckboxClick',
value: function handleCheckboxClick(root) {
var onCheck = this.props.onCheck;
var checkedTree = this.state.checkedTree;
this.updateCheckedTree(root.id, checkedTree[root.id].t !== 2 ? 2 : 0);
if (onCheck) {
onCheck(Object.keys(checkedTree).filter(function (k) {
return checkedTree[k].t === 2;
}).map(function (x) {
if (typeof root.id === 'number') {
x = +x;
}
return x;
}));
}
}
}, {
key: 'updateUpstream',
value: function updateUpstream(id, type, checkedTree) {
if (!id) return;
if (type === 2) {
checkedTree[id].t = Object.keys(checkedTree).filter(function (x) {
return checkedTree[x].p === id;
}).every(function (x) {
return checkedTree[x].t === 2;
}) ? 2 : 1;
} else if (type === 1) {
checkedTree[id].t = 1;
} else if (type === 0) {
checkedTree[id].t = Object.keys(checkedTree).filter(function (x) {
return checkedTree[x].p === id;
}).every(function (x) {
return checkedTree[x].t === 0;
}) ? 0 : 1;
}
if (checkedTree[id].p) {
this.updateUpstream(checkedTree[id].p, checkedTree[id].t, checkedTree);
}
}
}, {
key: 'updateDownstream',
value: function updateDownstream(id, type, checkedTree) {
var _this3 = this;
if (!id) return;
checkedTree[id].t = type;
var childrenId = Object.keys(checkedTree).filter(function (x) {
return checkedTree[x].p === id;
});
if (childrenId.length > 0) {
childrenId.forEach(function (childId) {
_this3.updateDownstream(childId, type, checkedTree);
});
}
}
}, {
key: 'updateCheckedTree',
value: function updateCheckedTree(id, type) {
var _this4 = this;
// shallow clone
// We can reuse most of the nodes
var checkedTree = (0, _clone2['default'])(this.state.checkedTree);
var parentId = checkedTree[id].p;
var childrenId = Object.keys(checkedTree).filter(function (x) {
return checkedTree[x].p === id.toString();
});
checkedTree[id].t = type;
this.updateUpstream(parentId, type, checkedTree);
childrenId.forEach(function (childId) {
_this4.updateDownstream(childId, type, checkedTree);
});
this.setState({ checkedTree: checkedTree });
}
}, {
key: 'updateCheckedTreeRecursive',
value: function updateCheckedTreeRecursive(root, parentId, func) {
var _this5 = this;
func(root, parentId);
if (root.children && root.children.length > 0) {
root.children.forEach(function (child) {
_this5.updateCheckedTreeRecursive(child, root.id, func);
});
}
}
}, {
key: 'updateWholeCheckedTree',
value: function updateWholeCheckedTree(checkedTree) {
var _this6 = this;
Object.keys(checkedTree).forEach(function (id) {
if (checkedTree[id].t === 2) {
_this6.updateUpstream(id, 2, checkedTree);
_this6.updateDownstream(id, 2, checkedTree);
}
});
}
}, {
key: 'initialCheckedTree',
value: function initialCheckedTree(data) {
var _this7 = this;
var newCheckedTree = {};
var defaultCheckedKeys = this.props.defaultCheckedKeys;
data.forEach(function (tree) {
_this7.updateCheckedTreeRecursive(tree, '', function (root, parentId) {
var isSetDefault = defaultCheckedKeys && (0, _find2['default'])(defaultCheckedKeys, function (x) {
return x === root.id;
}) >= 0;
newCheckedTree[root.id] = {
p: parentId.toString(),
t: isSetDefault ? 2 : 0
};
});
});
return newCheckedTree;
}
}, {
key: 'reloadCheckedTree',
value: function reloadCheckedTree(data) {
var _this8 = this;
var newCheckedTree = {};
var checkedTree = this.state.checkedTree;
data.forEach(function (tree) {
_this8.updateCheckedTreeRecursive(tree, '', function (root, parentId) {
newCheckedTree[root.id] = {
p: parentId.toString(),
t: checkedTree[root.id] ? checkedTree[root.id].t : 0
};
});
});
return newCheckedTree;
}
}, {
key: 'renderSwitcher',
value: function renderSwitcher(root) {
var _props3 = this.props,
foldable = _props3.foldable,
loadMore = _props3.loadMore;
if (!root.isLeaf && (loadMore || root.children && root.children.length > 0)) {
return _react2['default'].createElement('i', {
className: 'switcher',
onClick: foldable && this.handleExpandClick.bind(this, root)
});
}
}
}, {
key: 'renderCheckbox',
value: function renderCheckbox(root) {
var _props4 = this.props,
checkable = _props4.checkable,
disabledCheckedKeys = _props4.disabledCheckedKeys;
var isDisabled = disabledCheckedKeys && (0, _find2['default'])(disabledCheckedKeys, function (key) {
return key === root.id;
}) >= 0;
if (checkable) {
return _react2['default'].createElement(_checkbox2['default'], {
onChange: this.handleCheckboxClick.bind(this, root),
checked: this.state.checkedTree[root.id].t === 2,
indeterminate: this.state.checkedTree[root.id].t === 1,
disabled: isDisabled
});
}
}
}, {
key: 'renderOperations',
value: function renderOperations(root) {
var opts = this.props.operations;
if (opts) {
var optNodes = opts.map(function (opt) {
var shouldRender = opt.shouldRender || function () {
return true;
};
return shouldRender(root) && _react2['default'].createElement(
'span',
{
key: opt.name + '-' + root.id,
onClick: opt.action.bind(null, root),
className: 'opt'
},
typeof opt.icon === 'string' ? _react2['default'].createElement('i', { className: opt.icon }) : opt.icon,
' ',
opt.name
);
});
return _react2['default'].createElement(
'div',
{ className: 'operation' },
optNodes
);
}
}
}, {
key: 'renderTreeNodes',
value: function renderTreeNodes(roots) {
var _this9 = this;
var loadingNode = this.state.loadingNode;
var _props5 = this.props,
loadMore = _props5.loadMore,
prefix = _props5.prefix,
expandAll = _props5.expandAll,
render = _props5.render;
if (roots && roots.length > 0) {
return roots.map(function (root) {
// 单独节点的expand属性具有最高优先级,如果expand没有设置会根据是否设置loadMore
// 来判断是否收起,因为需要loadMore的节点是没有内容的,需要收起。在以上情况都不发生
// 的情况下以expandAll为准
var isShowChildren = expandAll;
if (loadMore) {
isShowChildren = root.expand;
}
var barClassName = (0, _classnames3['default'])(prefix + '-tree-bar', {
off: !isShowChildren
});
return _react2['default'].createElement(
'li',
{ key: '' + root.id },
_react2['default'].createElement(
'div',
{ className: barClassName },
_this9.renderSwitcher(root),
_react2['default'].createElement(
'div',
{ className: 'zent-tree-node' },
_this9.renderCheckbox(root),
loadingNode.indexOf(root.id) > -1 ? _ref3 : null,
_react2['default'].createElement(
'span',
{
className: 'content',
onClick: _this9.triggerSwitcherClick.bind(_this9, root)
},
render ? render(root) : root.title
),
_this9.renderOperations(root)
)
),
root.children && root.children.length > 0 && _react2['default'].createElement(
'ul',
{
key: 'ul-' + root.id,
className: prefix + '-tree-child',
style: isShowChildren ? {} : { display: 'none' }
},
_this9.renderTreeNodes(root.children)
)
);
});
}
}
}, {
key: 'render',
value: function render() {
var _props6 = this.props,
commonStyle = _props6.commonStyle,
data = _props6.data,
dataType = _props6.dataType,
prefix = _props6.prefix,
size = _props6.size;
var roots = this.formatDataIntoTree(deepClone(data), dataType);
var treeNodes = this.renderTreeNodes(roots);
var classNames = (0, _classnames3['default'])(prefix + '-tree', (0, _defineProperty3['default'])({}, prefix + '-tree-' + size, size !== 'medium'));
return _react2['default'].createElement(
'ul',
{ className: classNames, style: commonStyle },
treeNodes
);
}
}]);
return Tree;
}(_react.PureComponent || _react.Component);
Tree.propTypes = {
dataType: _propTypes2['default'].oneOf(['plain', 'tree']),
data: _propTypes2['default'].arrayOf(_propTypes2['default'].object),
isRoot: _propTypes2['default'].func,
loadMore: _propTypes2['default'].func,
foldable: _propTypes2['default'].bool,
checkable: _propTypes2['default'].bool,
autoExpandOnSelect: _propTypes2['default'].bool,
defaultCheckedKeys: _propTypes2['default'].arrayOf(_propTypes2['default'].any),
disabledCheckedKeys: _propTypes2['default'].arrayOf(_propTypes2['default'].any),
onCheck: _propTypes2['default'].func,
onExpand: _propTypes2['default'].func,
onSelect: _propTypes2['default'].func,
size: _propTypes2['default'].oneOf(['large', 'medium', 'small']),
operations: _propTypes2['default'].arrayOf(_propTypes2['default'].object),
render: _propTypes2['default'].func,
prefix: _propTypes2['default'].string
};
Tree.defaultProps = {
autoExpandOnSelect: true,
dataType: 'tree',
foldable: true,
checkable: false,
size: 'medium',
prefix: 'zent'
};
exports['default'] = Tree;