sculp-js 1.1.0 → 1.2.1

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.
Files changed (50) hide show
  1. package/README.md +6 -2
  2. package/lib/cjs/array.js +1 -178
  3. package/lib/cjs/async.js +1 -1
  4. package/lib/cjs/clipboard.js +1 -1
  5. package/lib/cjs/cookie.js +1 -1
  6. package/lib/cjs/date.js +1 -1
  7. package/lib/cjs/dom.js +1 -1
  8. package/lib/cjs/download.js +1 -1
  9. package/lib/cjs/easing.js +1 -1
  10. package/lib/cjs/file.js +1 -1
  11. package/lib/cjs/func.js +1 -1
  12. package/lib/cjs/index.js +7 -4
  13. package/lib/cjs/number.js +1 -1
  14. package/lib/cjs/object.js +1 -1
  15. package/lib/cjs/path.js +1 -1
  16. package/lib/cjs/qs.js +1 -1
  17. package/lib/cjs/random.js +1 -1
  18. package/lib/cjs/string.js +1 -1
  19. package/lib/cjs/tooltip.js +1 -1
  20. package/lib/cjs/tree.js +283 -0
  21. package/lib/cjs/type.js +1 -1
  22. package/lib/cjs/unique.js +1 -1
  23. package/lib/cjs/url.js +1 -1
  24. package/lib/cjs/watermark.js +1 -1
  25. package/lib/es/array.js +2 -176
  26. package/lib/es/async.js +1 -1
  27. package/lib/es/clipboard.js +1 -1
  28. package/lib/es/cookie.js +1 -1
  29. package/lib/es/date.js +1 -1
  30. package/lib/es/dom.js +1 -1
  31. package/lib/es/download.js +1 -1
  32. package/lib/es/easing.js +1 -1
  33. package/lib/es/file.js +1 -1
  34. package/lib/es/func.js +1 -1
  35. package/lib/es/index.js +3 -2
  36. package/lib/es/number.js +1 -1
  37. package/lib/es/object.js +1 -1
  38. package/lib/es/path.js +1 -1
  39. package/lib/es/qs.js +1 -1
  40. package/lib/es/random.js +1 -1
  41. package/lib/es/string.js +1 -1
  42. package/lib/es/tooltip.js +1 -1
  43. package/lib/es/tree.js +277 -0
  44. package/lib/es/type.js +1 -1
  45. package/lib/es/unique.js +1 -1
  46. package/lib/es/url.js +1 -1
  47. package/lib/es/watermark.js +1 -1
  48. package/lib/index.d.ts +86 -64
  49. package/lib/umd/index.js +273 -175
  50. package/package.json +7 -3
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>): Object;
363
+ declare function cloneDeep(obj: Object, map?: WeakMap<object, any>): AnyObject | AnyArray;
426
364
 
427
365
  /**
428
366
  * 标准化路径
@@ -745,4 +683,88 @@ declare const tooltipEvent: {
745
683
  handleMouseLeave: typeof handleMouseLeave;
746
684
  };
747
685
 
748
- export { type AnyArray, type AnyFunc, type AnyObject, type ArrayElements, type DateObj, type DateValue, type DebounceFunc, type FileType, HEX_POOL, type ICanvasWM, 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, 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 };
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
+ /**
701
+ * 深度优先遍历的Map函数(支持continue和break操作), 可用于insert tree item 和 remove tree item
702
+ * @param {ArrayLike<V>} tree 树形数据
703
+ * @param {Function} iterator 迭代函数, 返回值为true时continue, 返回值为false时break
704
+ * @param {string} children 定制子元素的key
705
+ * @param {boolean} isReverse 是否反向遍历
706
+ * @returns {any[]} 新的一棵树
707
+ */
708
+ declare function forEachMap<V>(tree: ArrayLike<V>, iterator: (val: V, i: number, currentArr: ArrayLike<V>, tree: ArrayLike<V>, parent: V | null, level: number) => boolean | any, children?: string, isReverse?: boolean): any[];
709
+ type IdLike = number | string;
710
+ interface ITreeConf {
711
+ id: string | number;
712
+ children: string;
713
+ }
714
+ /**
715
+ * 在树中找到 id 为某个值的节点,并返回上游的所有父级节点
716
+ *
717
+ * @param {ArrayLike<T>} tree - 树形数据
718
+ * @param {IdLike} nodeId - 元素ID
719
+ * @param {ITreeConf} config - 迭代配置项
720
+ * @returns {[IdLike[], ITreeItem<V>[]]} - 由parentId...childId, parentObject-childObject组成的二维数组
721
+ */
722
+ declare function searchTreeById<V>(tree: ArrayLike<V>, nodeId: IdLike, config?: ITreeConf): [IdLike[], ArrayLike<V>[]];
723
+ type WithChildren<T> = T & {
724
+ children?: WithChildren<T>[];
725
+ };
726
+ /**
727
+ * 根据 idProp 与 parentIdProp 从对象数组中构建对应的树
728
+ * 当 A[parentIdProp] === B[idProp] 时,对象A会被移动到对象B的children。
729
+ * 当一个对象的 parentIdProp 不与其他对象的 idProp 字段相等时,该对象被作为树的顶层节点
730
+ * @param {string} idProp 元素ID
731
+ * @param {string} parentIdProp 父元素ID
732
+ * @param {object[]} items 一维数组
733
+ * @returns {WithChildren<T>[]} 树
734
+ * @example
735
+ * const array = [
736
+ * { id: 'node-1', parent: 'root' },
737
+ * { id: 'node-2', parent: 'root' },
738
+ * { id: 'node-3', parent: 'node-2' },
739
+ * { id: 'node-4', parent: 'node-2' },
740
+ * { id: 'node-5', parent: 'node-4' },
741
+ * ]
742
+ * const tree = buildTree('id', 'parent', array)
743
+ * expect(tree).toEqual([
744
+ * { id: 'node-1', parent: 'root' },
745
+ * {
746
+ * id: 'node-2',
747
+ * parent: 'root',
748
+ * children: [
749
+ * { id: 'node-3', parent: 'node-2' },
750
+ * {
751
+ * id: 'node-4',
752
+ * parent: 'node-2',
753
+ * children: [{ id: 'node-5', parent: 'node-4' }],
754
+ * },
755
+ * ],
756
+ * },
757
+ * ])
758
+ */
759
+ declare function buildTree<ID extends string, PID extends string, T extends {
760
+ [key in ID | PID]: string;
761
+ }>(idProp: ID, parentIdProp: PID, items: T[]): WithChildren<T>[];
762
+ /**
763
+ * 扁平化数组转换成树(效率高于buildTree)
764
+ * @param {any[]} list
765
+ * @param {IFieldOptions} options
766
+ * @returns {any[]}
767
+ */
768
+ declare function formatTree(list: any[], options?: IFieldOptions): any[];
769
+
770
+ 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, forEachMap, 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.1
2
+ * sculp-js v1.2.1
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,276 @@
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
+ * 深度优先遍历的Map函数(支持continue和break操作), 可用于insert tree item 和 remove tree item
1885
+ * @param {ArrayLike<V>} tree 树形数据
1886
+ * @param {Function} iterator 迭代函数, 返回值为true时continue, 返回值为false时break
1887
+ * @param {string} children 定制子元素的key
1888
+ * @param {boolean} isReverse 是否反向遍历
1889
+ * @returns {any[]} 新的一棵树
1890
+ */
1891
+ function forEachMap(tree, iterator, children = 'children', isReverse = false) {
1892
+ let level = 0, isBreak = false;
1893
+ const newTree = [];
1894
+ const walk = (arr, parent, newTree) => {
1895
+ if (isReverse) {
1896
+ for (let i = arr.length - 1; i >= 0; i--) {
1897
+ if (isBreak) {
1898
+ break;
1899
+ }
1900
+ const re = iterator(arr[i], i, arr, tree, parent, level);
1901
+ if (re === false) {
1902
+ isBreak = true;
1903
+ break;
1904
+ }
1905
+ else if (re === true) {
1906
+ continue;
1907
+ }
1908
+ newTree.push(re);
1909
+ // @ts-ignore
1910
+ if (arr[i] && Array.isArray(arr[i][children])) {
1911
+ ++level;
1912
+ newTree[newTree.length - 1][children] = [];
1913
+ // @ts-ignore
1914
+ walk(arr[i][children], arr[i], newTree[newTree.length - 1][children]);
1915
+ }
1916
+ else {
1917
+ // children非有效数组时,移除该属性字段
1918
+ delete re[children];
1919
+ }
1920
+ }
1921
+ }
1922
+ else {
1923
+ for (let i = 0; i < arr.length; i++) {
1924
+ if (isBreak) {
1925
+ break;
1926
+ }
1927
+ const re = iterator(arr[i], i, arr, tree, parent, level);
1928
+ if (re === false) {
1929
+ isBreak = true;
1930
+ break;
1931
+ }
1932
+ else if (re === true) {
1933
+ continue;
1934
+ }
1935
+ newTree.push(re);
1936
+ // @ts-ignore
1937
+ if (arr[i] && Array.isArray(arr[i][children])) {
1938
+ ++level;
1939
+ newTree[newTree.length - 1][children] = [];
1940
+ // @ts-ignore
1941
+ walk(arr[i][children], arr[i], newTree[newTree.length - 1][children]);
1942
+ }
1943
+ else {
1944
+ // children非有效数组时,移除该属性字段
1945
+ delete re[children];
1946
+ }
1947
+ }
1948
+ }
1949
+ };
1950
+ walk(tree, null, newTree);
1951
+ return newTree;
1952
+ }
1953
+ /**
1954
+ * 在树中找到 id 为某个值的节点,并返回上游的所有父级节点
1955
+ *
1956
+ * @param {ArrayLike<T>} tree - 树形数据
1957
+ * @param {IdLike} nodeId - 元素ID
1958
+ * @param {ITreeConf} config - 迭代配置项
1959
+ * @returns {[IdLike[], ITreeItem<V>[]]} - 由parentId...childId, parentObject-childObject组成的二维数组
1960
+ */
1961
+ function searchTreeById(tree, nodeId, config) {
1962
+ const { children = 'children', id = 'id' } = config || {};
1963
+ const toFlatArray = (tree, parentId, parent) => {
1964
+ return tree.reduce((t, _) => {
1965
+ const child = _[children];
1966
+ return [
1967
+ ...t,
1968
+ parentId ? { ..._, parentId, parent } : _,
1969
+ ...(child && child.length ? toFlatArray(child, _[id], _) : [])
1970
+ ];
1971
+ }, []);
1972
+ };
1973
+ const getIds = (flatArray) => {
1974
+ let child = flatArray.find(_ => _[id] === nodeId);
1975
+ const { parent, parentId, ...other } = child;
1976
+ let ids = [nodeId], nodes = [other];
1977
+ while (child && child.parentId) {
1978
+ ids = [child.parentId, ...ids];
1979
+ nodes = [child.parent, ...nodes];
1980
+ child = flatArray.find(_ => _[id] === child.parentId); // eslint-disable-line
1981
+ }
1982
+ return [ids, nodes];
1983
+ };
1984
+ return getIds(toFlatArray(tree));
1985
+ }
1986
+ /**
1987
+ * 使用迭代函数转换数组
1988
+ * @param {T} array
1989
+ * @param {Function} callback 迭代函数
1990
+ * @returns {Array}
1991
+ */
1992
+ function flatMap(array, callback) {
1993
+ const result = [];
1994
+ array.forEach((value, index) => {
1995
+ result.push(...callback(value, index, array));
1996
+ });
1997
+ return result;
1998
+ }
1999
+ /**
2000
+ * 根据 idProp 与 parentIdProp 从对象数组中构建对应的树
2001
+ * 当 A[parentIdProp] === B[idProp] 时,对象A会被移动到对象B的children。
2002
+ * 当一个对象的 parentIdProp 不与其他对象的 idProp 字段相等时,该对象被作为树的顶层节点
2003
+ * @param {string} idProp 元素ID
2004
+ * @param {string} parentIdProp 父元素ID
2005
+ * @param {object[]} items 一维数组
2006
+ * @returns {WithChildren<T>[]} 树
2007
+ * @example
2008
+ * const array = [
2009
+ * { id: 'node-1', parent: 'root' },
2010
+ * { id: 'node-2', parent: 'root' },
2011
+ * { id: 'node-3', parent: 'node-2' },
2012
+ * { id: 'node-4', parent: 'node-2' },
2013
+ * { id: 'node-5', parent: 'node-4' },
2014
+ * ]
2015
+ * const tree = buildTree('id', 'parent', array)
2016
+ * expect(tree).toEqual([
2017
+ * { id: 'node-1', parent: 'root' },
2018
+ * {
2019
+ * id: 'node-2',
2020
+ * parent: 'root',
2021
+ * children: [
2022
+ * { id: 'node-3', parent: 'node-2' },
2023
+ * {
2024
+ * id: 'node-4',
2025
+ * parent: 'node-2',
2026
+ * children: [{ id: 'node-5', parent: 'node-4' }],
2027
+ * },
2028
+ * ],
2029
+ * },
2030
+ * ])
2031
+ */
2032
+ function buildTree(idProp, parentIdProp, items) {
2033
+ const wrapperMap = new Map();
2034
+ const ensure = (id) => {
2035
+ if (wrapperMap.has(id)) {
2036
+ return wrapperMap.get(id);
2037
+ }
2038
+ //@ts-ignore
2039
+ const wrapper = { id, parent: null, item: null, children: [] };
2040
+ wrapperMap.set(id, wrapper);
2041
+ return wrapper;
2042
+ };
2043
+ for (const item of items) {
2044
+ const parentWrapper = ensure(item[parentIdProp]);
2045
+ const itemWrapper = ensure(item[idProp]);
2046
+ //@ts-ignore
2047
+ itemWrapper.parent = parentWrapper;
2048
+ //@ts-ignore
2049
+ parentWrapper.children.push(itemWrapper);
2050
+ //@ts-ignore
2051
+ itemWrapper.item = item;
2052
+ }
2053
+ const topLevelWrappers = flatMap(Array.from(wrapperMap.values()).filter(wrapper => wrapper.parent === null), wrapper => wrapper.children);
2054
+ return unwrapRecursively(topLevelWrappers);
2055
+ function unwrapRecursively(wrapperArray) {
2056
+ const result = [];
2057
+ for (const wrapper of wrapperArray) {
2058
+ if (wrapper.children.length === 0) {
2059
+ result.push(wrapper.item);
2060
+ }
2061
+ else {
2062
+ result.push({
2063
+ ...wrapper.item,
2064
+ children: unwrapRecursively(wrapper.children)
2065
+ });
2066
+ }
2067
+ }
2068
+ return result;
2069
+ }
2070
+ }
2071
+ /**
2072
+ * 扁平化数组转换成树(效率高于buildTree)
2073
+ * @param {any[]} list
2074
+ * @param {IFieldOptions} options
2075
+ * @returns {any[]}
2076
+ */
2077
+ function formatTree(list, options = defaultFieldOptions) {
2078
+ const { keyField, childField, pidField } = options;
2079
+ const treeArr = [];
2080
+ const sourceMap = {};
2081
+ list.forEach(item => {
2082
+ sourceMap[item[keyField]] = item;
2083
+ });
2084
+ list.forEach(item => {
2085
+ const parent = sourceMap[item[pidField]];
2086
+ if (parent) {
2087
+ (parent[childField] || (parent[childField] = [])).push(item);
2088
+ }
2089
+ else {
2090
+ treeArr.push(item);
2091
+ }
2092
+ });
2093
+ return treeArr;
2094
+ }
2095
+
2000
2096
  exports.HEX_POOL = HEX_POOL;
2001
2097
  exports.STRING_ARABIC_NUMERALS = STRING_ARABIC_NUMERALS;
2002
2098
  exports.STRING_LOWERCASE_ALPHA = STRING_LOWERCASE_ALPHA;
@@ -2028,8 +2124,10 @@
2028
2124
  exports.downloadHref = downloadHref;
2029
2125
  exports.downloadURL = downloadURL;
2030
2126
  exports.forEachDeep = forEachDeep;
2127
+ exports.forEachMap = forEachMap;
2031
2128
  exports.formatDate = formatDate;
2032
2129
  exports.formatNumber = formatNumber;
2130
+ exports.formatTree = formatTree;
2033
2131
  exports.genCanvasWM = genCanvasWM;
2034
2132
  exports.getComputedCssVal = getComputedCssVal;
2035
2133
  exports.getGlobal = getGlobal;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sculp-js",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
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": {
@@ -36,13 +37,16 @@
36
37
  "lib"
37
38
  ],
38
39
  "keywords": [
39
- "typescript",
40
- "js-utils"
40
+ "sculp-js",
41
+ "js-utils",
42
+ "typescript"
41
43
  ],
42
44
  "engines": {
43
45
  "node": ">=16"
44
46
  },
45
47
  "repository": "git@github.com:chandq/sculp-js.git",
48
+ "license": "MIT",
49
+ "homepage": "https://github.com/chandq/sculp-js#readme",
46
50
  "dependencies": {
47
51
  "bezier-easing": "^2.1.0",
48
52
  "core-js": "^3.33.0"