UNPKG

13.9 kBJavaScriptView Raw
1/* eslint no-loop-func: 0*/
2
3import React from 'react';
4
5export function browser(navigator) {
6 let tem;
7 const ua = navigator.userAgent;
8 let M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
9 if (/trident/i.test(M[1])) {
10 tem = /\brv[ :]+(\d+)/g.exec(ua) || [];
11 return `IE ${tem[1] || ''}`;
12 }
13 if (M[1] === 'Chrome') {
14 tem = ua.match(/\b(OPR|Edge)\/(\d+)/);
15 if (tem) return tem.slice(1).join(' ').replace('OPR', 'Opera');
16 }
17 M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?'];
18 tem = ua.match(/version\/(\d+)/i);
19 if (tem) {
20 M.splice(1, 1, tem[1]);
21 }
22 return M.join(' ');
23}
24
25// export function getOffset(el) {
26// const obj = el.getBoundingClientRect();
27// return {
28// left: obj.left + document.body.scrollLeft,
29// top: obj.top + document.body.scrollTop,
30// width: obj.width,
31// height: obj.height
32// };
33// }
34
35// // iscroll offset
36// offset = function (el) {
37// var left = -el.offsetLeft,
38// top = -el.offsetTop;
39
40// // jshint -W084
41// while (el = el.offsetParent) {
42// left -= el.offsetLeft;
43// top -= el.offsetTop;
44// }
45// // jshint +W084
46
47// return {
48// left: left,
49// top: top
50// };
51// }
52
53/* eslint-disable */
54export function getOffset(ele) {
55 let doc, win, docElem, rect;
56
57 if (!ele.getClientRects().length) {
58 return { top: 0, left: 0 };
59 }
60
61 rect = ele.getBoundingClientRect();
62
63 if (rect.width || rect.height) {
64 doc = ele.ownerDocument;
65 win = doc.defaultView;
66 docElem = doc.documentElement;
67
68 return {
69 top: rect.top + win.pageYOffset - docElem.clientTop,
70 left: rect.left + win.pageXOffset - docElem.clientLeft
71 };
72 }
73
74 return rect;
75}
76/* eslint-enable */
77
78function getChildrenlength(children) {
79 let len = 1;
80 if (Array.isArray(children)) {
81 len = children.length;
82 }
83 return len;
84}
85
86function getSiblingPosition(index, len, siblingPosition) {
87 if (len === 1) {
88 siblingPosition.first = true;
89 siblingPosition.last = true;
90 } else {
91 siblingPosition.first = index === 0;
92 siblingPosition.last = index === len - 1;
93 }
94 return siblingPosition;
95}
96
97export function loopAllChildren(childs, callback, parent) {
98 const loop = (children, level, _parent) => {
99 const len = getChildrenlength(children);
100 React.Children.forEach(children, (item, index) => {
101 const pos = `${level}-${index}`;
102 if (item.props.children && item.type && item.type.isTreeNode) {
103 loop(item.props.children, pos, { node: item, pos });
104 }
105 callback(item, index, pos, item.key || pos, getSiblingPosition(index, len, {}), _parent);
106 });
107 };
108 loop(childs, 0, parent);
109}
110
111export function isInclude(smallArray, bigArray) {
112 return smallArray.every((ii, i) => {
113 return ii === bigArray[i];
114 });
115}
116// console.log(isInclude(['0', '1'], ['0', '10', '1']));
117
118
119// arr.length === 628, use time: ~20ms
120export function filterParentPosition(arr) {
121 const levelObj = {};
122 arr.forEach((item) => {
123 const posLen = item.split('-').length;
124 if (!levelObj[posLen]) {
125 levelObj[posLen] = [];
126 }
127 levelObj[posLen].push(item);
128 });
129 const levelArr = Object.keys(levelObj).sort();
130 for (let i = 0; i < levelArr.length; i++) {
131 if (levelArr[i + 1]) {
132 levelObj[levelArr[i]].forEach(ii => {
133 for (let j = i + 1; j < levelArr.length; j++) {
134 levelObj[levelArr[j]].forEach((_i, index) => {
135 if (isInclude(ii.split('-'), _i.split('-'))) {
136 levelObj[levelArr[j]][index] = null;
137 }
138 });
139 levelObj[levelArr[j]] = levelObj[levelArr[j]].filter(p => p);
140 }
141 });
142 }
143 }
144 let nArr = [];
145 levelArr.forEach(i => {
146 nArr = nArr.concat(levelObj[i]);
147 });
148 return nArr;
149}
150// console.log(filterParentPosition(
151// ['0-2', '0-3-3', '0-10', '0-10-0', '0-0-1', '0-0', '0-1-1', '0-1']
152// ));
153
154
155function stripTail(str) {
156 const arr = str.match(/(.+)(-[^-]+)$/);
157 let st = '';
158 if (arr && arr.length === 3) {
159 st = arr[1];
160 }
161 return st;
162}
163function splitPosition(pos) {
164 return pos.split('-');
165}
166
167export function handleCheckState(obj, checkedPositionArr, checkIt) {
168 // console.log(stripTail('0-101-000'));
169 let objKeys = Object.keys(obj);
170 // let s = Date.now();
171 objKeys.forEach((i, index) => {
172 const iArr = splitPosition(i);
173 let saved = false;
174 checkedPositionArr.forEach((_pos) => {
175 // 设置子节点,全选或全不选
176 const _posArr = splitPosition(_pos);
177 if (iArr.length > _posArr.length && isInclude(_posArr, iArr)) {
178 obj[i].halfChecked = false;
179 obj[i].checked = checkIt;
180 objKeys[index] = null;
181 }
182 if (iArr[0] === _posArr[0] && iArr[1] === _posArr[1]) {
183 // 如果
184 saved = true;
185 }
186 });
187 if (!saved) {
188 objKeys[index] = null;
189 }
190 });
191 // TODO: 循环 2470000 次耗时约 1400 ms。 性能瓶颈!
192 // console.log(Date.now()-s, checkedPositionArr.length * objKeys.length);
193 objKeys = objKeys.filter(i => i); // filter non null;
194
195 for (let pIndex = 0; pIndex < checkedPositionArr.length; pIndex++) {
196 // 循环设置父节点的 选中 或 半选状态
197 const loop = (__pos) => {
198 const _posLen = splitPosition(__pos).length;
199 if (_posLen <= 2) { // e.g. '0-0', '0-1'
200 return;
201 }
202 let sibling = 0;
203 let siblingChecked = 0;
204 const parentPosition = stripTail(__pos);
205 objKeys.forEach((i /* , index*/) => {
206 const iArr = splitPosition(i);
207 if (iArr.length === _posLen && isInclude(splitPosition(parentPosition), iArr)) {
208 sibling++;
209 if (obj[i].checked) {
210 siblingChecked++;
211 const _i = checkedPositionArr.indexOf(i);
212 if (_i > -1) {
213 checkedPositionArr.splice(_i, 1);
214 if (_i <= pIndex) {
215 pIndex--;
216 }
217 }
218 } else if (obj[i].halfChecked) {
219 siblingChecked += 0.5;
220 }
221 // objKeys[index] = null;
222 }
223 });
224 // objKeys = objKeys.filter(i => i); // filter non null;
225 const parent = obj[parentPosition];
226 // sibling 不会等于0
227 // 全不选 - 全选 - 半选
228 if (siblingChecked === 0) {
229 parent.checked = false;
230 parent.halfChecked = false;
231 } else if (siblingChecked === sibling) {
232 parent.checked = true;
233 parent.halfChecked = false;
234 } else {
235 parent.halfChecked = true;
236 parent.checked = false;
237 }
238 loop(parentPosition);
239 };
240 loop(checkedPositionArr[pIndex], pIndex);
241 }
242 // console.log(Date.now()-s, objKeys.length, checkIt);
243}
244
245export function getCheck(treeNodesStates) {
246 const halfCheckedKeys = [];
247 const checkedKeys = [];
248 const checkedNodes = [];
249 const checkedNodesPositions = [];
250 Object.keys(treeNodesStates).forEach((item) => {
251 const itemObj = treeNodesStates[item];
252 if (itemObj.checked) {
253 checkedKeys.push(itemObj.key);
254 checkedNodes.push(itemObj.node);
255 checkedNodesPositions.push({ node: itemObj.node, pos: item });
256 } else if (itemObj.halfChecked) {
257 halfCheckedKeys.push(itemObj.key);
258 }
259 });
260 return {
261 halfCheckedKeys, checkedKeys, checkedNodes, checkedNodesPositions, treeNodesStates,
262 };
263}
264
265export function getStrictlyValue(checkedKeys, halfChecked) {
266 if (halfChecked) {
267 return { checked: checkedKeys, halfChecked };
268 }
269 return checkedKeys;
270}
271
272export function arraysEqual(a, b) {
273 if (a === b) return true;
274 if (a === null || typeof a === 'undefined' || b === null || typeof b === 'undefined') {
275 return false;
276 }
277 if (a.length !== b.length) return false;
278
279 // If you don't care about the order of the elements inside
280 // the array, you should sort both arrays here.
281
282 for (let i = 0; i < a.length; ++i) {
283 if (a[i] !== b[i]) return false;
284 }
285 return true;
286}
287
288
289export function closest(el, selector) {
290 const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector;
291
292 while (el) {
293 if (matchesSelector.call(el, selector)) {
294 return el;
295 } else {
296 el = el.parentElement;
297 }
298 }
299 return null;
300}
301
302export function isTreeNode(node) {
303 return node && node.type && node.type.isTreeNode;
304}
305
306export function toArray(children) {
307 const ret = [];
308 React.Children.forEach(children, (c) => {
309 ret.push(c);
310 });
311 return ret;
312}
313
314export function getNodeChildren(children) {
315 return toArray(children).filter(isTreeNode);
316}
317
318let onlyTreeNodeWarned = false;
319
320export function warnOnlyTreeNode() {
321 if (onlyTreeNodeWarned) return;
322 onlyTreeNodeWarned = true;
323 console.warn('Tree only accept TreeNode as children.');
324}
325
326/**
327 * 将一维数组转换为树结构
328 * @param {*} treeData 扁平结构的 List 数组
329 * @param {*} attr 属性配置设置
330 * @param {*} flatTreeKeysMap 存储所有 key-value 的映射,方便获取各节点信息
331 */
332export function convertListToTree(treeData, attr, flatTreeKeysMap) {
333 let tree = []; //存储所有一级节点
334 let resData = treeData, //resData 存储截取的节点 + 父节点(除一级节点外)
335 resKeysMap = {}, //resData 的Map映射
336 treeKeysMap = {}; //tree 的Map映射
337 resData.map((element) => {
338 let key = attr.id;
339 resKeysMap[element[key]] = element;
340 });
341 // 查找父节点,为了补充不完整的数据结构
342 let findParentNode = (node) => {
343 let parentKey = node[attr.parendId];
344 if(parentKey !== attr.rootId) { //如果不是根节点,则继续递归
345 let item = flatTreeKeysMap[parentKey];
346 // 用 resKeysMap 判断,避免重复计算某节点的父节点
347 if(resKeysMap.hasOwnProperty(item[attr.id])) return;
348 resData.unshift(item);
349 resKeysMap[item[attr.id]] = item;
350 findParentNode(item);
351 }else{
352 // 用 treeKeysMap 判断,避免重复累加
353 if (!treeKeysMap.hasOwnProperty(node[attr.id]) ) {
354 let { key, title, children, isLeaf, ...otherProps } = node;
355 let obj = {
356 key,
357 title,
358 isLeaf,
359 children: []
360 }
361 tree.push(Object.assign(obj, {...otherProps}));
362 treeKeysMap[key] = node;
363 }
364 }
365 }
366 // 遍历 resData ,找到所有的一级节点
367 for (let i = 0; i < resData.length; i++) {
368 let item = resData[i];
369 if (item[attr.parendId] === attr.rootId && !treeKeysMap.hasOwnProperty(item[attr.id])) { //如果是根节点,就存放进 tree 对象中
370 let { key, title, children, ...otherProps } = item;
371 let obj = {
372 key: item[attr.id],
373 title: item[attr.name],
374 isLeaf: item[attr.isLeaf],
375 children: []
376 };
377 tree.push(Object.assign(obj, {...otherProps}));
378 treeKeysMap[key] = item;
379 resData.splice(i, 1);
380 i--;
381 }else { //递归查找根节点信息
382 findParentNode(item);
383 }
384 }
385 // console.log('resData',resKeysMap);
386 var run = function(treeArrs) {
387 if (resData.length > 0) {
388 for (let i = 0; i < treeArrs.length; i++) {
389 for (let j = 0; j < resData.length; j++) {
390 let item = resData[j];
391 if (treeArrs[i].key === item[attr.parendId]) {
392 let { key, title, children, ...otherProps } = item;
393 let obj = {
394 key: item[attr.id],
395 title: item[attr.name],
396 isLeaf: item[attr.isLeaf],
397 children: []
398 };
399 treeArrs[i].children.push(Object.assign(obj, {...otherProps}));
400 resData.splice(j, 1);
401 j--;
402 }
403 }
404 run(treeArrs[i].children);
405 }
406 }
407 };
408 run(tree);
409 return tree;
410}
411
412function isObject(value) {
413 const type = typeof value
414 return value != null && (type == 'object' || type == 'function')
415}
416
417/**
418 * 函数防抖
419 * @param {*} func
420 * @param {*} wait
421 * @param {*} immediate
422 */
423export function debounce(func, wait, immediate) {
424 let timeout;
425 return function debounceFunc() {
426 const context = this;
427 const args = arguments;
428 // https://fb.me/react-event-pooling
429 if (args[0] && args[0].persist) {
430 args[0].persist();
431 }
432 const later = () => {
433 timeout = null;
434 if (!immediate) {
435 func.apply(context, args);
436 }
437 };
438 const callNow = immediate && !timeout;
439 clearTimeout(timeout);
440 timeout = setTimeout(later, wait);
441 if (callNow) {
442 func.apply(context, args);
443 }
444 };
445}
446
447/**
448 * 函数节流
449 * @param {*} func 延时调用函数
450 * @param {*} wait 延迟多长时间
451 * @param {*} options 至少多长时间触发一次
452 * @return Function 延迟执行的方法
453 */
454export function throttle(func, wait, options) {
455 let leading = true
456 let trailing = true
457
458 if (typeof func !== 'function') {
459 throw new TypeError('Expected a function')
460 }
461 if (isObject(options)) {
462 leading = 'leading' in options ? !!options.leading : leading
463 trailing = 'trailing' in options ? !!options.trailing : trailing
464 }
465 return debounce(func, wait, {
466 leading,
467 trailing,
468 'maxWait': wait,
469 })
470}
\No newline at end of file