@codady/utils 0.0.5 → 0.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/CHANGELOG.md +40 -0
- package/dist/utils.cjs.js +66 -62
- package/dist/utils.cjs.min.js +3 -3
- package/dist/utils.esm.js +66 -62
- package/dist/utils.esm.min.js +3 -3
- package/dist/utils.umd.js +66 -62
- package/dist/utils.umd.min.js +3 -3
- package/dist.zip +0 -0
- package/package.json +1 -1
- package/src/{mutateMethods.js → mutableMethods.js} +2 -2
- package/src/{mutateMethods.ts → mutableMethods.ts} +4 -4
- package/src/wrapArrayMethods.js +90 -0
- package/src/wrapArrayMethods.ts +164 -0
- package/src/mutateArray.js +0 -98
- package/src/mutateArray.ts +0 -155
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,46 @@
|
|
|
2
2
|
|
|
3
3
|
All changes to Utils including new features, updates, and removals are documented here.
|
|
4
4
|
|
|
5
|
+
|
|
6
|
+
## [v0.0.7] - 2025-12-19
|
|
7
|
+
|
|
8
|
+
### Distribution Files
|
|
9
|
+
- **JS**: https://unpkg.com/@codady/utils@0.0.7/dist/js/utils.js
|
|
10
|
+
- **Zip**: https://unpkg.com/@codady/utils@0.0.7/dist.zip
|
|
11
|
+
|
|
12
|
+
### Changes
|
|
13
|
+
|
|
14
|
+
#### Fixed
|
|
15
|
+
- Some problems.
|
|
16
|
+
|
|
17
|
+
#### Added
|
|
18
|
+
- The `mutateArray` function has been renamed to `wrapArrayMethods`.`mutateArray`函数改名为`wrapArrayMethods`。
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
#### Removed
|
|
22
|
+
- Null
|
|
23
|
+
|
|
24
|
+
## [v0.0.6] - 2025-12-19
|
|
25
|
+
|
|
26
|
+
### Distribution Files
|
|
27
|
+
- **JS**: https://unpkg.com/@codady/utils@0.0.6/dist/js/utils.js
|
|
28
|
+
- **Zip**: https://unpkg.com/@codady/utils@0.0.6/dist.zip
|
|
29
|
+
|
|
30
|
+
### Changes
|
|
31
|
+
|
|
32
|
+
#### Fixed
|
|
33
|
+
- Some problems.
|
|
34
|
+
|
|
35
|
+
#### Added
|
|
36
|
+
- The `mutateMethods` variable has been renamed to `mutableMethods`.`mutateMethods`变量改名为`mutableMethods`。
|
|
37
|
+
- Added `props` parameter to `mutateArray`.mutateArray新增参数props。
|
|
38
|
+
- The `method` parameter in `onAfterMutate` callback of `mutateArray` has been renamed to `key`, and `result` renamed to `value`.mutateArray的onAfterMutate的参数method改名为key,result改名为value。
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
#### Removed
|
|
42
|
+
- Null
|
|
43
|
+
|
|
44
|
+
|
|
5
45
|
## [v0.0.3] - 2025-12-18
|
|
6
46
|
|
|
7
47
|
### Distribution Files
|
package/dist/utils.cjs.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
/*!
|
|
3
|
-
* @since Last modified: 2025-12-
|
|
3
|
+
* @since Last modified: 2025-12-19 10:14:9
|
|
4
4
|
* @name Utils for web front-end.
|
|
5
|
-
* @version 0.0.
|
|
5
|
+
* @version 0.0.7
|
|
6
6
|
* @author AXUI development team <3217728223@qq.com>
|
|
7
7
|
* @description This is a set of general-purpose JavaScript utility functions developed by the AXUI team. All functions are pure and do not involve CSS or other third-party libraries. They are suitable for any web front-end environment.
|
|
8
8
|
* @see {@link https://www.axui.cn|Official website}
|
|
@@ -102,74 +102,78 @@ const deepCloneToJSON = (data) => {
|
|
|
102
102
|
}
|
|
103
103
|
};
|
|
104
104
|
|
|
105
|
-
const
|
|
105
|
+
const mutableMethods = [
|
|
106
106
|
'push', 'pop', 'shift', 'unshift', 'splice',
|
|
107
107
|
'sort', 'reverse', 'copyWithin', 'fill'
|
|
108
108
|
];
|
|
109
109
|
|
|
110
|
-
const
|
|
110
|
+
const wrapArrayMethods = ({ target, onBeforeMutate = () => { }, onAfterMutate = () => { }, allowList, props = {}, }) => {
|
|
111
111
|
// Validation: Ensure target is an array and method is allowed
|
|
112
112
|
if (!Array.isArray(target)) {
|
|
113
113
|
throw new TypeError("The 'target' parameter must be an array.");
|
|
114
114
|
}
|
|
115
115
|
//使用默认
|
|
116
116
|
if (!allowList || allowList?.length) {
|
|
117
|
-
allowList =
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
117
|
+
allowList = mutableMethods;
|
|
118
|
+
}
|
|
119
|
+
const methods = {};
|
|
120
|
+
for (let method of allowList) {
|
|
121
|
+
methods[method] = function (...args) {
|
|
122
|
+
const context = {}, len = target.length;
|
|
123
|
+
// Capture "Pre-mutation" context for Undo/Redo/Tracking purposes
|
|
124
|
+
switch (method) {
|
|
125
|
+
// 'push' and 'unshift' are add operations, in undo/redo,
|
|
126
|
+
// we can determine the original data structure from result.length and args.length
|
|
127
|
+
// but methods that involve deletion need to record the deleted items to ensure the patch can restore the data structure during undo
|
|
128
|
+
case 'push':
|
|
129
|
+
case 'unshift':
|
|
130
|
+
context.addedItems = [...args];
|
|
131
|
+
break;
|
|
132
|
+
case 'pop':
|
|
133
|
+
context.poppedValue = target[len - 1];
|
|
134
|
+
break;
|
|
135
|
+
case 'shift':
|
|
136
|
+
context.shiftedValue = target[0];
|
|
137
|
+
break;
|
|
138
|
+
case 'splice':
|
|
139
|
+
const [s, d] = args,
|
|
140
|
+
// Calculate actual start index (handling negative values)
|
|
141
|
+
start = s < 0 ? Math.max(len + s, 0) : Math.min(s, len),
|
|
142
|
+
// Handle deleteCount defaults
|
|
143
|
+
deleteCount = d === undefined ? len - start : d;
|
|
144
|
+
context.deletedItems = target.slice(start, start + deleteCount);
|
|
145
|
+
break;
|
|
146
|
+
case 'sort':
|
|
147
|
+
case 'reverse':
|
|
148
|
+
// These methods reorder the whole array; requires a full shallow copy
|
|
149
|
+
context.oldSnapshot = [...target];
|
|
150
|
+
break;
|
|
151
|
+
case 'fill':
|
|
152
|
+
case 'copyWithin':
|
|
153
|
+
const startIdx = args[1] || 0, endIdx = args[2] === undefined ? len : args[2];
|
|
154
|
+
// Overwritten values
|
|
155
|
+
context.oldItems = target.slice(startIdx, endIdx);
|
|
156
|
+
context.start = startIdx;
|
|
157
|
+
context.end = endIdx;
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
// Execute the "onBeforeMutate" callback before mutation
|
|
161
|
+
onBeforeMutate?.(context);
|
|
162
|
+
// Execute the native array mutation
|
|
163
|
+
const value = Array.prototype[method].apply(target, args), patch = {
|
|
164
|
+
value,
|
|
165
|
+
key: method,
|
|
166
|
+
args,
|
|
167
|
+
context,
|
|
168
|
+
target,
|
|
169
|
+
...props
|
|
170
|
+
};
|
|
171
|
+
// Construct and trigger the patch callback
|
|
172
|
+
onAfterMutate?.(patch);
|
|
173
|
+
return value;
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
return methods;
|
|
173
177
|
};
|
|
174
178
|
|
|
175
179
|
const requireTypes = (data, require, cb) => {
|
|
@@ -207,8 +211,8 @@ const utils = {
|
|
|
207
211
|
//parseStr,
|
|
208
212
|
deepClone,
|
|
209
213
|
deepCloneToJSON,
|
|
210
|
-
|
|
211
|
-
|
|
214
|
+
wrapArrayMethods,
|
|
215
|
+
mutableMethods
|
|
212
216
|
};
|
|
213
217
|
|
|
214
218
|
module.exports = utils;
|
package/dist/utils.cjs.min.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @since Last modified: 2025-12-
|
|
2
|
+
* @since Last modified: 2025-12-19 10:14:9
|
|
3
3
|
* @name Utils for web front-end.
|
|
4
|
-
* @version 0.0.
|
|
4
|
+
* @version 0.0.7
|
|
5
5
|
* @author AXUI development team <3217728223@qq.com>
|
|
6
6
|
* @description This is a set of general-purpose JavaScript utility functions developed by the AXUI team. All functions are pure and do not involve CSS or other third-party libraries. They are suitable for any web front-end environment.
|
|
7
7
|
* @see {@link https://www.axui.cn|Official website}
|
|
@@ -12,4 +12,4 @@
|
|
|
12
12
|
* @copyright This software supports the MIT License, allowing free learning and commercial use, but please retain the terms 'ax,' 'axui,' 'AX,' and 'AXUI' within the software.
|
|
13
13
|
* @license MIT license
|
|
14
14
|
*/
|
|
15
|
-
"use strict";const getDataType=e=>{let t,r=Object.prototype.toString.call(e).slice(8,-1);return t="Function"===r&&/^\s*class\s+/.test(e.toString())?"Class":"Object"===r&&Object.getPrototypeOf(e)!==Object.prototype?"Instance":r,t},deepClone=e=>{const t=getDataType(e);if("Object"===t){const t={},r=Object.getOwnPropertySymbols(e);for(const r in e)t[r]=deepClone(e[r]);if(r.length>0)for(const o of r)t[o]=deepClone(e[o]);return t}if("Array"===t)return e.map(e=>deepClone(e));if("Date"===t)return new Date(e.getTime());if("RegExp"===t){const t=e;return new RegExp(t.source,t.flags)}return e},deepCloneToJSON=e=>{const t=getDataType(e);if("Object"===t){const t={};for(const r in e)t[r]=deepCloneToJSON(e[r]);for(const e in t)void 0===t[e]&&Reflect.deleteProperty(t,e);return t}if("Array"===t){return e.map((e,t)=>deepCloneToJSON(e)).filter(e=>void 0!==e)}return["Number","String","Boolean","Null"].includes(t)?e:void 0},requireTypes=(e,t,r)=>{let o=Array.isArray(t)?t:[t],
|
|
15
|
+
"use strict";const getDataType=e=>{let t,r=Object.prototype.toString.call(e).slice(8,-1);return t="Function"===r&&/^\s*class\s+/.test(e.toString())?"Class":"Object"===r&&Object.getPrototypeOf(e)!==Object.prototype?"Instance":r,t},deepClone=e=>{const t=getDataType(e);if("Object"===t){const t={},r=Object.getOwnPropertySymbols(e);for(const r in e)t[r]=deepClone(e[r]);if(r.length>0)for(const o of r)t[o]=deepClone(e[o]);return t}if("Array"===t)return e.map(e=>deepClone(e));if("Date"===t)return new Date(e.getTime());if("RegExp"===t){const t=e;return new RegExp(t.source,t.flags)}return e},deepCloneToJSON=e=>{const t=getDataType(e);if("Object"===t){const t={};for(const r in e)t[r]=deepCloneToJSON(e[r]);for(const e in t)void 0===t[e]&&Reflect.deleteProperty(t,e);return t}if("Array"===t){return e.map((e,t)=>deepCloneToJSON(e)).filter(e=>void 0!==e)}return["Number","String","Boolean","Null"].includes(t)?e:void 0},mutableMethods=["push","pop","shift","unshift","splice","sort","reverse","copyWithin","fill"],wrapArrayMethods=({target:e,onBeforeMutate:t=()=>{},onAfterMutate:r=()=>{},allowList:o,props:s={}})=>{if(!Array.isArray(e))throw new TypeError("The 'target' parameter must be an array.");o&&!o?.length||(o=mutableMethods);const a={};for(let n of o)a[n]=function(...o){const a={},p=e.length;switch(n){case"push":case"unshift":a.addedItems=[...o];break;case"pop":a.poppedValue=e[p-1];break;case"shift":a.shiftedValue=e[0];break;case"splice":const[t,r]=o,s=t<0?Math.max(p+t,0):Math.min(t,p),n=void 0===r?p-s:r;a.deletedItems=e.slice(s,s+n);break;case"sort":case"reverse":a.oldSnapshot=[...e];break;case"fill":case"copyWithin":const l=o[1]||0,i=void 0===o[2]?p:o[2];a.oldItems=e.slice(l,i),a.start=l,a.end=i}t?.(a);const l=Array.prototype[n].apply(e,o),i={value:l,key:n,args:o,context:a,target:e,...s};return r?.(i),l};return a},requireTypes=(e,t,r)=>{let o=Array.isArray(t)?t:[t],s=getDataType(e),a=s.toLowerCase(),n=o.map(e=>e.toLowerCase()),p=a.includes("html")?"element":a;if(r)try{if(!n.includes(p))throw new TypeError(`Expected data type(s): [${n.join(", ")}], but got: ${p}`)}catch(e){r(e,s)}else if(!n.includes(p))throw new TypeError(`Expected data type(s): [${n.join(", ")}], but got: ${p}`);return s},utils={getDataType:getDataType,requireTypes:requireTypes,deepClone:deepClone,deepCloneToJSON:deepCloneToJSON,wrapArrayMethods:wrapArrayMethods,mutableMethods:mutableMethods};module.exports=utils;
|
package/dist/utils.esm.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
/*!
|
|
3
|
-
* @since Last modified: 2025-12-
|
|
3
|
+
* @since Last modified: 2025-12-19 10:14:9
|
|
4
4
|
* @name Utils for web front-end.
|
|
5
|
-
* @version 0.0.
|
|
5
|
+
* @version 0.0.7
|
|
6
6
|
* @author AXUI development team <3217728223@qq.com>
|
|
7
7
|
* @description This is a set of general-purpose JavaScript utility functions developed by the AXUI team. All functions are pure and do not involve CSS or other third-party libraries. They are suitable for any web front-end environment.
|
|
8
8
|
* @see {@link https://www.axui.cn|Official website}
|
|
@@ -100,74 +100,78 @@ const deepCloneToJSON = (data) => {
|
|
|
100
100
|
}
|
|
101
101
|
};
|
|
102
102
|
|
|
103
|
-
const
|
|
103
|
+
const mutableMethods = [
|
|
104
104
|
'push', 'pop', 'shift', 'unshift', 'splice',
|
|
105
105
|
'sort', 'reverse', 'copyWithin', 'fill'
|
|
106
106
|
];
|
|
107
107
|
|
|
108
|
-
const
|
|
108
|
+
const wrapArrayMethods = ({ target, onBeforeMutate = () => { }, onAfterMutate = () => { }, allowList, props = {}, }) => {
|
|
109
109
|
// Validation: Ensure target is an array and method is allowed
|
|
110
110
|
if (!Array.isArray(target)) {
|
|
111
111
|
throw new TypeError("The 'target' parameter must be an array.");
|
|
112
112
|
}
|
|
113
113
|
//使用默认
|
|
114
114
|
if (!allowList || allowList?.length) {
|
|
115
|
-
allowList =
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
115
|
+
allowList = mutableMethods;
|
|
116
|
+
}
|
|
117
|
+
const methods = {};
|
|
118
|
+
for (let method of allowList) {
|
|
119
|
+
methods[method] = function (...args) {
|
|
120
|
+
const context = {}, len = target.length;
|
|
121
|
+
// Capture "Pre-mutation" context for Undo/Redo/Tracking purposes
|
|
122
|
+
switch (method) {
|
|
123
|
+
// 'push' and 'unshift' are add operations, in undo/redo,
|
|
124
|
+
// we can determine the original data structure from result.length and args.length
|
|
125
|
+
// but methods that involve deletion need to record the deleted items to ensure the patch can restore the data structure during undo
|
|
126
|
+
case 'push':
|
|
127
|
+
case 'unshift':
|
|
128
|
+
context.addedItems = [...args];
|
|
129
|
+
break;
|
|
130
|
+
case 'pop':
|
|
131
|
+
context.poppedValue = target[len - 1];
|
|
132
|
+
break;
|
|
133
|
+
case 'shift':
|
|
134
|
+
context.shiftedValue = target[0];
|
|
135
|
+
break;
|
|
136
|
+
case 'splice':
|
|
137
|
+
const [s, d] = args,
|
|
138
|
+
// Calculate actual start index (handling negative values)
|
|
139
|
+
start = s < 0 ? Math.max(len + s, 0) : Math.min(s, len),
|
|
140
|
+
// Handle deleteCount defaults
|
|
141
|
+
deleteCount = d === undefined ? len - start : d;
|
|
142
|
+
context.deletedItems = target.slice(start, start + deleteCount);
|
|
143
|
+
break;
|
|
144
|
+
case 'sort':
|
|
145
|
+
case 'reverse':
|
|
146
|
+
// These methods reorder the whole array; requires a full shallow copy
|
|
147
|
+
context.oldSnapshot = [...target];
|
|
148
|
+
break;
|
|
149
|
+
case 'fill':
|
|
150
|
+
case 'copyWithin':
|
|
151
|
+
const startIdx = args[1] || 0, endIdx = args[2] === undefined ? len : args[2];
|
|
152
|
+
// Overwritten values
|
|
153
|
+
context.oldItems = target.slice(startIdx, endIdx);
|
|
154
|
+
context.start = startIdx;
|
|
155
|
+
context.end = endIdx;
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
// Execute the "onBeforeMutate" callback before mutation
|
|
159
|
+
onBeforeMutate?.(context);
|
|
160
|
+
// Execute the native array mutation
|
|
161
|
+
const value = Array.prototype[method].apply(target, args), patch = {
|
|
162
|
+
value,
|
|
163
|
+
key: method,
|
|
164
|
+
args,
|
|
165
|
+
context,
|
|
166
|
+
target,
|
|
167
|
+
...props
|
|
168
|
+
};
|
|
169
|
+
// Construct and trigger the patch callback
|
|
170
|
+
onAfterMutate?.(patch);
|
|
171
|
+
return value;
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
return methods;
|
|
171
175
|
};
|
|
172
176
|
|
|
173
177
|
const requireTypes = (data, require, cb) => {
|
|
@@ -205,8 +209,8 @@ const utils = {
|
|
|
205
209
|
//parseStr,
|
|
206
210
|
deepClone,
|
|
207
211
|
deepCloneToJSON,
|
|
208
|
-
|
|
209
|
-
|
|
212
|
+
wrapArrayMethods,
|
|
213
|
+
mutableMethods
|
|
210
214
|
};
|
|
211
215
|
|
|
212
216
|
export { utils as default };
|
package/dist/utils.esm.min.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @since Last modified: 2025-12-
|
|
2
|
+
* @since Last modified: 2025-12-19 10:14:9
|
|
3
3
|
* @name Utils for web front-end.
|
|
4
|
-
* @version 0.0.
|
|
4
|
+
* @version 0.0.7
|
|
5
5
|
* @author AXUI development team <3217728223@qq.com>
|
|
6
6
|
* @description This is a set of general-purpose JavaScript utility functions developed by the AXUI team. All functions are pure and do not involve CSS or other third-party libraries. They are suitable for any web front-end environment.
|
|
7
7
|
* @see {@link https://www.axui.cn|Official website}
|
|
@@ -12,4 +12,4 @@
|
|
|
12
12
|
* @copyright This software supports the MIT License, allowing free learning and commercial use, but please retain the terms 'ax,' 'axui,' 'AX,' and 'AXUI' within the software.
|
|
13
13
|
* @license MIT license
|
|
14
14
|
*/
|
|
15
|
-
const getDataType=e=>{let t,r=Object.prototype.toString.call(e).slice(8,-1);return t="Function"===r&&/^\s*class\s+/.test(e.toString())?"Class":"Object"===r&&Object.getPrototypeOf(e)!==Object.prototype?"Instance":r,t},deepClone=e=>{const t=getDataType(e);if("Object"===t){const t={},r=Object.getOwnPropertySymbols(e);for(const r in e)t[r]=deepClone(e[r]);if(r.length>0)for(const o of r)t[o]=deepClone(e[o]);return t}if("Array"===t)return e.map(e=>deepClone(e));if("Date"===t)return new Date(e.getTime());if("RegExp"===t){const t=e;return new RegExp(t.source,t.flags)}return e},deepCloneToJSON=e=>{const t=getDataType(e);if("Object"===t){const t={};for(const r in e)t[r]=deepCloneToJSON(e[r]);for(const e in t)void 0===t[e]&&Reflect.deleteProperty(t,e);return t}if("Array"===t){return e.map((e,t)=>deepCloneToJSON(e)).filter(e=>void 0!==e)}return["Number","String","Boolean","Null"].includes(t)?e:void 0},requireTypes=(e,t,r)=>{let o=Array.isArray(t)?t:[t],
|
|
15
|
+
const getDataType=e=>{let t,r=Object.prototype.toString.call(e).slice(8,-1);return t="Function"===r&&/^\s*class\s+/.test(e.toString())?"Class":"Object"===r&&Object.getPrototypeOf(e)!==Object.prototype?"Instance":r,t},deepClone=e=>{const t=getDataType(e);if("Object"===t){const t={},r=Object.getOwnPropertySymbols(e);for(const r in e)t[r]=deepClone(e[r]);if(r.length>0)for(const o of r)t[o]=deepClone(e[o]);return t}if("Array"===t)return e.map(e=>deepClone(e));if("Date"===t)return new Date(e.getTime());if("RegExp"===t){const t=e;return new RegExp(t.source,t.flags)}return e},deepCloneToJSON=e=>{const t=getDataType(e);if("Object"===t){const t={};for(const r in e)t[r]=deepCloneToJSON(e[r]);for(const e in t)void 0===t[e]&&Reflect.deleteProperty(t,e);return t}if("Array"===t){return e.map((e,t)=>deepCloneToJSON(e)).filter(e=>void 0!==e)}return["Number","String","Boolean","Null"].includes(t)?e:void 0},mutableMethods=["push","pop","shift","unshift","splice","sort","reverse","copyWithin","fill"],wrapArrayMethods=({target:e,onBeforeMutate:t=()=>{},onAfterMutate:r=()=>{},allowList:o,props:a={}})=>{if(!Array.isArray(e))throw new TypeError("The 'target' parameter must be an array.");o&&!o?.length||(o=mutableMethods);const s={};for(let n of o)s[n]=function(...o){const s={},p=e.length;switch(n){case"push":case"unshift":s.addedItems=[...o];break;case"pop":s.poppedValue=e[p-1];break;case"shift":s.shiftedValue=e[0];break;case"splice":const[t,r]=o,a=t<0?Math.max(p+t,0):Math.min(t,p),n=void 0===r?p-a:r;s.deletedItems=e.slice(a,a+n);break;case"sort":case"reverse":s.oldSnapshot=[...e];break;case"fill":case"copyWithin":const l=o[1]||0,i=void 0===o[2]?p:o[2];s.oldItems=e.slice(l,i),s.start=l,s.end=i}t?.(s);const l=Array.prototype[n].apply(e,o),i={value:l,key:n,args:o,context:s,target:e,...a};return r?.(i),l};return s},requireTypes=(e,t,r)=>{let o=Array.isArray(t)?t:[t],a=getDataType(e),s=a.toLowerCase(),n=o.map(e=>e.toLowerCase()),p=s.includes("html")?"element":s;if(r)try{if(!n.includes(p))throw new TypeError(`Expected data type(s): [${n.join(", ")}], but got: ${p}`)}catch(e){r(e,a)}else if(!n.includes(p))throw new TypeError(`Expected data type(s): [${n.join(", ")}], but got: ${p}`);return a},utils={getDataType:getDataType,requireTypes:requireTypes,deepClone:deepClone,deepCloneToJSON:deepCloneToJSON,wrapArrayMethods:wrapArrayMethods,mutableMethods:mutableMethods};export{utils as default};
|
package/dist/utils.umd.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
/*!
|
|
3
|
-
* @since Last modified: 2025-12-
|
|
3
|
+
* @since Last modified: 2025-12-19 10:14:9
|
|
4
4
|
* @name Utils for web front-end.
|
|
5
|
-
* @version 0.0.
|
|
5
|
+
* @version 0.0.7
|
|
6
6
|
* @author AXUI development team <3217728223@qq.com>
|
|
7
7
|
* @description This is a set of general-purpose JavaScript utility functions developed by the AXUI team. All functions are pure and do not involve CSS or other third-party libraries. They are suitable for any web front-end environment.
|
|
8
8
|
* @see {@link https://www.axui.cn|Official website}
|
|
@@ -106,74 +106,78 @@
|
|
|
106
106
|
}
|
|
107
107
|
};
|
|
108
108
|
|
|
109
|
-
const
|
|
109
|
+
const mutableMethods = [
|
|
110
110
|
'push', 'pop', 'shift', 'unshift', 'splice',
|
|
111
111
|
'sort', 'reverse', 'copyWithin', 'fill'
|
|
112
112
|
];
|
|
113
113
|
|
|
114
|
-
const
|
|
114
|
+
const wrapArrayMethods = ({ target, onBeforeMutate = () => { }, onAfterMutate = () => { }, allowList, props = {}, }) => {
|
|
115
115
|
// Validation: Ensure target is an array and method is allowed
|
|
116
116
|
if (!Array.isArray(target)) {
|
|
117
117
|
throw new TypeError("The 'target' parameter must be an array.");
|
|
118
118
|
}
|
|
119
119
|
//使用默认
|
|
120
120
|
if (!allowList || allowList?.length) {
|
|
121
|
-
allowList =
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
121
|
+
allowList = mutableMethods;
|
|
122
|
+
}
|
|
123
|
+
const methods = {};
|
|
124
|
+
for (let method of allowList) {
|
|
125
|
+
methods[method] = function (...args) {
|
|
126
|
+
const context = {}, len = target.length;
|
|
127
|
+
// Capture "Pre-mutation" context for Undo/Redo/Tracking purposes
|
|
128
|
+
switch (method) {
|
|
129
|
+
// 'push' and 'unshift' are add operations, in undo/redo,
|
|
130
|
+
// we can determine the original data structure from result.length and args.length
|
|
131
|
+
// but methods that involve deletion need to record the deleted items to ensure the patch can restore the data structure during undo
|
|
132
|
+
case 'push':
|
|
133
|
+
case 'unshift':
|
|
134
|
+
context.addedItems = [...args];
|
|
135
|
+
break;
|
|
136
|
+
case 'pop':
|
|
137
|
+
context.poppedValue = target[len - 1];
|
|
138
|
+
break;
|
|
139
|
+
case 'shift':
|
|
140
|
+
context.shiftedValue = target[0];
|
|
141
|
+
break;
|
|
142
|
+
case 'splice':
|
|
143
|
+
const [s, d] = args,
|
|
144
|
+
// Calculate actual start index (handling negative values)
|
|
145
|
+
start = s < 0 ? Math.max(len + s, 0) : Math.min(s, len),
|
|
146
|
+
// Handle deleteCount defaults
|
|
147
|
+
deleteCount = d === undefined ? len - start : d;
|
|
148
|
+
context.deletedItems = target.slice(start, start + deleteCount);
|
|
149
|
+
break;
|
|
150
|
+
case 'sort':
|
|
151
|
+
case 'reverse':
|
|
152
|
+
// These methods reorder the whole array; requires a full shallow copy
|
|
153
|
+
context.oldSnapshot = [...target];
|
|
154
|
+
break;
|
|
155
|
+
case 'fill':
|
|
156
|
+
case 'copyWithin':
|
|
157
|
+
const startIdx = args[1] || 0, endIdx = args[2] === undefined ? len : args[2];
|
|
158
|
+
// Overwritten values
|
|
159
|
+
context.oldItems = target.slice(startIdx, endIdx);
|
|
160
|
+
context.start = startIdx;
|
|
161
|
+
context.end = endIdx;
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
// Execute the "onBeforeMutate" callback before mutation
|
|
165
|
+
onBeforeMutate?.(context);
|
|
166
|
+
// Execute the native array mutation
|
|
167
|
+
const value = Array.prototype[method].apply(target, args), patch = {
|
|
168
|
+
value,
|
|
169
|
+
key: method,
|
|
170
|
+
args,
|
|
171
|
+
context,
|
|
172
|
+
target,
|
|
173
|
+
...props
|
|
174
|
+
};
|
|
175
|
+
// Construct and trigger the patch callback
|
|
176
|
+
onAfterMutate?.(patch);
|
|
177
|
+
return value;
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
return methods;
|
|
177
181
|
};
|
|
178
182
|
|
|
179
183
|
const requireTypes = (data, require, cb) => {
|
|
@@ -211,8 +215,8 @@
|
|
|
211
215
|
//parseStr,
|
|
212
216
|
deepClone,
|
|
213
217
|
deepCloneToJSON,
|
|
214
|
-
|
|
215
|
-
|
|
218
|
+
wrapArrayMethods,
|
|
219
|
+
mutableMethods
|
|
216
220
|
};
|
|
217
221
|
|
|
218
222
|
return utils;
|
package/dist/utils.umd.min.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @since Last modified: 2025-12-
|
|
2
|
+
* @since Last modified: 2025-12-19 10:14:9
|
|
3
3
|
* @name Utils for web front-end.
|
|
4
|
-
* @version 0.0.
|
|
4
|
+
* @version 0.0.7
|
|
5
5
|
* @author AXUI development team <3217728223@qq.com>
|
|
6
6
|
* @description This is a set of general-purpose JavaScript utility functions developed by the AXUI team. All functions are pure and do not involve CSS or other third-party libraries. They are suitable for any web front-end environment.
|
|
7
7
|
* @see {@link https://www.axui.cn|Official website}
|
|
@@ -12,4 +12,4 @@
|
|
|
12
12
|
* @copyright This software supports the MIT License, allowing free learning and commercial use, but please retain the terms 'ax,' 'axui,' 'AX,' and 'AXUI' within the software.
|
|
13
13
|
* @license MIT license
|
|
14
14
|
*/
|
|
15
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).utils=t()}(this,function(){"use strict";const getDataType=e=>{let t,r=Object.prototype.toString.call(e).slice(8,-1);return t="Function"===r&&/^\s*class\s+/.test(e.toString())?"Class":"Object"===r&&Object.getPrototypeOf(e)!==Object.prototype?"Instance":r,t},deepClone=e=>{const t=getDataType(e);if("Object"===t){const t={},r=Object.getOwnPropertySymbols(e);for(const r in e)t[r]=deepClone(e[r]);if(r.length>0)for(const o of r)t[o]=deepClone(e[o]);return t}if("Array"===t)return e.map(e=>deepClone(e));if("Date"===t)return new Date(e.getTime());if("RegExp"===t){const t=e;return new RegExp(t.source,t.flags)}return e},deepCloneToJSON=e=>{const t=getDataType(e);if("Object"===t){const t={};for(const r in e)t[r]=deepCloneToJSON(e[r]);for(const e in t)void 0===t[e]&&Reflect.deleteProperty(t,e);return t}if("Array"===t){return e.map((e,t)=>deepCloneToJSON(e)).filter(e=>void 0!==e)}return["Number","String","Boolean","Null"].includes(t)?e:void 0};return{getDataType:getDataType,requireTypes:(e,t,r)=>{let o=Array.isArray(t)?t:[t],
|
|
15
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).utils=t()}(this,function(){"use strict";const getDataType=e=>{let t,r=Object.prototype.toString.call(e).slice(8,-1);return t="Function"===r&&/^\s*class\s+/.test(e.toString())?"Class":"Object"===r&&Object.getPrototypeOf(e)!==Object.prototype?"Instance":r,t},deepClone=e=>{const t=getDataType(e);if("Object"===t){const t={},r=Object.getOwnPropertySymbols(e);for(const r in e)t[r]=deepClone(e[r]);if(r.length>0)for(const o of r)t[o]=deepClone(e[o]);return t}if("Array"===t)return e.map(e=>deepClone(e));if("Date"===t)return new Date(e.getTime());if("RegExp"===t){const t=e;return new RegExp(t.source,t.flags)}return e},deepCloneToJSON=e=>{const t=getDataType(e);if("Object"===t){const t={};for(const r in e)t[r]=deepCloneToJSON(e[r]);for(const e in t)void 0===t[e]&&Reflect.deleteProperty(t,e);return t}if("Array"===t){return e.map((e,t)=>deepCloneToJSON(e)).filter(e=>void 0!==e)}return["Number","String","Boolean","Null"].includes(t)?e:void 0},e=["push","pop","shift","unshift","splice","sort","reverse","copyWithin","fill"];return{getDataType:getDataType,requireTypes:(e,t,r)=>{let o=Array.isArray(t)?t:[t],s=getDataType(e),n=s.toLowerCase(),a=o.map(e=>e.toLowerCase()),i=n.includes("html")?"element":n;if(r)try{if(!a.includes(i))throw new TypeError(`Expected data type(s): [${a.join(", ")}], but got: ${i}`)}catch(e){r(e,s)}else if(!a.includes(i))throw new TypeError(`Expected data type(s): [${a.join(", ")}], but got: ${i}`);return s},deepClone:deepClone,deepCloneToJSON:deepCloneToJSON,wrapArrayMethods:({target:t,onBeforeMutate:r=()=>{},onAfterMutate:o=()=>{},allowList:s,props:n={}})=>{if(!Array.isArray(t))throw new TypeError("The 'target' parameter must be an array.");s&&!s?.length||(s=e);const a={};for(let e of s)a[e]=function(...s){const a={},i=t.length;switch(e){case"push":case"unshift":a.addedItems=[...s];break;case"pop":a.poppedValue=t[i-1];break;case"shift":a.shiftedValue=t[0];break;case"splice":const[e,r]=s,o=e<0?Math.max(i+e,0):Math.min(e,i),n=void 0===r?i-o:r;a.deletedItems=t.slice(o,o+n);break;case"sort":case"reverse":a.oldSnapshot=[...t];break;case"fill":case"copyWithin":const c=s[1]||0,l=void 0===s[2]?i:s[2];a.oldItems=t.slice(c,l),a.start=c,a.end=l}r?.(a);const c=Array.prototype[e].apply(t,s),l={value:c,key:e,args:s,context:a,target:t,...n};return o?.(l),c};return a},mutableMethods:e}});
|
package/dist.zip
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codady/utils",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"author": "AXUI Development Team",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "This is a set of general-purpose JavaScript utility functions developed by the AXUI team. All functions are pure and do not involve CSS or other third-party libraries. They are suitable for any web front-end environment.",
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @since Last modified: 2025/12/
|
|
2
|
+
* @since Last modified: 2025/12/19 10:06:07
|
|
3
3
|
* A list of supported array mutation methods.
|
|
4
4
|
* These methods are commonly used to modify the contents of an array.
|
|
5
5
|
* These methods can be tracked and handled in various use cases, such as undo/redo functionality,
|
|
6
6
|
* or when applying certain transformations to arrays.
|
|
7
7
|
*
|
|
8
8
|
*/
|
|
9
|
-
export type
|
|
10
|
-
const
|
|
9
|
+
export type ArrayMutationNames = ('push' | 'pop' | 'shift' | 'unshift' | 'splice' | 'sort' | 'reverse' | 'copyWithin' | 'fill')[];
|
|
10
|
+
const mutableMethods: ArrayMutationNames = [
|
|
11
11
|
'push', 'pop', 'shift', 'unshift', 'splice',
|
|
12
12
|
'sort', 'reverse', 'copyWithin', 'fill'
|
|
13
13
|
];
|
|
14
14
|
|
|
15
|
-
export default
|
|
15
|
+
export default mutableMethods;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since Last modified: 2025/12/19 10:14:03
|
|
3
|
+
* Mutates an array by applying one of the allowed mutation methods such as push, pop, shift, unshift, splice, etc.
|
|
4
|
+
* Supports tracking of added, removed, and updated items for undo/redo or tracking purposes.
|
|
5
|
+
* The mutation methods that can be tracked include push, pop, shift, unshift, splice, sort, reverse, fill, and copyWithin.
|
|
6
|
+
*
|
|
7
|
+
* @template T - Array element type
|
|
8
|
+
* @param {MutationOptions<T>} options - Configuration object containing the target array and callbacks.
|
|
9
|
+
* @returns {ArrayMutationMethods<T>} - An object containing all mutable array methods bound to the target.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* // 获取方法对象后调用
|
|
13
|
+
* const arr = [1, 2, 3];
|
|
14
|
+
* const methods = mutateArray({
|
|
15
|
+
* target: arr,
|
|
16
|
+
* onAfterMutate: (patch) => { console.log(patch); }
|
|
17
|
+
* });
|
|
18
|
+
* methods.push(4); // arr is now [1, 2, 3, 4]
|
|
19
|
+
*/
|
|
20
|
+
'use strict';
|
|
21
|
+
import mutableMethods from "./mutableMethods";
|
|
22
|
+
const wrapArrayMethods = ({ target, onBeforeMutate = () => { }, onAfterMutate = () => { }, allowList, props = {}, }) => {
|
|
23
|
+
// Validation: Ensure target is an array and method is allowed
|
|
24
|
+
if (!Array.isArray(target)) {
|
|
25
|
+
throw new TypeError("The 'target' parameter must be an array.");
|
|
26
|
+
}
|
|
27
|
+
//使用默认
|
|
28
|
+
if (!allowList || allowList?.length) {
|
|
29
|
+
allowList = mutableMethods;
|
|
30
|
+
}
|
|
31
|
+
const methods = {};
|
|
32
|
+
for (let method of allowList) {
|
|
33
|
+
methods[method] = function (...args) {
|
|
34
|
+
const context = {}, len = target.length;
|
|
35
|
+
// Capture "Pre-mutation" context for Undo/Redo/Tracking purposes
|
|
36
|
+
switch (method) {
|
|
37
|
+
// 'push' and 'unshift' are add operations, in undo/redo,
|
|
38
|
+
// we can determine the original data structure from result.length and args.length
|
|
39
|
+
// but methods that involve deletion need to record the deleted items to ensure the patch can restore the data structure during undo
|
|
40
|
+
case 'push':
|
|
41
|
+
case 'unshift':
|
|
42
|
+
context.addedItems = [...args];
|
|
43
|
+
break;
|
|
44
|
+
case 'pop':
|
|
45
|
+
context.poppedValue = target[len - 1];
|
|
46
|
+
break;
|
|
47
|
+
case 'shift':
|
|
48
|
+
context.shiftedValue = target[0];
|
|
49
|
+
break;
|
|
50
|
+
case 'splice':
|
|
51
|
+
const [s, d] = args,
|
|
52
|
+
// Calculate actual start index (handling negative values)
|
|
53
|
+
start = s < 0 ? Math.max(len + s, 0) : Math.min(s, len),
|
|
54
|
+
// Handle deleteCount defaults
|
|
55
|
+
deleteCount = d === undefined ? len - start : d;
|
|
56
|
+
context.deletedItems = target.slice(start, start + deleteCount);
|
|
57
|
+
break;
|
|
58
|
+
case 'sort':
|
|
59
|
+
case 'reverse':
|
|
60
|
+
// These methods reorder the whole array; requires a full shallow copy
|
|
61
|
+
context.oldSnapshot = [...target];
|
|
62
|
+
break;
|
|
63
|
+
case 'fill':
|
|
64
|
+
case 'copyWithin':
|
|
65
|
+
const startIdx = args[1] || 0, endIdx = args[2] === undefined ? len : args[2];
|
|
66
|
+
// Overwritten values
|
|
67
|
+
context.oldItems = target.slice(startIdx, endIdx);
|
|
68
|
+
context.start = startIdx;
|
|
69
|
+
context.end = endIdx;
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
// Execute the "onBeforeMutate" callback before mutation
|
|
73
|
+
onBeforeMutate?.(context);
|
|
74
|
+
// Execute the native array mutation
|
|
75
|
+
const value = Array.prototype[method].apply(target, args), patch = {
|
|
76
|
+
value,
|
|
77
|
+
key: method,
|
|
78
|
+
args,
|
|
79
|
+
context,
|
|
80
|
+
target,
|
|
81
|
+
...props
|
|
82
|
+
};
|
|
83
|
+
// Construct and trigger the patch callback
|
|
84
|
+
onAfterMutate?.(patch);
|
|
85
|
+
return value;
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return methods;
|
|
89
|
+
};
|
|
90
|
+
export default wrapArrayMethods;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since Last modified: 2025/12/19 10:14:03
|
|
3
|
+
* Mutates an array by applying one of the allowed mutation methods such as push, pop, shift, unshift, splice, etc.
|
|
4
|
+
* Supports tracking of added, removed, and updated items for undo/redo or tracking purposes.
|
|
5
|
+
* The mutation methods that can be tracked include push, pop, shift, unshift, splice, sort, reverse, fill, and copyWithin.
|
|
6
|
+
*
|
|
7
|
+
* @template T - Array element type
|
|
8
|
+
* @param {MutationOptions<T>} options - Configuration object containing the target array and callbacks.
|
|
9
|
+
* @returns {ArrayMutationMethods<T>} - An object containing all mutable array methods bound to the target.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* // 获取方法对象后调用
|
|
13
|
+
* const arr = [1, 2, 3];
|
|
14
|
+
* const methods = mutateArray({
|
|
15
|
+
* target: arr,
|
|
16
|
+
* onAfterMutate: (patch) => { console.log(patch); }
|
|
17
|
+
* });
|
|
18
|
+
* methods.push(4); // arr is now [1, 2, 3, 4]
|
|
19
|
+
*/
|
|
20
|
+
'use strict';
|
|
21
|
+
|
|
22
|
+
import mutableMethods, { ArrayMutationNames } from "./mutableMethods";
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Defines the structure of the captured state before mutation.
|
|
27
|
+
*/
|
|
28
|
+
export interface MutationContextt {
|
|
29
|
+
addedItems?: any[]; // Items added to the array
|
|
30
|
+
poppedValue?: any; // Value popped from the array
|
|
31
|
+
shiftedValue?: any; // Value shifted from the array
|
|
32
|
+
deletedItems?: any[]; // Items removed from the array
|
|
33
|
+
oldSnapshot?: any[]; // Snapshot of the array before methods like 'sort' or 'reverse'
|
|
34
|
+
oldItems?: any[]; // Items overwritten (for methods like 'fill' or 'copyWithin')
|
|
35
|
+
start?: number; // Starting index for mutations like 'fill' or 'splice'
|
|
36
|
+
end?: number; // Ending index for mutations like 'fill' or 'splice'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The data object passed to the callback after mutation.
|
|
41
|
+
*/
|
|
42
|
+
export interface MutationPatch<T = any> {
|
|
43
|
+
key: string; // The mutation method (e.g., 'push', 'pop', 'splice', etc.)
|
|
44
|
+
value: any; // The result of the mutation (the modified array)
|
|
45
|
+
args: any[]; // Arguments passed to the mutation method
|
|
46
|
+
context: MutationContextt; // The state before mutation
|
|
47
|
+
target: T[]; // Reference to the mutated array
|
|
48
|
+
[key: string]: any; // Additional properties from props
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The return type of wrapArrayMethods - an object containing all mutable array methods
|
|
52
|
+
*/
|
|
53
|
+
export interface ArrayMutationMethods<T = any> {
|
|
54
|
+
push: (...args: any[]) => number;
|
|
55
|
+
pop: () => T | undefined;
|
|
56
|
+
shift: () => T | undefined;
|
|
57
|
+
unshift: (...args: T[]) => number;
|
|
58
|
+
splice: (start: number, deleteCount?: number, ...items: T[]) => T[];
|
|
59
|
+
sort: (compareFn?: (a: T, b: T) => number) => T[];
|
|
60
|
+
reverse: () => T[];
|
|
61
|
+
fill: (value: T, start?: number, end?: number) => T[];
|
|
62
|
+
copyWithin: (target: number, start: number, end?: number) => T[];
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Configuration options for the wrapArrayMethods function.
|
|
66
|
+
*/
|
|
67
|
+
export interface MutationOptions<T = any> {
|
|
68
|
+
target: T[]; // The array to mutate
|
|
69
|
+
onBeforeMutate?: (context: MutationContextt) => void; // Callback triggered before the mutation
|
|
70
|
+
onAfterMutate?: (patch: MutationPatch) => void; // Callback triggered after the mutation
|
|
71
|
+
allowList?: ArrayMutationNames; // Optional list of allowed mutation methods
|
|
72
|
+
props?: Record<string, any>; // Additional properties to attach to the patch
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
const wrapArrayMethods = <T = any>({
|
|
77
|
+
target,
|
|
78
|
+
onBeforeMutate = () => { },
|
|
79
|
+
onAfterMutate = () => { },
|
|
80
|
+
allowList,
|
|
81
|
+
props = {},
|
|
82
|
+
}: MutationOptions<T>): ArrayMutationMethods<T> => {
|
|
83
|
+
|
|
84
|
+
// Validation: Ensure target is an array and method is allowed
|
|
85
|
+
if (!Array.isArray(target)) {
|
|
86
|
+
throw new TypeError("The 'target' parameter must be an array.");
|
|
87
|
+
}
|
|
88
|
+
//使用默认
|
|
89
|
+
if (!allowList || allowList?.length) {
|
|
90
|
+
allowList = mutableMethods;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const methods: Partial<ArrayMutationMethods<T>> = {};
|
|
94
|
+
|
|
95
|
+
for (let method of allowList) {
|
|
96
|
+
|
|
97
|
+
methods[method as keyof ArrayMutationMethods<T>] = function (...args: any) {
|
|
98
|
+
|
|
99
|
+
const context: MutationContextt = {},
|
|
100
|
+
len = target.length;
|
|
101
|
+
|
|
102
|
+
// Capture "Pre-mutation" context for Undo/Redo/Tracking purposes
|
|
103
|
+
switch (method) {
|
|
104
|
+
// 'push' and 'unshift' are add operations, in undo/redo,
|
|
105
|
+
// we can determine the original data structure from result.length and args.length
|
|
106
|
+
// but methods that involve deletion need to record the deleted items to ensure the patch can restore the data structure during undo
|
|
107
|
+
case 'push':
|
|
108
|
+
case 'unshift':
|
|
109
|
+
context.addedItems = [...args];
|
|
110
|
+
break;
|
|
111
|
+
case 'pop':
|
|
112
|
+
context.poppedValue = target[len - 1];
|
|
113
|
+
break;
|
|
114
|
+
case 'shift':
|
|
115
|
+
context.shiftedValue = target[0];
|
|
116
|
+
break;
|
|
117
|
+
case 'splice':
|
|
118
|
+
const [s, d] = args,
|
|
119
|
+
// Calculate actual start index (handling negative values)
|
|
120
|
+
start = s < 0 ? Math.max(len + s, 0) : Math.min(s, len),
|
|
121
|
+
// Handle deleteCount defaults
|
|
122
|
+
deleteCount = d === undefined ? len - start : d;
|
|
123
|
+
context.deletedItems = target.slice(start, start + deleteCount);
|
|
124
|
+
break;
|
|
125
|
+
case 'sort':
|
|
126
|
+
case 'reverse':
|
|
127
|
+
// These methods reorder the whole array; requires a full shallow copy
|
|
128
|
+
context.oldSnapshot = [...target];
|
|
129
|
+
break;
|
|
130
|
+
case 'fill':
|
|
131
|
+
case 'copyWithin':
|
|
132
|
+
const startIdx = args[1] || 0,
|
|
133
|
+
endIdx = args[2] === undefined ? len : args[2];
|
|
134
|
+
// Overwritten values
|
|
135
|
+
context.oldItems = target.slice(startIdx, endIdx);
|
|
136
|
+
context.start = startIdx;
|
|
137
|
+
context.end = endIdx;
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Execute the "onBeforeMutate" callback before mutation
|
|
142
|
+
onBeforeMutate?.(context);
|
|
143
|
+
|
|
144
|
+
// Execute the native array mutation
|
|
145
|
+
const value = (Array.prototype as any)[method].apply(target, args),
|
|
146
|
+
patch: MutationPatch<T> = {
|
|
147
|
+
value,
|
|
148
|
+
key: method,
|
|
149
|
+
args,
|
|
150
|
+
context,
|
|
151
|
+
target,
|
|
152
|
+
...props
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// Construct and trigger the patch callback
|
|
156
|
+
onAfterMutate?.(patch);
|
|
157
|
+
return value;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return methods as ArrayMutationMethods<T>;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export default wrapArrayMethods;
|
package/src/mutateArray.js
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @since Last modified: 2025/12/18 21:20:26
|
|
3
|
-
* Mutates an array by applying one of the allowed mutation methods such as push, pop, shift, unshift, splice, etc.
|
|
4
|
-
* Supports tracking of added, removed, and updated items for undo/redo or tracking purposes.
|
|
5
|
-
* The mutation methods that can be tracked include push, pop, shift, unshift, splice, sort, reverse, fill, and copyWithin.
|
|
6
|
-
*
|
|
7
|
-
* @template T
|
|
8
|
-
* @param {MutateOptions} options - Configuration object containing the target array, method to apply, arguments for the method, and an optional patch callback.
|
|
9
|
-
* @returns {any} - The result of the array mutation.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* const arr = [1, 2, 3];
|
|
13
|
-
* mutateArray({
|
|
14
|
-
* target: arr,
|
|
15
|
-
* method: 'push',
|
|
16
|
-
* args: [4],
|
|
17
|
-
* onPatch: (patch) => { console.log(patch); }
|
|
18
|
-
* });
|
|
19
|
-
* // arr is now [1, 2, 3, 4]
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* const arr = [1, 2, 3];
|
|
23
|
-
* mutateArray({
|
|
24
|
-
* target: arr,
|
|
25
|
-
* method: 'splice',
|
|
26
|
-
* args: [1, 1, 4],
|
|
27
|
-
* onPatch: (patch) => { console.log(patch); }
|
|
28
|
-
* });
|
|
29
|
-
* // arr is now [1, 4, 3]
|
|
30
|
-
* // The patch callback logs the mutation details including deleted items and added items.
|
|
31
|
-
*/
|
|
32
|
-
'use strict';
|
|
33
|
-
import mutateMethods from "./mutateMethods";
|
|
34
|
-
const mutateArray = ({ target, method, args = [], onBeforeMutate = () => { }, onAfterMutate = () => { }, allowList }) => {
|
|
35
|
-
// Validation: Ensure target is an array and method is allowed
|
|
36
|
-
if (!Array.isArray(target)) {
|
|
37
|
-
throw new TypeError("The 'target' parameter must be an array.");
|
|
38
|
-
}
|
|
39
|
-
//使用默认
|
|
40
|
-
if (!allowList || allowList?.length) {
|
|
41
|
-
allowList = mutateMethods;
|
|
42
|
-
}
|
|
43
|
-
if (!allowList?.includes(method)) {
|
|
44
|
-
throw new Error(`Method "${method}" is not in the allowList or is not a mutation method.`);
|
|
45
|
-
}
|
|
46
|
-
const context = {}, len = target.length;
|
|
47
|
-
// Capture "Pre-mutation" context for Undo/Redo/Tracking purposes
|
|
48
|
-
switch (method) {
|
|
49
|
-
// 'push' and 'unshift' are add operations, in undo/redo,
|
|
50
|
-
// we can determine the original data structure from result.length and args.length
|
|
51
|
-
// but methods that involve deletion need to record the deleted items to ensure the patch can restore the data structure during undo
|
|
52
|
-
case 'push':
|
|
53
|
-
case 'unshift':
|
|
54
|
-
context.addedItems = [...args];
|
|
55
|
-
break;
|
|
56
|
-
case 'pop':
|
|
57
|
-
context.poppedValue = target[len - 1];
|
|
58
|
-
break;
|
|
59
|
-
case 'shift':
|
|
60
|
-
context.shiftedValue = target[0];
|
|
61
|
-
break;
|
|
62
|
-
case 'splice':
|
|
63
|
-
const [s, d] = args,
|
|
64
|
-
// Calculate actual start index (handling negative values)
|
|
65
|
-
start = s < 0 ? Math.max(len + s, 0) : Math.min(s, len),
|
|
66
|
-
// Handle deleteCount defaults
|
|
67
|
-
deleteCount = d === undefined ? len - start : d;
|
|
68
|
-
context.deletedItems = target.slice(start, start + deleteCount);
|
|
69
|
-
break;
|
|
70
|
-
case 'sort':
|
|
71
|
-
case 'reverse':
|
|
72
|
-
// These methods reorder the whole array; requires a full shallow copy
|
|
73
|
-
context.oldSnapshot = [...target];
|
|
74
|
-
break;
|
|
75
|
-
case 'fill':
|
|
76
|
-
case 'copyWithin':
|
|
77
|
-
const startIdx = args[1] || 0, endIdx = args[2] === undefined ? len : args[2];
|
|
78
|
-
// Overwritten values
|
|
79
|
-
context.oldItems = target.slice(startIdx, endIdx);
|
|
80
|
-
context.start = startIdx;
|
|
81
|
-
context.end = endIdx;
|
|
82
|
-
break;
|
|
83
|
-
}
|
|
84
|
-
// Execute the "onBeforeMutate" callback before mutation
|
|
85
|
-
onBeforeMutate && onBeforeMutate(context);
|
|
86
|
-
// Execute the native array mutation
|
|
87
|
-
const result = Array.prototype[method].apply(target, args);
|
|
88
|
-
// Construct and trigger the patch callback
|
|
89
|
-
onAfterMutate && onAfterMutate({
|
|
90
|
-
method,
|
|
91
|
-
args,
|
|
92
|
-
context,
|
|
93
|
-
result,
|
|
94
|
-
timestamp: Date.now()
|
|
95
|
-
});
|
|
96
|
-
return result;
|
|
97
|
-
};
|
|
98
|
-
export default mutateArray;
|
package/src/mutateArray.ts
DELETED
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @since Last modified: 2025/12/18 21:20:26
|
|
3
|
-
* Mutates an array by applying one of the allowed mutation methods such as push, pop, shift, unshift, splice, etc.
|
|
4
|
-
* Supports tracking of added, removed, and updated items for undo/redo or tracking purposes.
|
|
5
|
-
* The mutation methods that can be tracked include push, pop, shift, unshift, splice, sort, reverse, fill, and copyWithin.
|
|
6
|
-
*
|
|
7
|
-
* @template T
|
|
8
|
-
* @param {MutateOptions} options - Configuration object containing the target array, method to apply, arguments for the method, and an optional patch callback.
|
|
9
|
-
* @returns {any} - The result of the array mutation.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* const arr = [1, 2, 3];
|
|
13
|
-
* mutateArray({
|
|
14
|
-
* target: arr,
|
|
15
|
-
* method: 'push',
|
|
16
|
-
* args: [4],
|
|
17
|
-
* onPatch: (patch) => { console.log(patch); }
|
|
18
|
-
* });
|
|
19
|
-
* // arr is now [1, 2, 3, 4]
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* const arr = [1, 2, 3];
|
|
23
|
-
* mutateArray({
|
|
24
|
-
* target: arr,
|
|
25
|
-
* method: 'splice',
|
|
26
|
-
* args: [1, 1, 4],
|
|
27
|
-
* onPatch: (patch) => { console.log(patch); }
|
|
28
|
-
* });
|
|
29
|
-
* // arr is now [1, 4, 3]
|
|
30
|
-
* // The patch callback logs the mutation details including deleted items and added items.
|
|
31
|
-
*/
|
|
32
|
-
'use strict';
|
|
33
|
-
|
|
34
|
-
import mutateMethods from "./mutateMethods";
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Defines the structure of the captured state before mutation.
|
|
39
|
-
*/
|
|
40
|
-
export interface MutateContext {
|
|
41
|
-
addedItems?: any[]; // Items added to the array
|
|
42
|
-
poppedValue?: any; // Value popped from the array
|
|
43
|
-
shiftedValue?: any; // Value shifted from the array
|
|
44
|
-
deletedItems?: any[]; // Items removed from the array
|
|
45
|
-
oldSnapshot?: any[]; // Snapshot of the array before methods like 'sort' or 'reverse'
|
|
46
|
-
oldItems?: any[]; // Items overwritten (for methods like 'fill' or 'copyWithin')
|
|
47
|
-
start?: number; // Starting index for mutations like 'fill' or 'splice'
|
|
48
|
-
end?: number; // Ending index for mutations like 'fill' or 'splice'
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* The data object passed to the callback after mutation.
|
|
53
|
-
*/
|
|
54
|
-
export interface MutatePatch {
|
|
55
|
-
method: string; // The mutation method (e.g., 'push', 'pop', 'splice', etc.)
|
|
56
|
-
args: any[]; // Arguments passed to the mutation method
|
|
57
|
-
context: MutateContext; // The state before mutation
|
|
58
|
-
result: any; // The result of the mutation (the modified array)
|
|
59
|
-
timestamp: number; // The timestamp of the mutation
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Configuration options for the mutateArray function.
|
|
64
|
-
*/
|
|
65
|
-
export interface MutateOptions {
|
|
66
|
-
target: any[]; // The array to mutate
|
|
67
|
-
method: string; // The mutation method to apply (e.g., 'push', 'splice', etc.)
|
|
68
|
-
args?: any[]; // Optional arguments to pass to the mutation method
|
|
69
|
-
onBeforeMutate?: (context: MutateContext) => void; // Callback triggered before the mutation
|
|
70
|
-
onAfterMutate?: (patch: MutatePatch) => void; // Callback triggered after the mutation
|
|
71
|
-
allowList?: string[]; // Optional list of allowed mutation methods
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const mutateArray = ({
|
|
75
|
-
target,
|
|
76
|
-
method,
|
|
77
|
-
args = [],
|
|
78
|
-
onBeforeMutate = () => { },
|
|
79
|
-
onAfterMutate = () => { },
|
|
80
|
-
allowList
|
|
81
|
-
}: MutateOptions): any => {
|
|
82
|
-
|
|
83
|
-
// Validation: Ensure target is an array and method is allowed
|
|
84
|
-
if (!Array.isArray(target)) {
|
|
85
|
-
throw new TypeError("The 'target' parameter must be an array.");
|
|
86
|
-
}
|
|
87
|
-
//使用默认
|
|
88
|
-
if (!allowList || allowList?.length) {
|
|
89
|
-
allowList = mutateMethods;
|
|
90
|
-
}
|
|
91
|
-
if (!allowList?.includes(method)) {
|
|
92
|
-
throw new Error(`Method "${method}" is not in the allowList or is not a mutation method.`);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const context: MutateContext = {},
|
|
96
|
-
len = target.length;
|
|
97
|
-
|
|
98
|
-
// Capture "Pre-mutation" context for Undo/Redo/Tracking purposes
|
|
99
|
-
switch (method) {
|
|
100
|
-
// 'push' and 'unshift' are add operations, in undo/redo,
|
|
101
|
-
// we can determine the original data structure from result.length and args.length
|
|
102
|
-
// but methods that involve deletion need to record the deleted items to ensure the patch can restore the data structure during undo
|
|
103
|
-
case 'push':
|
|
104
|
-
case 'unshift':
|
|
105
|
-
context.addedItems = [...args];
|
|
106
|
-
break;
|
|
107
|
-
case 'pop':
|
|
108
|
-
context.poppedValue = target[len - 1];
|
|
109
|
-
break;
|
|
110
|
-
case 'shift':
|
|
111
|
-
context.shiftedValue = target[0];
|
|
112
|
-
break;
|
|
113
|
-
case 'splice':
|
|
114
|
-
const [s, d] = args,
|
|
115
|
-
// Calculate actual start index (handling negative values)
|
|
116
|
-
start = s < 0 ? Math.max(len + s, 0) : Math.min(s, len),
|
|
117
|
-
// Handle deleteCount defaults
|
|
118
|
-
deleteCount = d === undefined ? len - start : d;
|
|
119
|
-
context.deletedItems = target.slice(start, start + deleteCount);
|
|
120
|
-
break;
|
|
121
|
-
case 'sort':
|
|
122
|
-
case 'reverse':
|
|
123
|
-
// These methods reorder the whole array; requires a full shallow copy
|
|
124
|
-
context.oldSnapshot = [...target];
|
|
125
|
-
break;
|
|
126
|
-
case 'fill':
|
|
127
|
-
case 'copyWithin':
|
|
128
|
-
const startIdx = args[1] || 0,
|
|
129
|
-
endIdx = args[2] === undefined ? len : args[2];
|
|
130
|
-
// Overwritten values
|
|
131
|
-
context.oldItems = target.slice(startIdx, endIdx);
|
|
132
|
-
context.start = startIdx;
|
|
133
|
-
context.end = endIdx;
|
|
134
|
-
break;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Execute the "onBeforeMutate" callback before mutation
|
|
138
|
-
onBeforeMutate && onBeforeMutate(context);
|
|
139
|
-
|
|
140
|
-
// Execute the native array mutation
|
|
141
|
-
const result = (Array.prototype as any)[method].apply(target, args);
|
|
142
|
-
|
|
143
|
-
// Construct and trigger the patch callback
|
|
144
|
-
onAfterMutate && onAfterMutate({
|
|
145
|
-
method,
|
|
146
|
-
args,
|
|
147
|
-
context,
|
|
148
|
-
result,
|
|
149
|
-
timestamp: Date.now()
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
return result;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
export default mutateArray;
|