@ntnyq/utils 0.6.4 → 0.7.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/index.d.ts +546 -404
- package/dist/index.js +831 -612
- package/package.json +10 -17
- package/dist/index.cjs +0 -928
- package/dist/index.d.cts +0 -621
package/dist/index.js
CHANGED
|
@@ -1,811 +1,1030 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
//#region src/fn/noop.ts
|
|
2
|
+
/**
|
|
3
|
+
* A function that does nothing.
|
|
4
|
+
*/
|
|
5
|
+
function noop() {}
|
|
6
|
+
/**
|
|
7
|
+
* Alias of {@link noop}.
|
|
8
|
+
*/
|
|
9
|
+
const NOOP = noop;
|
|
5
10
|
|
|
6
|
-
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/fn/once.ts
|
|
7
13
|
function once(func) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
let called = false;
|
|
15
|
+
return function(...args) {
|
|
16
|
+
if (called) return false;
|
|
17
|
+
called = true;
|
|
18
|
+
func.apply(this, args);
|
|
19
|
+
return true;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/is/dom.ts
|
|
25
|
+
/**
|
|
26
|
+
* @file is/dom.ts
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* Check if given value is an HTMLElement
|
|
30
|
+
* @param value - The value to check
|
|
31
|
+
* @returns True if the value is an HTMLElement, false otherwise
|
|
32
|
+
*/
|
|
20
33
|
function isHTMLElement(value) {
|
|
21
|
-
|
|
34
|
+
return typeof value === "object" && value !== null && "nodeType" in value && value.nodeType === Node.ELEMENT_NODE && value instanceof HTMLElement;
|
|
22
35
|
}
|
|
23
36
|
|
|
24
|
-
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/is/core.ts
|
|
25
39
|
function getObjectType(value) {
|
|
26
|
-
|
|
40
|
+
return Object.prototype.toString.call(value).slice(8, -1);
|
|
27
41
|
}
|
|
28
42
|
function isUndefined(value) {
|
|
29
|
-
|
|
43
|
+
return value === void 0;
|
|
30
44
|
}
|
|
31
45
|
function isNull(value) {
|
|
32
|
-
|
|
46
|
+
return value === null;
|
|
33
47
|
}
|
|
34
48
|
function isNil(value) {
|
|
35
|
-
|
|
49
|
+
return isNull(value) || isUndefined(value);
|
|
36
50
|
}
|
|
37
|
-
|
|
51
|
+
const isNullOrUndefined = isNil;
|
|
38
52
|
function isString(value) {
|
|
39
|
-
|
|
53
|
+
return typeof value === "string";
|
|
40
54
|
}
|
|
41
55
|
function isEmptyString(value) {
|
|
42
|
-
|
|
56
|
+
return isString(value) && value.length === 0;
|
|
43
57
|
}
|
|
44
58
|
function isNonEmptyString(value) {
|
|
45
|
-
|
|
59
|
+
return isString(value) && value.length > 0;
|
|
46
60
|
}
|
|
47
61
|
function isWhitespaceString(value) {
|
|
48
|
-
|
|
62
|
+
return isString(value) && /^\s*$/.test(value);
|
|
49
63
|
}
|
|
50
64
|
function isEmptyStringOrWhitespace(value) {
|
|
51
|
-
|
|
65
|
+
return isEmptyString(value) || isWhitespaceString(value);
|
|
52
66
|
}
|
|
53
67
|
function isNumbericString(value) {
|
|
54
|
-
|
|
68
|
+
return isString(value) && !isEmptyStringOrWhitespace(value) && !Number.isNaN(Number(value));
|
|
55
69
|
}
|
|
56
70
|
function isNumber(value) {
|
|
57
|
-
|
|
71
|
+
return typeof value === "number";
|
|
58
72
|
}
|
|
59
73
|
function isZero(value) {
|
|
60
|
-
|
|
74
|
+
return value === 0;
|
|
61
75
|
}
|
|
62
76
|
function isNaN(value) {
|
|
63
|
-
|
|
77
|
+
return Number.isNaN(value);
|
|
64
78
|
}
|
|
65
79
|
function isInteger(value) {
|
|
66
|
-
|
|
80
|
+
return Number.isInteger(value);
|
|
67
81
|
}
|
|
68
82
|
function isBigInt(value) {
|
|
69
|
-
|
|
83
|
+
return typeof value === "bigint";
|
|
70
84
|
}
|
|
71
85
|
function isBoolean(value) {
|
|
72
|
-
|
|
86
|
+
return typeof value === "boolean";
|
|
73
87
|
}
|
|
74
88
|
function isTruthy(value) {
|
|
75
|
-
|
|
89
|
+
return Boolean(value);
|
|
76
90
|
}
|
|
77
91
|
function isFunction(value) {
|
|
78
|
-
|
|
92
|
+
return typeof value === "function";
|
|
79
93
|
}
|
|
80
94
|
function isArray(value) {
|
|
81
|
-
|
|
95
|
+
return Array.isArray(value);
|
|
82
96
|
}
|
|
83
97
|
function isEmptyArray(value) {
|
|
84
|
-
|
|
98
|
+
return isArray(value) && value.length === 0;
|
|
85
99
|
}
|
|
86
100
|
function isNonEmptyArray(value) {
|
|
87
|
-
|
|
101
|
+
return isArray(value) && value.length > 0;
|
|
88
102
|
}
|
|
89
103
|
function isObject(value) {
|
|
90
|
-
|
|
104
|
+
return (typeof value === "object" || isFunction(value)) && !isNull(value);
|
|
91
105
|
}
|
|
92
106
|
function isEmptyObject(value) {
|
|
93
|
-
|
|
107
|
+
return isObject(value) && !isMap(value) && !isSet(value) && Object.keys(value).length === 0;
|
|
94
108
|
}
|
|
95
109
|
function isMap(value) {
|
|
96
|
-
|
|
110
|
+
return getObjectType(value) === "Map";
|
|
97
111
|
}
|
|
98
112
|
function isEmptyMap(value) {
|
|
99
|
-
|
|
113
|
+
return isMap(value) && value.size === 0;
|
|
100
114
|
}
|
|
101
115
|
function isSet(value) {
|
|
102
|
-
|
|
116
|
+
return getObjectType(value) === "Set";
|
|
103
117
|
}
|
|
104
118
|
function isEmptySet(value) {
|
|
105
|
-
|
|
119
|
+
return isSet(value) && value.size === 0;
|
|
106
120
|
}
|
|
107
121
|
function isRegExp(value) {
|
|
108
|
-
|
|
122
|
+
return getObjectType(value) === "RegExp";
|
|
109
123
|
}
|
|
110
124
|
function isError(value) {
|
|
111
|
-
|
|
125
|
+
return getObjectType(value) === "Error";
|
|
112
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* @internal
|
|
129
|
+
*/
|
|
113
130
|
function hasPromiseApi(value) {
|
|
114
|
-
|
|
115
|
-
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
116
|
-
isFunction(value?.then) && isFunction(value?.catch)
|
|
117
|
-
);
|
|
131
|
+
return isFunction(value?.then) && isFunction(value?.catch);
|
|
118
132
|
}
|
|
119
133
|
function isNativePromise(value) {
|
|
120
|
-
|
|
134
|
+
return getObjectType(value) === "Promise";
|
|
121
135
|
}
|
|
122
136
|
function isPromise(value) {
|
|
123
|
-
|
|
137
|
+
return isNativePromise(value) || hasPromiseApi(value);
|
|
124
138
|
}
|
|
125
139
|
function isIterable(value) {
|
|
126
|
-
|
|
140
|
+
return isFunction(value?.[Symbol.iterator]);
|
|
127
141
|
}
|
|
128
142
|
|
|
129
|
-
|
|
143
|
+
//#endregion
|
|
144
|
+
//#region src/is/isDeepEqual.ts
|
|
145
|
+
/**
|
|
146
|
+
* check if two values are deeply equal
|
|
147
|
+
*/
|
|
130
148
|
function isDeepEqual(value1, value2) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
return Object.is(value1, value2);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// src/dom/scrollIntoView.ts
|
|
149
|
+
const type1 = getObjectType(value1);
|
|
150
|
+
const type2 = getObjectType(value2);
|
|
151
|
+
if (type1 !== type2) return false;
|
|
152
|
+
if (isArray(value1) && isArray(value2)) {
|
|
153
|
+
if (value1.length !== value2.length) return false;
|
|
154
|
+
return value1.every((item, index) => isDeepEqual(item, value2[index]));
|
|
155
|
+
}
|
|
156
|
+
if (isObject(value1) && isObject(value2)) {
|
|
157
|
+
const keys = Object.keys(value1);
|
|
158
|
+
if (keys.length !== Object.keys(value2).length) return false;
|
|
159
|
+
return keys.every((key) => isDeepEqual(value1[key], value2[key]));
|
|
160
|
+
}
|
|
161
|
+
return Object.is(value1, value2);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
//#endregion
|
|
165
|
+
//#region src/dom/scrollIntoView.ts
|
|
166
|
+
/**
|
|
167
|
+
* Scroll element into view if it is out of view.
|
|
168
|
+
*
|
|
169
|
+
* @param element - element to scroll
|
|
170
|
+
* @param options - scroll options
|
|
171
|
+
*/
|
|
158
172
|
function scrollElementIntoView(element, options = {}) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
173
|
+
const body = document.body;
|
|
174
|
+
const { parent = body,...scrollIntoViewOptions } = options;
|
|
175
|
+
if (parent === body) {
|
|
176
|
+
parent.scrollIntoView(scrollIntoViewOptions);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const parentRect = parent.getBoundingClientRect();
|
|
180
|
+
const elementRect = element.getBoundingClientRect();
|
|
181
|
+
const isHorizontal = parent.scrollWidth > parent.scrollHeight;
|
|
182
|
+
const isOutOfView = isHorizontal ? elementRect.left < parentRect.left || elementRect.right > parentRect.right : elementRect.top < parentRect.top || elementRect.bottom > parentRect.bottom;
|
|
183
|
+
if (isOutOfView) parent.scrollIntoView(scrollIntoViewOptions);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region src/dom/openExternalURL.ts
|
|
188
|
+
/**
|
|
189
|
+
* Open external url
|
|
190
|
+
* @param url - URL to open
|
|
191
|
+
* @param options - open options
|
|
192
|
+
* @returns window proxy
|
|
193
|
+
*/
|
|
175
194
|
function openExternalURL(url, options = {}) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
195
|
+
const { target = "_blank" } = options;
|
|
196
|
+
const proxy = window.open(url, target);
|
|
197
|
+
return proxy;
|
|
179
198
|
}
|
|
180
199
|
|
|
181
|
-
|
|
200
|
+
//#endregion
|
|
201
|
+
//#region src/dom/isVisibleInViewport.ts
|
|
202
|
+
/**
|
|
203
|
+
* Check if element is in viewport
|
|
204
|
+
* @param element - checked element
|
|
205
|
+
* @param targetWindow - window
|
|
206
|
+
* @returns true if element is in viewport, false otherwise
|
|
207
|
+
*/
|
|
182
208
|
function isElementVisibleInViewport(element, targetWindow = window) {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
209
|
+
const { top, left, bottom, right } = element.getBoundingClientRect();
|
|
210
|
+
const { innerWidth, innerHeight } = targetWindow;
|
|
211
|
+
return (top >= 0 && top <= innerHeight || bottom >= 0 && bottom <= innerHeight) && (left >= 0 && left <= innerWidth || right >= 0 && right <= innerWidth);
|
|
186
212
|
}
|
|
187
213
|
|
|
188
|
-
|
|
189
|
-
|
|
214
|
+
//#endregion
|
|
215
|
+
//#region src/env/isBrowser.ts
|
|
216
|
+
/**
|
|
217
|
+
* @file env.ts
|
|
218
|
+
*/
|
|
219
|
+
/**
|
|
220
|
+
* Checks if the code is running in a browser
|
|
221
|
+
*
|
|
222
|
+
* @returns boolean - true if the code is running in a browser
|
|
223
|
+
*/
|
|
224
|
+
const isBrowser = () => typeof document !== "undefined";
|
|
190
225
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
226
|
+
//#endregion
|
|
227
|
+
//#region src/html/escape.ts
|
|
228
|
+
const htmlEscapeMap = {
|
|
229
|
+
"&": "&",
|
|
230
|
+
"<": "<",
|
|
231
|
+
">": ">",
|
|
232
|
+
"'": "'",
|
|
233
|
+
"\"": """
|
|
198
234
|
};
|
|
199
|
-
|
|
235
|
+
const htmlEscapeRegexp = /[&<>'"]/g;
|
|
236
|
+
/**
|
|
237
|
+
* Escape html chars
|
|
238
|
+
*/
|
|
200
239
|
function escapeHTML(str) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
"'": "'",
|
|
215
|
-
""": '"',
|
|
216
|
-
""": '"'
|
|
240
|
+
return str.replace(htmlEscapeRegexp, (char) => htmlEscapeMap[char]);
|
|
241
|
+
}
|
|
242
|
+
const htmlUnescapeMap = {
|
|
243
|
+
"&": "&",
|
|
244
|
+
"&": "&",
|
|
245
|
+
"<": "<",
|
|
246
|
+
"<": "<",
|
|
247
|
+
">": ">",
|
|
248
|
+
">": ">",
|
|
249
|
+
"'": "'",
|
|
250
|
+
"'": "'",
|
|
251
|
+
""": "\"",
|
|
252
|
+
""": "\""
|
|
217
253
|
};
|
|
218
|
-
|
|
254
|
+
const htmlUnescapeRegexp = /&(amp|#38|lt|#60|gt|#62|apos|#39|quot|#34);/g;
|
|
255
|
+
/**
|
|
256
|
+
* Unescape html chars
|
|
257
|
+
*/
|
|
219
258
|
function unescapeHTML(str) {
|
|
220
|
-
|
|
221
|
-
htmlUnescapeRegexp,
|
|
222
|
-
(char) => htmlUnescapeMap[char]
|
|
223
|
-
);
|
|
259
|
+
return str.replace(htmlUnescapeRegexp, (char) => htmlUnescapeMap[char]);
|
|
224
260
|
}
|
|
225
261
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
262
|
+
//#endregion
|
|
263
|
+
//#region src/misc/raf.ts
|
|
264
|
+
const root = isBrowser() ? window : globalThis;
|
|
265
|
+
let prev = Date.now();
|
|
229
266
|
function mockRAF(fn) {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
}
|
|
267
|
+
const curr = Date.now();
|
|
268
|
+
const ms = Math.max(0, 16 - (curr - prev));
|
|
269
|
+
const id = setTimeout(fn, ms);
|
|
270
|
+
prev = curr + ms;
|
|
271
|
+
return id;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Request animation frame
|
|
275
|
+
*
|
|
276
|
+
* @param fn - callback
|
|
277
|
+
* @returns id
|
|
278
|
+
*/
|
|
236
279
|
function rAF(fn) {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
}
|
|
280
|
+
const raf = root.requestAnimationFrame || mockRAF;
|
|
281
|
+
return raf.call(root, fn);
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Cancel animation frame
|
|
285
|
+
*
|
|
286
|
+
* @param id - id
|
|
287
|
+
* @returns void
|
|
288
|
+
*/
|
|
240
289
|
function cAF(id) {
|
|
241
|
-
|
|
242
|
-
|
|
290
|
+
const caf = root.cancelAnimationFrame || root.clearTimeout;
|
|
291
|
+
return caf.call(root, id);
|
|
243
292
|
}
|
|
244
293
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
294
|
+
//#endregion
|
|
295
|
+
//#region src/misc/time.ts
|
|
296
|
+
/**
|
|
297
|
+
* @file time utils
|
|
298
|
+
* @module Time
|
|
299
|
+
*/
|
|
300
|
+
const ONE_SECOND = 1e3;
|
|
301
|
+
const ONE_MINUTE = 60 * ONE_SECOND;
|
|
302
|
+
const ONE_HOUR = 60 * ONE_MINUTE;
|
|
303
|
+
const ONE_DAY = 24 * ONE_HOUR;
|
|
304
|
+
const ONE_WEEK = 7 * ONE_DAY;
|
|
251
305
|
function seconds(count) {
|
|
252
|
-
|
|
306
|
+
return count * ONE_SECOND;
|
|
253
307
|
}
|
|
254
308
|
function minutes(count) {
|
|
255
|
-
|
|
309
|
+
return count * ONE_MINUTE;
|
|
256
310
|
}
|
|
257
311
|
function hours(count) {
|
|
258
|
-
|
|
312
|
+
return count * ONE_HOUR;
|
|
259
313
|
}
|
|
260
314
|
function days(count) {
|
|
261
|
-
|
|
315
|
+
return count * ONE_DAY;
|
|
262
316
|
}
|
|
263
317
|
function weeks(count) {
|
|
264
|
-
|
|
318
|
+
return count * ONE_WEEK;
|
|
265
319
|
}
|
|
266
320
|
|
|
267
|
-
|
|
321
|
+
//#endregion
|
|
322
|
+
//#region src/misc/clamp.ts
|
|
323
|
+
/**
|
|
324
|
+
* Clamps a number between a minimum and maximum value
|
|
325
|
+
* @param value - the value to clamp within the given range
|
|
326
|
+
* @param min - the minimum value to clamp
|
|
327
|
+
* @param max - the maximum value to clamp
|
|
328
|
+
* @returns the new value
|
|
329
|
+
*/
|
|
268
330
|
function clamp(value, min = Number.NEGATIVE_INFINITY, max = Number.POSITIVE_INFINITY) {
|
|
269
|
-
|
|
331
|
+
return Math.min(Math.max(value, min), max);
|
|
270
332
|
}
|
|
271
333
|
|
|
272
|
-
|
|
334
|
+
//#endregion
|
|
335
|
+
//#region src/misc/waitFor.ts
|
|
336
|
+
/**
|
|
337
|
+
* Wait for a number of milliseconds
|
|
338
|
+
*
|
|
339
|
+
* @param ms - millseconds to wait
|
|
340
|
+
* @returns a promise that resolves after ms milliseconds
|
|
341
|
+
*
|
|
342
|
+
* @example
|
|
343
|
+
* ```
|
|
344
|
+
* import { waitFor } from '@ntnyq/utils'
|
|
345
|
+
* await waitFor(3e3)
|
|
346
|
+
* // do somthing after 3 seconds
|
|
347
|
+
* ```
|
|
348
|
+
*/
|
|
273
349
|
async function waitFor(ms) {
|
|
274
|
-
|
|
350
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
275
351
|
}
|
|
276
352
|
|
|
277
|
-
|
|
353
|
+
//#endregion
|
|
354
|
+
//#region src/misc/throttle.ts
|
|
355
|
+
/**
|
|
356
|
+
* Throttle a function to limit its execution to a maximum of once per a specified time interval.
|
|
357
|
+
*
|
|
358
|
+
* @param delay - Zero or greater delay in milliseconds
|
|
359
|
+
* @param callback - A function to be throttled
|
|
360
|
+
* @param options - throttle options
|
|
361
|
+
* @returns A throttled function
|
|
362
|
+
*/
|
|
278
363
|
function throttle(delay, callback, options = {}) {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
isDebounce ? clear : exec,
|
|
313
|
-
isDebounce ? delay : delay - elapsed
|
|
314
|
-
);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
wrapper.cancel = cancel;
|
|
318
|
-
return wrapper;
|
|
364
|
+
const { isDebounce } = options;
|
|
365
|
+
/**
|
|
366
|
+
* Track the last time `callback` was executed
|
|
367
|
+
*/
|
|
368
|
+
let lastExec = 0;
|
|
369
|
+
let cancelled = false;
|
|
370
|
+
let timeoutId;
|
|
371
|
+
function clearExistingTimeout() {
|
|
372
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
373
|
+
}
|
|
374
|
+
function cancel() {
|
|
375
|
+
clearExistingTimeout();
|
|
376
|
+
cancelled = true;
|
|
377
|
+
}
|
|
378
|
+
function wrapper(...args) {
|
|
379
|
+
if (cancelled) return;
|
|
380
|
+
const _this = this;
|
|
381
|
+
const now = Date.now();
|
|
382
|
+
const elapsed = now - lastExec;
|
|
383
|
+
function clear() {
|
|
384
|
+
timeoutId = void 0;
|
|
385
|
+
}
|
|
386
|
+
function exec(cur) {
|
|
387
|
+
lastExec = cur || Date.now();
|
|
388
|
+
callback.apply(_this, args);
|
|
389
|
+
}
|
|
390
|
+
if (isDebounce && !timeoutId) exec(now);
|
|
391
|
+
clearExistingTimeout();
|
|
392
|
+
if (!isDebounce && elapsed > delay) exec(now);
|
|
393
|
+
else timeoutId = setTimeout(isDebounce ? clear : exec, isDebounce ? delay : delay - elapsed);
|
|
394
|
+
}
|
|
395
|
+
wrapper.cancel = cancel;
|
|
396
|
+
return wrapper;
|
|
319
397
|
}
|
|
320
398
|
function debounce(delay, callback, options = {}) {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
399
|
+
return throttle(delay, callback, {
|
|
400
|
+
...options,
|
|
401
|
+
isDebounce: true
|
|
402
|
+
});
|
|
325
403
|
}
|
|
326
404
|
|
|
327
|
-
|
|
328
|
-
|
|
405
|
+
//#endregion
|
|
406
|
+
//#region src/misc/warnOnce.ts
|
|
407
|
+
/**
|
|
408
|
+
* Cached warnings
|
|
409
|
+
*/
|
|
410
|
+
const warned = new Set();
|
|
411
|
+
/**
|
|
412
|
+
* Warn message only once
|
|
413
|
+
*
|
|
414
|
+
* @param message - warning message
|
|
415
|
+
*/
|
|
329
416
|
function warnOnce(message) {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
warned.add(message);
|
|
334
|
-
console.warn(message);
|
|
417
|
+
if (warned.has(message)) return;
|
|
418
|
+
warned.add(message);
|
|
419
|
+
console.warn(message);
|
|
335
420
|
}
|
|
336
421
|
|
|
337
|
-
|
|
422
|
+
//#endregion
|
|
423
|
+
//#region src/array/at.ts
|
|
424
|
+
/**
|
|
425
|
+
* Get array item by index, negative for backward
|
|
426
|
+
* @param array - given array
|
|
427
|
+
* @param index - index of item
|
|
428
|
+
* @returns undefined if not match, otherwise matched item
|
|
429
|
+
*/
|
|
338
430
|
function at(array, index) {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
431
|
+
const length = array.length;
|
|
432
|
+
if (!length) return void 0;
|
|
433
|
+
if (index < 0) index += length;
|
|
434
|
+
return array[index];
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Get the last item of given array
|
|
438
|
+
* @param array - given array
|
|
439
|
+
* @returns undefined if empty array, otherwise last item
|
|
440
|
+
*/
|
|
346
441
|
function last(array) {
|
|
347
|
-
|
|
442
|
+
return at(array, -1);
|
|
348
443
|
}
|
|
349
444
|
|
|
350
|
-
|
|
445
|
+
//#endregion
|
|
446
|
+
//#region src/array/chunk.ts
|
|
447
|
+
/**
|
|
448
|
+
* Splits an array into smaller chunks of a given size.
|
|
449
|
+
* @param array - The array to split
|
|
450
|
+
* @param size - The size of each chunk
|
|
451
|
+
* @returns An array of arrays, where each sub-array has `size` elements from the original array.
|
|
452
|
+
*/
|
|
351
453
|
function chunk(array, size) {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
}
|
|
356
|
-
return result;
|
|
454
|
+
const result = [];
|
|
455
|
+
for (let i = 0; i < array.length; i += size) result.push(array.slice(i, i + size));
|
|
456
|
+
return result;
|
|
357
457
|
}
|
|
358
458
|
|
|
359
|
-
|
|
459
|
+
//#endregion
|
|
460
|
+
//#region src/array/unique.ts
|
|
461
|
+
/**
|
|
462
|
+
* Returns a new array with unique values.
|
|
463
|
+
* @param array - The array to process.
|
|
464
|
+
* @returns The new array.
|
|
465
|
+
*/
|
|
360
466
|
function unique(array) {
|
|
361
|
-
|
|
362
|
-
}
|
|
467
|
+
return Array.from(new Set(array));
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Returns a new array with unique values.
|
|
471
|
+
* @param array - The array to process.
|
|
472
|
+
* @param equalFn - The function to compare values.
|
|
473
|
+
* @returns The new array.
|
|
474
|
+
*/
|
|
363
475
|
function uniqueBy(array, equalFn) {
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
return acc;
|
|
370
|
-
}, []);
|
|
476
|
+
return array.reduce((acc, cur) => {
|
|
477
|
+
const idx = acc.findIndex((item) => equalFn(item, cur));
|
|
478
|
+
if (idx === -1) acc.push(cur);
|
|
479
|
+
return acc;
|
|
480
|
+
}, []);
|
|
371
481
|
}
|
|
372
482
|
|
|
373
|
-
|
|
483
|
+
//#endregion
|
|
484
|
+
//#region src/array/toArray.ts
|
|
485
|
+
/**
|
|
486
|
+
* Converts a value to an array.
|
|
487
|
+
* @param array - The value to convert.
|
|
488
|
+
* @returns The array.
|
|
489
|
+
*/
|
|
374
490
|
function toArray(array) {
|
|
375
|
-
|
|
376
|
-
|
|
491
|
+
array = array ?? [];
|
|
492
|
+
return Array.isArray(array) ? array : [array];
|
|
377
493
|
}
|
|
378
494
|
|
|
379
|
-
|
|
495
|
+
//#endregion
|
|
496
|
+
//#region src/array/arrayable.ts
|
|
497
|
+
/**
|
|
498
|
+
* Convert `Arrayable<T>` to `Array<T>` and flatten the result
|
|
499
|
+
* @param array - given array
|
|
500
|
+
* @returns Array<T>
|
|
501
|
+
*/
|
|
380
502
|
function flattenArrayable(array) {
|
|
381
|
-
|
|
503
|
+
return toArray(array).flat();
|
|
382
504
|
}
|
|
505
|
+
/**
|
|
506
|
+
* Use rest arguments to merge arrays
|
|
507
|
+
* @param args - rest arguments
|
|
508
|
+
* @returns Array<T>
|
|
509
|
+
*/
|
|
383
510
|
function mergeArrayable(...args) {
|
|
384
|
-
|
|
511
|
+
return args.flatMap((i) => toArray(i));
|
|
385
512
|
}
|
|
386
513
|
|
|
387
|
-
|
|
514
|
+
//#endregion
|
|
515
|
+
//#region src/array/intersect.ts
|
|
516
|
+
/**
|
|
517
|
+
* Get intersect items
|
|
518
|
+
*
|
|
519
|
+
* @returns intersect items
|
|
520
|
+
*/
|
|
388
521
|
function intersect(a, b) {
|
|
389
|
-
|
|
522
|
+
return a.filter((item) => b.includes(item));
|
|
390
523
|
}
|
|
391
524
|
|
|
392
|
-
|
|
525
|
+
//#endregion
|
|
526
|
+
//#region src/array/isArrayEqual.ts
|
|
527
|
+
/**
|
|
528
|
+
* Check if values of two arrays are equal
|
|
529
|
+
* @param array1 - array 1
|
|
530
|
+
* @param array2 - array 2
|
|
531
|
+
* @returns `true` if equal
|
|
532
|
+
*/
|
|
393
533
|
function isArrayEqual(array1, array2) {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
}
|
|
397
|
-
return array1.every((item, idx) => item === array2[idx]);
|
|
534
|
+
if (array1.length !== array2.length) return false;
|
|
535
|
+
return array1.every((item, idx) => item === array2[idx]);
|
|
398
536
|
}
|
|
399
537
|
|
|
400
|
-
|
|
538
|
+
//#endregion
|
|
539
|
+
//#region src/string/pad.ts
|
|
401
540
|
function createPadString(options) {
|
|
402
|
-
|
|
403
|
-
|
|
541
|
+
const { length, char } = options;
|
|
542
|
+
return (value) => (char.repeat(length) + value).slice(-length);
|
|
404
543
|
}
|
|
405
544
|
|
|
406
|
-
|
|
545
|
+
//#endregion
|
|
546
|
+
//#region src/string/join.ts
|
|
547
|
+
/**
|
|
548
|
+
* Joins an array of strings or numbers into a single string.
|
|
549
|
+
* @param array - An array of strings or numbers.
|
|
550
|
+
* @param options - An object of options.
|
|
551
|
+
* @returns A string.
|
|
552
|
+
*/
|
|
407
553
|
function join(array, options = {}) {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
554
|
+
const { separator = "" } = options;
|
|
555
|
+
if (!Array.isArray(array) || !array.length) return "";
|
|
556
|
+
return array.filter((v) => Boolean(v) || v === 0).join(separator);
|
|
411
557
|
}
|
|
412
558
|
|
|
413
|
-
|
|
559
|
+
//#endregion
|
|
560
|
+
//#region src/string/slash.ts
|
|
561
|
+
/**
|
|
562
|
+
* Replace backslash to slash
|
|
563
|
+
*/
|
|
414
564
|
function slash(input) {
|
|
415
|
-
|
|
565
|
+
return input.replace(/\\/g, "/");
|
|
416
566
|
}
|
|
417
567
|
|
|
418
|
-
|
|
568
|
+
//#endregion
|
|
569
|
+
//#region src/number/random.ts
|
|
570
|
+
/**
|
|
571
|
+
* random an integer by given range
|
|
572
|
+
*
|
|
573
|
+
* @param min - min value
|
|
574
|
+
* @param max - max value
|
|
575
|
+
* @returns random integer in range
|
|
576
|
+
*/
|
|
419
577
|
function randomNumber(min, max = 0, options = {}) {
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
578
|
+
if (max === 0) {
|
|
579
|
+
max = min;
|
|
580
|
+
min = 0;
|
|
581
|
+
}
|
|
582
|
+
if (min > max) [min, max] = [max, min];
|
|
583
|
+
return Math.trunc(Math.random() * (max - min + (options.includeMax ? 1 : 0)) + min);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
//#endregion
|
|
587
|
+
//#region src/number/toInteger.ts
|
|
588
|
+
/**
|
|
589
|
+
* Transforms a value to an integer.
|
|
590
|
+
* @param value - The value to convert to an integer.
|
|
591
|
+
* @param options - Options for the conversion.
|
|
592
|
+
* @returns The converted integer.
|
|
593
|
+
*/
|
|
594
|
+
function toInteger(value, options = {}) {
|
|
595
|
+
const { defaultValue = 0, allowDecimal = false, allowNaN = false, onError = "useDefault", min, max, outOfRange = "clamp" } = options;
|
|
596
|
+
let numberValue;
|
|
597
|
+
let result;
|
|
598
|
+
if (isNumber(value)) numberValue = value;
|
|
599
|
+
else if (isString(value)) {
|
|
600
|
+
const trimmed = value.trim();
|
|
601
|
+
if (isEmptyString(trimmed)) {
|
|
602
|
+
if (onError === "throwError") throw new TypeError("Cannot convert empty string to an integer");
|
|
603
|
+
return onError === "returnOriginal" ? value : defaultValue;
|
|
604
|
+
}
|
|
605
|
+
numberValue = Number(trimmed);
|
|
606
|
+
} else if (isNullOrUndefined(value)) {
|
|
607
|
+
if (onError === "throwError") throw new TypeError(`Cannot convert ${value} to an integer`);
|
|
608
|
+
return onError === "useDefault" ? value : defaultValue;
|
|
609
|
+
} else numberValue = Number(value);
|
|
610
|
+
if (isNaN(numberValue)) {
|
|
611
|
+
if (allowNaN) return numberValue;
|
|
612
|
+
if (onError === "throwError") throw new TypeError(`Cannot convert NaN to an integer`);
|
|
613
|
+
return onError === "returnOriginal" ? value : defaultValue;
|
|
614
|
+
}
|
|
615
|
+
if (allowDecimal) result = Math.floor(numberValue);
|
|
616
|
+
else {
|
|
617
|
+
if (numberValue % 1 !== 0) {
|
|
618
|
+
if (onError === "throwError") throw new Error("Decimal values are not allowed");
|
|
619
|
+
return onError === "returnOriginal" ? value : defaultValue;
|
|
620
|
+
}
|
|
621
|
+
result = numberValue;
|
|
622
|
+
}
|
|
623
|
+
if (!isUndefined(min) || !isUndefined(max)) {
|
|
624
|
+
const minVal = min ?? -Infinity;
|
|
625
|
+
const maxVal = max ?? Infinity;
|
|
626
|
+
if (result < minVal || result > maxVal) {
|
|
627
|
+
if (outOfRange === "throwError") throw new RangeError(`Value ${result} is out of range [${minVal}, ${maxVal}]`);
|
|
628
|
+
if (outOfRange === "useDefault") return defaultValue;
|
|
629
|
+
if (outOfRange === "clamp") result = Math.max(minVal, Math.min(maxVal, numberValue));
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
return result;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
//#endregion
|
|
636
|
+
//#region src/string/random.ts
|
|
637
|
+
/**
|
|
638
|
+
* randome a string useing given chars
|
|
639
|
+
*
|
|
640
|
+
* @param length - string length
|
|
641
|
+
* @param chars - string chars
|
|
642
|
+
* @returns random string
|
|
643
|
+
*/
|
|
434
644
|
function randomString(length = 16, chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") {
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
}
|
|
439
|
-
return result.join("");
|
|
645
|
+
const result = [];
|
|
646
|
+
for (let i = length; i > 0; --i) result.push(chars[randomNumber(chars.length)]);
|
|
647
|
+
return result.join("");
|
|
440
648
|
}
|
|
441
649
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
650
|
+
//#endregion
|
|
651
|
+
//#region src/string/slugify.ts
|
|
652
|
+
const rControl = /[\u0000-\u001F]/g;
|
|
653
|
+
const rSpecial = /[\s~`!@#$%^&*()\-_+=[\]{}|\\;:"'“”‘’<>,.?/]+/g;
|
|
654
|
+
const rCombining = /[\u0300-\u036F]/g;
|
|
655
|
+
/**
|
|
656
|
+
* Default slugify function
|
|
657
|
+
*/
|
|
446
658
|
function slugify(str) {
|
|
447
|
-
|
|
659
|
+
return str.normalize("NFKD").replace(rCombining, "").replace(rControl, "").replace(rSpecial, "-").replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "").replace(/^(\d)/, "_$1").toLowerCase();
|
|
448
660
|
}
|
|
449
661
|
|
|
450
|
-
|
|
451
|
-
|
|
662
|
+
//#endregion
|
|
663
|
+
//#region src/string/unindent.ts
|
|
664
|
+
const _RE_FULL_WS = /^\s*$/;
|
|
665
|
+
/**
|
|
666
|
+
* Remove common leading whitespace from a template string
|
|
667
|
+
* Empty lines at the beginning and end of the template string are also removed.
|
|
668
|
+
* @param input - template string
|
|
669
|
+
*
|
|
670
|
+
* @example
|
|
671
|
+
*
|
|
672
|
+
* ```ts
|
|
673
|
+
* const str = unindent`
|
|
674
|
+
* if (foo) {
|
|
675
|
+
* bar()
|
|
676
|
+
* }
|
|
677
|
+
* `
|
|
678
|
+
* ```
|
|
679
|
+
*/
|
|
452
680
|
function unindent(input) {
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
681
|
+
const lines = (typeof input === "string" ? input : input[0]).split("\n");
|
|
682
|
+
const whitespaceLines = lines.map((line) => _RE_FULL_WS.test(line));
|
|
683
|
+
const commonIndent = lines.reduce((min, line, idx) => {
|
|
684
|
+
if (whitespaceLines[idx]) return min;
|
|
685
|
+
const indent = line.match(/^\s*/)?.[0].length;
|
|
686
|
+
return indent === void 0 ? min : Math.min(min, indent);
|
|
687
|
+
}, Number.POSITIVE_INFINITY);
|
|
688
|
+
let emptylinesHead = 0;
|
|
689
|
+
while (emptylinesHead < lines.length && whitespaceLines[emptylinesHead]) emptylinesHead++;
|
|
690
|
+
let emptylinesTail = 0;
|
|
691
|
+
while (emptylinesTail < lines.length && whitespaceLines[lines.length - emptylinesTail - 1]) emptylinesTail++;
|
|
692
|
+
return lines.slice(emptylinesHead, lines.length - emptylinesTail).map((line) => line.slice(commonIndent)).join("\n");
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
//#endregion
|
|
696
|
+
//#region src/string/getLength.ts
|
|
697
|
+
let segmenter;
|
|
698
|
+
function isASCII(value) {
|
|
699
|
+
return /^[\u0020-\u007F]*$/u.test(value);
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* Counts graphemes in a given string
|
|
703
|
+
* @param value - A string to count graphemes.
|
|
704
|
+
* @returns The number of graphemes in `value`.
|
|
705
|
+
*/
|
|
706
|
+
function getStringLength(value) {
|
|
707
|
+
if (isASCII(value)) return value.length;
|
|
708
|
+
segmenter ??= new Intl.Segmenter();
|
|
709
|
+
return [...segmenter.segment(value)].length;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
//#endregion
|
|
713
|
+
//#region src/string/ensurePrefix.ts
|
|
474
714
|
function ensurePrefix(input, prefix) {
|
|
475
|
-
|
|
715
|
+
return input.startsWith(prefix) ? input : `${prefix}${input}`;
|
|
476
716
|
}
|
|
477
717
|
|
|
478
|
-
|
|
718
|
+
//#endregion
|
|
719
|
+
//#region src/string/ensureSuffix.ts
|
|
479
720
|
function ensureSuffix(input, suffix) {
|
|
480
|
-
|
|
721
|
+
return input.endsWith(suffix) ? input : `${input}${suffix}`;
|
|
481
722
|
}
|
|
482
723
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
724
|
+
//#endregion
|
|
725
|
+
//#region src/color/color.ts
|
|
726
|
+
const pad2 = createPadString({
|
|
727
|
+
length: 2,
|
|
728
|
+
char: "0"
|
|
729
|
+
});
|
|
730
|
+
const RE_VALID_HEX_COLOR = /^#(?:[0-9a-f]{6}|[0-9a-f]{3})$/i;
|
|
486
731
|
function validateHexColor(hex) {
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
732
|
+
if (hex.length !== 4 && hex.length !== 7) return false;
|
|
733
|
+
if (!hex.startsWith("#")) return false;
|
|
734
|
+
return RE_VALID_HEX_COLOR.test(hex);
|
|
490
735
|
}
|
|
491
736
|
function normalizeHexString(hex) {
|
|
492
|
-
|
|
493
|
-
}
|
|
494
|
-
var Color = class
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
const amount = Math.round(percentage / 100 * 255);
|
|
564
|
-
return new _Color(
|
|
565
|
-
Math.max(this.red - amount, 0),
|
|
566
|
-
Math.max(this.green - amount, 0),
|
|
567
|
-
Math.max(this.blue - amount, 0),
|
|
568
|
-
this.alpha
|
|
569
|
-
);
|
|
570
|
-
}
|
|
737
|
+
return hex.length === 6 ? hex : hex.replace(/./g, "$&$&");
|
|
738
|
+
}
|
|
739
|
+
var Color = class Color {
|
|
740
|
+
red = 0;
|
|
741
|
+
green = 0;
|
|
742
|
+
blue = 0;
|
|
743
|
+
alpha = 1;
|
|
744
|
+
constructor(red = 0, green = 0, blue = 0, alpha = 1) {
|
|
745
|
+
this.red = red;
|
|
746
|
+
this.green = green;
|
|
747
|
+
this.blue = blue;
|
|
748
|
+
this.alpha = alpha;
|
|
749
|
+
}
|
|
750
|
+
static fromRGB(red, green, blue) {
|
|
751
|
+
return new Color(red, green, blue);
|
|
752
|
+
}
|
|
753
|
+
static fromRGBA(red, green, blue, alpha) {
|
|
754
|
+
return new Color(red, green, blue, alpha);
|
|
755
|
+
}
|
|
756
|
+
static fromHex(hex) {
|
|
757
|
+
if (!validateHexColor(hex)) throw new Error("Invalid hex color");
|
|
758
|
+
const [red, green, blue] = normalizeHexString(hex.slice(1)).match(/.{2}/g)?.map((value) => Number.parseInt(value, 16)) ?? [
|
|
759
|
+
0,
|
|
760
|
+
0,
|
|
761
|
+
0
|
|
762
|
+
];
|
|
763
|
+
return new Color(red, green, blue);
|
|
764
|
+
}
|
|
765
|
+
get brightness() {
|
|
766
|
+
return (this.red * 299 + this.green * 587 + this.blue * 114) / 1e3;
|
|
767
|
+
}
|
|
768
|
+
get isDark() {
|
|
769
|
+
return this.brightness < 128;
|
|
770
|
+
}
|
|
771
|
+
get isLight() {
|
|
772
|
+
return !this.isDark;
|
|
773
|
+
}
|
|
774
|
+
toHexString(isUpperCase = true) {
|
|
775
|
+
const hexString = `#${pad2(this.red.toString(16))}${pad2(this.green.toString(16))}${pad2(this.blue.toString(16))}`;
|
|
776
|
+
return isUpperCase ? hexString.toUpperCase() : hexString;
|
|
777
|
+
}
|
|
778
|
+
toRGBAString() {
|
|
779
|
+
return `rgba(${this.red}, ${this.green}, ${this.blue}, ${this.alpha})`;
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* add alpha value to {@link Color}
|
|
783
|
+
*
|
|
784
|
+
* @param alpha - alpha value
|
|
785
|
+
* @returns instance of {@link Color}
|
|
786
|
+
*/
|
|
787
|
+
withAlpha(alpha = 1) {
|
|
788
|
+
return new Color(this.red, this.green, this.blue, alpha);
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* lighten the color by percentage
|
|
792
|
+
*
|
|
793
|
+
* @param percentage - percentage to lighten
|
|
794
|
+
*/
|
|
795
|
+
lighten(percentage = 0) {
|
|
796
|
+
const amount = Math.round(percentage / 100 * 255);
|
|
797
|
+
return new Color(Math.min(this.red + amount, 255), Math.min(this.green + amount, 255), Math.min(this.blue + amount, 255), this.alpha);
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* darken the color by percentage
|
|
801
|
+
*
|
|
802
|
+
* @param percentage - percentage to darken
|
|
803
|
+
*/
|
|
804
|
+
darken(percentage = 0) {
|
|
805
|
+
const amount = Math.round(percentage / 100 * 255);
|
|
806
|
+
return new Color(Math.max(this.red - amount, 0), Math.max(this.green - amount, 0), Math.max(this.blue - amount, 0), this.alpha);
|
|
807
|
+
}
|
|
571
808
|
};
|
|
572
809
|
|
|
573
|
-
|
|
574
|
-
|
|
810
|
+
//#endregion
|
|
811
|
+
//#region src/color/random.ts
|
|
812
|
+
/**
|
|
813
|
+
* the maximum value of RGB
|
|
814
|
+
*/
|
|
815
|
+
const MAX_RGB = 255;
|
|
816
|
+
/**
|
|
817
|
+
* get a random RGB color
|
|
818
|
+
* @returns a random RGB color
|
|
819
|
+
*/
|
|
575
820
|
function randomRGBColor() {
|
|
576
|
-
|
|
821
|
+
return `rgb(${randomNumber(MAX_RGB)}, ${randomNumber(MAX_RGB)}, ${randomNumber(MAX_RGB)})`;
|
|
577
822
|
}
|
|
823
|
+
/**
|
|
824
|
+
* get a random RGBA color
|
|
825
|
+
* @returns a random RGBA color
|
|
826
|
+
*/
|
|
578
827
|
function randomRGBAColor() {
|
|
579
|
-
|
|
828
|
+
return `rgba(${randomNumber(MAX_RGB)}, ${randomNumber(MAX_RGB)}, ${randomNumber(MAX_RGB)}, ${Math.random().toFixed(1)})`;
|
|
580
829
|
}
|
|
830
|
+
/**
|
|
831
|
+
* get a random hex color
|
|
832
|
+
* @returns a random hex color
|
|
833
|
+
*/
|
|
581
834
|
function randomHexColor() {
|
|
582
|
-
|
|
835
|
+
return `#${Math.random().toString(16).slice(2, 8)}`;
|
|
583
836
|
}
|
|
584
837
|
|
|
585
|
-
|
|
838
|
+
//#endregion
|
|
839
|
+
//#region src/proxy/enhance.ts
|
|
840
|
+
/**
|
|
841
|
+
* enhance object
|
|
842
|
+
* @module proxy
|
|
843
|
+
*/
|
|
586
844
|
function enhance(module, extra) {
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
845
|
+
return new Proxy(module, {
|
|
846
|
+
get(target, key, receiver) {
|
|
847
|
+
if (Reflect.has(extra, key)) return Reflect.get(extra, key, receiver);
|
|
848
|
+
return Reflect.get(target, key, receiver);
|
|
849
|
+
},
|
|
850
|
+
has(target, key) {
|
|
851
|
+
return Reflect.has(extra, key) || Reflect.has(target, key);
|
|
852
|
+
}
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
//#endregion
|
|
857
|
+
//#region src/module/interopDefault.ts
|
|
858
|
+
/**
|
|
859
|
+
* Interop default export from a module
|
|
860
|
+
*
|
|
861
|
+
* @param mod - The module
|
|
862
|
+
* @returns The default export
|
|
863
|
+
*
|
|
864
|
+
* @example
|
|
865
|
+
*
|
|
866
|
+
* ```ts
|
|
867
|
+
* import { interopDefault } from '@ntnyq/utils'
|
|
868
|
+
*
|
|
869
|
+
* const { unindent } = await interopDefault(import('@ntnyq/utils'))
|
|
870
|
+
* ```
|
|
871
|
+
*/
|
|
601
872
|
async function interopDefault(mod) {
|
|
602
|
-
|
|
603
|
-
|
|
873
|
+
const resolved = await mod;
|
|
874
|
+
return resolved.default || resolved;
|
|
604
875
|
}
|
|
605
876
|
|
|
606
|
-
|
|
877
|
+
//#endregion
|
|
878
|
+
//#region src/module/resolveSubOptions.ts
|
|
879
|
+
/**
|
|
880
|
+
* Resolve sub options `boolean | Options` to `Options`
|
|
881
|
+
* @param options - core options
|
|
882
|
+
* @param key - sub options key
|
|
883
|
+
* @returns resolved sub options
|
|
884
|
+
*
|
|
885
|
+
* @example
|
|
886
|
+
*
|
|
887
|
+
* ```ts
|
|
888
|
+
* import { resolveSubOptions } from '@ntnyq/utils'
|
|
889
|
+
*
|
|
890
|
+
* interface Options {
|
|
891
|
+
* compile?: boolean | {
|
|
892
|
+
* include?: string[]
|
|
893
|
+
* exclude?: string[]
|
|
894
|
+
* }
|
|
895
|
+
* }
|
|
896
|
+
*
|
|
897
|
+
* const options: Options = {
|
|
898
|
+
* compile: true
|
|
899
|
+
* }
|
|
900
|
+
*
|
|
901
|
+
* console.log(resolveSubOptions(options, 'compile'))
|
|
902
|
+
*
|
|
903
|
+
* // => {}
|
|
904
|
+
* ```
|
|
905
|
+
*/
|
|
607
906
|
function resolveSubOptions(options, key) {
|
|
608
|
-
|
|
907
|
+
return typeof options[key] === "boolean" ? {} : options[key] || {};
|
|
609
908
|
}
|
|
610
909
|
|
|
611
|
-
|
|
910
|
+
//#endregion
|
|
911
|
+
//#region src/object/omit.ts
|
|
612
912
|
function omit(object, ...keys) {
|
|
613
|
-
|
|
614
|
-
|
|
913
|
+
keys.forEach((key) => delete object[key]);
|
|
914
|
+
return object;
|
|
615
915
|
}
|
|
616
916
|
|
|
617
|
-
|
|
917
|
+
//#endregion
|
|
918
|
+
//#region src/object/hasOwn.ts
|
|
919
|
+
/**
|
|
920
|
+
* check object has a property with given key
|
|
921
|
+
* @param object - the object to check
|
|
922
|
+
* @param key - the key to check
|
|
923
|
+
* @returns true if object has a property with given key, false otherwise
|
|
924
|
+
*/
|
|
618
925
|
function hasOwn(object, key) {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
}
|
|
622
|
-
return Object.prototype.hasOwnProperty.call(object, key);
|
|
926
|
+
if (object === null) return false;
|
|
927
|
+
return Object.prototype.hasOwnProperty.call(object, key);
|
|
623
928
|
}
|
|
624
929
|
|
|
625
|
-
|
|
930
|
+
//#endregion
|
|
931
|
+
//#region src/object/pick.ts
|
|
626
932
|
function pick(object, keys) {
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
);
|
|
933
|
+
return Object.assign(
|
|
934
|
+
{},
|
|
935
|
+
// eslint-disable-next-line array-callback-return
|
|
936
|
+
...keys.map((key) => {
|
|
937
|
+
if (object && hasOwn(object, key)) return { [key]: object[key] };
|
|
938
|
+
})
|
|
939
|
+
);
|
|
635
940
|
}
|
|
636
941
|
|
|
637
|
-
|
|
942
|
+
//#endregion
|
|
943
|
+
//#region src/object/clean.ts
|
|
944
|
+
/**
|
|
945
|
+
* clean undefined, null, zero, empty string, empty array, empty object from object
|
|
946
|
+
* @param obj - object to be cleaned
|
|
947
|
+
* @param options - clean options
|
|
948
|
+
* @returns cleaned object
|
|
949
|
+
*/
|
|
638
950
|
function cleanObject(obj, options = {}) {
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
}
|
|
663
|
-
if (cleanEmptyString && isEmptyString(v)) {
|
|
664
|
-
delete obj[key];
|
|
665
|
-
}
|
|
666
|
-
if (cleanEmptyArray && isEmptyArray(v)) {
|
|
667
|
-
delete obj[key];
|
|
668
|
-
}
|
|
669
|
-
if (cleanEmptyObject && isEmptyObject(v)) {
|
|
670
|
-
delete obj[key];
|
|
671
|
-
}
|
|
672
|
-
if (recursive && isObject(v)) {
|
|
673
|
-
cleanObject(v, options);
|
|
674
|
-
}
|
|
675
|
-
});
|
|
676
|
-
return obj;
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
// src/object/isPlainObject.ts
|
|
951
|
+
const { cleanUndefined = true, cleanNull = true, cleanNaN = true, cleanZero = false, cleanEmptyString = false, cleanEmptyArray = false, cleanEmptyObject = false, recursive = true } = options;
|
|
952
|
+
Object.keys(obj).forEach((key) => {
|
|
953
|
+
const v = obj[key];
|
|
954
|
+
if (cleanUndefined && isUndefined(v)) delete obj[key];
|
|
955
|
+
if (cleanNull && isNull(v)) delete obj[key];
|
|
956
|
+
if (cleanZero && isZero(v)) delete obj[key];
|
|
957
|
+
if (cleanNaN && isZero(v)) delete obj[key];
|
|
958
|
+
if (cleanEmptyString && isEmptyString(v)) delete obj[key];
|
|
959
|
+
if (cleanEmptyArray && isEmptyArray(v)) delete obj[key];
|
|
960
|
+
if (cleanEmptyObject && isEmptyObject(v)) delete obj[key];
|
|
961
|
+
if (recursive && isObject(v)) cleanObject(v, options);
|
|
962
|
+
});
|
|
963
|
+
return obj;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
//#endregion
|
|
967
|
+
//#region src/object/isPlainObject.ts
|
|
968
|
+
/**
|
|
969
|
+
* Check if a value is a plain object (not an array, Date, RegExp, Map, Set, etc.)
|
|
970
|
+
*
|
|
971
|
+
* @param value - Checked value
|
|
972
|
+
* @copyright {@link https://github.com/sindresorhus/is/blob/main/source/index.ts}
|
|
973
|
+
*/
|
|
680
974
|
function isPlainObject(value) {
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
975
|
+
if (!isObject(value)) return false;
|
|
976
|
+
const prototype = Object.getPrototypeOf(value);
|
|
977
|
+
return (prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null) && !(Symbol.toStringTag in value) && !(Symbol.iterator in value);
|
|
684
978
|
}
|
|
685
979
|
|
|
686
|
-
|
|
980
|
+
//#endregion
|
|
981
|
+
//#region src/object/sortObject.ts
|
|
982
|
+
/**
|
|
983
|
+
* Sort object properties
|
|
984
|
+
*/
|
|
687
985
|
function sortObject(obj, options = {}) {
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
}
|
|
707
|
-
return sortKeys(obj);
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
// src/constants/char.ts
|
|
711
|
-
var SPECIAL_CHAR = {
|
|
712
|
-
newline: "\n"
|
|
713
|
-
};
|
|
986
|
+
const { compareFn = (a, b) => a.localeCompare(b) } = options;
|
|
987
|
+
function sortKeys(obj$1) {
|
|
988
|
+
const sortedKeys = Object.keys(obj$1).sort(compareFn);
|
|
989
|
+
const result = {};
|
|
990
|
+
for (const key of sortedKeys) {
|
|
991
|
+
const value = obj$1[key];
|
|
992
|
+
let newValue;
|
|
993
|
+
if (options.deep && isPlainObject(value)) newValue = sortKeys(value);
|
|
994
|
+
else newValue = value;
|
|
995
|
+
Object.defineProperty(result, key, {
|
|
996
|
+
...Object.getOwnPropertyDescriptor(obj$1, key),
|
|
997
|
+
value: newValue
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
return result;
|
|
1001
|
+
}
|
|
1002
|
+
return sortKeys(obj);
|
|
1003
|
+
}
|
|
714
1004
|
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
hours,
|
|
742
|
-
interopDefault,
|
|
743
|
-
intersect,
|
|
744
|
-
isArray,
|
|
745
|
-
isArrayEqual,
|
|
746
|
-
isBigInt,
|
|
747
|
-
isBoolean,
|
|
748
|
-
isBrowser,
|
|
749
|
-
isDeepEqual,
|
|
750
|
-
isElementVisibleInViewport,
|
|
751
|
-
isEmptyArray,
|
|
752
|
-
isEmptyMap,
|
|
753
|
-
isEmptyObject,
|
|
754
|
-
isEmptySet,
|
|
755
|
-
isEmptyString,
|
|
756
|
-
isEmptyStringOrWhitespace,
|
|
757
|
-
isError,
|
|
758
|
-
isFunction,
|
|
759
|
-
isHTMLElement,
|
|
760
|
-
isInteger,
|
|
761
|
-
isIterable,
|
|
762
|
-
isMap,
|
|
763
|
-
isNaN,
|
|
764
|
-
isNativePromise,
|
|
765
|
-
isNil,
|
|
766
|
-
isNonEmptyArray,
|
|
767
|
-
isNonEmptyString,
|
|
768
|
-
isNull,
|
|
769
|
-
isNullOrUndefined,
|
|
770
|
-
isNumber,
|
|
771
|
-
isNumbericString,
|
|
772
|
-
isObject,
|
|
773
|
-
isPromise,
|
|
774
|
-
isRegExp,
|
|
775
|
-
isSet,
|
|
776
|
-
isString,
|
|
777
|
-
isTruthy,
|
|
778
|
-
isUndefined,
|
|
779
|
-
isWhitespaceString,
|
|
780
|
-
isZero,
|
|
781
|
-
join,
|
|
782
|
-
last,
|
|
783
|
-
mergeArrayable,
|
|
784
|
-
minutes,
|
|
785
|
-
noop,
|
|
786
|
-
omit,
|
|
787
|
-
once,
|
|
788
|
-
openExternalURL,
|
|
789
|
-
pick,
|
|
790
|
-
rAF,
|
|
791
|
-
randomHexColor,
|
|
792
|
-
randomNumber,
|
|
793
|
-
randomRGBAColor,
|
|
794
|
-
randomRGBColor,
|
|
795
|
-
randomString,
|
|
796
|
-
resolveSubOptions,
|
|
797
|
-
scrollElementIntoView,
|
|
798
|
-
seconds,
|
|
799
|
-
slash,
|
|
800
|
-
slugify,
|
|
801
|
-
sortObject,
|
|
802
|
-
throttle,
|
|
803
|
-
toArray,
|
|
804
|
-
unescapeHTML,
|
|
805
|
-
unindent,
|
|
806
|
-
unique,
|
|
807
|
-
uniqueBy,
|
|
808
|
-
waitFor,
|
|
809
|
-
warnOnce,
|
|
810
|
-
weeks
|
|
811
|
-
};
|
|
1005
|
+
//#endregion
|
|
1006
|
+
//#region src/constants/char.ts
|
|
1007
|
+
/**
|
|
1008
|
+
* Special chars
|
|
1009
|
+
*/
|
|
1010
|
+
const SPECIAL_CHAR = { newline: "\n" };
|
|
1011
|
+
|
|
1012
|
+
//#endregion
|
|
1013
|
+
//#region src/constants/regexp.ts
|
|
1014
|
+
/**
|
|
1015
|
+
* 注释正则
|
|
1016
|
+
*
|
|
1017
|
+
* 匹配 \<!-- 或 /* 开头的注释,直到 --> 或 *\/ 结尾
|
|
1018
|
+
*/
|
|
1019
|
+
const RE_COMMENTS = /(?:<!--|\/\*)([\s\S]*?)(?:-->|\*\/)/g;
|
|
1020
|
+
/**
|
|
1021
|
+
* JavaScript line comment
|
|
1022
|
+
*/
|
|
1023
|
+
const RE_LINE_COMMENT = /\/\/.*/;
|
|
1024
|
+
/**
|
|
1025
|
+
* JavaScript block comment
|
|
1026
|
+
*/
|
|
1027
|
+
const RE_BLOCK_COMMENT = /\/\*[\s\S]*?\*\//g;
|
|
1028
|
+
|
|
1029
|
+
//#endregion
|
|
1030
|
+
export { Color, NOOP, RE_BLOCK_COMMENT, RE_COMMENTS, RE_LINE_COMMENT, SPECIAL_CHAR, at, cAF, chunk, clamp, cleanObject, createPadString, days, debounce, enhance, ensurePrefix, ensureSuffix, escapeHTML, flattenArrayable, getObjectType, getStringLength, hasOwn, hours, interopDefault, intersect, isArray, isArrayEqual, isBigInt, isBoolean, isBrowser, isDeepEqual, isElementVisibleInViewport, isEmptyArray, isEmptyMap, isEmptyObject, isEmptySet, isEmptyString, isEmptyStringOrWhitespace, isError, isFunction, isHTMLElement, isInteger, isIterable, isMap, isNaN, isNativePromise, isNil, isNonEmptyArray, isNonEmptyString, isNull, isNullOrUndefined, isNumber, isNumbericString, isObject, isPromise, isRegExp, isSet, isString, isTruthy, isUndefined, isWhitespaceString, isZero, join, last, mergeArrayable, minutes, noop, omit, once, openExternalURL, pick, rAF, randomHexColor, randomNumber, randomRGBAColor, randomRGBColor, randomString, resolveSubOptions, scrollElementIntoView, seconds, slash, slugify, sortObject, throttle, toArray, toInteger, unescapeHTML, unindent, unique, uniqueBy, waitFor, warnOnce, weeks };
|