pen-it 1.0.5 → 1.0.7
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 +81 -2
- package/dist/array/index.d.ts +62 -0
- package/dist/browser/index.d.ts +15 -0
- package/dist/index.cjs +110 -0
- package/dist/index.js +104 -1
- package/package.json +54 -62
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# pen-it
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
用“笔”锚定记忆,一个轻量级前端工具库,覆盖类型检查、日期格式化、数字处理、深克隆、本地存储、Cookie 及浏览器 API。零依赖,支持 Tree-shaking,让常用函数信手拈来。
|
|
4
4
|
|
|
5
5
|
## 安装
|
|
6
6
|
|
|
@@ -418,6 +418,45 @@ import { isArray, formatFull, deepClone, unique } from 'pen-it'
|
|
|
418
418
|
toArray(document.querySelectorAll('div'))
|
|
419
419
|
```
|
|
420
420
|
|
|
421
|
+
- **`mergeArrays(...arrays)`** — 合并多个数组。
|
|
422
|
+
|
|
423
|
+
```ts
|
|
424
|
+
mergeArrays([1, 2], [3, 4], [5]) // [1, 2, 3, 4, 5]
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
- **`flatten(arr, depth?)`** — 多维数组扁平化到指定层级(默认 1 层,传 `Infinity` 完全展开)。
|
|
428
|
+
|
|
429
|
+
```ts
|
|
430
|
+
flatten([1, [2, [3, 4]], 5]) // [1, 2, [3, 4], 5]
|
|
431
|
+
flatten([1, [2, [3, 4]], 5], Infinity) // [1, 2, 3, 4, 5]
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
- **`arrFind(arr, key, value)`** — 对象数组中按 key-value 查找第一个匹配项。
|
|
435
|
+
|
|
436
|
+
```ts
|
|
437
|
+
arrFind([{ id: 1 }, { id: 2 }], 'id', 2) // { id: 2 }
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
- **`groupBy(arr, key)`** — 对象数组按指定属性分组。
|
|
441
|
+
|
|
442
|
+
```ts
|
|
443
|
+
groupBy([{ type: 'fruit' }, { type: 'vegetable' }], 'type')
|
|
444
|
+
// => { fruit: [...], vegetable: [...] }
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
- **`filterEmptyValues(obj)`** — 移除对象中值为空(null / undefined / 空字符串)的属性。
|
|
448
|
+
|
|
449
|
+
```ts
|
|
450
|
+
filterEmptyValues({ a: 1, b: '', c: null }) // { a: 1 }
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
- **`createRange(length, mapFn?)`** — 快速生成范围数组。
|
|
454
|
+
|
|
455
|
+
```ts
|
|
456
|
+
createRange(5) // [0, 1, 2, 3, 4]
|
|
457
|
+
createRange(3, (i) => i * 2) // [0, 2, 4]
|
|
458
|
+
```
|
|
459
|
+
|
|
421
460
|
---
|
|
422
461
|
|
|
423
462
|
### Cookie — `cookie`
|
|
@@ -504,6 +543,17 @@ import { isArray, formatFull, deepClone, unique } from 'pen-it'
|
|
|
504
543
|
const off = onScroll(y => console.log(y))
|
|
505
544
|
```
|
|
506
545
|
|
|
546
|
+
#### 可视区域检测
|
|
547
|
+
|
|
548
|
+
- **`observeIntersection(target, onEnter, onLeave?, options?)`** — 监听元素进入/离开可视区域,返回清理函数。
|
|
549
|
+
|
|
550
|
+
```ts
|
|
551
|
+
const cleanup = observeIntersection(
|
|
552
|
+
document.querySelector('.footer')!,
|
|
553
|
+
() => loadMore(10),
|
|
554
|
+
);
|
|
555
|
+
```
|
|
556
|
+
|
|
507
557
|
---
|
|
508
558
|
|
|
509
559
|
### 防抖与节流 — `control`
|
|
@@ -522,4 +572,33 @@ import { isArray, formatFull, deepClone, unique } from 'pen-it'
|
|
|
522
572
|
const fn = throttle((val: string) => console.log(val), 500)
|
|
523
573
|
fn('a'); fn('b'); fn('c')
|
|
524
574
|
// => 'a',500ms 后输出 'c'
|
|
525
|
-
```
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
---
|
|
578
|
+
|
|
579
|
+
## 更新日志
|
|
580
|
+
|
|
581
|
+
### v1.0.7
|
|
582
|
+
|
|
583
|
+
- **体积优化** — 移除JSDoc `@example` 示例注释(共 294 行),减少包体积
|
|
584
|
+
- **文档** — README 底部新增更新日志模块
|
|
585
|
+
|
|
586
|
+
### v1.0.6
|
|
587
|
+
|
|
588
|
+
- **数组模块** — 新增 `mergeArrays` 数组合并、`flatten` 数组扁平化(支持任意层级)、`arrFind` 数组项查询、`groupBy` JSON 数组分组、`filterEmptyValues` 对象移除空值属性、`createRange` 快速生成范围数组
|
|
589
|
+
- **浏览器模块** — 新增 `observeIntersection` 可视区域检测(基于 IntersectionObserver)
|
|
590
|
+
|
|
591
|
+
### v1.0.5
|
|
592
|
+
|
|
593
|
+
- **控制模块** — 新增 `debounce` 防抖与 `throttle` 节流
|
|
594
|
+
|
|
595
|
+
### v1.0.4
|
|
596
|
+
|
|
597
|
+
- 初始版本,包含以下模块:
|
|
598
|
+
- **类型判断** — `isArray` `isObject` `is` `isFunction` `isAsyncFunction` `isPromise` `isDate` `isNumber` `isInt` `isFloat` `isString` `isBoolean` `isSymbol` `isPrimitive` `isNull` `isDef` `isUnDef` `isNullOrUnDef` `isEmpty` `isEmptySv` `isEqual` `isHexColor` `isValidEmail` `isPC` `isWindow` `isElement` `isIOS` 共 27 个函数
|
|
599
|
+
- **格式化** — 日期时间(`formatFull` `formatFullReplace` `formatYMD` `formatWeek`)、数字货币(`formatRmb` `formatNum` `percentCN` `compactEN` `compactCN` `signed`)、字符串(`maskPhone` `spacePhone` `capitalize` `kebabToCamel` `camelToKebab` `toCamel` `firstUpper` `firstLower` `reverse` `trimAll` `truncate` `truncateByWords`)共 22 个函数
|
|
600
|
+
- **存储** — `localGet` `localSet` `localRm` `localClear` 共 4 个函数
|
|
601
|
+
- **拷贝** — `deepClone` `deepCloneWithJSON` `shallowClone` 共 3 个函数
|
|
602
|
+
- **数组** — `unique` `uniqueByKey` `sortNumAsc` `sortNumDesc` `sortByKey` `toArray` 共 6 个函数
|
|
603
|
+
- **Cookie** — `setCookie` `getCookie` `delCookie` 共 3 个函数
|
|
604
|
+
- **浏览器** — `getUrlParams` `getUrlParam` `toQueryString` `copyToClipboard` `downloadFile` `exportJSON` `scrollToTop` `scrollToBottom` `onScroll` 共 9 个函数
|
package/dist/array/index.d.ts
CHANGED
|
@@ -55,3 +55,65 @@ export declare const sortByKey: <T extends Record<string, any>>(arr: T[], key: k
|
|
|
55
55
|
* // => [div, div, ...]
|
|
56
56
|
*/
|
|
57
57
|
export declare const toArray: <T>(arrayLike: ArrayLike<T>) => T[];
|
|
58
|
+
/**
|
|
59
|
+
* 合并多个数组
|
|
60
|
+
* @param arrays - 一个或多个数组
|
|
61
|
+
* @returns 合并后的新数组
|
|
62
|
+
* @example
|
|
63
|
+
* mergeArrays([1, 2], [3, 4], [5])
|
|
64
|
+
* // => [1, 2, 3, 4, 5]
|
|
65
|
+
*/
|
|
66
|
+
export declare const mergeArrays: <T>(...arrays: T[][]) => T[];
|
|
67
|
+
/**
|
|
68
|
+
* 将多维数组扁平化到指定层级
|
|
69
|
+
* @param arr - 多维数组
|
|
70
|
+
* @param depth - 扁平化深度,默认 1(仅展开一层)。传 Infinity 可完全扁平化为一维
|
|
71
|
+
* @returns 扁平化后的数组
|
|
72
|
+
* @example
|
|
73
|
+
* flatten([1, [2, [3, 4]], 5])
|
|
74
|
+
* // => [1, 2, [3, 4], 5]
|
|
75
|
+
* @example
|
|
76
|
+
* flatten([1, [2, [3, 4]], 5], Infinity)
|
|
77
|
+
* // => [1, 2, 3, 4, 5]
|
|
78
|
+
*/
|
|
79
|
+
export declare const flatten: <T>(arr: any[], depth?: number) => T[];
|
|
80
|
+
/**
|
|
81
|
+
* 在对象数组中按 key-value 查找第一个匹配项
|
|
82
|
+
* @param arr - 对象数组
|
|
83
|
+
* @param key - 要匹配的键名
|
|
84
|
+
* @param value - 要匹配的键值
|
|
85
|
+
* @returns 第一个匹配的对象,无匹配则返回 undefined
|
|
86
|
+
* @example
|
|
87
|
+
* arrFind([{ id: 1, name: "a" }, { id: 2, name: "b" }], "id", 2)
|
|
88
|
+
* // => { id: 2, name: "b" }
|
|
89
|
+
*/
|
|
90
|
+
export declare const arrFind: <T extends Record<string, any>>(arr: T[], key: keyof T, value: T[keyof T]) => T | undefined;
|
|
91
|
+
/**
|
|
92
|
+
* 将对象数组按指定属性分组
|
|
93
|
+
* @param arr - 对象数组
|
|
94
|
+
* @param key - 用于分组的属性名
|
|
95
|
+
* @returns 以属性值为键、对应数组为值的对象
|
|
96
|
+
* @example
|
|
97
|
+
* groupBy([{ type: "fruit", name: "apple" }, { type: "vegetable", name: "carrot" }], "type")
|
|
98
|
+
* // => { fruit: [{ type: "fruit", name: "apple" }], vegetable: [{ type: "vegetable", name: "carrot" }] }
|
|
99
|
+
*/
|
|
100
|
+
export declare const groupBy: <T extends Record<string, any>>(arr: T[], key: keyof T) => Record<string, T[]>;
|
|
101
|
+
/**
|
|
102
|
+
* 移除对象中值为空(null / undefined / 空字符串)的属性
|
|
103
|
+
* @param obj - 待过滤的对象
|
|
104
|
+
* @returns 过滤后的新对象
|
|
105
|
+
* @example
|
|
106
|
+
* filterEmptyValues({ a: 1, b: "", c: null, d: 3 })
|
|
107
|
+
* // => { a: 1, d: 3 }
|
|
108
|
+
*/
|
|
109
|
+
export declare const filterEmptyValues: <T extends Record<string, any>>(obj: T) => Partial<T>;
|
|
110
|
+
/**
|
|
111
|
+
* 根据长度和映射函数快速生成数组
|
|
112
|
+
* @param length - 数组长度
|
|
113
|
+
* @param mapFn - 映射函数,接收索引返回元素值,默认为返回索引
|
|
114
|
+
* @returns 生成的数组
|
|
115
|
+
* @example
|
|
116
|
+
* createRange(5)
|
|
117
|
+
* // => [0, 1, 2, 3, 4]
|
|
118
|
+
*/
|
|
119
|
+
export declare const createRange: <T = number>(length: number, mapFn?: (index: number) => T) => T[];
|
package/dist/browser/index.d.ts
CHANGED
|
@@ -76,3 +76,18 @@ export declare const scrollToBottom: (behavior?: ScrollBehavior) => void;
|
|
|
76
76
|
* cleanup(); // 停止监听
|
|
77
77
|
*/
|
|
78
78
|
export declare const onScroll: (callback: (scrollY: number) => void) => () => void;
|
|
79
|
+
/**
|
|
80
|
+
* 监听目标元素是否进入/离开可视区域
|
|
81
|
+
* @param target - 目标 DOM 元素
|
|
82
|
+
* @param onEnter - 元素进入可视区域时的回调
|
|
83
|
+
* @param onLeave - 元素离开可视区域时的回调(可选)
|
|
84
|
+
* @param options - IntersectionObserver 配置项
|
|
85
|
+
* @returns 清理函数,调用后停止观察
|
|
86
|
+
* @example
|
|
87
|
+
* const cleanup = observeIntersection(
|
|
88
|
+
* document.querySelector(".footer")!,
|
|
89
|
+
* () => loadMore(10),
|
|
90
|
+
* );
|
|
91
|
+
* cleanup(); // 停止观察
|
|
92
|
+
*/
|
|
93
|
+
export declare const observeIntersection: (target: Element, onEnter: (entry: IntersectionObserverEntry) => void, onLeave?: (entry: IntersectionObserverEntry) => void, options?: IntersectionObserverInit) => () => void;
|
package/dist/index.cjs
CHANGED
|
@@ -844,6 +844,85 @@ const sortByKey = (arr, key, order = "asc") => {
|
|
|
844
844
|
const toArray = (arrayLike) => {
|
|
845
845
|
return Array.from(arrayLike);
|
|
846
846
|
};
|
|
847
|
+
/**
|
|
848
|
+
* 合并多个数组
|
|
849
|
+
* @param arrays - 一个或多个数组
|
|
850
|
+
* @returns 合并后的新数组
|
|
851
|
+
* @example
|
|
852
|
+
* mergeArrays([1, 2], [3, 4], [5])
|
|
853
|
+
* // => [1, 2, 3, 4, 5]
|
|
854
|
+
*/
|
|
855
|
+
const mergeArrays = (...arrays) => {
|
|
856
|
+
return [].concat(...arrays);
|
|
857
|
+
};
|
|
858
|
+
/**
|
|
859
|
+
* 将多维数组扁平化到指定层级
|
|
860
|
+
* @param arr - 多维数组
|
|
861
|
+
* @param depth - 扁平化深度,默认 1(仅展开一层)。传 Infinity 可完全扁平化为一维
|
|
862
|
+
* @returns 扁平化后的数组
|
|
863
|
+
* @example
|
|
864
|
+
* flatten([1, [2, [3, 4]], 5])
|
|
865
|
+
* // => [1, 2, [3, 4], 5]
|
|
866
|
+
* @example
|
|
867
|
+
* flatten([1, [2, [3, 4]], 5], Infinity)
|
|
868
|
+
* // => [1, 2, 3, 4, 5]
|
|
869
|
+
*/
|
|
870
|
+
const flatten = (arr, depth = 1) => {
|
|
871
|
+
return arr.flat(depth);
|
|
872
|
+
};
|
|
873
|
+
/**
|
|
874
|
+
* 在对象数组中按 key-value 查找第一个匹配项
|
|
875
|
+
* @param arr - 对象数组
|
|
876
|
+
* @param key - 要匹配的键名
|
|
877
|
+
* @param value - 要匹配的键值
|
|
878
|
+
* @returns 第一个匹配的对象,无匹配则返回 undefined
|
|
879
|
+
* @example
|
|
880
|
+
* arrFind([{ id: 1, name: "a" }, { id: 2, name: "b" }], "id", 2)
|
|
881
|
+
* // => { id: 2, name: "b" }
|
|
882
|
+
*/
|
|
883
|
+
const arrFind = (arr, key, value) => {
|
|
884
|
+
return arr.find((item) => item[key] === value);
|
|
885
|
+
};
|
|
886
|
+
/**
|
|
887
|
+
* 将对象数组按指定属性分组
|
|
888
|
+
* @param arr - 对象数组
|
|
889
|
+
* @param key - 用于分组的属性名
|
|
890
|
+
* @returns 以属性值为键、对应数组为值的对象
|
|
891
|
+
* @example
|
|
892
|
+
* groupBy([{ type: "fruit", name: "apple" }, { type: "vegetable", name: "carrot" }], "type")
|
|
893
|
+
* // => { fruit: [{ type: "fruit", name: "apple" }], vegetable: [{ type: "vegetable", name: "carrot" }] }
|
|
894
|
+
*/
|
|
895
|
+
const groupBy = (arr, key) => {
|
|
896
|
+
return arr.reduce((acc, item) => {
|
|
897
|
+
const groupKey = String(item[key]);
|
|
898
|
+
if (!acc[groupKey]) acc[groupKey] = [];
|
|
899
|
+
acc[groupKey].push(item);
|
|
900
|
+
return acc;
|
|
901
|
+
}, {});
|
|
902
|
+
};
|
|
903
|
+
/**
|
|
904
|
+
* 移除对象中值为空(null / undefined / 空字符串)的属性
|
|
905
|
+
* @param obj - 待过滤的对象
|
|
906
|
+
* @returns 过滤后的新对象
|
|
907
|
+
* @example
|
|
908
|
+
* filterEmptyValues({ a: 1, b: "", c: null, d: 3 })
|
|
909
|
+
* // => { a: 1, d: 3 }
|
|
910
|
+
*/
|
|
911
|
+
const filterEmptyValues = (obj) => {
|
|
912
|
+
return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== null && v !== void 0 && v !== ""));
|
|
913
|
+
};
|
|
914
|
+
/**
|
|
915
|
+
* 根据长度和映射函数快速生成数组
|
|
916
|
+
* @param length - 数组长度
|
|
917
|
+
* @param mapFn - 映射函数,接收索引返回元素值,默认为返回索引
|
|
918
|
+
* @returns 生成的数组
|
|
919
|
+
* @example
|
|
920
|
+
* createRange(5)
|
|
921
|
+
* // => [0, 1, 2, 3, 4]
|
|
922
|
+
*/
|
|
923
|
+
const createRange = (length, mapFn) => {
|
|
924
|
+
return Array.from({ length }, (_, i) => mapFn ? mapFn(i) : i);
|
|
925
|
+
};
|
|
847
926
|
//#endregion
|
|
848
927
|
//#region src/cookie/index.ts
|
|
849
928
|
/**
|
|
@@ -1019,6 +1098,30 @@ const onScroll = (callback) => {
|
|
|
1019
1098
|
window.addEventListener("scroll", handler);
|
|
1020
1099
|
return () => window.removeEventListener("scroll", handler);
|
|
1021
1100
|
};
|
|
1101
|
+
/**
|
|
1102
|
+
* 监听目标元素是否进入/离开可视区域
|
|
1103
|
+
* @param target - 目标 DOM 元素
|
|
1104
|
+
* @param onEnter - 元素进入可视区域时的回调
|
|
1105
|
+
* @param onLeave - 元素离开可视区域时的回调(可选)
|
|
1106
|
+
* @param options - IntersectionObserver 配置项
|
|
1107
|
+
* @returns 清理函数,调用后停止观察
|
|
1108
|
+
* @example
|
|
1109
|
+
* const cleanup = observeIntersection(
|
|
1110
|
+
* document.querySelector(".footer")!,
|
|
1111
|
+
* () => loadMore(10),
|
|
1112
|
+
* );
|
|
1113
|
+
* cleanup(); // 停止观察
|
|
1114
|
+
*/
|
|
1115
|
+
const observeIntersection = (target, onEnter, onLeave, options) => {
|
|
1116
|
+
const observer = new IntersectionObserver((entries) => {
|
|
1117
|
+
entries.forEach((entry) => {
|
|
1118
|
+
if (entry.isIntersecting) onEnter(entry);
|
|
1119
|
+
else if (onLeave) onLeave(entry);
|
|
1120
|
+
});
|
|
1121
|
+
}, options);
|
|
1122
|
+
observer.observe(target);
|
|
1123
|
+
return () => observer.disconnect();
|
|
1124
|
+
};
|
|
1022
1125
|
//#endregion
|
|
1023
1126
|
//#region src/control/index.ts
|
|
1024
1127
|
/**
|
|
@@ -1106,19 +1209,23 @@ const throttle = (fn, interval = 300, { leading = true, trailing = true } = {})
|
|
|
1106
1209
|
return throttled;
|
|
1107
1210
|
};
|
|
1108
1211
|
//#endregion
|
|
1212
|
+
exports.arrFind = arrFind;
|
|
1109
1213
|
exports.camelToKebab = camelToKebab;
|
|
1110
1214
|
exports.capitalize = capitalize;
|
|
1111
1215
|
exports.compactCN = compactCN;
|
|
1112
1216
|
exports.compactEN = compactEN;
|
|
1113
1217
|
exports.copyToClipboard = copyToClipboard;
|
|
1218
|
+
exports.createRange = createRange;
|
|
1114
1219
|
exports.debounce = debounce;
|
|
1115
1220
|
exports.deepClone = deepClone;
|
|
1116
1221
|
exports.deepCloneWithJSON = deepCloneWithJSON;
|
|
1117
1222
|
exports.delCookie = delCookie;
|
|
1118
1223
|
exports.downloadFile = downloadFile;
|
|
1119
1224
|
exports.exportJSON = exportJSON;
|
|
1225
|
+
exports.filterEmptyValues = filterEmptyValues;
|
|
1120
1226
|
exports.firstLower = firstLower;
|
|
1121
1227
|
exports.firstUpper = firstUpper;
|
|
1228
|
+
exports.flatten = flatten;
|
|
1122
1229
|
exports.formatFull = formatFull;
|
|
1123
1230
|
exports.formatFullReplace = formatFullReplace;
|
|
1124
1231
|
exports.formatNum = formatNum;
|
|
@@ -1128,6 +1235,7 @@ exports.formatYMD = formatYMD;
|
|
|
1128
1235
|
exports.getCookie = getCookie;
|
|
1129
1236
|
exports.getUrlParam = getUrlParam;
|
|
1130
1237
|
exports.getUrlParams = getUrlParams;
|
|
1238
|
+
exports.groupBy = groupBy;
|
|
1131
1239
|
exports.is = is;
|
|
1132
1240
|
exports.isArray = isArray;
|
|
1133
1241
|
exports.isAsyncFunction = isAsyncFunction;
|
|
@@ -1161,6 +1269,8 @@ exports.localGet = localGet;
|
|
|
1161
1269
|
exports.localRm = localRm;
|
|
1162
1270
|
exports.localSet = localSet;
|
|
1163
1271
|
exports.maskPhone = maskPhone;
|
|
1272
|
+
exports.mergeArrays = mergeArrays;
|
|
1273
|
+
exports.observeIntersection = observeIntersection;
|
|
1164
1274
|
exports.onScroll = onScroll;
|
|
1165
1275
|
exports.percentCN = percentCN;
|
|
1166
1276
|
exports.reverse = reverse;
|
package/dist/index.js
CHANGED
|
@@ -843,6 +843,85 @@ const sortByKey = (arr, key, order = "asc") => {
|
|
|
843
843
|
const toArray = (arrayLike) => {
|
|
844
844
|
return Array.from(arrayLike);
|
|
845
845
|
};
|
|
846
|
+
/**
|
|
847
|
+
* 合并多个数组
|
|
848
|
+
* @param arrays - 一个或多个数组
|
|
849
|
+
* @returns 合并后的新数组
|
|
850
|
+
* @example
|
|
851
|
+
* mergeArrays([1, 2], [3, 4], [5])
|
|
852
|
+
* // => [1, 2, 3, 4, 5]
|
|
853
|
+
*/
|
|
854
|
+
const mergeArrays = (...arrays) => {
|
|
855
|
+
return [].concat(...arrays);
|
|
856
|
+
};
|
|
857
|
+
/**
|
|
858
|
+
* 将多维数组扁平化到指定层级
|
|
859
|
+
* @param arr - 多维数组
|
|
860
|
+
* @param depth - 扁平化深度,默认 1(仅展开一层)。传 Infinity 可完全扁平化为一维
|
|
861
|
+
* @returns 扁平化后的数组
|
|
862
|
+
* @example
|
|
863
|
+
* flatten([1, [2, [3, 4]], 5])
|
|
864
|
+
* // => [1, 2, [3, 4], 5]
|
|
865
|
+
* @example
|
|
866
|
+
* flatten([1, [2, [3, 4]], 5], Infinity)
|
|
867
|
+
* // => [1, 2, 3, 4, 5]
|
|
868
|
+
*/
|
|
869
|
+
const flatten = (arr, depth = 1) => {
|
|
870
|
+
return arr.flat(depth);
|
|
871
|
+
};
|
|
872
|
+
/**
|
|
873
|
+
* 在对象数组中按 key-value 查找第一个匹配项
|
|
874
|
+
* @param arr - 对象数组
|
|
875
|
+
* @param key - 要匹配的键名
|
|
876
|
+
* @param value - 要匹配的键值
|
|
877
|
+
* @returns 第一个匹配的对象,无匹配则返回 undefined
|
|
878
|
+
* @example
|
|
879
|
+
* arrFind([{ id: 1, name: "a" }, { id: 2, name: "b" }], "id", 2)
|
|
880
|
+
* // => { id: 2, name: "b" }
|
|
881
|
+
*/
|
|
882
|
+
const arrFind = (arr, key, value) => {
|
|
883
|
+
return arr.find((item) => item[key] === value);
|
|
884
|
+
};
|
|
885
|
+
/**
|
|
886
|
+
* 将对象数组按指定属性分组
|
|
887
|
+
* @param arr - 对象数组
|
|
888
|
+
* @param key - 用于分组的属性名
|
|
889
|
+
* @returns 以属性值为键、对应数组为值的对象
|
|
890
|
+
* @example
|
|
891
|
+
* groupBy([{ type: "fruit", name: "apple" }, { type: "vegetable", name: "carrot" }], "type")
|
|
892
|
+
* // => { fruit: [{ type: "fruit", name: "apple" }], vegetable: [{ type: "vegetable", name: "carrot" }] }
|
|
893
|
+
*/
|
|
894
|
+
const groupBy = (arr, key) => {
|
|
895
|
+
return arr.reduce((acc, item) => {
|
|
896
|
+
const groupKey = String(item[key]);
|
|
897
|
+
if (!acc[groupKey]) acc[groupKey] = [];
|
|
898
|
+
acc[groupKey].push(item);
|
|
899
|
+
return acc;
|
|
900
|
+
}, {});
|
|
901
|
+
};
|
|
902
|
+
/**
|
|
903
|
+
* 移除对象中值为空(null / undefined / 空字符串)的属性
|
|
904
|
+
* @param obj - 待过滤的对象
|
|
905
|
+
* @returns 过滤后的新对象
|
|
906
|
+
* @example
|
|
907
|
+
* filterEmptyValues({ a: 1, b: "", c: null, d: 3 })
|
|
908
|
+
* // => { a: 1, d: 3 }
|
|
909
|
+
*/
|
|
910
|
+
const filterEmptyValues = (obj) => {
|
|
911
|
+
return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== null && v !== void 0 && v !== ""));
|
|
912
|
+
};
|
|
913
|
+
/**
|
|
914
|
+
* 根据长度和映射函数快速生成数组
|
|
915
|
+
* @param length - 数组长度
|
|
916
|
+
* @param mapFn - 映射函数,接收索引返回元素值,默认为返回索引
|
|
917
|
+
* @returns 生成的数组
|
|
918
|
+
* @example
|
|
919
|
+
* createRange(5)
|
|
920
|
+
* // => [0, 1, 2, 3, 4]
|
|
921
|
+
*/
|
|
922
|
+
const createRange = (length, mapFn) => {
|
|
923
|
+
return Array.from({ length }, (_, i) => mapFn ? mapFn(i) : i);
|
|
924
|
+
};
|
|
846
925
|
//#endregion
|
|
847
926
|
//#region src/cookie/index.ts
|
|
848
927
|
/**
|
|
@@ -1018,6 +1097,30 @@ const onScroll = (callback) => {
|
|
|
1018
1097
|
window.addEventListener("scroll", handler);
|
|
1019
1098
|
return () => window.removeEventListener("scroll", handler);
|
|
1020
1099
|
};
|
|
1100
|
+
/**
|
|
1101
|
+
* 监听目标元素是否进入/离开可视区域
|
|
1102
|
+
* @param target - 目标 DOM 元素
|
|
1103
|
+
* @param onEnter - 元素进入可视区域时的回调
|
|
1104
|
+
* @param onLeave - 元素离开可视区域时的回调(可选)
|
|
1105
|
+
* @param options - IntersectionObserver 配置项
|
|
1106
|
+
* @returns 清理函数,调用后停止观察
|
|
1107
|
+
* @example
|
|
1108
|
+
* const cleanup = observeIntersection(
|
|
1109
|
+
* document.querySelector(".footer")!,
|
|
1110
|
+
* () => loadMore(10),
|
|
1111
|
+
* );
|
|
1112
|
+
* cleanup(); // 停止观察
|
|
1113
|
+
*/
|
|
1114
|
+
const observeIntersection = (target, onEnter, onLeave, options) => {
|
|
1115
|
+
const observer = new IntersectionObserver((entries) => {
|
|
1116
|
+
entries.forEach((entry) => {
|
|
1117
|
+
if (entry.isIntersecting) onEnter(entry);
|
|
1118
|
+
else if (onLeave) onLeave(entry);
|
|
1119
|
+
});
|
|
1120
|
+
}, options);
|
|
1121
|
+
observer.observe(target);
|
|
1122
|
+
return () => observer.disconnect();
|
|
1123
|
+
};
|
|
1021
1124
|
//#endregion
|
|
1022
1125
|
//#region src/control/index.ts
|
|
1023
1126
|
/**
|
|
@@ -1105,4 +1208,4 @@ const throttle = (fn, interval = 300, { leading = true, trailing = true } = {})
|
|
|
1105
1208
|
return throttled;
|
|
1106
1209
|
};
|
|
1107
1210
|
//#endregion
|
|
1108
|
-
export { camelToKebab, capitalize, compactCN, compactEN, copyToClipboard, debounce, deepClone, deepCloneWithJSON, delCookie, downloadFile, exportJSON, firstLower, firstUpper, formatFull, formatFullReplace, formatNum, formatRmb, formatWeek, formatYMD, getCookie, getUrlParam, getUrlParams, is, isArray, isAsyncFunction, isBoolean, isDate, isDef, isElement, isEmpty, isEmptySv, isEqual, isFloat, isFunction, isHexColor, isIOS, isInt, isNull, isNullOrUnDef, isNumber, isObject, isPC, isPrimitive, isPromise, isString, isSymbol, isUnDef, isValidEmail, isWindow, kebabToCamel, localClear, localGet, localRm, localSet, maskPhone, onScroll, percentCN, reverse, scrollToBottom, scrollToTop, setCookie, shallowClone, signed, sortByKey, sortNumAsc, sortNumDesc, spacePhone, throttle, toArray, toCamel, toQueryString, trimAll, truncate, truncateByWords, unique, uniqueByKey };
|
|
1211
|
+
export { arrFind, camelToKebab, capitalize, compactCN, compactEN, copyToClipboard, createRange, debounce, deepClone, deepCloneWithJSON, delCookie, downloadFile, exportJSON, filterEmptyValues, firstLower, firstUpper, flatten, formatFull, formatFullReplace, formatNum, formatRmb, formatWeek, formatYMD, getCookie, getUrlParam, getUrlParams, groupBy, is, isArray, isAsyncFunction, isBoolean, isDate, isDef, isElement, isEmpty, isEmptySv, isEqual, isFloat, isFunction, isHexColor, isIOS, isInt, isNull, isNullOrUnDef, isNumber, isObject, isPC, isPrimitive, isPromise, isString, isSymbol, isUnDef, isValidEmail, isWindow, kebabToCamel, localClear, localGet, localRm, localSet, maskPhone, mergeArrays, observeIntersection, onScroll, percentCN, reverse, scrollToBottom, scrollToTop, setCookie, shallowClone, signed, sortByKey, sortNumAsc, sortNumDesc, spacePhone, throttle, toArray, toCamel, toQueryString, trimAll, truncate, truncateByWords, unique, uniqueByKey };
|
package/package.json
CHANGED
|
@@ -1,63 +1,55 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "pen-it",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "用笔锚定记忆中的前端常用工具函数,一个轻量级的前端实用程序库,支持类型检查、日期/数字格式化、字符串操作、数据存储、深度克隆、数组操作、Cookie 以及浏览器 API",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "dist/index.cjs",
|
|
7
|
-
"module": "dist/index.js",
|
|
8
|
-
"types": "dist/index.d.ts",
|
|
9
|
-
"exports": {
|
|
10
|
-
".": {
|
|
11
|
-
"import": "./dist/index.js",
|
|
12
|
-
"require": "./dist/index.cjs",
|
|
13
|
-
"types": "./dist/index.d.ts"
|
|
14
|
-
}
|
|
15
|
-
},
|
|
16
|
-
"scripts": {
|
|
17
|
-
"build": "rimraf dist && rolldown -c rolldown.config.js",
|
|
18
|
-
"test": "vitest"
|
|
19
|
-
},
|
|
20
|
-
"files": [
|
|
21
|
-
"dist"
|
|
22
|
-
],
|
|
23
|
-
"keywords": [
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"@vitest/coverage-v8": "^4.1.8",
|
|
56
|
-
"jsdom": "^29.1.1",
|
|
57
|
-
"rimraf": "^6.1.3",
|
|
58
|
-
"rolldown": "^1.0.3",
|
|
59
|
-
"tslib": "^2.8.1",
|
|
60
|
-
"typescript": "^6.0.3",
|
|
61
|
-
"vitest": "^4.1.8"
|
|
62
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "pen-it",
|
|
3
|
+
"version": "1.0.7",
|
|
4
|
+
"description": "用笔锚定记忆中的前端常用工具函数,一个轻量级的前端实用程序库,支持类型检查、日期/数字格式化、字符串操作、数据存储、深度克隆、数组操作、Cookie 以及浏览器 API",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.cjs",
|
|
7
|
+
"module": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"require": "./dist/index.cjs",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "rimraf dist && rolldown -c rolldown.config.js",
|
|
18
|
+
"test": "vitest"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"keywords": [
|
|
24
|
+
"pen-it",
|
|
25
|
+
"utils",
|
|
26
|
+
"function",
|
|
27
|
+
"copy",
|
|
28
|
+
"cookie",
|
|
29
|
+
"format",
|
|
30
|
+
"data types",
|
|
31
|
+
"storage",
|
|
32
|
+
"browser"
|
|
33
|
+
],
|
|
34
|
+
"author": "newborn_calf",
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/Libao-Jun/pen-it"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/Libao-Jun/pen-it#readme",
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/Libao-Jun/pen-it/issues"
|
|
43
|
+
},
|
|
44
|
+
"packageManager": "pnpm@10.19.0",
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@rollup/plugin-typescript": "^12.3.0",
|
|
47
|
+
"@vitest/coverage-v8": "^4.1.8",
|
|
48
|
+
"jsdom": "^29.1.1",
|
|
49
|
+
"rimraf": "^6.1.3",
|
|
50
|
+
"rolldown": "^1.0.3",
|
|
51
|
+
"tslib": "^2.8.1",
|
|
52
|
+
"typescript": "^6.0.3",
|
|
53
|
+
"vitest": "^4.1.8"
|
|
54
|
+
}
|
|
63
55
|
}
|