elementdrawing 1.0.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/LICENSE +21 -0
- package/dist/elementdrawing.min.js +3 -0
- package/dist/elementdrawing.min.js.LICENSE.txt +8 -0
- package/dist/elementdrawing.min.js.map +1 -0
- package/dist/index.html +1 -0
- package/package.json +127 -0
- package/src/core/bridge.h +855 -0
- package/src/core/diff.c +900 -0
- package/src/core/element.c +1078 -0
- package/src/core/event.c +813 -0
- package/src/core/fiber.c +1027 -0
- package/src/core/hooks.c +919 -0
- package/src/core/renderer.c +963 -0
- package/src/core/scheduler.c +702 -0
- package/src/core/state.c +803 -0
- package/src/css/animations.css +779 -0
- package/src/css/base.css +615 -0
- package/src/css/components.css +1311 -0
- package/src/css/tailwind.css +370 -0
- package/src/css/themes.css +517 -0
- package/src/css/utilities.css +475 -0
- package/src/index.js +746 -0
- package/src/js/animation.js +655 -0
- package/src/js/dom.js +665 -0
- package/src/js/events.js +585 -0
- package/src/js/http.js +446 -0
- package/src/js/index.js +26 -0
- package/src/js/router.js +483 -0
- package/src/js/store.js +539 -0
- package/src/js/utils.js +593 -0
- package/src/js/validator.js +529 -0
- package/src/jsx/components/Accordion.jsx +210 -0
- package/src/jsx/components/Alert.jsx +169 -0
- package/src/jsx/components/Avatar.jsx +214 -0
- package/src/jsx/components/Badge.jsx +136 -0
- package/src/jsx/components/Breadcrumb.jsx +200 -0
- package/src/jsx/components/Button.jsx +188 -0
- package/src/jsx/components/Card.jsx +192 -0
- package/src/jsx/components/Carousel.jsx +278 -0
- package/src/jsx/components/Checkbox.jsx +215 -0
- package/src/jsx/components/Dialog.jsx +242 -0
- package/src/jsx/components/Drawer.jsx +190 -0
- package/src/jsx/components/Dropdown.jsx +268 -0
- package/src/jsx/components/Form.jsx +274 -0
- package/src/jsx/components/Input.jsx +285 -0
- package/src/jsx/components/Menu.jsx +276 -0
- package/src/jsx/components/Modal.jsx +274 -0
- package/src/jsx/components/Navbar.jsx +292 -0
- package/src/jsx/components/Pagination.jsx +268 -0
- package/src/jsx/components/Progress.jsx +252 -0
- package/src/jsx/components/Radio.jsx +208 -0
- package/src/jsx/components/Select.jsx +397 -0
- package/src/jsx/components/Sidebar.jsx +250 -0
- package/src/jsx/components/Slider.jsx +310 -0
- package/src/jsx/components/Spinner.jsx +198 -0
- package/src/jsx/components/Switch.jsx +201 -0
- package/src/jsx/components/Table.jsx +332 -0
- package/src/jsx/components/Tabs.jsx +227 -0
- package/src/jsx/components/Textarea.jsx +212 -0
- package/src/jsx/components/Toast.jsx +270 -0
- package/src/jsx/components/Tooltip.jsx +178 -0
- package/src/jsx/components/Typography.jsx +299 -0
- package/src/jsx/components/index.jsx +70 -0
- package/src/jsx/core/element.js +3 -0
- package/src/jsx/hooks/index.js +356 -0
- package/src/jsx/hooks/useCallback.js +472 -0
- package/src/jsx/hooks/useContext.js +586 -0
- package/src/jsx/hooks/useEffect.js +704 -0
- package/src/jsx/hooks/useLayoutEffect.js +508 -0
- package/src/jsx/hooks/useMemo.js +689 -0
- package/src/jsx/hooks/useReducer.js +729 -0
- package/src/jsx/hooks/useRef.js +542 -0
- package/src/jsx/hooks/useState.js +854 -0
- package/src/jsx/runtime/commit.js +903 -0
- package/src/jsx/runtime/createElement.js +860 -0
- package/src/jsx/runtime/index.js +356 -0
- package/src/jsx/runtime/reconcile.js +687 -0
- package/src/jsx/runtime/render.js +914 -0
package/src/js/utils.js
ADDED
|
@@ -0,0 +1,593 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* General Utility Functions
|
|
3
|
+
* ElementDrawing Framework - Type checking, object/array/string/number/function/
|
|
4
|
+
* date/color/URL utilities for comprehensive data manipulation.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
// ─── Type Checking ────────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
const isString = (v) => typeof v === 'string';
|
|
12
|
+
const isNumber = (v) => typeof v === 'number' && !isNaN(v);
|
|
13
|
+
const isBoolean = (v) => typeof v === 'boolean';
|
|
14
|
+
const isFunction = (v) => typeof v === 'function';
|
|
15
|
+
const isObject = (v) => v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
16
|
+
const isArray = Array.isArray;
|
|
17
|
+
const isNull = (v) => v === null;
|
|
18
|
+
const isUndefined = (v) => v === undefined;
|
|
19
|
+
const isDate = (v) => v instanceof Date && !isNaN(v.getTime());
|
|
20
|
+
const isRegExp = (v) => v instanceof RegExp;
|
|
21
|
+
const isPromise = (v) => v && typeof v.then === 'function';
|
|
22
|
+
const isElement = (v) => v instanceof HTMLElement || v instanceof SVGElement;
|
|
23
|
+
const isPlainObject = (v) => Object.prototype.toString.call(v) === '[object Object]';
|
|
24
|
+
const isNil = (v) => v === null || v === undefined;
|
|
25
|
+
const isFiniteNum = (v) => Number.isFinite(v);
|
|
26
|
+
const isInteger = Number.isInteger;
|
|
27
|
+
|
|
28
|
+
// ─── Object Utilities ─────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
function deepClone(obj, cache) {
|
|
31
|
+
if (obj === null || typeof obj !== 'object') return obj;
|
|
32
|
+
if (isDate(obj)) return new Date(obj.getTime());
|
|
33
|
+
if (isRegExp(obj)) return new RegExp(obj.source, obj.flags);
|
|
34
|
+
if (isElement(obj)) return obj;
|
|
35
|
+
|
|
36
|
+
cache = cache || new WeakMap();
|
|
37
|
+
if (cache.has(obj)) return cache.get(obj);
|
|
38
|
+
|
|
39
|
+
const clone = Array.isArray(obj) ? [] : {};
|
|
40
|
+
cache.set(obj, clone);
|
|
41
|
+
|
|
42
|
+
Object.keys(obj).forEach((key) => {
|
|
43
|
+
clone[key] = deepClone(obj[key], cache);
|
|
44
|
+
});
|
|
45
|
+
return clone;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function deepMerge(target) {
|
|
49
|
+
const sources = Array.prototype.slice.call(arguments, 1);
|
|
50
|
+
sources.forEach((source) => {
|
|
51
|
+
if (!isObject(source)) return;
|
|
52
|
+
Object.keys(source).forEach((key) => {
|
|
53
|
+
if (isObject(source[key]) && isObject(target[key])) {
|
|
54
|
+
target[key] = deepMerge({}, target[key], source[key]);
|
|
55
|
+
} else {
|
|
56
|
+
target[key] = source[key];
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
return target;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function shallowMerge(target) {
|
|
64
|
+
const sources = Array.prototype.slice.call(arguments, 1);
|
|
65
|
+
sources.forEach((source) => {
|
|
66
|
+
if (!source) return;
|
|
67
|
+
Object.keys(source).forEach((key) => { target[key] = source[key]; });
|
|
68
|
+
});
|
|
69
|
+
return target;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function pick(obj, keys) {
|
|
73
|
+
const result = {};
|
|
74
|
+
keys.forEach((key) => {
|
|
75
|
+
if (obj && Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
76
|
+
result[key] = obj[key];
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function omit(obj, keys) {
|
|
83
|
+
const keySet = new Set(keys);
|
|
84
|
+
const result = {};
|
|
85
|
+
Object.keys(obj).forEach((key) => {
|
|
86
|
+
if (!keySet.has(key)) result[key] = obj[key];
|
|
87
|
+
});
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function get(obj, path, defaultValue) {
|
|
92
|
+
const keys = path.replace(/\[(\d+)\]/g, '.$1').split('.');
|
|
93
|
+
let result = obj;
|
|
94
|
+
for (let i = 0; i < keys.length; i++) {
|
|
95
|
+
if (result == null) return defaultValue;
|
|
96
|
+
result = result[keys[i]];
|
|
97
|
+
}
|
|
98
|
+
return result === undefined ? defaultValue : result;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function set(obj, path, value) {
|
|
102
|
+
const keys = path.replace(/\[(\d+)\]/g, '.$1').split('.');
|
|
103
|
+
let current = obj;
|
|
104
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
105
|
+
if (!current[keys[i]]) current[keys[i]] = {};
|
|
106
|
+
current = current[keys[i]];
|
|
107
|
+
}
|
|
108
|
+
current[keys[keys.length - 1]] = value;
|
|
109
|
+
return obj;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function has(obj, path) {
|
|
113
|
+
const keys = path.replace(/\[(\d+)\]/g, '.$1').split('.');
|
|
114
|
+
let current = obj;
|
|
115
|
+
for (let i = 0; i < keys.length; i++) {
|
|
116
|
+
if (current == null || !Object.prototype.hasOwnProperty.call(current, keys[i])) return false;
|
|
117
|
+
current = current[keys[i]];
|
|
118
|
+
}
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const keys = Object.keys;
|
|
123
|
+
const values = Object.values;
|
|
124
|
+
const entries = Object.entries;
|
|
125
|
+
const fromEntries = Object.fromEntries;
|
|
126
|
+
|
|
127
|
+
function isEmpty(obj) {
|
|
128
|
+
if (obj == null) return true;
|
|
129
|
+
if (isArray(obj) || isString(obj)) return obj.length === 0;
|
|
130
|
+
if (isObject(obj)) return Object.keys(obj).length === 0;
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function isEqual(a, b) {
|
|
135
|
+
if (Object.is(a, b)) return true;
|
|
136
|
+
if (a == null || b == null) return false;
|
|
137
|
+
if (typeof a !== typeof b) return false;
|
|
138
|
+
if (isDate(a) && isDate(b)) return a.getTime() === b.getTime();
|
|
139
|
+
if (isRegExp(a) && isRegExp(b)) return a.source === b.source && a.flags === b.flags;
|
|
140
|
+
if (isArray(a) && isArray(b)) {
|
|
141
|
+
if (a.length !== b.length) return false;
|
|
142
|
+
return a.every((v, i) => isEqual(v, b[i]));
|
|
143
|
+
}
|
|
144
|
+
if (isObject(a) && isObject(b)) {
|
|
145
|
+
const aKeys = Object.keys(a);
|
|
146
|
+
const bKeys = Object.keys(b);
|
|
147
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
148
|
+
return aKeys.every((key) => isEqual(a[key], b[key]));
|
|
149
|
+
}
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function flattenObject(obj, prefix) {
|
|
154
|
+
prefix = prefix || '';
|
|
155
|
+
const result = {};
|
|
156
|
+
Object.keys(obj).forEach((key) => {
|
|
157
|
+
const path = prefix ? prefix + '.' + key : key;
|
|
158
|
+
if (isObject(obj[key]) && !isDate(obj[key]) && !isRegExp(obj[key])) {
|
|
159
|
+
Object.assign(result, flattenObject(obj[key], path));
|
|
160
|
+
} else {
|
|
161
|
+
result[path] = obj[key];
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
return result;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ─── Array Utilities ──────────────────────────────────────────────────────────
|
|
168
|
+
|
|
169
|
+
function unique(arr) { return Array.from(new Set(arr)); }
|
|
170
|
+
function flatten(arr, depth) { return arr.flat(depth !== undefined ? depth : Infinity); }
|
|
171
|
+
|
|
172
|
+
function chunk(arr, size) {
|
|
173
|
+
const result = [];
|
|
174
|
+
for (let i = 0; i < arr.length; i += size) {
|
|
175
|
+
result.push(arr.slice(i, i + size));
|
|
176
|
+
}
|
|
177
|
+
return result;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function groupBy(arr, key) {
|
|
181
|
+
return arr.reduce((groups, item) => {
|
|
182
|
+
const val = typeof key === 'function' ? key(item) : item[key];
|
|
183
|
+
(groups[val] = groups[val] || []).push(item);
|
|
184
|
+
return groups;
|
|
185
|
+
}, {});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function sortBy(arr, key, order) {
|
|
189
|
+
return arr.slice().sort((a, b) => {
|
|
190
|
+
const aVal = typeof key === 'function' ? key(a) : a[key];
|
|
191
|
+
const bVal = typeof key === 'function' ? key(b) : b[key];
|
|
192
|
+
const cmp = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
|
|
193
|
+
return order === 'desc' ? -cmp : cmp;
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function filterBy(arr, key, value) {
|
|
198
|
+
if (typeof key === 'function') return arr.filter(key);
|
|
199
|
+
return arr.filter((item) => item[key] === value);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function findBy(arr, key, value) {
|
|
203
|
+
if (typeof key === 'function') return arr.find(key);
|
|
204
|
+
return arr.find((item) => item[key] === value);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function countBy(arr, key) {
|
|
208
|
+
return arr.reduce((counts, item) => {
|
|
209
|
+
const val = typeof key === 'function' ? key(item) : item[key];
|
|
210
|
+
counts[val] = (counts[val] || 0) + 1;
|
|
211
|
+
return counts;
|
|
212
|
+
}, {});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function partition(arr, predicate) {
|
|
216
|
+
const pass = [], fail = [];
|
|
217
|
+
arr.forEach((item) => (predicate(item) ? pass : fail).push(item));
|
|
218
|
+
return [pass, fail];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function zip() {
|
|
222
|
+
const arrays = Array.prototype.slice.call(arguments);
|
|
223
|
+
const len = Math.min.apply(null, arrays.map((a) => a.length));
|
|
224
|
+
const result = [];
|
|
225
|
+
for (let i = 0; i < len; i++) {
|
|
226
|
+
result.push(arrays.map((a) => a[i]));
|
|
227
|
+
}
|
|
228
|
+
return result;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function unzip(arr) {
|
|
232
|
+
const len = Math.max.apply(null, arr.map((a) => a.length));
|
|
233
|
+
const result = [];
|
|
234
|
+
for (let i = 0; i < len; i++) {
|
|
235
|
+
result.push(arr.map((a) => a[i]));
|
|
236
|
+
}
|
|
237
|
+
return result;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function difference(a, b) { return a.filter((x) => !b.includes(x)); }
|
|
241
|
+
function intersection(a, b) { return a.filter((x) => b.includes(x)); }
|
|
242
|
+
function union(a, b) { return unique(a.concat(b)); }
|
|
243
|
+
|
|
244
|
+
function shuffle(arr) {
|
|
245
|
+
const result = arr.slice();
|
|
246
|
+
for (let i = result.length - 1; i > 0; i--) {
|
|
247
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
248
|
+
const tmp = result[i]; result[i] = result[j]; result[j] = tmp;
|
|
249
|
+
}
|
|
250
|
+
return result;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function sample(arr, n) {
|
|
254
|
+
if (n === undefined) return arr[Math.floor(Math.random() * arr.length)];
|
|
255
|
+
return shuffle(arr).slice(0, n);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function range(start, end, step) {
|
|
259
|
+
if (end === undefined) { end = start; start = 0; }
|
|
260
|
+
step = step || 1;
|
|
261
|
+
const result = [];
|
|
262
|
+
for (let i = start; step > 0 ? i < end : i > end; i += step) {
|
|
263
|
+
result.push(i);
|
|
264
|
+
}
|
|
265
|
+
return result;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ─── String Utilities ─────────────────────────────────────────────────────────
|
|
269
|
+
|
|
270
|
+
function capitalize(str) {
|
|
271
|
+
return str ? str.charAt(0).toUpperCase() + str.slice(1) : '';
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function camelCase(str) {
|
|
275
|
+
return str.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : '');
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function kebabCase(str) {
|
|
279
|
+
return str.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/[\s_]+/g, '-').toLowerCase();
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function snakeCase(str) {
|
|
283
|
+
return str.replace(/([a-z])([A-Z])/g, '$1_$2').replace(/[\s-]+/g, '_').toLowerCase();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function pascalCase(str) {
|
|
287
|
+
return camelCase(str).replace(/^(.)/, (_, c) => c.toUpperCase());
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function truncate(str, len, suffix) {
|
|
291
|
+
suffix = suffix || '...';
|
|
292
|
+
return str.length <= len ? str : str.slice(0, len - suffix.length) + suffix;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function padStart(str, len, fill) { return String(str).padStart(len, fill || ' '); }
|
|
296
|
+
function padEnd(str, len, fill) { return String(str).padEnd(len, fill || ' '); }
|
|
297
|
+
function trim(str, chars) {
|
|
298
|
+
if (!chars) return String(str).trim();
|
|
299
|
+
const re = new RegExp('^[' + chars + ']+|[' + chars + ']+$', 'g');
|
|
300
|
+
return String(str).replace(re, '');
|
|
301
|
+
}
|
|
302
|
+
function repeat(str, n) { return String(str).repeat(n); }
|
|
303
|
+
|
|
304
|
+
function template(str, data) {
|
|
305
|
+
return str.replace(/\{\{(\w+)\}\}/g, (_, key) => data[key] !== undefined ? data[key] : '');
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function escapeHtml(str) {
|
|
309
|
+
const map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' };
|
|
310
|
+
return String(str).replace(/[&<>"']/g, (c) => map[c]);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function unescapeHtml(str) {
|
|
314
|
+
const map = { '&': '&', '<': '<', '>': '>', '"': '"', ''': "'" };
|
|
315
|
+
return String(str).replace(/&(?:amp|lt|gt|quot|#39);/g, (c) => map[c]);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function formatNumber(num, decimals, sep) {
|
|
319
|
+
decimals = decimals !== undefined ? decimals : 2;
|
|
320
|
+
sep = sep || ',';
|
|
321
|
+
const parts = Number(num).toFixed(decimals).split('.');
|
|
322
|
+
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, sep);
|
|
323
|
+
return parts.join('.');
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function formatCurrency(num, symbol, decimals) {
|
|
327
|
+
symbol = symbol || '$';
|
|
328
|
+
return symbol + formatNumber(num, decimals || 2);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// ─── Number Utilities ─────────────────────────────────────────────────────────
|
|
332
|
+
|
|
333
|
+
function clamp(num, min, max) { return Math.min(Math.max(num, min), max); }
|
|
334
|
+
function random(min, max) { return Math.random() * (max - min) + min; }
|
|
335
|
+
function randomInt(min, max) { return Math.floor(random(min, max + 1)); }
|
|
336
|
+
function round(num, decimals) { const f = Math.pow(10, decimals || 0); return Math.round(num * f) / f; }
|
|
337
|
+
function ceil(num, decimals) { const f = Math.pow(10, decimals || 0); return Math.ceil(num * f) / f; }
|
|
338
|
+
function floor(num, decimals) { const f = Math.pow(10, decimals || 0); return Math.floor(num * f) / f; }
|
|
339
|
+
function sum(arr) { return arr.reduce((a, b) => a + b, 0); }
|
|
340
|
+
function avg(arr) { return arr.length ? sum(arr) / arr.length : 0; }
|
|
341
|
+
function min(arr) { return Math.min.apply(null, arr); }
|
|
342
|
+
function max(arr) { return Math.max.apply(null, arr); }
|
|
343
|
+
function percent(value, total) { return total === 0 ? 0 : (value / total) * 100; }
|
|
344
|
+
function isBetween(num, lo, hi) { return num >= lo && num <= hi; }
|
|
345
|
+
function lerp(a, b, t) { return a + (b - a) * t; }
|
|
346
|
+
function toFixed(num, d) { return Number(Number(num).toFixed(d || 0)); }
|
|
347
|
+
|
|
348
|
+
// ─── Function Utilities ───────────────────────────────────────────────────────
|
|
349
|
+
|
|
350
|
+
function debounce(fn, delay) {
|
|
351
|
+
let timer;
|
|
352
|
+
const debounced = function () {
|
|
353
|
+
const args = arguments, ctx = this;
|
|
354
|
+
clearTimeout(timer);
|
|
355
|
+
timer = setTimeout(() => fn.apply(ctx, args), delay);
|
|
356
|
+
};
|
|
357
|
+
debounced.cancel = () => clearTimeout(timer);
|
|
358
|
+
debounced.flush = function () { clearTimeout(timer); fn.apply(this, arguments); };
|
|
359
|
+
return debounced;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function throttle(fn, limit) {
|
|
363
|
+
let lastTime = 0, timer;
|
|
364
|
+
return function () {
|
|
365
|
+
const now = Date.now(), args = arguments, ctx = this;
|
|
366
|
+
if (now - lastTime >= limit) {
|
|
367
|
+
clearTimeout(timer);
|
|
368
|
+
lastTime = now;
|
|
369
|
+
fn.apply(ctx, args);
|
|
370
|
+
} else if (!timer) {
|
|
371
|
+
timer = setTimeout(() => { lastTime = now; timer = null; fn.apply(ctx, args); }, limit - (now - lastTime));
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function memoize(fn, resolver) {
|
|
377
|
+
const cache = new Map();
|
|
378
|
+
const memoized = function () {
|
|
379
|
+
const key = resolver ? resolver.apply(null, arguments) : JSON.stringify(arguments);
|
|
380
|
+
if (cache.has(key)) return cache.get(key);
|
|
381
|
+
const result = fn.apply(this, arguments);
|
|
382
|
+
cache.set(key, result);
|
|
383
|
+
return result;
|
|
384
|
+
};
|
|
385
|
+
memoized.cache = cache;
|
|
386
|
+
memoized.clear = () => cache.clear();
|
|
387
|
+
return memoized;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function once(fn) {
|
|
391
|
+
let called = false, result;
|
|
392
|
+
return function () {
|
|
393
|
+
if (called) return result;
|
|
394
|
+
called = true;
|
|
395
|
+
result = fn.apply(this, arguments);
|
|
396
|
+
return result;
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function curry(fn) {
|
|
401
|
+
const arity = fn.length;
|
|
402
|
+
return function curried() {
|
|
403
|
+
const args = Array.prototype.slice.call(arguments);
|
|
404
|
+
if (args.length >= arity) return fn.apply(this, args);
|
|
405
|
+
return function () { return curried.apply(this, args.concat(Array.prototype.slice.call(arguments))); };
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function compose() {
|
|
410
|
+
const fns = Array.prototype.slice.call(arguments);
|
|
411
|
+
return function (x) { return fns.reduceRight((v, f) => f(v), x); };
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function pipe() {
|
|
415
|
+
const fns = Array.prototype.slice.call(arguments);
|
|
416
|
+
return function (x) { return fns.reduce((v, f) => f(v), x); };
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function partial(fn) {
|
|
420
|
+
const preset = Array.prototype.slice.call(arguments, 1);
|
|
421
|
+
return function () { return fn.apply(this, preset.concat(Array.prototype.slice.call(arguments))); };
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function noop() {}
|
|
425
|
+
function identity(v) { return v; }
|
|
426
|
+
|
|
427
|
+
// ─── Date Utilities ───────────────────────────────────────────────────────────
|
|
428
|
+
|
|
429
|
+
function formatDate(date, format) {
|
|
430
|
+
date = isDate(date) ? date : new Date(date);
|
|
431
|
+
if (isNaN(date.getTime())) return '';
|
|
432
|
+
format = format || 'YYYY-MM-DD';
|
|
433
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
434
|
+
const tokens = {
|
|
435
|
+
'YYYY': date.getFullYear(), 'MM': pad(date.getMonth() + 1), 'DD': pad(date.getDate()),
|
|
436
|
+
'HH': pad(date.getHours()), 'mm': pad(date.getMinutes()), 'ss': pad(date.getSeconds()),
|
|
437
|
+
};
|
|
438
|
+
let result = format;
|
|
439
|
+
Object.keys(tokens).forEach((key) => { result = result.replace(key, tokens[key]); });
|
|
440
|
+
return result;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function parseDate(str, format) {
|
|
444
|
+
if (!format) return new Date(str);
|
|
445
|
+
const map = {};
|
|
446
|
+
format.replace(/(YYYY|MM|DD|HH|mm|ss)/g, (m, p, o) => { map[p] = o; return m; });
|
|
447
|
+
const y = str.substr(map.YYYY, 4), mo = str.substr(map.MM, 2), d = str.substr(map.DD, 2);
|
|
448
|
+
return new Date(y, mo - 1, d);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function addDays(date, days) { const d = new Date(date); d.setDate(d.getDate() + days); return d; }
|
|
452
|
+
function diffDays(a, b) { return Math.round((new Date(a) - new Date(b)) / 86400000); }
|
|
453
|
+
function isToday(date) { const d = new Date(date), t = new Date(); return d.toDateString() === t.toDateString(); }
|
|
454
|
+
function isWeekend(date) { const day = new Date(date).getDay(); return day === 0 || day === 6; }
|
|
455
|
+
function getWeek(date) { const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())); d.setUTCDate(d.getUTCDate() + 4 - (d.getUTCDay() || 7)); return Math.ceil(((d - new Date(Date.UTC(d.getUTCFullYear(), 0, 1))) / 86400000 + 1) / 7); }
|
|
456
|
+
function getQuarter(date) { return Math.ceil((new Date(date).getMonth() + 1) / 3); }
|
|
457
|
+
|
|
458
|
+
// ─── Color Utilities ──────────────────────────────────────────────────────────
|
|
459
|
+
|
|
460
|
+
function hexToRgb(hex) {
|
|
461
|
+
hex = hex.replace('#', '');
|
|
462
|
+
if (hex.length === 3) hex = hex.split('').map((c) => c + c).join('');
|
|
463
|
+
const n = parseInt(hex, 16);
|
|
464
|
+
return { r: (n >> 16) & 255, g: (n >> 8) & 255, b: n & 255 };
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function rgbToHex(r, g, b) {
|
|
468
|
+
return '#' + [r, g, b].map((c) => Math.max(0, Math.min(255, Math.round(c))).toString(16).padStart(2, '0')).join('');
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function hexToHsl(hex) {
|
|
472
|
+
const { r, g, b } = hexToRgb(hex);
|
|
473
|
+
const rn = r / 255, gn = g / 255, bn = b / 255;
|
|
474
|
+
const max = Math.max(rn, gn, bn), min = Math.min(rn, gn, bn);
|
|
475
|
+
let h, s, l = (max + min) / 2;
|
|
476
|
+
if (max === min) { h = s = 0; } else {
|
|
477
|
+
const d = max - min;
|
|
478
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
479
|
+
switch (max) {
|
|
480
|
+
case rn: h = ((gn - bn) / d + (gn < bn ? 6 : 0)) / 6; break;
|
|
481
|
+
case gn: h = ((bn - rn) / d + 2) / 6; break;
|
|
482
|
+
case bn: h = ((rn - gn) / d + 4) / 6; break;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
return { h: Math.round(h * 360), s: Math.round(s * 100), l: Math.round(l * 100) };
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function hslToHex(h, s, l) {
|
|
489
|
+
s /= 100; l /= 100;
|
|
490
|
+
const a = s * Math.min(l, 1 - l);
|
|
491
|
+
const f = (n) => {
|
|
492
|
+
const k = (n + h / 30) % 12;
|
|
493
|
+
const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
|
|
494
|
+
return Math.round(255 * color).toString(16).padStart(2, '0');
|
|
495
|
+
};
|
|
496
|
+
return '#' + f(0) + f(8) + f(4);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
function lighten(hex, amount) {
|
|
500
|
+
const { r, g, b } = hexToRgb(hex);
|
|
501
|
+
return rgbToHex(r + (255 - r) * amount / 100, g + (255 - g) * amount / 100, b + (255 - b) * amount / 100);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
function darken(hex, amount) {
|
|
505
|
+
const { r, g, b } = hexToRgb(hex);
|
|
506
|
+
return rgbToHex(r * (1 - amount / 100), g * (1 - amount / 100), b * (1 - amount / 100));
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
function alpha(hex, a) {
|
|
510
|
+
const { r, g, b } = hexToRgb(hex);
|
|
511
|
+
return 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
function isValidColor(str) {
|
|
515
|
+
if (!str) return false;
|
|
516
|
+
if (/^#([0-9A-Fa-f]{3}){1,2}$/.test(str)) return true;
|
|
517
|
+
if (/^rgb\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*\)$/.test(str)) return true;
|
|
518
|
+
if (/^rgba\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*,\s*[\d.]+\s*\)$/.test(str)) return true;
|
|
519
|
+
if (/^hsl\(\s*\d+\s*,\s*\d+%\s*,\s*\d+%\s*\)$/.test(str)) return true;
|
|
520
|
+
const opt = new Option();
|
|
521
|
+
opt.style.color = str;
|
|
522
|
+
return opt.style.color !== '';
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// ─── URL Utilities ────────────────────────────────────────────────────────────
|
|
526
|
+
|
|
527
|
+
function parseUrl(url) {
|
|
528
|
+
try { return new URL(url); } catch (e) { return null; }
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
function buildUrl(base, path, params) {
|
|
532
|
+
let url = base;
|
|
533
|
+
if (path) url = url.replace(/\/$/, '') + '/' + path.replace(/^\//, '');
|
|
534
|
+
if (params) {
|
|
535
|
+
const qs = Object.keys(params).map((k) => encodeURIComponent(k) + '=' + encodeURIComponent(params[k])).join('&');
|
|
536
|
+
if (qs) url += (url.includes('?') ? '&' : '?') + qs;
|
|
537
|
+
}
|
|
538
|
+
return url;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
function getQueryParam(name, url) {
|
|
542
|
+
url = url || window.location.href;
|
|
543
|
+
const u = new URL(url);
|
|
544
|
+
return u.searchParams.get(name);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function setQueryParam(name, value, url) {
|
|
548
|
+
url = url || window.location.href;
|
|
549
|
+
const u = new URL(url);
|
|
550
|
+
u.searchParams.set(name, value);
|
|
551
|
+
return u.toString();
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
function getHash(url) {
|
|
555
|
+
url = url || window.location.href;
|
|
556
|
+
const idx = url.indexOf('#');
|
|
557
|
+
return idx !== -1 ? url.slice(idx + 1) : '';
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
function setHash(hash, url) {
|
|
561
|
+
url = url || window.location.href;
|
|
562
|
+
const idx = url.indexOf('#');
|
|
563
|
+
const base = idx !== -1 ? url.slice(0, idx) : url;
|
|
564
|
+
return base + '#' + hash;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// ─── Exports ──────────────────────────────────────────────────────────────────
|
|
568
|
+
|
|
569
|
+
module.exports = {
|
|
570
|
+
// Type checking
|
|
571
|
+
isString, isNumber, isBoolean, isFunction, isObject, isArray, isNull, isUndefined,
|
|
572
|
+
isDate, isRegExp, isPromise, isElement, isPlainObject, isNil, isFiniteNum, isInteger,
|
|
573
|
+
// Object utilities
|
|
574
|
+
deepClone, deepMerge, shallowMerge, pick, omit, get, set, has, keys, values,
|
|
575
|
+
entries, fromEntries, isEmpty, isEqual, flattenObject,
|
|
576
|
+
// Array utilities
|
|
577
|
+
unique, flatten, chunk, groupBy, sortBy, filterBy, findBy, countBy, partition,
|
|
578
|
+
zip, unzip, difference, intersection, union, shuffle, sample, range,
|
|
579
|
+
// String utilities
|
|
580
|
+
capitalize, camelCase, kebabCase, snakeCase, pascalCase, truncate, padStart,
|
|
581
|
+
padEnd, trim, repeat, template, escapeHtml, unescapeHtml, formatNumber, formatCurrency,
|
|
582
|
+
// Number utilities
|
|
583
|
+
clamp, random, randomInt, round, ceil, floor, sum, avg, min, max, percent,
|
|
584
|
+
isBetween, lerp, toFixed,
|
|
585
|
+
// Function utilities
|
|
586
|
+
debounce, throttle, memoize, once, curry, compose, pipe, partial, noop, identity,
|
|
587
|
+
// Date utilities
|
|
588
|
+
formatDate, parseDate, addDays, diffDays, isToday, isWeekend, getWeek, getQuarter,
|
|
589
|
+
// Color utilities
|
|
590
|
+
hexToRgb, rgbToHex, hexToHsl, hslToHex, lighten, darken, alpha, isValidColor,
|
|
591
|
+
// URL utilities
|
|
592
|
+
parseUrl, buildUrl, getQueryParam, setQueryParam, getHash, setHash,
|
|
593
|
+
};
|