@cmtlyt/lingshu-toolkit 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/607.js +311 -0
- package/dist/707.js +6 -1
- package/dist/react/index.d.ts +7 -0
- package/dist/react/index.js +122 -3
- package/dist/react/use-force-update/index.d.ts +1 -0
- package/dist/react/use-force-update/index.js +6 -0
- package/dist/react/use-force-update/index.test.d.ts +1 -0
- package/dist/react/use-ref-state/index.d.ts +8 -0
- package/dist/react/use-ref-state/index.js +35 -0
- package/dist/react/use-ref-state/index.test.d.ts +1 -0
- package/dist/react/use-storage/index.d.ts +2 -0
- package/dist/react/use-storage/index.js +15 -0
- package/dist/react/use-storage/index.test.d.ts +1 -0
- package/dist/react/use-valid-data/index.d.ts +3 -3
- package/dist/shared/animation/__test__/animation-pause-resume.test.d.ts +1 -0
- package/dist/shared/animation/__test__/animation.test.d.ts +1 -0
- package/dist/shared/animation/__test__/step-animation.test.d.ts +1 -0
- package/dist/shared/animation/__test__/utils.test.d.ts +1 -0
- package/dist/shared/animation/index.d.ts +3 -0
- package/dist/shared/animation/index.js +71 -0
- package/dist/shared/animation/types.d.ts +22 -0
- package/dist/shared/animation/types.js +0 -0
- package/dist/shared/animation/utils.d.ts +16 -0
- package/dist/shared/animation/utils.js +138 -0
- package/dist/shared/condition-merge/index.d.ts +1 -5
- package/dist/shared/create-storage-handler/index.browser.test.d.ts +1 -0
- package/dist/shared/create-storage-handler/index.d.ts +10 -0
- package/dist/shared/create-storage-handler/index.js +68 -0
- package/dist/shared/create-storage-handler/index.test.d.ts +1 -0
- package/dist/shared/data-handler/index.d.ts +7 -2
- package/dist/shared/data-handler/tools.d.ts +36 -12
- package/dist/shared/data-handler/tools.js +7 -1
- package/dist/shared/data-handler/types.d.ts +2 -2
- package/dist/shared/index.d.ts +5 -0
- package/dist/shared/index.js +1 -0
- package/dist/shared/types/base.d.ts +3 -0
- package/dist/shared/with-resolvers/index.d.ts +6 -0
- package/dist/shared/with-resolvers/index.js +15 -0
- package/dist/shared/with-resolvers/index.test.d.ts +1 -0
- package/dist/test/utils.d.ts +13 -0
- package/package.json +6 -4
package/dist/607.js
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import { dataHandler, logger, $dt, throwError, $t, throwType } from "./707.js";
|
|
2
|
+
function withResolvers() {
|
|
3
|
+
return 'function' == typeof Promise.withResolvers ? Promise.withResolvers() : (()=>{
|
|
4
|
+
const resolver = {
|
|
5
|
+
promise: null,
|
|
6
|
+
resolve: null,
|
|
7
|
+
reject: null
|
|
8
|
+
};
|
|
9
|
+
resolver.promise = new Promise((resolve, reject)=>{
|
|
10
|
+
resolver.resolve = resolve;
|
|
11
|
+
resolver.reject = reject;
|
|
12
|
+
});
|
|
13
|
+
return resolver;
|
|
14
|
+
})();
|
|
15
|
+
}
|
|
16
|
+
const noop = ()=>void 0;
|
|
17
|
+
const identity = (_v)=>_v;
|
|
18
|
+
function getType(_v) {
|
|
19
|
+
return Object.prototype.toString.call(_v).slice(8, -1).toLowerCase();
|
|
20
|
+
}
|
|
21
|
+
function getNextValueHandler(from, to, valueFormatter) {
|
|
22
|
+
const type = getType(from);
|
|
23
|
+
const context = {
|
|
24
|
+
progress: 0,
|
|
25
|
+
valueFormatter
|
|
26
|
+
};
|
|
27
|
+
const baseNextValue = (_from, _to)=>{
|
|
28
|
+
const { valueFormatter: formatter, progress } = context;
|
|
29
|
+
if ('number' !== getType(_from)) return _from;
|
|
30
|
+
return formatter(_from + (_to - _from) * progress);
|
|
31
|
+
};
|
|
32
|
+
const arrayHandler = (_from, _to)=>{
|
|
33
|
+
const result = Array.from(_from, (item, idx)=>{
|
|
34
|
+
if (Array.isArray(item)) return arrayHandler(item, _to[idx]);
|
|
35
|
+
if ('object' === getType(item)) return objectHandler(item, _to[idx]);
|
|
36
|
+
return baseNextValue(item, _to[idx]);
|
|
37
|
+
});
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
const objectHandler = (_from, _to)=>{
|
|
41
|
+
const result = {};
|
|
42
|
+
const keys = Reflect.ownKeys(_from);
|
|
43
|
+
for(let i = 0; i < keys.length; i++){
|
|
44
|
+
const key = keys[i];
|
|
45
|
+
const fromValue = _from[key];
|
|
46
|
+
const toValue = _to[key];
|
|
47
|
+
if (Array.isArray(_from[key])) result[key] = arrayHandler(fromValue, toValue);
|
|
48
|
+
else if ('object' === getType(fromValue)) result[key] = objectHandler(fromValue, toValue);
|
|
49
|
+
else result[key] = baseNextValue(fromValue, toValue);
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
};
|
|
53
|
+
let nextValueHandler = baseNextValue;
|
|
54
|
+
if ('array' === type) nextValueHandler = arrayHandler;
|
|
55
|
+
else if ('object' === type) nextValueHandler = objectHandler;
|
|
56
|
+
return (progress)=>{
|
|
57
|
+
context.progress = progress;
|
|
58
|
+
return nextValueHandler(from, to);
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function matchValid(from, to, valueParser) {
|
|
62
|
+
const fromType = getType(from);
|
|
63
|
+
const toType = getType(to);
|
|
64
|
+
if (fromType !== toType) throwType('animation/stepAnimation', 'from and to must be the same type');
|
|
65
|
+
if ('array' === fromType) {
|
|
66
|
+
if (from.length !== to.length) throwType('animation/stepAnimation', 'from and to must be the same length');
|
|
67
|
+
const result = [
|
|
68
|
+
Array.from({
|
|
69
|
+
length: from.length
|
|
70
|
+
}),
|
|
71
|
+
Array.from({
|
|
72
|
+
length: to.length
|
|
73
|
+
})
|
|
74
|
+
];
|
|
75
|
+
for(let i = 0; i < from.length; i++){
|
|
76
|
+
const [fromItem, toItem] = matchValid(from[i], to[i], valueParser);
|
|
77
|
+
result[0][i] = fromItem;
|
|
78
|
+
result[1][i] = toItem;
|
|
79
|
+
}
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
if ('object' === fromType) {
|
|
83
|
+
const toKeys = Reflect.ownKeys(to);
|
|
84
|
+
const fromKeys = new Set(Reflect.ownKeys(from));
|
|
85
|
+
const result = [
|
|
86
|
+
{},
|
|
87
|
+
{}
|
|
88
|
+
];
|
|
89
|
+
for(let i = 0; i < toKeys.length; i++){
|
|
90
|
+
const key = toKeys[i];
|
|
91
|
+
if (!fromKeys.has(key)) throwType('animation/stepAnimation', `from does not have this key: ${String(key)}`);
|
|
92
|
+
const [fromItem, toItem] = matchValid(from[key], to[key], valueParser);
|
|
93
|
+
result[0][key] = fromItem;
|
|
94
|
+
result[1][key] = toItem;
|
|
95
|
+
}
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
if ('number' !== fromType) return [
|
|
99
|
+
valueParser(from),
|
|
100
|
+
valueParser(to)
|
|
101
|
+
];
|
|
102
|
+
return [
|
|
103
|
+
from,
|
|
104
|
+
to
|
|
105
|
+
];
|
|
106
|
+
}
|
|
107
|
+
function createNextTick(resolvers, rcSignal) {
|
|
108
|
+
const nextTick = (()=>{
|
|
109
|
+
if ('function' == typeof globalThis.requestAnimationFrame) return globalThis.requestAnimationFrame;
|
|
110
|
+
return (callback)=>setTimeout(callback, 16);
|
|
111
|
+
})();
|
|
112
|
+
return (callback)=>{
|
|
113
|
+
if (rcSignal.stopSignal) return true;
|
|
114
|
+
nextTick(()=>tryRun(callback, resolvers, (error)=>{
|
|
115
|
+
rcSignal.stop();
|
|
116
|
+
resolvers.reject(error);
|
|
117
|
+
}));
|
|
118
|
+
return false;
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
async function tryRun(callback, resolvers, customErrorHandler) {
|
|
122
|
+
return new Promise((resolve, reject)=>{
|
|
123
|
+
const result = callback();
|
|
124
|
+
if (result && 'function' == typeof result.then) return result.then(resolve, reject);
|
|
125
|
+
resolve();
|
|
126
|
+
}).catch(customErrorHandler || resolvers.reject);
|
|
127
|
+
}
|
|
128
|
+
function createRunningControllerSignal(startFn, options) {
|
|
129
|
+
const { onStart, onStop, onClear } = options;
|
|
130
|
+
const resolvers = withResolvers();
|
|
131
|
+
const ctrl = {
|
|
132
|
+
stopSignal: true,
|
|
133
|
+
resolvers,
|
|
134
|
+
stop: ()=>{
|
|
135
|
+
ctrl.stopSignal = true;
|
|
136
|
+
onStop();
|
|
137
|
+
},
|
|
138
|
+
start: ()=>{
|
|
139
|
+
ctrl.stopSignal = false;
|
|
140
|
+
onStart();
|
|
141
|
+
startFn();
|
|
142
|
+
},
|
|
143
|
+
clear: ()=>{
|
|
144
|
+
ctrl.stopSignal = true;
|
|
145
|
+
onClear();
|
|
146
|
+
resolvers.resolve(true);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
return ctrl;
|
|
150
|
+
}
|
|
151
|
+
function* stepAnimation(from, to, step, options = {}) {
|
|
152
|
+
if (!Number.isInteger(step) || step <= 0) throwError('stepAnimation', 'step must be a positive integer', RangeError);
|
|
153
|
+
const { parser: valueParser = identity, formatter: valueFormatter = identity } = options;
|
|
154
|
+
const [validFrom, validTo] = matchValid(from, to, valueParser);
|
|
155
|
+
const getNextValue = getNextValueHandler(validFrom, validTo, valueFormatter);
|
|
156
|
+
for(let i = 0; i <= step; i++){
|
|
157
|
+
const value = getNextValue(i / step);
|
|
158
|
+
yield value;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const validInfo = $dt({
|
|
162
|
+
autoStart: $t.boolean(true),
|
|
163
|
+
easing: $t["function"](()=>identity),
|
|
164
|
+
onStart: $t["function"](()=>noop),
|
|
165
|
+
onStop: $t["function"](()=>noop),
|
|
166
|
+
onClear: $t["function"](()=>noop),
|
|
167
|
+
onUpdate: $t["function"](()=>noop),
|
|
168
|
+
onComplete: $t["function"](()=>noop),
|
|
169
|
+
formatter: $t["function"](()=>identity),
|
|
170
|
+
parser: $t["function"](()=>identity)
|
|
171
|
+
});
|
|
172
|
+
function animation(from, to, duration, options = {}) {
|
|
173
|
+
if (duration <= 0 || !Number.isInteger(duration)) throwError('animation', 'duration must be a positive integer', RangeError);
|
|
174
|
+
const validOptions = dataHandler(options, validInfo, {
|
|
175
|
+
unwrap: true
|
|
176
|
+
});
|
|
177
|
+
const [validFrom, validTo] = matchValid(from, to, validOptions.parser);
|
|
178
|
+
const { autoStart, easing, onComplete, onUpdate, formatter: valueFormatter } = validOptions;
|
|
179
|
+
const getNextValue = getNextValueHandler(validFrom, validTo, valueFormatter);
|
|
180
|
+
let startTime = 0;
|
|
181
|
+
let hasStarted = false;
|
|
182
|
+
const rcSignal = createRunningControllerSignal(()=>{
|
|
183
|
+
const now = performance.now();
|
|
184
|
+
startTime += now;
|
|
185
|
+
const stopFlag = nextTick(tick);
|
|
186
|
+
if (stopFlag) {
|
|
187
|
+
startTime -= performance.now();
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (!hasStarted) {
|
|
191
|
+
onUpdate(getNextValue(0));
|
|
192
|
+
hasStarted = true;
|
|
193
|
+
}
|
|
194
|
+
}, validOptions);
|
|
195
|
+
const { resolvers } = rcSignal;
|
|
196
|
+
const nextTick = createNextTick(resolvers, rcSignal);
|
|
197
|
+
const tick = ()=>{
|
|
198
|
+
const elapsed = performance.now() - startTime;
|
|
199
|
+
const progress = easing(Math.min(elapsed / duration, 1));
|
|
200
|
+
const value = getNextValue(progress);
|
|
201
|
+
onUpdate(value);
|
|
202
|
+
if (elapsed < duration) {
|
|
203
|
+
const stopFlag = nextTick(tick);
|
|
204
|
+
if (stopFlag) startTime = -elapsed;
|
|
205
|
+
} else {
|
|
206
|
+
onComplete();
|
|
207
|
+
resolvers.resolve(false);
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
if (false !== autoStart) rcSignal.start();
|
|
211
|
+
return {
|
|
212
|
+
promise: resolvers.promise,
|
|
213
|
+
stop: rcSignal.stop,
|
|
214
|
+
start: rcSignal.start,
|
|
215
|
+
clear: rcSignal.clear
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function getEmpty(_v) {
|
|
219
|
+
return Array.isArray(_v) ? [] : {};
|
|
220
|
+
}
|
|
221
|
+
function valueCheck(_v) {
|
|
222
|
+
return Array.isArray(_v) || 'object' == typeof _v;
|
|
223
|
+
}
|
|
224
|
+
function conditionMerge(...input) {
|
|
225
|
+
const conditionItems = (input.length > 1 ? input : input[0]).map((item)=>{
|
|
226
|
+
let result = null;
|
|
227
|
+
if (Array.isArray(item)) {
|
|
228
|
+
const [condition, value, fullback] = item;
|
|
229
|
+
result = {
|
|
230
|
+
condition,
|
|
231
|
+
value,
|
|
232
|
+
fullback
|
|
233
|
+
};
|
|
234
|
+
} else if (Object.getOwnPropertyDescriptor(item, 'condition')) result = item;
|
|
235
|
+
else throwType('conditionMerge', 'input must be an ConditionItem');
|
|
236
|
+
const validValue = valueCheck(result.value);
|
|
237
|
+
const validFullback = void 0 === result.fullback || valueCheck(result.fullback);
|
|
238
|
+
if (!(validValue && validFullback)) throwType('conditionMerge', 'value and fullback must be an array or object');
|
|
239
|
+
return result;
|
|
240
|
+
});
|
|
241
|
+
const result = getEmpty(conditionItems[0].value);
|
|
242
|
+
const mergeFn = Array.isArray(result) ? (a1, a2)=>Reflect.apply(Array.prototype.splice.bind(a1, a1.length, 0), null, a2) : Object.assign;
|
|
243
|
+
for(let i = 0, item = conditionItems[i]; i < conditionItems.length; item = conditionItems[++i])mergeFn(result, item.condition ? item.value : item.fullback || getEmpty(item.value));
|
|
244
|
+
return result;
|
|
245
|
+
}
|
|
246
|
+
const create_storage_handler_validInfo = $dt({
|
|
247
|
+
storageKey: 'validString',
|
|
248
|
+
storageType: $t["enum"]([
|
|
249
|
+
'local',
|
|
250
|
+
'session',
|
|
251
|
+
'memory'
|
|
252
|
+
], 'local'),
|
|
253
|
+
autoSaveInterval: $t.number(0)
|
|
254
|
+
});
|
|
255
|
+
const memoryStorage = {
|
|
256
|
+
data: {},
|
|
257
|
+
getItem (key) {
|
|
258
|
+
return this.data[key];
|
|
259
|
+
},
|
|
260
|
+
setItem (key, value) {
|
|
261
|
+
this.data[key] = value;
|
|
262
|
+
},
|
|
263
|
+
removeItem (key) {
|
|
264
|
+
delete this.data[key];
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
function getStorage(storageType) {
|
|
268
|
+
try {
|
|
269
|
+
if ('memory' === storageType) return memoryStorage;
|
|
270
|
+
return 'local' === storageType ? localStorage : sessionStorage;
|
|
271
|
+
} catch {
|
|
272
|
+
logger.warn('createStorage', 'Failed to access localStorage or sessionStorage, using memoryStorage instead.');
|
|
273
|
+
return memoryStorage;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
const CLEAR_FLAG = Symbol('cleared');
|
|
277
|
+
function createStorageHandler(storageKey, initialData, options = {}) {
|
|
278
|
+
const { storageKey: validStorageKey, storageType, autoSaveInterval } = dataHandler({
|
|
279
|
+
storageKey,
|
|
280
|
+
...options
|
|
281
|
+
}, create_storage_handler_validInfo, {
|
|
282
|
+
unwrap: true
|
|
283
|
+
});
|
|
284
|
+
const storage = getStorage(storageType);
|
|
285
|
+
const storageData = storage.getItem(validStorageKey);
|
|
286
|
+
const context = {
|
|
287
|
+
data: storageData ? JSON.parse(storageData) : initialData || {}
|
|
288
|
+
};
|
|
289
|
+
return {
|
|
290
|
+
get (key) {
|
|
291
|
+
if (context.data === CLEAR_FLAG) throwError('createStorageHandler', 'Storage has been cleared.');
|
|
292
|
+
if (null == key) return context.data;
|
|
293
|
+
return context.data[key];
|
|
294
|
+
},
|
|
295
|
+
set (value, key) {
|
|
296
|
+
if (context.data === CLEAR_FLAG) throwError('createStorageHandler', 'Storage has been cleared.');
|
|
297
|
+
if (null == key) context.data = value;
|
|
298
|
+
else context.data[key] = value;
|
|
299
|
+
if (autoSaveInterval > 0) setTimeout(()=>{
|
|
300
|
+
storage.setItem(validStorageKey, JSON.stringify(context.data));
|
|
301
|
+
}, autoSaveInterval);
|
|
302
|
+
else storage.setItem(validStorageKey, JSON.stringify(context.data));
|
|
303
|
+
},
|
|
304
|
+
clear () {
|
|
305
|
+
context.data = CLEAR_FLAG;
|
|
306
|
+
storage.removeItem(validStorageKey);
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
Symbol('__PACK__');
|
|
311
|
+
export { animation, conditionMerge, createStorageHandler, stepAnimation, withResolvers };
|
package/dist/707.js
CHANGED
|
@@ -34,7 +34,12 @@ const $t = {
|
|
|
34
34
|
object: typeHandler('object'),
|
|
35
35
|
array: typeHandler('array'),
|
|
36
36
|
function: typeHandler('function'),
|
|
37
|
-
symbol: typeHandler('symbol')
|
|
37
|
+
symbol: typeHandler('symbol'),
|
|
38
|
+
enum: (list, fullback)=>{
|
|
39
|
+
if (!Array.isArray(list)) throwType('$t.enum', 'list must be an array');
|
|
40
|
+
const set = new Set(list);
|
|
41
|
+
return typeHandler('enum', (_v)=>set.has(_v))(fullback);
|
|
42
|
+
}
|
|
38
43
|
};
|
|
39
44
|
function defineTransform(dataInfo) {
|
|
40
45
|
const verifyInfo = {};
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
export * from './use-boolean';
|
|
1
2
|
export * from './use-controllable-value';
|
|
2
3
|
export * from './use-counter';
|
|
4
|
+
export * from './use-force-update';
|
|
5
|
+
export * from './use-mount';
|
|
6
|
+
export * from './use-ref-state';
|
|
7
|
+
export * from './use-storage';
|
|
8
|
+
export * from './use-title';
|
|
9
|
+
export * from './use-toggle';
|
|
3
10
|
export * from './use-valid-data';
|
package/dist/react/index.js
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
|
-
import { useEffect, useEffectEvent, useMemo, useRef, useState } from "react";
|
|
2
|
-
import { dataHandler, logger, $dt, $t } from "../707.js";
|
|
1
|
+
import { useEffect, useEffectEvent, useMemo, useReducer, useRef, useState } from "react";
|
|
2
|
+
import { dataHandler, logger, $dt, $t, throwType } from "../707.js";
|
|
3
|
+
import { createStorageHandler } from "../607.js";
|
|
4
|
+
function useToggle(defualtValue = false, reverseValue) {
|
|
5
|
+
const [state, setState] = useState(defualtValue);
|
|
6
|
+
const toggleRef = useRef([
|
|
7
|
+
defualtValue,
|
|
8
|
+
void 0 === reverseValue ? !defualtValue : reverseValue
|
|
9
|
+
]);
|
|
10
|
+
const actions = useMemo(()=>{
|
|
11
|
+
const [left, right] = toggleRef.current;
|
|
12
|
+
return {
|
|
13
|
+
set: (value)=>{
|
|
14
|
+
if (value !== left && value !== right) throwType('useToggle', 'value is not left or right');
|
|
15
|
+
setState(value);
|
|
16
|
+
},
|
|
17
|
+
setLeft: ()=>setState(left),
|
|
18
|
+
setRight: ()=>setState(right),
|
|
19
|
+
toggle: ()=>setState((prev)=>prev === left ? right : left)
|
|
20
|
+
};
|
|
21
|
+
}, []);
|
|
22
|
+
return [
|
|
23
|
+
state,
|
|
24
|
+
actions
|
|
25
|
+
];
|
|
26
|
+
}
|
|
27
|
+
function useBoolean(defaultValue = false) {
|
|
28
|
+
const [state, { toggle, set }] = useToggle(!!defaultValue);
|
|
29
|
+
const actions = useMemo(()=>({
|
|
30
|
+
toggle,
|
|
31
|
+
setTrue: ()=>set(true),
|
|
32
|
+
setFalse: ()=>set(false),
|
|
33
|
+
set: (value)=>set(!!value)
|
|
34
|
+
}), []);
|
|
35
|
+
return [
|
|
36
|
+
state,
|
|
37
|
+
actions
|
|
38
|
+
];
|
|
39
|
+
}
|
|
3
40
|
function useValidData(data, verifyInfo, options) {
|
|
4
41
|
const verifyInfoRef = useRef(verifyInfo);
|
|
5
42
|
const optionsRef = useRef(options);
|
|
@@ -84,5 +121,87 @@ function useCounter(initialValue = 0, options = {}) {
|
|
|
84
121
|
actions
|
|
85
122
|
];
|
|
86
123
|
}
|
|
124
|
+
function useForceUpdate() {
|
|
125
|
+
const [, forceUpdate] = useReducer((prev)=>(prev + 1) % 10, 0);
|
|
126
|
+
return forceUpdate;
|
|
127
|
+
}
|
|
128
|
+
function useMount(callback) {
|
|
129
|
+
const callbackRef = useRef(callback);
|
|
130
|
+
useEffect(()=>{
|
|
131
|
+
dataHandler({
|
|
132
|
+
fn: callbackRef.current
|
|
133
|
+
}, {
|
|
134
|
+
fn: $t["function"]()
|
|
135
|
+
}, {
|
|
136
|
+
strict: true
|
|
137
|
+
});
|
|
138
|
+
callbackRef.current();
|
|
139
|
+
}, []);
|
|
140
|
+
}
|
|
141
|
+
function clone(_v) {
|
|
142
|
+
return structuredClone(_v);
|
|
143
|
+
}
|
|
144
|
+
function useRefState(initialState) {
|
|
145
|
+
const stateRef = useRef(initialState);
|
|
146
|
+
const forceUpdate = useForceUpdate();
|
|
147
|
+
const ctrl = useMemo(()=>{
|
|
148
|
+
const origin = clone(stateRef.current);
|
|
149
|
+
const updateHandler = (update = true)=>void (update && forceUpdate());
|
|
150
|
+
const patchState = (updater, update = true)=>{
|
|
151
|
+
updater(stateRef.current);
|
|
152
|
+
updateHandler(update);
|
|
153
|
+
};
|
|
154
|
+
const setState = (state, update = true)=>{
|
|
155
|
+
stateRef.current = state;
|
|
156
|
+
updateHandler(update);
|
|
157
|
+
};
|
|
158
|
+
return {
|
|
159
|
+
patchState,
|
|
160
|
+
forceUpdate,
|
|
161
|
+
getState: ()=>stateRef.current,
|
|
162
|
+
setState,
|
|
163
|
+
reset: (update = true)=>setState(clone(origin), update)
|
|
164
|
+
};
|
|
165
|
+
}, [
|
|
166
|
+
forceUpdate
|
|
167
|
+
]);
|
|
168
|
+
return [
|
|
169
|
+
stateRef.current,
|
|
170
|
+
ctrl
|
|
171
|
+
];
|
|
172
|
+
}
|
|
173
|
+
function useStorage(storageKey, options, initialData) {
|
|
174
|
+
const optionsRef = useRef({
|
|
175
|
+
initialData,
|
|
176
|
+
options
|
|
177
|
+
});
|
|
178
|
+
return useMemo(()=>{
|
|
179
|
+
const { initialData: _initialData, options: _options } = optionsRef.current;
|
|
180
|
+
return createStorageHandler(storageKey, _initialData || {}, _options);
|
|
181
|
+
}, [
|
|
182
|
+
storageKey
|
|
183
|
+
]);
|
|
184
|
+
}
|
|
185
|
+
function setTitle(title) {
|
|
186
|
+
if (!title) return;
|
|
187
|
+
document.title = title;
|
|
188
|
+
}
|
|
189
|
+
const use_title_validInfo = $dt({
|
|
190
|
+
restoreOnUnmount: $t.boolean(true)
|
|
191
|
+
});
|
|
192
|
+
function useTitle(title, options = {}) {
|
|
193
|
+
const { restoreOnUnmount } = useValidData(options, use_title_validInfo);
|
|
194
|
+
useEffect(()=>{
|
|
195
|
+
const originalTitle = document.title;
|
|
196
|
+
setTitle(title);
|
|
197
|
+
return ()=>{
|
|
198
|
+
if (restoreOnUnmount) setTitle(originalTitle);
|
|
199
|
+
};
|
|
200
|
+
}, [
|
|
201
|
+
title,
|
|
202
|
+
restoreOnUnmount
|
|
203
|
+
]);
|
|
204
|
+
return setTitle;
|
|
205
|
+
}
|
|
87
206
|
export { $dt, $t, defineTransform } from "../707.js";
|
|
88
|
-
export { useControllableValue, useCounter, useValidData };
|
|
207
|
+
export { useBoolean, useControllableValue, useCounter, useForceUpdate, useMount, useRefState, useStorage, useTitle, useToggle, useValidData };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function useForceUpdate(): import("react").ActionDispatch<[]>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface UseRefStateCtrl<T> {
|
|
2
|
+
patchState: (updater: (draft: T) => void, update?: boolean) => void;
|
|
3
|
+
forceUpdate: () => void;
|
|
4
|
+
getState: () => T;
|
|
5
|
+
setState: (state: T, update?: boolean) => void;
|
|
6
|
+
reset: (update?: boolean) => void;
|
|
7
|
+
}
|
|
8
|
+
export declare function useRefState<T>(initialState: T): readonly [T, UseRefStateCtrl<T>];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { useMemo, useRef } from "react";
|
|
2
|
+
import { useForceUpdate } from "../use-force-update/index.js";
|
|
3
|
+
function clone(_v) {
|
|
4
|
+
return structuredClone(_v);
|
|
5
|
+
}
|
|
6
|
+
function useRefState(initialState) {
|
|
7
|
+
const stateRef = useRef(initialState);
|
|
8
|
+
const forceUpdate = useForceUpdate();
|
|
9
|
+
const ctrl = useMemo(()=>{
|
|
10
|
+
const origin = clone(stateRef.current);
|
|
11
|
+
const updateHandler = (update = true)=>void (update && forceUpdate());
|
|
12
|
+
const patchState = (updater, update = true)=>{
|
|
13
|
+
updater(stateRef.current);
|
|
14
|
+
updateHandler(update);
|
|
15
|
+
};
|
|
16
|
+
const setState = (state, update = true)=>{
|
|
17
|
+
stateRef.current = state;
|
|
18
|
+
updateHandler(update);
|
|
19
|
+
};
|
|
20
|
+
return {
|
|
21
|
+
patchState,
|
|
22
|
+
forceUpdate,
|
|
23
|
+
getState: ()=>stateRef.current,
|
|
24
|
+
setState,
|
|
25
|
+
reset: (update = true)=>setState(clone(origin), update)
|
|
26
|
+
};
|
|
27
|
+
}, [
|
|
28
|
+
forceUpdate
|
|
29
|
+
]);
|
|
30
|
+
return [
|
|
31
|
+
stateRef.current,
|
|
32
|
+
ctrl
|
|
33
|
+
];
|
|
34
|
+
}
|
|
35
|
+
export { useRefState };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { type CreateStorageOptions } from '../../shared/create-storage-handler';
|
|
2
|
+
export declare function useStorage<T extends Record<string, any>>(storageKey: string, options?: Partial<CreateStorageOptions>, initialData?: T): import("../../shared/create-storage-handler").StorageHandler<T>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useMemo, useRef } from "react";
|
|
2
|
+
import { createStorageHandler } from "../../shared/create-storage-handler/index.js";
|
|
3
|
+
function useStorage(storageKey, options, initialData) {
|
|
4
|
+
const optionsRef = useRef({
|
|
5
|
+
initialData,
|
|
6
|
+
options
|
|
7
|
+
});
|
|
8
|
+
return useMemo(()=>{
|
|
9
|
+
const { initialData: _initialData, options: _options } = optionsRef.current;
|
|
10
|
+
return createStorageHandler(storageKey, _initialData || {}, _options);
|
|
11
|
+
}, [
|
|
12
|
+
storageKey
|
|
13
|
+
]);
|
|
14
|
+
}
|
|
15
|
+
export { useStorage };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { DataHandlerOptions, Handler } from '../../shared/data-handler/types';
|
|
2
2
|
export * from '../../shared/data-handler/tools';
|
|
3
|
-
export declare function useValidData<T extends Record<PropertyKey, any>, O extends DataHandlerOptions<T> = DataHandlerOptions<T> & {
|
|
3
|
+
export declare function useValidData<T extends Record<PropertyKey, any>, H extends Handler<T> = Handler<T>, O extends DataHandlerOptions<T> = DataHandlerOptions<T> & {
|
|
4
4
|
unwrap: true;
|
|
5
|
-
}>(data: T, verifyInfo:
|
|
6
|
-
result: T & O["defaultValue"];
|
|
5
|
+
}>(data: T, verifyInfo: H, options?: O): O["unwrap"] extends true ? import("../../shared").Printify<import("../../shared/data-handler").Transform2Type<H> extends infer T_1 ? T_1 extends import("../../shared/data-handler").Transform2Type<H> ? T_1 extends (...args: any[]) => any ? T & O["defaultValue"] : T & O["defaultValue"] & { [K in keyof T_1]: import("../../shared").Equal<T_1[K], any> extends true ? Required<T & O["defaultValue"]>[K] : T_1[K]; } : never : never> : {
|
|
6
|
+
result: import("../../shared").Printify<import("../../shared/data-handler").Transform2Type<H> extends infer T_2 ? T_2 extends import("../../shared/data-handler").Transform2Type<H> ? T_2 extends (...args: any[]) => any ? T & O["defaultValue"] : T & O["defaultValue"] & { [K_1 in keyof T_2]: import("../../shared").Equal<T_2[K_1], any> extends true ? Required<T & O["defaultValue"]>[K_1] : T_2[K_1]; } : never : never>;
|
|
7
7
|
errors: string[];
|
|
8
8
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { AnimationBaseOptions, AnimationOptions, AnimationResult } from './types';
|
|
2
|
+
export declare function stepAnimation<T>(from: T, to: T, step: number, options?: AnimationBaseOptions): Generator<T, void, unknown>;
|
|
3
|
+
export declare function animation<T>(from: T, to: T, duration: number, options?: AnimationOptions): AnimationResult;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { $dt, $t, dataHandler } from "../data-handler/index.js";
|
|
2
|
+
import { throwError } from "../throw-error/index.js";
|
|
3
|
+
import { createNextTick, createRunningControllerSignal, getNextValueHandler, identity, matchValid, noop } from "./utils.js";
|
|
4
|
+
function* stepAnimation(from, to, step, options = {}) {
|
|
5
|
+
if (!Number.isInteger(step) || step <= 0) throwError('stepAnimation', 'step must be a positive integer', RangeError);
|
|
6
|
+
const { parser: valueParser = identity, formatter: valueFormatter = identity } = options;
|
|
7
|
+
const [validFrom, validTo] = matchValid(from, to, valueParser);
|
|
8
|
+
const getNextValue = getNextValueHandler(validFrom, validTo, valueFormatter);
|
|
9
|
+
for(let i = 0; i <= step; i++){
|
|
10
|
+
const value = getNextValue(i / step);
|
|
11
|
+
yield value;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const validInfo = $dt({
|
|
15
|
+
autoStart: $t.boolean(true),
|
|
16
|
+
easing: $t["function"](()=>identity),
|
|
17
|
+
onStart: $t["function"](()=>noop),
|
|
18
|
+
onStop: $t["function"](()=>noop),
|
|
19
|
+
onClear: $t["function"](()=>noop),
|
|
20
|
+
onUpdate: $t["function"](()=>noop),
|
|
21
|
+
onComplete: $t["function"](()=>noop),
|
|
22
|
+
formatter: $t["function"](()=>identity),
|
|
23
|
+
parser: $t["function"](()=>identity)
|
|
24
|
+
});
|
|
25
|
+
function animation(from, to, duration, options = {}) {
|
|
26
|
+
if (duration <= 0 || !Number.isInteger(duration)) throwError('animation', 'duration must be a positive integer', RangeError);
|
|
27
|
+
const validOptions = dataHandler(options, validInfo, {
|
|
28
|
+
unwrap: true
|
|
29
|
+
});
|
|
30
|
+
const [validFrom, validTo] = matchValid(from, to, validOptions.parser);
|
|
31
|
+
const { autoStart, easing, onComplete, onUpdate, formatter: valueFormatter } = validOptions;
|
|
32
|
+
const getNextValue = getNextValueHandler(validFrom, validTo, valueFormatter);
|
|
33
|
+
let startTime = 0;
|
|
34
|
+
let hasStarted = false;
|
|
35
|
+
const rcSignal = createRunningControllerSignal(()=>{
|
|
36
|
+
const now = performance.now();
|
|
37
|
+
startTime += now;
|
|
38
|
+
const stopFlag = nextTick(tick);
|
|
39
|
+
if (stopFlag) {
|
|
40
|
+
startTime -= performance.now();
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (!hasStarted) {
|
|
44
|
+
onUpdate(getNextValue(0));
|
|
45
|
+
hasStarted = true;
|
|
46
|
+
}
|
|
47
|
+
}, validOptions);
|
|
48
|
+
const { resolvers } = rcSignal;
|
|
49
|
+
const nextTick = createNextTick(resolvers, rcSignal);
|
|
50
|
+
const tick = ()=>{
|
|
51
|
+
const elapsed = performance.now() - startTime;
|
|
52
|
+
const progress = easing(Math.min(elapsed / duration, 1));
|
|
53
|
+
const value = getNextValue(progress);
|
|
54
|
+
onUpdate(value);
|
|
55
|
+
if (elapsed < duration) {
|
|
56
|
+
const stopFlag = nextTick(tick);
|
|
57
|
+
if (stopFlag) startTime = -elapsed;
|
|
58
|
+
} else {
|
|
59
|
+
onComplete();
|
|
60
|
+
resolvers.resolve(false);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
if (false !== autoStart) rcSignal.start();
|
|
64
|
+
return {
|
|
65
|
+
promise: resolvers.promise,
|
|
66
|
+
stop: rcSignal.stop,
|
|
67
|
+
start: rcSignal.start,
|
|
68
|
+
clear: rcSignal.clear
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export { animation, stepAnimation };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type Formatter = (value: number) => any;
|
|
2
|
+
export interface AnimationBaseOptions {
|
|
3
|
+
parser?: (value: any) => number;
|
|
4
|
+
formatter?: Formatter;
|
|
5
|
+
}
|
|
6
|
+
export interface AnimationOptions extends AnimationBaseOptions {
|
|
7
|
+
autoStart?: boolean;
|
|
8
|
+
easing?: (time: number) => number;
|
|
9
|
+
onStart?: () => void;
|
|
10
|
+
onStop?: () => void;
|
|
11
|
+
onClear?: () => void;
|
|
12
|
+
onUpdate?: (value: any) => void;
|
|
13
|
+
onComplete?: () => void;
|
|
14
|
+
}
|
|
15
|
+
export interface AnimationCtrl {
|
|
16
|
+
stop: () => void;
|
|
17
|
+
start: () => void;
|
|
18
|
+
clear: () => void;
|
|
19
|
+
}
|
|
20
|
+
export type AnimationResult = AnimationCtrl & {
|
|
21
|
+
promise: Promise<boolean>;
|
|
22
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type Resolver } from '../with-resolvers';
|
|
2
|
+
import type { AnimationOptions, Formatter } from './types';
|
|
3
|
+
export declare const noop: () => undefined;
|
|
4
|
+
export declare const identity: <T>(_v: T) => T;
|
|
5
|
+
export declare function getNextValueHandler(from: any, to: any, valueFormatter: Formatter): (progress: number) => any;
|
|
6
|
+
export declare function matchValid(from: any, to: any, valueParser: (value: any) => number): number[] | unknown[][] | [Record<PropertyKey, any>, Record<PropertyKey, any>];
|
|
7
|
+
export declare function createNextTick(resolvers: Resolver<any>, rcSignal: RCSignal): (callback: (...args: any[]) => void) => boolean;
|
|
8
|
+
export declare function tryRun(callback: () => any, resolvers: Resolver<any>, customErrorHandler?: (err: any) => void): Promise<void>;
|
|
9
|
+
export declare function createRunningControllerSignal(startFn: () => void, options: Required<AnimationOptions>): {
|
|
10
|
+
stopSignal: boolean;
|
|
11
|
+
resolvers: Resolver<boolean>;
|
|
12
|
+
stop: () => void;
|
|
13
|
+
start: () => void;
|
|
14
|
+
clear: () => void;
|
|
15
|
+
};
|
|
16
|
+
export type RCSignal = Omit<ReturnType<typeof createRunningControllerSignal>, 'resolvers'>;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { throwType } from "../throw-error/index.js";
|
|
2
|
+
import { withResolvers } from "../with-resolvers/index.js";
|
|
3
|
+
const noop = ()=>void 0;
|
|
4
|
+
const identity = (_v)=>_v;
|
|
5
|
+
function getType(_v) {
|
|
6
|
+
return Object.prototype.toString.call(_v).slice(8, -1).toLowerCase();
|
|
7
|
+
}
|
|
8
|
+
function getNextValueHandler(from, to, valueFormatter) {
|
|
9
|
+
const type = getType(from);
|
|
10
|
+
const context = {
|
|
11
|
+
progress: 0,
|
|
12
|
+
valueFormatter
|
|
13
|
+
};
|
|
14
|
+
const baseNextValue = (_from, _to)=>{
|
|
15
|
+
const { valueFormatter: formatter, progress } = context;
|
|
16
|
+
if ('number' !== getType(_from)) return _from;
|
|
17
|
+
return formatter(_from + (_to - _from) * progress);
|
|
18
|
+
};
|
|
19
|
+
const arrayHandler = (_from, _to)=>{
|
|
20
|
+
const result = Array.from(_from, (item, idx)=>{
|
|
21
|
+
if (Array.isArray(item)) return arrayHandler(item, _to[idx]);
|
|
22
|
+
if ('object' === getType(item)) return objectHandler(item, _to[idx]);
|
|
23
|
+
return baseNextValue(item, _to[idx]);
|
|
24
|
+
});
|
|
25
|
+
return result;
|
|
26
|
+
};
|
|
27
|
+
const objectHandler = (_from, _to)=>{
|
|
28
|
+
const result = {};
|
|
29
|
+
const keys = Reflect.ownKeys(_from);
|
|
30
|
+
for(let i = 0; i < keys.length; i++){
|
|
31
|
+
const key = keys[i];
|
|
32
|
+
const fromValue = _from[key];
|
|
33
|
+
const toValue = _to[key];
|
|
34
|
+
if (Array.isArray(_from[key])) result[key] = arrayHandler(fromValue, toValue);
|
|
35
|
+
else if ('object' === getType(fromValue)) result[key] = objectHandler(fromValue, toValue);
|
|
36
|
+
else result[key] = baseNextValue(fromValue, toValue);
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
let nextValueHandler = baseNextValue;
|
|
41
|
+
if ('array' === type) nextValueHandler = arrayHandler;
|
|
42
|
+
else if ('object' === type) nextValueHandler = objectHandler;
|
|
43
|
+
return (progress)=>{
|
|
44
|
+
context.progress = progress;
|
|
45
|
+
return nextValueHandler(from, to);
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function matchValid(from, to, valueParser) {
|
|
49
|
+
const fromType = getType(from);
|
|
50
|
+
const toType = getType(to);
|
|
51
|
+
if (fromType !== toType) throwType('animation/stepAnimation', 'from and to must be the same type');
|
|
52
|
+
if ('array' === fromType) {
|
|
53
|
+
if (from.length !== to.length) throwType('animation/stepAnimation', 'from and to must be the same length');
|
|
54
|
+
const result = [
|
|
55
|
+
Array.from({
|
|
56
|
+
length: from.length
|
|
57
|
+
}),
|
|
58
|
+
Array.from({
|
|
59
|
+
length: to.length
|
|
60
|
+
})
|
|
61
|
+
];
|
|
62
|
+
for(let i = 0; i < from.length; i++){
|
|
63
|
+
const [fromItem, toItem] = matchValid(from[i], to[i], valueParser);
|
|
64
|
+
result[0][i] = fromItem;
|
|
65
|
+
result[1][i] = toItem;
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
if ('object' === fromType) {
|
|
70
|
+
const toKeys = Reflect.ownKeys(to);
|
|
71
|
+
const fromKeys = new Set(Reflect.ownKeys(from));
|
|
72
|
+
const result = [
|
|
73
|
+
{},
|
|
74
|
+
{}
|
|
75
|
+
];
|
|
76
|
+
for(let i = 0; i < toKeys.length; i++){
|
|
77
|
+
const key = toKeys[i];
|
|
78
|
+
if (!fromKeys.has(key)) throwType('animation/stepAnimation', `from does not have this key: ${String(key)}`);
|
|
79
|
+
const [fromItem, toItem] = matchValid(from[key], to[key], valueParser);
|
|
80
|
+
result[0][key] = fromItem;
|
|
81
|
+
result[1][key] = toItem;
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
if ('number' !== fromType) return [
|
|
86
|
+
valueParser(from),
|
|
87
|
+
valueParser(to)
|
|
88
|
+
];
|
|
89
|
+
return [
|
|
90
|
+
from,
|
|
91
|
+
to
|
|
92
|
+
];
|
|
93
|
+
}
|
|
94
|
+
function createNextTick(resolvers, rcSignal) {
|
|
95
|
+
const nextTick = (()=>{
|
|
96
|
+
if ('function' == typeof globalThis.requestAnimationFrame) return globalThis.requestAnimationFrame;
|
|
97
|
+
return (callback)=>setTimeout(callback, 16);
|
|
98
|
+
})();
|
|
99
|
+
return (callback)=>{
|
|
100
|
+
if (rcSignal.stopSignal) return true;
|
|
101
|
+
nextTick(()=>tryRun(callback, resolvers, (error)=>{
|
|
102
|
+
rcSignal.stop();
|
|
103
|
+
resolvers.reject(error);
|
|
104
|
+
}));
|
|
105
|
+
return false;
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
async function tryRun(callback, resolvers, customErrorHandler) {
|
|
109
|
+
return new Promise((resolve, reject)=>{
|
|
110
|
+
const result = callback();
|
|
111
|
+
if (result && 'function' == typeof result.then) return result.then(resolve, reject);
|
|
112
|
+
resolve();
|
|
113
|
+
}).catch(customErrorHandler || resolvers.reject);
|
|
114
|
+
}
|
|
115
|
+
function createRunningControllerSignal(startFn, options) {
|
|
116
|
+
const { onStart, onStop, onClear } = options;
|
|
117
|
+
const resolvers = withResolvers();
|
|
118
|
+
const ctrl = {
|
|
119
|
+
stopSignal: true,
|
|
120
|
+
resolvers,
|
|
121
|
+
stop: ()=>{
|
|
122
|
+
ctrl.stopSignal = true;
|
|
123
|
+
onStop();
|
|
124
|
+
},
|
|
125
|
+
start: ()=>{
|
|
126
|
+
ctrl.stopSignal = false;
|
|
127
|
+
onStart();
|
|
128
|
+
startFn();
|
|
129
|
+
},
|
|
130
|
+
clear: ()=>{
|
|
131
|
+
ctrl.stopSignal = true;
|
|
132
|
+
onClear();
|
|
133
|
+
resolvers.resolve(true);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
return ctrl;
|
|
137
|
+
}
|
|
138
|
+
export { createNextTick, createRunningControllerSignal, getNextValueHandler, identity, matchValid, noop, tryRun };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IsPrimitive, UnionToIntersection } from '../types';
|
|
1
|
+
import type { IsPrimitive, Printify, UnionToIntersection } from '../types';
|
|
2
2
|
type AssertValue = Record<PropertyKey, any> | any[];
|
|
3
3
|
type ConditionArrayItem = [boolean, AssertValue, AssertValue?];
|
|
4
4
|
type ConditionObjItem = {
|
|
@@ -25,11 +25,7 @@ type MergedResult<T extends ConditionItem[]> = T extends [
|
|
|
25
25
|
...infer Last extends [ConditionItem, ...ConditionItem[]]
|
|
26
26
|
] ? ParseConditionValue<First> & MergedResult<Last> : ParseConditionValue<T[0]>;
|
|
27
27
|
type CMInput = ConditionItem[];
|
|
28
|
-
type Printify<T> = T extends any[] ? T : [T] extends [never] ? T : {
|
|
29
|
-
[K in keyof T]: T[K];
|
|
30
|
-
};
|
|
31
28
|
type FormatResult<T extends any[]> = T[0] & Record<PropertyKey, any>;
|
|
32
29
|
export declare function conditionMerge<T extends CMInput>(...input: T): FormatResult<MergedResult<T>>;
|
|
33
30
|
export declare function conditionMerge<T extends CMInput>(input: T): FormatResult<MergedResult<T>>;
|
|
34
31
|
export {};
|
|
35
|
-
t {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface CreateStorageOptions {
|
|
2
|
+
storageType: 'local' | 'session' | 'memory';
|
|
3
|
+
autoSaveInterval: number;
|
|
4
|
+
}
|
|
5
|
+
export interface StorageHandler<T extends Record<string, any>> {
|
|
6
|
+
get: <K extends keyof T | (string & {})>(key?: K) => string extends K ? T : T[K & keyof T];
|
|
7
|
+
set: <K extends keyof T | (string & {})>(value: string extends K ? T : T[K & keyof T], key?: K) => void;
|
|
8
|
+
clear: () => void;
|
|
9
|
+
}
|
|
10
|
+
export declare function createStorageHandler<T extends Record<string, any>>(storageKey: string, initialData?: T, options?: Partial<CreateStorageOptions>): StorageHandler<T>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { $dt, $t, dataHandler } from "../data-handler/index.js";
|
|
2
|
+
import { logger } from "../logger/index.js";
|
|
3
|
+
import { throwError } from "../throw-error/index.js";
|
|
4
|
+
const validInfo = $dt({
|
|
5
|
+
storageKey: 'validString',
|
|
6
|
+
storageType: $t["enum"]([
|
|
7
|
+
'local',
|
|
8
|
+
'session',
|
|
9
|
+
'memory'
|
|
10
|
+
], 'local'),
|
|
11
|
+
autoSaveInterval: $t.number(0)
|
|
12
|
+
});
|
|
13
|
+
const memoryStorage = {
|
|
14
|
+
data: {},
|
|
15
|
+
getItem (key) {
|
|
16
|
+
return this.data[key];
|
|
17
|
+
},
|
|
18
|
+
setItem (key, value) {
|
|
19
|
+
this.data[key] = value;
|
|
20
|
+
},
|
|
21
|
+
removeItem (key) {
|
|
22
|
+
delete this.data[key];
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
function getStorage(storageType) {
|
|
26
|
+
try {
|
|
27
|
+
if ('memory' === storageType) return memoryStorage;
|
|
28
|
+
return 'local' === storageType ? localStorage : sessionStorage;
|
|
29
|
+
} catch {
|
|
30
|
+
logger.warn('createStorage', 'Failed to access localStorage or sessionStorage, using memoryStorage instead.');
|
|
31
|
+
return memoryStorage;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const CLEAR_FLAG = Symbol('cleared');
|
|
35
|
+
function createStorageHandler(storageKey, initialData, options = {}) {
|
|
36
|
+
const { storageKey: validStorageKey, storageType, autoSaveInterval } = dataHandler({
|
|
37
|
+
storageKey,
|
|
38
|
+
...options
|
|
39
|
+
}, validInfo, {
|
|
40
|
+
unwrap: true
|
|
41
|
+
});
|
|
42
|
+
const storage = getStorage(storageType);
|
|
43
|
+
const storageData = storage.getItem(validStorageKey);
|
|
44
|
+
const context = {
|
|
45
|
+
data: storageData ? JSON.parse(storageData) : initialData || {}
|
|
46
|
+
};
|
|
47
|
+
return {
|
|
48
|
+
get (key) {
|
|
49
|
+
if (context.data === CLEAR_FLAG) throwError('createStorageHandler', 'Storage has been cleared.');
|
|
50
|
+
if (null == key) return context.data;
|
|
51
|
+
return context.data[key];
|
|
52
|
+
},
|
|
53
|
+
set (value, key) {
|
|
54
|
+
if (context.data === CLEAR_FLAG) throwError('createStorageHandler', 'Storage has been cleared.');
|
|
55
|
+
if (null == key) context.data = value;
|
|
56
|
+
else context.data[key] = value;
|
|
57
|
+
if (autoSaveInterval > 0) setTimeout(()=>{
|
|
58
|
+
storage.setItem(validStorageKey, JSON.stringify(context.data));
|
|
59
|
+
}, autoSaveInterval);
|
|
60
|
+
else storage.setItem(validStorageKey, JSON.stringify(context.data));
|
|
61
|
+
},
|
|
62
|
+
clear () {
|
|
63
|
+
context.data = CLEAR_FLAG;
|
|
64
|
+
storage.removeItem(validStorageKey);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
export { createStorageHandler };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import type { Equal, Printify } from '../types';
|
|
2
|
+
import type { Transform2Type } from './tools';
|
|
1
3
|
import type { DataHandlerOptions, Handler } from './types';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
+
type MergeResult<BaseResult extends Record<PropertyKey, any>, HandlerResult extends Handler<any>> = Printify<HandlerResult extends (...args: any[]) => any ? BaseResult : BaseResult & {
|
|
5
|
+
[K in keyof HandlerResult]: Equal<HandlerResult[K], any> extends true ? Required<BaseResult>[K] : HandlerResult[K];
|
|
6
|
+
}>;
|
|
7
|
+
export declare function dataHandler<M extends Record<PropertyKey, any>, H extends Handler<M> = Handler<M>, O extends DataHandlerOptions<M> = DataHandlerOptions<M>>(data: M & Partial<O['defaultValue']>, handler: H, options?: O): O['unwrap'] extends true ? MergeResult<M & O['defaultValue'], Transform2Type<H>> : {
|
|
8
|
+
result: MergeResult<M & O['defaultValue'], Transform2Type<H>>;
|
|
4
9
|
errors: string[];
|
|
5
10
|
};
|
|
6
11
|
export * from './tools';
|
|
@@ -1,18 +1,42 @@
|
|
|
1
1
|
import type { Handler } from './types';
|
|
2
2
|
type TypeHandler = NonNullable<Exclude<Handler<any>, (...args: any[]) => any>[string]>;
|
|
3
|
-
type
|
|
3
|
+
type TypeHandlerParams = Parameters<TypeHandler> extends [any, ...infer Rest] ? Rest : never;
|
|
4
|
+
type ParseType<T extends string> = T extends keyof TypeMap ? TypeMap[T] : any;
|
|
5
|
+
type TypeHandlerInfo<T extends string> = (value: ParseType<T>, ...args: TypeHandlerParams) => ReturnType<TypeHandler>;
|
|
6
|
+
interface TypeMap {
|
|
7
|
+
notNullable: any & {};
|
|
8
|
+
string: string;
|
|
9
|
+
validString: string;
|
|
10
|
+
number: number;
|
|
11
|
+
validNumber: number;
|
|
12
|
+
boolean: boolean;
|
|
13
|
+
object: Record<PropertyKey, any>;
|
|
14
|
+
array: any[];
|
|
15
|
+
function: (...args: any[]) => any;
|
|
16
|
+
symbol: symbol;
|
|
17
|
+
enum: any & {};
|
|
18
|
+
}
|
|
4
19
|
export declare const $t: {
|
|
5
|
-
notNullable: (fullback?:
|
|
6
|
-
string: (fullback?:
|
|
7
|
-
validString: (fullback?:
|
|
8
|
-
number: (fullback?:
|
|
9
|
-
validNumber: (fullback?:
|
|
10
|
-
boolean: (fullback?:
|
|
11
|
-
object: (fullback?:
|
|
12
|
-
array: (fullback?:
|
|
13
|
-
function: (fullback?:
|
|
14
|
-
symbol: (fullback?:
|
|
20
|
+
notNullable: (fullback?: any) => TypeHandlerInfo<"notNullable">;
|
|
21
|
+
string: (fullback?: string | ((_v: any) => string) | undefined) => TypeHandlerInfo<"string">;
|
|
22
|
+
validString: (fullback?: string | ((_v: any) => string) | undefined) => TypeHandlerInfo<"validString">;
|
|
23
|
+
number: (fullback?: number | ((_v: any) => number) | undefined) => TypeHandlerInfo<"number">;
|
|
24
|
+
validNumber: (fullback?: number | ((_v: any) => number) | undefined) => TypeHandlerInfo<"validNumber">;
|
|
25
|
+
boolean: (fullback?: boolean | ((_v: any) => boolean) | undefined) => TypeHandlerInfo<"boolean">;
|
|
26
|
+
object: (fullback?: Record<PropertyKey, any> | ((_v: any) => Record<PropertyKey, any>) | undefined) => TypeHandlerInfo<"object">;
|
|
27
|
+
array: (fullback?: any[] | ((_v: any) => any[]) | undefined) => TypeHandlerInfo<"array">;
|
|
28
|
+
function: (fullback?: ((_v: any) => (...args: any[]) => any) | undefined) => TypeHandlerInfo<"function">;
|
|
29
|
+
symbol: (fullback?: symbol | ((_v: any) => symbol) | undefined) => TypeHandlerInfo<"symbol">;
|
|
30
|
+
enum: <T>(list: T[], fullback?: T) => TypeHandlerInfo<"enum">;
|
|
15
31
|
};
|
|
16
|
-
|
|
32
|
+
type TransformMap = typeof $t;
|
|
33
|
+
type TransformKey = Exclude<keyof TransformMap, 'enum'>;
|
|
34
|
+
type DataTransformResult<D extends Record<PropertyKey, TransformKey | TypeHandler | undefined>> = {
|
|
35
|
+
[K in keyof D]: D[K] extends TransformKey ? TransformMap[D[K]] : D[K];
|
|
36
|
+
};
|
|
37
|
+
export type Transform2Type<R extends DataTransformResult<any>> = {
|
|
38
|
+
[K in keyof R]: R[K] extends TypeHandlerInfo<infer T> ? ParseType<T> & {} : any & {};
|
|
39
|
+
};
|
|
40
|
+
export declare function defineTransform<T extends Record<PropertyKey, any>, D extends Partial<Record<keyof T, TransformKey | TypeHandler>> = Partial<Record<keyof T, TransformKey | TypeHandler>>>(dataInfo: D): DataTransformResult<D>;
|
|
17
41
|
export declare const $dt: typeof defineTransform;
|
|
18
42
|
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { logger } from "../logger/index.js";
|
|
2
|
+
import { throwType } from "../throw-error/index.js";
|
|
2
3
|
function getType(_v) {
|
|
3
4
|
return Object.prototype.toString.call(_v).slice(8, -1).toLowerCase();
|
|
4
5
|
}
|
|
@@ -21,7 +22,12 @@ const $t = {
|
|
|
21
22
|
object: typeHandler('object'),
|
|
22
23
|
array: typeHandler('array'),
|
|
23
24
|
function: typeHandler('function'),
|
|
24
|
-
symbol: typeHandler('symbol')
|
|
25
|
+
symbol: typeHandler('symbol'),
|
|
26
|
+
enum: (list, fullback)=>{
|
|
27
|
+
if (!Array.isArray(list)) throwType('$t.enum', 'list must be an array');
|
|
28
|
+
const set = new Set(list);
|
|
29
|
+
return typeHandler('enum', (_v)=>set.has(_v))(fullback);
|
|
30
|
+
}
|
|
25
31
|
};
|
|
26
32
|
function defineTransform(dataInfo) {
|
|
27
33
|
const verifyInfo = {};
|
|
@@ -8,8 +8,8 @@ export interface Actions {
|
|
|
8
8
|
transform: <T>(value: T) => T;
|
|
9
9
|
}
|
|
10
10
|
export type Handler<M extends Record<PropertyKey, any>> = Partial<{
|
|
11
|
-
[K in keyof M]: (value:
|
|
12
|
-
}> | (<K extends keyof M>(value:
|
|
11
|
+
[K in keyof M]: (value: any, action: Actions, option: M) => false | (any & {});
|
|
12
|
+
}> | (<K extends keyof M>(value: any, key: K, action: Actions, option: M) => false | (any & {}));
|
|
13
13
|
export interface ActionContext {
|
|
14
14
|
errors: string[];
|
|
15
15
|
transforms: [PropertyKey, any][];
|
package/dist/shared/index.d.ts
CHANGED
package/dist/shared/index.js
CHANGED
|
@@ -3,3 +3,6 @@ export type NonUnion<T> = Equal<[T] extends [never] ? never : T[], T extends any
|
|
|
3
3
|
export type UnionToIntersection<U> = [U] extends [never] ? never : (U extends any ? (_v: U) => void : never) extends (_v: infer I) => void ? I : never;
|
|
4
4
|
export type IsPrimitive<T> = T extends number | string | boolean | symbol | bigint | null | undefined ? true : false;
|
|
5
5
|
export type IsBasicType<T> = T extends number | string | boolean | symbol | bigint ? true : false;
|
|
6
|
+
export type Printify<T> = T extends any[] ? T : [T] extends [never] ? T : {
|
|
7
|
+
[K in keyof T]: T[K];
|
|
8
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
function withResolvers() {
|
|
2
|
+
return 'function' == typeof Promise.withResolvers ? Promise.withResolvers() : (()=>{
|
|
3
|
+
const resolver = {
|
|
4
|
+
promise: null,
|
|
5
|
+
resolve: null,
|
|
6
|
+
reject: null
|
|
7
|
+
};
|
|
8
|
+
resolver.promise = new Promise((resolve, reject)=>{
|
|
9
|
+
resolver.resolve = resolve;
|
|
10
|
+
resolver.reject = reject;
|
|
11
|
+
});
|
|
12
|
+
return resolver;
|
|
13
|
+
})();
|
|
14
|
+
}
|
|
15
|
+
export { withResolvers };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Component, type PropsWithChildren } from 'react';
|
|
2
|
+
export declare class ErrorBoundary extends Component<PropsWithChildren<{
|
|
3
|
+
onError: () => void;
|
|
4
|
+
}>> {
|
|
5
|
+
state: {
|
|
6
|
+
hasError: boolean;
|
|
7
|
+
};
|
|
8
|
+
static getDerivedStateFromError(): {
|
|
9
|
+
hasError: boolean;
|
|
10
|
+
};
|
|
11
|
+
componentDidCatch(): void;
|
|
12
|
+
render(): import("react").ReactNode;
|
|
13
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cmtlyt/lingshu-toolkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/shared.js",
|
|
6
6
|
"module": "./dist/shared.js",
|
|
@@ -52,10 +52,12 @@
|
|
|
52
52
|
"@vitest/browser-playwright": "4.0.18",
|
|
53
53
|
"@vitest/coverage-v8": "4.0.18",
|
|
54
54
|
"@vitest/ui": "4.0.18",
|
|
55
|
+
"cross-env": "^10.1.0",
|
|
55
56
|
"esno": "^4.8.0",
|
|
56
57
|
"husky": "^9.1.7",
|
|
57
58
|
"jsdom": "^27.4.0",
|
|
58
59
|
"lint-staged": "^16.2.7",
|
|
60
|
+
"playwright": "^1.58.1",
|
|
59
61
|
"react": "^19.2.4",
|
|
60
62
|
"react-dom": "^19.2.4",
|
|
61
63
|
"rspress": "^1.47.1",
|
|
@@ -115,9 +117,9 @@
|
|
|
115
117
|
"dev:registry": "pnpm dlx serve src/public -p 3000",
|
|
116
118
|
"preview:docs": "rspress preview",
|
|
117
119
|
"format": "biome format --write",
|
|
118
|
-
"test": "vitest --config=vitest.
|
|
119
|
-
"test:ci": "vitest run --config=vitest.
|
|
120
|
-
"test:lib:ci": "vitest run",
|
|
120
|
+
"test": "vitest --config=vitest.project.config.ts --coverage.enabled --ui",
|
|
121
|
+
"test:ci": "cross-env gen_file_disabled=true vitest run --config=vitest.project.config.ts",
|
|
122
|
+
"test:lib:ci": "cross-env gen_file_disabled=true vitest run",
|
|
121
123
|
"test:lib": "vitest --coverage.enabled --ui",
|
|
122
124
|
"script:gen-file": "esno scripts/gen-file.ts"
|
|
123
125
|
}
|