sculp-js 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -1
- package/lib/cjs/array.js +1 -178
- package/lib/cjs/async.js +1 -1
- package/lib/cjs/clipboard.js +1 -1
- package/lib/cjs/cookie.js +1 -1
- package/lib/cjs/date.js +1 -1
- package/lib/cjs/dom.js +1 -1
- package/lib/cjs/download.js +1 -1
- package/lib/cjs/easing.js +1 -1
- package/lib/cjs/file.js +1 -1
- package/lib/cjs/func.js +1 -1
- package/lib/cjs/index.js +6 -4
- package/lib/cjs/number.js +1 -1
- package/lib/cjs/object.js +1 -1
- package/lib/cjs/path.js +1 -1
- package/lib/cjs/qs.js +1 -1
- package/lib/cjs/random.js +1 -1
- package/lib/cjs/string.js +1 -1
- package/lib/cjs/tooltip.js +1 -1
- package/lib/cjs/tree.js +212 -0
- package/lib/cjs/type.js +1 -1
- package/lib/cjs/unique.js +1 -1
- package/lib/cjs/url.js +1 -1
- package/lib/cjs/watermark.js +1 -1
- package/lib/es/array.js +2 -176
- package/lib/es/async.js +1 -1
- package/lib/es/clipboard.js +1 -1
- package/lib/es/cookie.js +1 -1
- package/lib/es/date.js +1 -1
- package/lib/es/dom.js +1 -1
- package/lib/es/download.js +1 -1
- package/lib/es/easing.js +1 -1
- package/lib/es/file.js +1 -1
- package/lib/es/func.js +1 -1
- package/lib/es/index.js +3 -2
- package/lib/es/number.js +1 -1
- package/lib/es/object.js +1 -1
- package/lib/es/path.js +1 -1
- package/lib/es/qs.js +1 -1
- package/lib/es/random.js +1 -1
- package/lib/es/string.js +1 -1
- package/lib/es/tooltip.js +1 -1
- package/lib/es/tree.js +207 -0
- package/lib/es/type.js +1 -1
- package/lib/es/unique.js +1 -1
- package/lib/es/url.js +1 -1
- package/lib/es/watermark.js +1 -1
- package/lib/index.d.ts +77 -64
- package/lib/umd/index.js +202 -175
- package/package.json +4 -1
package/lib/index.d.ts
CHANGED
|
@@ -74,68 +74,6 @@ declare function arrayInsertBefore(array: AnyArray, start: number, to: number):
|
|
|
74
74
|
* @returns {V[]}
|
|
75
75
|
*/
|
|
76
76
|
declare function arrayRemove<V>(array: V[], expect: (val: V, idx: number) => boolean): V[];
|
|
77
|
-
/**
|
|
78
|
-
* 自定义深度优先遍历函数(支持continue和break操作)
|
|
79
|
-
* @param {ArrayLike<V>} tree 树形数据
|
|
80
|
-
* @param {Function} iterator 迭代函数, 返回值为true时continue, 返回值为false时break
|
|
81
|
-
* @param {string} children 定制子元素的key
|
|
82
|
-
* @param {boolean} isReverse 是否反向遍历
|
|
83
|
-
* @returns {*}
|
|
84
|
-
*/
|
|
85
|
-
declare function forEachDeep<V>(tree: ArrayLike<V>, iterator: (val: V, i: number, arr: ArrayLike<V>, parent: V | null, level: number) => boolean | void, children?: string, isReverse?: boolean): void;
|
|
86
|
-
type IdLike = number | string;
|
|
87
|
-
interface ITreeConf {
|
|
88
|
-
id: string | number;
|
|
89
|
-
children: string;
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* 在树中找到 id 为某个值的节点,并返回上游的所有父级节点
|
|
93
|
-
*
|
|
94
|
-
* @param {ArrayLike<T>} tree - 树形数据
|
|
95
|
-
* @param {IdLike} nodeId - 元素ID
|
|
96
|
-
* @param {ITreeConf} config - 迭代配置项
|
|
97
|
-
* @returns {[IdLike[], ITreeItem<V>[]]} - 由parentId...childId, parentObject-childObject组成的二维数组
|
|
98
|
-
*/
|
|
99
|
-
declare function searchTreeById<V>(tree: ArrayLike<V>, nodeId: IdLike, config?: ITreeConf): [IdLike[], ArrayLike<V>[]];
|
|
100
|
-
type WithChildren<T> = T & {
|
|
101
|
-
children?: WithChildren<T>[];
|
|
102
|
-
};
|
|
103
|
-
/**
|
|
104
|
-
* 根据 idProp 与 parentIdProp 从对象数组中构建对应的树
|
|
105
|
-
* 当 A[parentIdProp] === B[idProp] 时,对象A会被移动到对象B的children。
|
|
106
|
-
* 当一个对象的 parentIdProp 不与其他对象的 idProp 字段相等时,该对象被作为树的顶层节点
|
|
107
|
-
* @param {string} idProp 元素ID
|
|
108
|
-
* @param {string} parentIdProp 父元素ID
|
|
109
|
-
* @param {object[]} items 一维数组
|
|
110
|
-
* @returns {WithChildren<T>[]} 树
|
|
111
|
-
* @example
|
|
112
|
-
* const array = [
|
|
113
|
-
* { id: 'node-1', parent: 'root' },
|
|
114
|
-
* { id: 'node-2', parent: 'root' },
|
|
115
|
-
* { id: 'node-3', parent: 'node-2' },
|
|
116
|
-
* { id: 'node-4', parent: 'node-2' },
|
|
117
|
-
* { id: 'node-5', parent: 'node-4' },
|
|
118
|
-
* ]
|
|
119
|
-
* const tree = buildTree('id', 'parent', array)
|
|
120
|
-
* expect(tree).toEqual([
|
|
121
|
-
* { id: 'node-1', parent: 'root' },
|
|
122
|
-
* {
|
|
123
|
-
* id: 'node-2',
|
|
124
|
-
* parent: 'root',
|
|
125
|
-
* children: [
|
|
126
|
-
* { id: 'node-3', parent: 'node-2' },
|
|
127
|
-
* {
|
|
128
|
-
* id: 'node-4',
|
|
129
|
-
* parent: 'node-2',
|
|
130
|
-
* children: [{ id: 'node-5', parent: 'node-4' }],
|
|
131
|
-
* },
|
|
132
|
-
* ],
|
|
133
|
-
* },
|
|
134
|
-
* ])
|
|
135
|
-
*/
|
|
136
|
-
declare function buildTree<ID extends string, PID extends string, T extends {
|
|
137
|
-
[key in ID | PID]: string;
|
|
138
|
-
}>(idProp: ID, parentIdProp: PID, items: T[]): WithChildren<T>[];
|
|
139
77
|
|
|
140
78
|
/**
|
|
141
79
|
* 复制文本
|
|
@@ -422,7 +360,7 @@ declare function objectGet(obj: AnyObject, path: string, strict?: boolean): {
|
|
|
422
360
|
* @param {WeakMap} map
|
|
423
361
|
* @returns {AnyObject | AnyArray}
|
|
424
362
|
*/
|
|
425
|
-
declare function cloneDeep(obj: Object, map?: WeakMap<object, any>):
|
|
363
|
+
declare function cloneDeep(obj: Object, map?: WeakMap<object, any>): AnyObject | AnyArray;
|
|
426
364
|
|
|
427
365
|
/**
|
|
428
366
|
* 标准化路径
|
|
@@ -745,4 +683,79 @@ declare const tooltipEvent: {
|
|
|
745
683
|
handleMouseLeave: typeof handleMouseLeave;
|
|
746
684
|
};
|
|
747
685
|
|
|
748
|
-
|
|
686
|
+
interface IFieldOptions {
|
|
687
|
+
keyField: string;
|
|
688
|
+
childField: string;
|
|
689
|
+
pidField: string;
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* 自定义深度优先遍历函数(支持continue和break操作), 可用于insert tree item 和 remove tree item
|
|
693
|
+
* @param {ArrayLike<V>} tree 树形数据
|
|
694
|
+
* @param {Function} iterator 迭代函数, 返回值为true时continue, 返回值为false时break
|
|
695
|
+
* @param {string} children 定制子元素的key
|
|
696
|
+
* @param {boolean} isReverse 是否反向遍历
|
|
697
|
+
* @returns {*}
|
|
698
|
+
*/
|
|
699
|
+
declare function forEachDeep<V>(tree: ArrayLike<V>, iterator: (val: V, i: number, currentArr: ArrayLike<V>, tree: ArrayLike<V>, parent: V | null, level: number) => boolean | void, children?: string, isReverse?: boolean): void;
|
|
700
|
+
type IdLike = number | string;
|
|
701
|
+
interface ITreeConf {
|
|
702
|
+
id: string | number;
|
|
703
|
+
children: string;
|
|
704
|
+
}
|
|
705
|
+
/**
|
|
706
|
+
* 在树中找到 id 为某个值的节点,并返回上游的所有父级节点
|
|
707
|
+
*
|
|
708
|
+
* @param {ArrayLike<T>} tree - 树形数据
|
|
709
|
+
* @param {IdLike} nodeId - 元素ID
|
|
710
|
+
* @param {ITreeConf} config - 迭代配置项
|
|
711
|
+
* @returns {[IdLike[], ITreeItem<V>[]]} - 由parentId...childId, parentObject-childObject组成的二维数组
|
|
712
|
+
*/
|
|
713
|
+
declare function searchTreeById<V>(tree: ArrayLike<V>, nodeId: IdLike, config?: ITreeConf): [IdLike[], ArrayLike<V>[]];
|
|
714
|
+
type WithChildren<T> = T & {
|
|
715
|
+
children?: WithChildren<T>[];
|
|
716
|
+
};
|
|
717
|
+
/**
|
|
718
|
+
* 根据 idProp 与 parentIdProp 从对象数组中构建对应的树
|
|
719
|
+
* 当 A[parentIdProp] === B[idProp] 时,对象A会被移动到对象B的children。
|
|
720
|
+
* 当一个对象的 parentIdProp 不与其他对象的 idProp 字段相等时,该对象被作为树的顶层节点
|
|
721
|
+
* @param {string} idProp 元素ID
|
|
722
|
+
* @param {string} parentIdProp 父元素ID
|
|
723
|
+
* @param {object[]} items 一维数组
|
|
724
|
+
* @returns {WithChildren<T>[]} 树
|
|
725
|
+
* @example
|
|
726
|
+
* const array = [
|
|
727
|
+
* { id: 'node-1', parent: 'root' },
|
|
728
|
+
* { id: 'node-2', parent: 'root' },
|
|
729
|
+
* { id: 'node-3', parent: 'node-2' },
|
|
730
|
+
* { id: 'node-4', parent: 'node-2' },
|
|
731
|
+
* { id: 'node-5', parent: 'node-4' },
|
|
732
|
+
* ]
|
|
733
|
+
* const tree = buildTree('id', 'parent', array)
|
|
734
|
+
* expect(tree).toEqual([
|
|
735
|
+
* { id: 'node-1', parent: 'root' },
|
|
736
|
+
* {
|
|
737
|
+
* id: 'node-2',
|
|
738
|
+
* parent: 'root',
|
|
739
|
+
* children: [
|
|
740
|
+
* { id: 'node-3', parent: 'node-2' },
|
|
741
|
+
* {
|
|
742
|
+
* id: 'node-4',
|
|
743
|
+
* parent: 'node-2',
|
|
744
|
+
* children: [{ id: 'node-5', parent: 'node-4' }],
|
|
745
|
+
* },
|
|
746
|
+
* ],
|
|
747
|
+
* },
|
|
748
|
+
* ])
|
|
749
|
+
*/
|
|
750
|
+
declare function buildTree<ID extends string, PID extends string, T extends {
|
|
751
|
+
[key in ID | PID]: string;
|
|
752
|
+
}>(idProp: ID, parentIdProp: PID, items: T[]): WithChildren<T>[];
|
|
753
|
+
/**
|
|
754
|
+
* 扁平化数组转换成树(效率高于buildTree)
|
|
755
|
+
* @param {any[]} list
|
|
756
|
+
* @param {IFieldOptions} options
|
|
757
|
+
* @returns {any[]}
|
|
758
|
+
*/
|
|
759
|
+
declare function formatTree(list: any[], options?: IFieldOptions): any[];
|
|
760
|
+
|
|
761
|
+
export { type AnyArray, type AnyFunc, type AnyObject, type ArrayElements, type DateObj, type DateValue, type DebounceFunc, type FileType, HEX_POOL, type ICanvasWM, type IFieldOptions, type ITreeConf, type IdLike, type LooseParamValue, type LooseParams, type ObjectAssignItem, type OnceFunc, type Params, type PartialDeep, type RandomString, type ReadyCallback, type Replacer, STRING_ARABIC_NUMERALS, STRING_LOWERCASE_ALPHA, STRING_POOL, STRING_UPPERCASE_ALPHA, type SetStyle, type SmoothScrollOptions, type Style, type ThrottleFunc, UNIQUE_NUMBER_SAFE_LENGTH, type UniqueString, type Url, type WithChildren, addClass, arrayEach, arrayEachAsync, arrayInsertBefore, arrayLike, arrayRemove, asyncMap, buildTree, calculateDate, calculateDateTime, chooseLocalFile, cloneDeep, cookieDel, cookieGet, cookieSet, copyText, dateParse, dateToEnd, dateToStart, debounce, downloadBlob, downloadData, downloadHref, downloadURL, forEachDeep, formatDate, formatNumber, formatTree, genCanvasWM, getComputedCssVal, getGlobal, getStrWidthPx, getStyle, hasClass, isArray, isBigInt, isBoolean, isDate, isDomReady, isError, isFunction, isNaN, isNull, isNumber, isObject, isPlainObject, isPrimitive, isRegExp, isString, isSymbol, isUndefined, isValidDate, numberAbbr, numberToHex, objectAssign, objectEach, objectEachAsync, objectFill, objectGet, objectHas, objectMap, objectAssign as objectMerge, objectOmit, objectPick, onDomReady, once, pathJoin, pathNormalize, qsParse, qsStringify, randomNumber, randomString, randomUuid, removeClass, searchTreeById, setGlobal, setStyle, smoothScroll, stringAssign, stringCamelCase, stringEscapeHtml, stringFill, stringFormat, stringKebabCase, throttle, tooltipEvent, typeIs, uniqueNumber, uniqueString, urlDelParams, urlParse, urlSetParams, urlStringify, wait };
|
package/lib/umd/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* sculp-js v1.0
|
|
2
|
+
* sculp-js v1.1.0
|
|
3
3
|
* (c) 2023-2023 chandq
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
*/
|
|
@@ -361,180 +361,6 @@
|
|
|
361
361
|
indexes.forEach((val, idx) => array.splice(val - idx, 1));
|
|
362
362
|
return array;
|
|
363
363
|
}
|
|
364
|
-
/**
|
|
365
|
-
* 自定义深度优先遍历函数(支持continue和break操作)
|
|
366
|
-
* @param {ArrayLike<V>} tree 树形数据
|
|
367
|
-
* @param {Function} iterator 迭代函数, 返回值为true时continue, 返回值为false时break
|
|
368
|
-
* @param {string} children 定制子元素的key
|
|
369
|
-
* @param {boolean} isReverse 是否反向遍历
|
|
370
|
-
* @returns {*}
|
|
371
|
-
*/
|
|
372
|
-
function forEachDeep(tree, iterator, children = 'children', isReverse = false) {
|
|
373
|
-
let level = 0, isBreak = false;
|
|
374
|
-
const walk = (arr, parent) => {
|
|
375
|
-
if (isReverse) {
|
|
376
|
-
for (let i = arr.length - 1; i >= 0; i--) {
|
|
377
|
-
if (isBreak) {
|
|
378
|
-
break;
|
|
379
|
-
}
|
|
380
|
-
const re = iterator(arr[i], i, tree, parent, level);
|
|
381
|
-
if (re === false) {
|
|
382
|
-
isBreak = true;
|
|
383
|
-
break;
|
|
384
|
-
}
|
|
385
|
-
else if (re === true) {
|
|
386
|
-
continue;
|
|
387
|
-
}
|
|
388
|
-
// @ts-ignore
|
|
389
|
-
if (Array.isArray(arr[i][children])) {
|
|
390
|
-
++level;
|
|
391
|
-
// @ts-ignore
|
|
392
|
-
walk(arr[i][children], arr[i]);
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
else {
|
|
397
|
-
for (let i = 0; i < arr.length; i++) {
|
|
398
|
-
if (isBreak) {
|
|
399
|
-
break;
|
|
400
|
-
}
|
|
401
|
-
const re = iterator(arr[i], i, tree, parent, level);
|
|
402
|
-
if (re === false) {
|
|
403
|
-
isBreak = true;
|
|
404
|
-
break;
|
|
405
|
-
}
|
|
406
|
-
else if (re === true) {
|
|
407
|
-
continue;
|
|
408
|
-
}
|
|
409
|
-
// @ts-ignore
|
|
410
|
-
if (Array.isArray(arr[i][children])) {
|
|
411
|
-
++level;
|
|
412
|
-
// @ts-ignore
|
|
413
|
-
walk(arr[i][children], arr[i]);
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
};
|
|
418
|
-
walk(tree, null);
|
|
419
|
-
}
|
|
420
|
-
/**
|
|
421
|
-
* 在树中找到 id 为某个值的节点,并返回上游的所有父级节点
|
|
422
|
-
*
|
|
423
|
-
* @param {ArrayLike<T>} tree - 树形数据
|
|
424
|
-
* @param {IdLike} nodeId - 元素ID
|
|
425
|
-
* @param {ITreeConf} config - 迭代配置项
|
|
426
|
-
* @returns {[IdLike[], ITreeItem<V>[]]} - 由parentId...childId, parentObject-childObject组成的二维数组
|
|
427
|
-
*/
|
|
428
|
-
function searchTreeById(tree, nodeId, config) {
|
|
429
|
-
const { children = 'children', id = 'id' } = config || {};
|
|
430
|
-
const toFlatArray = (tree, parentId, parent) => {
|
|
431
|
-
return tree.reduce((t, _) => {
|
|
432
|
-
const child = _[children];
|
|
433
|
-
return [
|
|
434
|
-
...t,
|
|
435
|
-
parentId ? { ..._, parentId, parent } : _,
|
|
436
|
-
...(child && child.length ? toFlatArray(child, _[id], _) : [])
|
|
437
|
-
];
|
|
438
|
-
}, []);
|
|
439
|
-
};
|
|
440
|
-
const getIds = (flatArray) => {
|
|
441
|
-
let child = flatArray.find(_ => _[id] === nodeId);
|
|
442
|
-
const { parent, parentId, ...other } = child;
|
|
443
|
-
let ids = [nodeId], nodes = [other];
|
|
444
|
-
while (child && child.parentId) {
|
|
445
|
-
ids = [child.parentId, ...ids];
|
|
446
|
-
nodes = [child.parent, ...nodes];
|
|
447
|
-
child = flatArray.find(_ => _[id] === child.parentId); // eslint-disable-line
|
|
448
|
-
}
|
|
449
|
-
return [ids, nodes];
|
|
450
|
-
};
|
|
451
|
-
return getIds(toFlatArray(tree));
|
|
452
|
-
}
|
|
453
|
-
/**
|
|
454
|
-
* 使用迭代函数转换数组
|
|
455
|
-
* @param {T} array
|
|
456
|
-
* @param {Function} callback 迭代函数
|
|
457
|
-
* @return {Array}
|
|
458
|
-
*/
|
|
459
|
-
function flatMap(array, callback) {
|
|
460
|
-
const result = [];
|
|
461
|
-
array.forEach((value, index) => {
|
|
462
|
-
result.push(...callback(value, index, array));
|
|
463
|
-
});
|
|
464
|
-
return result;
|
|
465
|
-
}
|
|
466
|
-
/**
|
|
467
|
-
* 根据 idProp 与 parentIdProp 从对象数组中构建对应的树
|
|
468
|
-
* 当 A[parentIdProp] === B[idProp] 时,对象A会被移动到对象B的children。
|
|
469
|
-
* 当一个对象的 parentIdProp 不与其他对象的 idProp 字段相等时,该对象被作为树的顶层节点
|
|
470
|
-
* @param {string} idProp 元素ID
|
|
471
|
-
* @param {string} parentIdProp 父元素ID
|
|
472
|
-
* @param {object[]} items 一维数组
|
|
473
|
-
* @returns {WithChildren<T>[]} 树
|
|
474
|
-
* @example
|
|
475
|
-
* const array = [
|
|
476
|
-
* { id: 'node-1', parent: 'root' },
|
|
477
|
-
* { id: 'node-2', parent: 'root' },
|
|
478
|
-
* { id: 'node-3', parent: 'node-2' },
|
|
479
|
-
* { id: 'node-4', parent: 'node-2' },
|
|
480
|
-
* { id: 'node-5', parent: 'node-4' },
|
|
481
|
-
* ]
|
|
482
|
-
* const tree = buildTree('id', 'parent', array)
|
|
483
|
-
* expect(tree).toEqual([
|
|
484
|
-
* { id: 'node-1', parent: 'root' },
|
|
485
|
-
* {
|
|
486
|
-
* id: 'node-2',
|
|
487
|
-
* parent: 'root',
|
|
488
|
-
* children: [
|
|
489
|
-
* { id: 'node-3', parent: 'node-2' },
|
|
490
|
-
* {
|
|
491
|
-
* id: 'node-4',
|
|
492
|
-
* parent: 'node-2',
|
|
493
|
-
* children: [{ id: 'node-5', parent: 'node-4' }],
|
|
494
|
-
* },
|
|
495
|
-
* ],
|
|
496
|
-
* },
|
|
497
|
-
* ])
|
|
498
|
-
*/
|
|
499
|
-
function buildTree(idProp, parentIdProp, items) {
|
|
500
|
-
const wrapperMap = new Map();
|
|
501
|
-
const ensure = (id) => {
|
|
502
|
-
if (wrapperMap.has(id)) {
|
|
503
|
-
return wrapperMap.get(id);
|
|
504
|
-
}
|
|
505
|
-
//@ts-ignore
|
|
506
|
-
const wrapper = { id, parent: null, item: null, children: [] };
|
|
507
|
-
wrapperMap.set(id, wrapper);
|
|
508
|
-
return wrapper;
|
|
509
|
-
};
|
|
510
|
-
for (const item of items) {
|
|
511
|
-
const parentWrapper = ensure(item[parentIdProp]);
|
|
512
|
-
const itemWrapper = ensure(item[idProp]);
|
|
513
|
-
//@ts-ignore
|
|
514
|
-
itemWrapper.parent = parentWrapper;
|
|
515
|
-
//@ts-ignore
|
|
516
|
-
parentWrapper.children.push(itemWrapper);
|
|
517
|
-
//@ts-ignore
|
|
518
|
-
itemWrapper.item = item;
|
|
519
|
-
}
|
|
520
|
-
const topLevelWrappers = flatMap(Array.from(wrapperMap.values()).filter(wrapper => wrapper.parent === null), wrapper => wrapper.children);
|
|
521
|
-
return unwrapRecursively(topLevelWrappers);
|
|
522
|
-
function unwrapRecursively(wrapperArray) {
|
|
523
|
-
const result = [];
|
|
524
|
-
for (const wrapper of wrapperArray) {
|
|
525
|
-
if (wrapper.children.length === 0) {
|
|
526
|
-
result.push(wrapper.item);
|
|
527
|
-
}
|
|
528
|
-
else {
|
|
529
|
-
result.push({
|
|
530
|
-
...wrapper.item,
|
|
531
|
-
children: unwrapRecursively(wrapper.children)
|
|
532
|
-
});
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
return result;
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
364
|
|
|
539
365
|
// @ref https://cubic-bezier.com/
|
|
540
366
|
const easingDefines = {
|
|
@@ -1997,6 +1823,206 @@
|
|
|
1997
1823
|
}
|
|
1998
1824
|
const tooltipEvent = { handleMouseEnter, handleMouseLeave };
|
|
1999
1825
|
|
|
1826
|
+
const defaultFieldOptions = { keyField: 'key', childField: 'children', pidField: 'pid' };
|
|
1827
|
+
/**
|
|
1828
|
+
* 自定义深度优先遍历函数(支持continue和break操作), 可用于insert tree item 和 remove tree item
|
|
1829
|
+
* @param {ArrayLike<V>} tree 树形数据
|
|
1830
|
+
* @param {Function} iterator 迭代函数, 返回值为true时continue, 返回值为false时break
|
|
1831
|
+
* @param {string} children 定制子元素的key
|
|
1832
|
+
* @param {boolean} isReverse 是否反向遍历
|
|
1833
|
+
* @returns {*}
|
|
1834
|
+
*/
|
|
1835
|
+
function forEachDeep(tree, iterator, children = 'children', isReverse = false) {
|
|
1836
|
+
let level = 0, isBreak = false;
|
|
1837
|
+
const walk = (arr, parent) => {
|
|
1838
|
+
if (isReverse) {
|
|
1839
|
+
for (let i = arr.length - 1; i >= 0; i--) {
|
|
1840
|
+
if (isBreak) {
|
|
1841
|
+
break;
|
|
1842
|
+
}
|
|
1843
|
+
const re = iterator(arr[i], i, arr, tree, parent, level);
|
|
1844
|
+
if (re === false) {
|
|
1845
|
+
isBreak = true;
|
|
1846
|
+
break;
|
|
1847
|
+
}
|
|
1848
|
+
else if (re === true) {
|
|
1849
|
+
continue;
|
|
1850
|
+
}
|
|
1851
|
+
// @ts-ignore
|
|
1852
|
+
if (arr[i] && Array.isArray(arr[i][children])) {
|
|
1853
|
+
++level;
|
|
1854
|
+
// @ts-ignore
|
|
1855
|
+
walk(arr[i][children], arr[i]);
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
else {
|
|
1860
|
+
for (let i = 0; i < arr.length; i++) {
|
|
1861
|
+
if (isBreak) {
|
|
1862
|
+
break;
|
|
1863
|
+
}
|
|
1864
|
+
const re = iterator(arr[i], i, arr, tree, parent, level);
|
|
1865
|
+
if (re === false) {
|
|
1866
|
+
isBreak = true;
|
|
1867
|
+
break;
|
|
1868
|
+
}
|
|
1869
|
+
else if (re === true) {
|
|
1870
|
+
continue;
|
|
1871
|
+
}
|
|
1872
|
+
// @ts-ignore
|
|
1873
|
+
if (arr[i] && Array.isArray(arr[i][children])) {
|
|
1874
|
+
++level;
|
|
1875
|
+
// @ts-ignore
|
|
1876
|
+
walk(arr[i][children], arr[i]);
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
};
|
|
1881
|
+
walk(tree, null);
|
|
1882
|
+
}
|
|
1883
|
+
/**
|
|
1884
|
+
* 在树中找到 id 为某个值的节点,并返回上游的所有父级节点
|
|
1885
|
+
*
|
|
1886
|
+
* @param {ArrayLike<T>} tree - 树形数据
|
|
1887
|
+
* @param {IdLike} nodeId - 元素ID
|
|
1888
|
+
* @param {ITreeConf} config - 迭代配置项
|
|
1889
|
+
* @returns {[IdLike[], ITreeItem<V>[]]} - 由parentId...childId, parentObject-childObject组成的二维数组
|
|
1890
|
+
*/
|
|
1891
|
+
function searchTreeById(tree, nodeId, config) {
|
|
1892
|
+
const { children = 'children', id = 'id' } = config || {};
|
|
1893
|
+
const toFlatArray = (tree, parentId, parent) => {
|
|
1894
|
+
return tree.reduce((t, _) => {
|
|
1895
|
+
const child = _[children];
|
|
1896
|
+
return [
|
|
1897
|
+
...t,
|
|
1898
|
+
parentId ? { ..._, parentId, parent } : _,
|
|
1899
|
+
...(child && child.length ? toFlatArray(child, _[id], _) : [])
|
|
1900
|
+
];
|
|
1901
|
+
}, []);
|
|
1902
|
+
};
|
|
1903
|
+
const getIds = (flatArray) => {
|
|
1904
|
+
let child = flatArray.find(_ => _[id] === nodeId);
|
|
1905
|
+
const { parent, parentId, ...other } = child;
|
|
1906
|
+
let ids = [nodeId], nodes = [other];
|
|
1907
|
+
while (child && child.parentId) {
|
|
1908
|
+
ids = [child.parentId, ...ids];
|
|
1909
|
+
nodes = [child.parent, ...nodes];
|
|
1910
|
+
child = flatArray.find(_ => _[id] === child.parentId); // eslint-disable-line
|
|
1911
|
+
}
|
|
1912
|
+
return [ids, nodes];
|
|
1913
|
+
};
|
|
1914
|
+
return getIds(toFlatArray(tree));
|
|
1915
|
+
}
|
|
1916
|
+
/**
|
|
1917
|
+
* 使用迭代函数转换数组
|
|
1918
|
+
* @param {T} array
|
|
1919
|
+
* @param {Function} callback 迭代函数
|
|
1920
|
+
* @returns {Array}
|
|
1921
|
+
*/
|
|
1922
|
+
function flatMap(array, callback) {
|
|
1923
|
+
const result = [];
|
|
1924
|
+
array.forEach((value, index) => {
|
|
1925
|
+
result.push(...callback(value, index, array));
|
|
1926
|
+
});
|
|
1927
|
+
return result;
|
|
1928
|
+
}
|
|
1929
|
+
/**
|
|
1930
|
+
* 根据 idProp 与 parentIdProp 从对象数组中构建对应的树
|
|
1931
|
+
* 当 A[parentIdProp] === B[idProp] 时,对象A会被移动到对象B的children。
|
|
1932
|
+
* 当一个对象的 parentIdProp 不与其他对象的 idProp 字段相等时,该对象被作为树的顶层节点
|
|
1933
|
+
* @param {string} idProp 元素ID
|
|
1934
|
+
* @param {string} parentIdProp 父元素ID
|
|
1935
|
+
* @param {object[]} items 一维数组
|
|
1936
|
+
* @returns {WithChildren<T>[]} 树
|
|
1937
|
+
* @example
|
|
1938
|
+
* const array = [
|
|
1939
|
+
* { id: 'node-1', parent: 'root' },
|
|
1940
|
+
* { id: 'node-2', parent: 'root' },
|
|
1941
|
+
* { id: 'node-3', parent: 'node-2' },
|
|
1942
|
+
* { id: 'node-4', parent: 'node-2' },
|
|
1943
|
+
* { id: 'node-5', parent: 'node-4' },
|
|
1944
|
+
* ]
|
|
1945
|
+
* const tree = buildTree('id', 'parent', array)
|
|
1946
|
+
* expect(tree).toEqual([
|
|
1947
|
+
* { id: 'node-1', parent: 'root' },
|
|
1948
|
+
* {
|
|
1949
|
+
* id: 'node-2',
|
|
1950
|
+
* parent: 'root',
|
|
1951
|
+
* children: [
|
|
1952
|
+
* { id: 'node-3', parent: 'node-2' },
|
|
1953
|
+
* {
|
|
1954
|
+
* id: 'node-4',
|
|
1955
|
+
* parent: 'node-2',
|
|
1956
|
+
* children: [{ id: 'node-5', parent: 'node-4' }],
|
|
1957
|
+
* },
|
|
1958
|
+
* ],
|
|
1959
|
+
* },
|
|
1960
|
+
* ])
|
|
1961
|
+
*/
|
|
1962
|
+
function buildTree(idProp, parentIdProp, items) {
|
|
1963
|
+
const wrapperMap = new Map();
|
|
1964
|
+
const ensure = (id) => {
|
|
1965
|
+
if (wrapperMap.has(id)) {
|
|
1966
|
+
return wrapperMap.get(id);
|
|
1967
|
+
}
|
|
1968
|
+
//@ts-ignore
|
|
1969
|
+
const wrapper = { id, parent: null, item: null, children: [] };
|
|
1970
|
+
wrapperMap.set(id, wrapper);
|
|
1971
|
+
return wrapper;
|
|
1972
|
+
};
|
|
1973
|
+
for (const item of items) {
|
|
1974
|
+
const parentWrapper = ensure(item[parentIdProp]);
|
|
1975
|
+
const itemWrapper = ensure(item[idProp]);
|
|
1976
|
+
//@ts-ignore
|
|
1977
|
+
itemWrapper.parent = parentWrapper;
|
|
1978
|
+
//@ts-ignore
|
|
1979
|
+
parentWrapper.children.push(itemWrapper);
|
|
1980
|
+
//@ts-ignore
|
|
1981
|
+
itemWrapper.item = item;
|
|
1982
|
+
}
|
|
1983
|
+
const topLevelWrappers = flatMap(Array.from(wrapperMap.values()).filter(wrapper => wrapper.parent === null), wrapper => wrapper.children);
|
|
1984
|
+
return unwrapRecursively(topLevelWrappers);
|
|
1985
|
+
function unwrapRecursively(wrapperArray) {
|
|
1986
|
+
const result = [];
|
|
1987
|
+
for (const wrapper of wrapperArray) {
|
|
1988
|
+
if (wrapper.children.length === 0) {
|
|
1989
|
+
result.push(wrapper.item);
|
|
1990
|
+
}
|
|
1991
|
+
else {
|
|
1992
|
+
result.push({
|
|
1993
|
+
...wrapper.item,
|
|
1994
|
+
children: unwrapRecursively(wrapper.children)
|
|
1995
|
+
});
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
return result;
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
/**
|
|
2002
|
+
* 扁平化数组转换成树(效率高于buildTree)
|
|
2003
|
+
* @param {any[]} list
|
|
2004
|
+
* @param {IFieldOptions} options
|
|
2005
|
+
* @returns {any[]}
|
|
2006
|
+
*/
|
|
2007
|
+
function formatTree(list, options = defaultFieldOptions) {
|
|
2008
|
+
const { keyField, childField, pidField } = options;
|
|
2009
|
+
const treeArr = [];
|
|
2010
|
+
const sourceMap = {};
|
|
2011
|
+
list.forEach(item => {
|
|
2012
|
+
sourceMap[item[keyField]] = item;
|
|
2013
|
+
});
|
|
2014
|
+
list.forEach(item => {
|
|
2015
|
+
const parent = sourceMap[item[pidField]];
|
|
2016
|
+
if (parent) {
|
|
2017
|
+
(parent[childField] || (parent[childField] = [])).push(item);
|
|
2018
|
+
}
|
|
2019
|
+
else {
|
|
2020
|
+
treeArr.push(item);
|
|
2021
|
+
}
|
|
2022
|
+
});
|
|
2023
|
+
return treeArr;
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2000
2026
|
exports.HEX_POOL = HEX_POOL;
|
|
2001
2027
|
exports.STRING_ARABIC_NUMERALS = STRING_ARABIC_NUMERALS;
|
|
2002
2028
|
exports.STRING_LOWERCASE_ALPHA = STRING_LOWERCASE_ALPHA;
|
|
@@ -2030,6 +2056,7 @@
|
|
|
2030
2056
|
exports.forEachDeep = forEachDeep;
|
|
2031
2057
|
exports.formatDate = formatDate;
|
|
2032
2058
|
exports.formatNumber = formatNumber;
|
|
2059
|
+
exports.formatTree = formatTree;
|
|
2033
2060
|
exports.genCanvasWM = genCanvasWM;
|
|
2034
2061
|
exports.getComputedCssVal = getComputedCssVal;
|
|
2035
2062
|
exports.getGlobal = getGlobal;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sculp-js",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"packageManager": "npm@8.19.2",
|
|
5
5
|
"description": "js工具库",
|
|
6
6
|
"scripts": {
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"main": "lib/cjs/index.js",
|
|
23
23
|
"module": "lib/es/index.js",
|
|
24
24
|
"browser": "lib/umd/index.js",
|
|
25
|
+
"unpkg": "lib/umd/index.js",
|
|
25
26
|
"types": "lib/index.d.ts",
|
|
26
27
|
"typings": "lib/index.d.ts",
|
|
27
28
|
"exports": {
|
|
@@ -43,6 +44,8 @@
|
|
|
43
44
|
"node": ">=16"
|
|
44
45
|
},
|
|
45
46
|
"repository": "git@github.com:chandq/sculp-js.git",
|
|
47
|
+
"license": "MIT",
|
|
48
|
+
"homepage": "https://github.com/chandq/sculp-js#readme",
|
|
46
49
|
"dependencies": {
|
|
47
50
|
"bezier-easing": "^2.1.0",
|
|
48
51
|
"core-js": "^3.33.0"
|