@gateweb/react-utils 1.16.0 → 1.17.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/cjs/index.js CHANGED
@@ -1,762 +1,18 @@
1
1
  Object.defineProperty(exports, '__esModule', { value: true });
2
2
 
3
+ var webStorage12s = require('./webStorage-12s-P8HpaTRP.js');
3
4
  var dayjs = require('dayjs');
4
5
  var queryStore12s = require('./queryStore-12s-q_SLGgYH.js');
5
6
  var React = require('react');
6
7
  var useCountdown12s = require('./useCountdown-12s-uiqhgllY.js');
7
8
  var useDisclosure12s = require('./useDisclosure-12s-SZtbSE4A.js');
8
9
  var download12s = require('./download-12s-DKxkL92w.js');
9
- var webStorage12s = require('./webStorage-12s-DHr9PcPl.js');
10
10
 
11
11
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
12
12
 
13
13
  var dayjs__default = /*#__PURE__*/_interopDefault(dayjs);
14
14
  var React__default = /*#__PURE__*/_interopDefault(React);
15
15
 
16
- const FILE_SIZE_UNITS$1 = [
17
- 'Bytes',
18
- 'KB',
19
- 'MB',
20
- 'GB',
21
- 'TB',
22
- 'PB',
23
- 'EB',
24
- 'ZB',
25
- 'YB'
26
- ];
27
- /**
28
- * 代表字節大小的類,提供各種格式化和轉換方法
29
- */ class ByteSize {
30
- /**
31
- * 建立一個新的 ByteSize 實例
32
- * @param bytes 位元組數值
33
- */ constructor(bytes){
34
- this.bytes = bytes;
35
- }
36
- /**
37
- * 取得原始位元組數值
38
- */ get value() {
39
- return this.bytes;
40
- }
41
- /**
42
- * 將位元組轉換為指定單位的數值
43
- *
44
- * @param unit 目標單位 (例如 'KB', 'MB', 'GB')
45
- * @returns 轉換後的數值
46
- *
47
- * @example
48
- * ```ts
49
- * const size = createByteSize(1024);
50
- * size.to('KB'); // 1
51
- * size.to('MB'); // 0.0009765625
52
- * ```
53
- */ to(unit) {
54
- if (!FILE_SIZE_UNITS$1.includes(unit)) {
55
- console.warn(`Invalid unit: ${unit}. Valid units are: ${FILE_SIZE_UNITS$1.join(', ')}`);
56
- return this.bytes;
57
- }
58
- const k = 1024;
59
- const i = FILE_SIZE_UNITS$1.indexOf(unit);
60
- return this.bytes / k ** i;
61
- }
62
- /**
63
- * 轉換為適當單位的可讀字符串
64
- *
65
- * @param unit 指定單位 (例如 'KB', 'MB', 'GB'),不指定則自動選擇最適合的單位
66
- * @param decimals 小數點位數 (預設為 2)
67
- * @returns 格式化後的字符串
68
- *
69
- * @example
70
- * ```ts
71
- * const size = createByteSize(1024);
72
- * size.format(); // '1.00 KB'
73
- * size.format('KB'); // '1.00 KB'
74
- * size.format('MB', 3); // '0.001 MB'
75
- * ```
76
- */ format(unit, decimals = 2) {
77
- if (this.bytes === 0) return `0 ${unit || 'Bytes'}`;
78
- const k = 1024;
79
- // 如果指定了有效單位,則使用;否則,計算最適合的單位
80
- const i = unit && FILE_SIZE_UNITS$1.includes(unit) ? FILE_SIZE_UNITS$1.indexOf(unit) : Math.floor(Math.log(this.bytes) / Math.log(k));
81
- return `${(this.bytes / k ** i).toFixed(decimals)} ${FILE_SIZE_UNITS$1[i]}`;
82
- }
83
- /**
84
- * 比較兩個 ByteSize 實例
85
- *
86
- * @param other 要比較的另一個 ByteSize 實例
87
- * @returns 比較結果:-1 表示小於,0 表示等於,1 表示大於
88
- */ compareTo(other) {
89
- if (this.bytes < other.value) return -1;
90
- if (this.bytes > other.value) return 1;
91
- return 0;
92
- }
93
- /**
94
- * 轉換為字符串表示
95
- * @returns 預設格式化的字符串
96
- */ toString() {
97
- return this.format();
98
- }
99
- }
100
- /**
101
- * 將位元組轉換為可讀字符串 (保留舊的 API 以確保向後兼容)
102
- *
103
- * @param bytes 位元組數值
104
- * @param unit 目標單位 (例如 'KB', 'MB', 'GB'),不指定則自動選擇最適合的單位
105
- * @param decimals 小數點位數 (預設為 2)
106
- * @returns 格式化後的字符串
107
- *
108
- * @example
109
- * ```ts
110
- * convertBytes(0) // '0 Bytes'
111
- * convertBytes(1024, 'KB') // '1 KB'
112
- * convertBytes(1024, 'KB', 2) // '1.00 KB'
113
- * convertBytes(1024 * 1024, 'MB') // '1 MB'
114
- * convertBytes(1024 * 1024, 'KB') // '1024 KB'
115
- * ```
116
- */ const convertBytes = (bytes, unit, decimals = 2)=>new ByteSize(bytes).format(unit, decimals);
117
-
118
- /**
119
- * 檢查兩個值是否相等(包含陣列等引用型別)
120
- * - 支援基本型別的直接比較
121
- * - 特別處理空陣列比較
122
- * - 支援非空陣列的深度比較
123
- *
124
- * @param value1 - 第一個值
125
- * @param value2 - 第二個值
126
- * @returns 如果值相等則返回 true
127
- *
128
- * @example
129
- * // 基本型別比較
130
- * isEqual(1, 1); // true
131
- * isEqual('abc', 'abc'); // true
132
- * isEqual(null, null); // true
133
- *
134
- * // 陣列比較
135
- * isEqual([], []); // true
136
- * isEqual([1, 2], [1, 2]); // true
137
- * isEqual([1, 2], [1, 3]); // false
138
- */ const isEqual = (value1, value2)=>{
139
- // 處理基本型別
140
- if (value1 === value2) {
141
- return true;
142
- }
143
- // 處理空陣列
144
- if (Array.isArray(value1) && Array.isArray(value2) && value1.length === 0 && value2.length === 0) {
145
- return true;
146
- }
147
- // 兩者都是非 null 的物件(包含陣列)
148
- if (value1 !== null && value2 !== null && typeof value1 === 'object' && typeof value2 === 'object') {
149
- // 處理非空陣列
150
- if (Array.isArray(value1) && Array.isArray(value2)) {
151
- if (value1.length !== value2.length) {
152
- return false;
153
- }
154
- return value1.every((item, index)=>isEqual(item, value2[index]));
155
- }
156
- // 未來可以擴展這裡,增加其他物件型別的深度比較
157
- }
158
- return false;
159
- };
160
-
161
- /**
162
- * 將指定格式的物件轉換成類似 enum 的物件
163
- *
164
- * @param enumObject enum 物件
165
- * @param valueKey value 的 key
166
- *
167
- * @example
168
- * const myObj = {
169
- * A: { value: 'a' },
170
- * B: { value: 'b', other: 'other' },
171
- * } as const;
172
- * const enumCode = extractEnumLikeObject(myObj, 'value');
173
- * // => { A: 'a', B: 'b' }
174
- */ const extractEnumLikeObject = (enumObject, valueKey)=>Object.entries(enumObject).reduce((acc, [key, value])=>({
175
- ...acc,
176
- [key]: value[valueKey]
177
- }), {});
178
- // 實作
179
- function createEnumLikeObject(obj, options) {
180
- const name = options?.name;
181
- const scene = options?.scene;
182
- const valueKey = options?.valueKey ?? 'value';
183
- const Enum = extractEnumLikeObject(obj, valueKey);
184
- // List:根據有無 scene 做不同處理
185
- let list;
186
- if (scene) {
187
- // scenes 版本:解構指定 scene 的 label
188
- list = Object.entries(obj).map(([key, item])=>({
189
- key,
190
- value: item?.[valueKey],
191
- ...item.scenes?.[scene] ?? {}
192
- }));
193
- } else {
194
- // label only 版本
195
- list = Object.entries(obj).map(([key, item])=>({
196
- key,
197
- value: item?.[valueKey],
198
- ...item
199
- }));
200
- }
201
- function getLabel(value) {
202
- const targetItem = list.find((item)=>// 如果 list 項目內含有 valueKey 對應欄位,優先以該欄位比對;否則以 value 欄位比對
203
- valueKey in item ? item[valueKey] === value : item.value === value);
204
- if (!targetItem) return String(value);
205
- if ('label' in targetItem) return targetItem.label;
206
- return String(value);
207
- }
208
- if (name) {
209
- // 命名版:使用 Enum${name}, ${name}List, get${name}Label 鍵名
210
- return {
211
- [`Enum${name}`]: Enum,
212
- [`${name}List`]: list,
213
- [`get${name}Label`]: getLabel
214
- };
215
- }
216
- // 未命名:提供預設鍵名 Enum, List, getLabel
217
- return {
218
- Enum,
219
- List: list,
220
- getLabel
221
- };
222
- }
223
-
224
- /**
225
- * simulate a fake api request
226
- *
227
- * @param returnValue the value to return
228
- * @param result the result of the request
229
- * @param time the time to wait before resolving
230
- *
231
- * @example
232
- *
233
- * const result = await fakeApi({ foo: 'bar' });
234
- * console.log(result); // { result: true, data: { foo: 'bar' } }
235
- */ const fakeApi = (returnValue, result = true, time = 1000)=>new Promise((resolve)=>{
236
- setTimeout(()=>{
237
- if (result) resolve({
238
- result,
239
- data: returnValue
240
- });
241
- else resolve({
242
- result,
243
- message: 'error'
244
- });
245
- }, time);
246
- });
247
-
248
- const MimeTypeMap = {
249
- jpeg: 'image/jpeg',
250
- jpg: 'image/jpeg',
251
- gif: 'image/gif',
252
- png: 'image/png',
253
- pdf: 'application/pdf',
254
- zip: 'application/zip',
255
- csv: 'text/csv',
256
- ppt: 'application/vnd.ms-powerpoint',
257
- pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
258
- xls: 'application/vnd.ms-excel',
259
- xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
260
- doc: 'application/msword',
261
- docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
262
- txt: 'text/plain'
263
- };
264
- const OtherMimeType = 'application/octet-stream';
265
-
266
- /**
267
- * 檢查檔案是否為合法的檔案類型
268
- *
269
- * `accepts` 可同時接受副檔名以及 MIME 類型
270
- *
271
- * @param file 檔案
272
- * @param accepts 允許的類型
273
- *
274
- * @example
275
- *
276
- * ```js
277
- * validateFileType({ type: 'image/png' }, ['image/png', 'image/jpeg']) // true
278
- * validateFileType({ type: 'image/png' }, ['image/jpeg']) // false
279
- * validateFileType({ type: 'image/png' }, ['image/*']) // true
280
- * validateFileType({ name: '圖片.png', type: 'image/png' }, ['.png']) // true
281
- * ```
282
- */ const validateFileType = (file, accepts)=>{
283
- if (accepts.length === 0) return true;
284
- // 獲取文件的MIME類型
285
- const fileMimeType = file.type;
286
- // 提取副檔名(含 .,且轉小寫)
287
- const fileExt = file.name.includes('.') ? `.${file.name.split('.').pop().toLowerCase()}` : '';
288
- return accepts.some((accept)=>{
289
- if (accept.startsWith('.')) {
290
- // 以副檔名檢查,忽略大小寫
291
- return fileExt === accept.toLowerCase();
292
- }
293
- if (accept === fileMimeType) {
294
- return true;
295
- }
296
- if (accept.endsWith('/*')) {
297
- return accept.split('/')[0] === fileMimeType.split('/')[0];
298
- }
299
- return false;
300
- });
301
- };
302
- /**
303
- * 根據檔案副檔名取得對應的 MIME Type
304
- *
305
- * 目前支援的副檔名有:pdf, csv, jpeg, jpg, png, zip, txt
306
- * 除此之外的副檔名皆回傳 application/octet-stream
307
- *
308
- * @param fileExtension 檔案副檔名
309
- *
310
- * @example
311
- *
312
- * getMimeType('pdf') // 'application/pdf'
313
- * getMimeType('csv') // 'text/csv'
314
- * getMimeType('jpeg') // 'image/jpeg'
315
- * getMimeType('jpg') // 'image/jpeg'
316
- * getMimeType('png') // 'image/png'
317
- * getMimeType('txt') // 'text/plain'
318
- * getMimeType('zip') // 'application/zip'
319
- * getMimeType('mp4') // 'application/octet-stream'
320
- * getMimeType('xls') // 'application/vnd.ms-excel'
321
- * getMimeType('xlsx') // 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
322
- * getMimeType('doc') // 'application/msword'
323
- * getMimeType('docx') // 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
324
- *
325
- * @deprecated use `parseFileInfoFromFilename` instead
326
- */ const getMimeType = (fileName)=>{
327
- const ext = (fileName.split('.').pop() || '').toLowerCase();
328
- return MimeTypeMap[ext] ?? OtherMimeType;
329
- };
330
- /**
331
- * 用來解析後端在 response header content-disposition 的內容
332
- *
333
- * 一般來說格式會有以下兩種
334
- *
335
- * - Content-Disposition: attachment; filename="file name.jpg"
336
- * - Content-Disposition: attachment; filename*=UTF-8''file%20name2.jpg
337
- *
338
- * 如果格式正確就會取得檔案名稱(包含副檔名),優先取 filename* 內的內容
339
- *
340
- * @param disposition Content-Disposition
341
- *
342
- * @example
343
- *
344
- * parseFilenameFromDisposition('attachment; filename="file name1.jpg') // file name.jpg
345
- * parseFilenameFromDisposition('attachment; filename*=UTF-8''file%20name2.jpg') // file name2.jpg
346
- * parseFilenameFromDisposition('attachment; filename="file name.jpg; filename*=UTF-8''file%20name2.jpg') // file name2.jpg
347
- */ const parseFilenameFromDisposition = (disposition)=>{
348
- // 1. 先找 filename*
349
- const filenameStarMatch = disposition.match(/filename\*\s*=\s*(?:UTF-8'')?([^;]+)/i);
350
- if (filenameStarMatch && filenameStarMatch[1]) {
351
- // 依 RFC 5987 格式(UTF-8''URL-ENCODED),要先 decode
352
- try {
353
- return decodeURIComponent(filenameStarMatch[1].replace(/(^['"]|['"]$)/g, ''));
354
- } catch {
355
- // fallback,如果 decode 失敗,直接傳回原值
356
- return filenameStarMatch[1];
357
- }
358
- }
359
- // 2. 沒有 filename*,再找 filename
360
- const filenameMatch = disposition.match(/filename\s*=\s*("?)([^";]+)\1/i);
361
- if (filenameMatch && filenameMatch[2]) {
362
- return filenameMatch[2];
363
- }
364
- // 3. 都沒有則 undefined
365
- return undefined;
366
- };
367
- /**
368
- * 解析 `filename` 回傳檔名、副檔名、MIME type
369
- *
370
- * @param filename 檔案名稱
371
- *
372
- * @example
373
- *
374
- * parseFileInfoFromFilename('image.jpg') // ['image', 'jpg', 'image/jpeg']
375
- * parseFileInfoFromFilename('image') // ['image', '', 'application/octet-stream']
376
- */ const parseFileInfoFromFilename = (filename)=>{
377
- const lastDot = filename.lastIndexOf('.');
378
- if (lastDot === -1) return [
379
- filename,
380
- '',
381
- OtherMimeType
382
- ]; // 沒有副檔名
383
- return [
384
- filename.slice(0, lastDot),
385
- filename.slice(lastDot + 1),
386
- MimeTypeMap[filename.slice(lastDot + 1)] ?? OtherMimeType
387
- ];
388
- };
389
-
390
- // const isProduction: boolean = process.env.NODE_ENV === 'production';
391
- const prefix = 'Invariant failed';
392
- // Throw an error if the condition fails
393
- // Strip out error messages for production
394
- // > Not providing an inline default argument for message as the result is smaller
395
- function invariant(condition, // Can provide a string, or a function that returns a string for cases where
396
- // the message takes a fair amount of effort to compute
397
- message) {
398
- if (condition) {
399
- return;
400
- }
401
- // Condition not passed
402
- // In production we strip the message but still throw
403
- if (process.env.NODE_ENV === 'production') {
404
- throw new Error(prefix);
405
- }
406
- // When not in production we allow the message to pass through
407
- // *This block will be removed in production builds*
408
- const provided = typeof message === 'function' ? message() : message;
409
- // Options:
410
- // 1. message provided: `${prefix}: ${provided}`
411
- // 2. message not provided: prefix
412
- const value = provided ? `${prefix}: ${provided}` : prefix;
413
- throw new Error(value);
414
- }
415
-
416
- /**
417
- * 判斷執行環境是否為 Server(node.js) 端
418
- */ const isServer = ()=>typeof window === 'undefined';
419
-
420
- /**
421
- * 將物件中的某些 key 排除
422
- *
423
- * @param object 原始物件
424
- * @param keys 要排除的 key
425
- *
426
- * @example
427
- * const a = { a: 1, b: 2, c: 3, d: 4 };
428
- * const b = omit(a, 'a', 'b'); // { c: 3, d: 4 }
429
- */ const omit = (object, ...keys)=>Object.keys(object).reduce((acc, cur)=>{
430
- if (keys.includes(cur)) {
431
- return acc;
432
- }
433
- return {
434
- ...acc,
435
- [cur]: object[cur]
436
- };
437
- }, {});
438
- /**
439
- * 將物件中的某些 value 排除
440
- *
441
- * @param object 原始物件
442
- * @param values 要排除的 value
443
- *
444
- * @example
445
- * const a = { a: undefined, b: null, c: 3, d: 4, e: [] };
446
- * const b = omitByValue(a, undefined, null, []); // { c: 3, d: 4 }
447
- */ const omitByValue = (object, ...values)=>Object.entries(object).reduce((acc, [key, value])=>{
448
- // 使用深度比較檢查值是否應該被排除
449
- const shouldOmit = values.some((excludeValue)=>isEqual(value, excludeValue));
450
- if (shouldOmit) {
451
- return acc;
452
- }
453
- return {
454
- ...acc,
455
- [key]: value
456
- };
457
- }, {});
458
- /**
459
- * extract the object fields by the given keys
460
- *
461
- * @param object - the object to pick fields from
462
- * @param keys - the keys to pick
463
- *
464
- * @example
465
- *
466
- * const a = { a: 1, b: 2, c: 3, d: 4 };
467
- * const b = pick(a, 'a', 'b'); // { a: 1, b: 2 }
468
- */ const pick = (object, ...keys)=>keys.reduce((acc, cur)=>({
469
- ...acc,
470
- [cur]: object[cur]
471
- }), {});
472
- /**
473
- * extract the object fields by the given values
474
- *
475
- * @param object - the object to pick fields from
476
- * @param values - the values to pick
477
- *
478
- * @example
479
- *
480
- * const a = { a: 1, b: 2, c: 3, d: 4, e: [] };
481
- * const b = pickByValue(a, 1, 2, []); // { a: 1, b: 2, e: [] }
482
- */ const pickByValue = (object, ...values)=>Object.entries(object).reduce((acc, [key, value])=>{
483
- // 使用深度比較檢查值是否應該被選擇
484
- const shouldPick = values.some((pickValue)=>isEqual(value, pickValue));
485
- if (shouldPick) {
486
- return {
487
- ...acc,
488
- [key]: value
489
- };
490
- }
491
- return acc;
492
- }, {});
493
- const isObject = (value)=>value !== null && typeof value === 'object';
494
- /**
495
- * merge two objects deeply
496
- *
497
- * @param target - the target object
498
- * @param source - the source object
499
- *
500
- * @example
501
- *
502
- * const obja = { a: { a1: { a11: 'value 1', a12: 'value 2' }, a2: 'value 3' }, b: 'value 4' };
503
- * const objb = { a: { a1: { a13: 'value 5', a14: 'value 6' }, a3: 'value 7' }};
504
- *
505
- * const mergeResult = deepMerge(obja, objb); // { a: { a1: { a11: 'value 1', a12: 'value 2', a13: 'value 5', a14: 'value 6' }, a2: 'value 3', a3: 'value 7' }, b: 'value 4' }
506
- *
507
- */ const deepMerge = (target, source)=>Object.entries(source).reduce((acc, [key, value])=>{
508
- if (isObject(value)) {
509
- acc[key] = key in target && isObject(target[key]) ? deepMerge(target[key], value) : value;
510
- } else {
511
- acc[key] = value;
512
- }
513
- return acc;
514
- }, {
515
- ...target
516
- });
517
- /**
518
- * A utility function to deeply clone an object.
519
- * @param obj - The object to clone.
520
- *
521
- * @example
522
- *
523
- * const original = { a: 1, b: { c: 2 } };
524
- * const cloned = deepClone(original);
525
- * console.log(cloned); // { a: 1, b: { c: 2 } }
526
- *
527
- */ const deepClone = (obj)=>{
528
- if (obj === null || typeof obj !== 'object') {
529
- return obj;
530
- }
531
- if (obj instanceof Date) {
532
- return new Date(obj.getTime());
533
- }
534
- if (Array.isArray(obj)) {
535
- return obj.map((item)=>deepClone(item));
536
- }
537
- const cloned = Object.keys(obj).reduce((acc, key)=>{
538
- acc[key] = deepClone(obj[key]);
539
- return acc;
540
- }, {});
541
- return cloned;
542
- };
543
- /**
544
- * A utility function to rename a key in an object.
545
- *
546
- * @param obj - The object to modify.
547
- * @param oldKey - The key to rename.
548
- * @param newKey - The new key name.
549
- *
550
- * @example
551
- *
552
- * const obj = { a: 1, b: 2 };
553
- * const newObj = renameKey(obj, 'a', 'c');
554
- * console.log(newObj); // { c: 1, b: 2 }
555
- */ const renameKey = (obj, oldKey, newKey)=>{
556
- // 建立一個淺拷貝,避免修改原始物件
557
- const { [oldKey]: oldValue, ...rest } = obj;
558
- // 回傳新的物件:用新 key 存舊值,其他 key 保留
559
- return {
560
- ...rest,
561
- [newKey]: oldValue
562
- };
563
- };
564
-
565
- /**
566
- * 將嵌套物件的所有屬性設為指定的布林值
567
- * @param obj - 目標物件
568
- * @param boolValue - 布林值
569
- *
570
- * @example
571
- * const obj = { a: { b: 1, c: 2 }, d: 3 };
572
- * const result = setBooleanToNestedObject(obj, true);
573
- * console.log(result); // { a: { b: true, c: true }, d: true }
574
- */ const setBooleanToNestedObject = (obj, boolValue)=>Object.entries(obj).reduce((acc, [key, value])=>{
575
- if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
576
- // 如果是嵌套物件,遞歸處理
577
- acc[key] = setBooleanToNestedObject(value, boolValue);
578
- } else {
579
- // 如果是基本類型,設為布林值
580
- acc[key] = boolValue;
581
- }
582
- return acc;
583
- }, {});
584
- /**
585
- * 深度合併配置物件的通用工具
586
- *
587
- * @param defaultConfig - 預設配置
588
- * @param customConfig - 自訂配置
589
- *
590
- * @example
591
- * const defaultConfig = { a: true, b: { b1: true, b2: true, b3: false }, c: true, d: false };
592
- * mergeConfig(defaultConfig, { b: { b1: false }, d: true }); // { a: true, b: { b1: false, b2: true, b3: false }, c: true, d: true }
593
- * mergeConfig(defaultConfig, { b: false }); // { a: true, b: { b1: false, b2: false, b3: false }, c: true, d: false }
594
- * mergeConfig(defaultConfig, { b: true }); // { a: true, b: { b1: true, b2: true, b3: true }, c: true, d: false }
595
- */ const mergeConfig = (defaultConfig, customConfig = {})=>{
596
- // 深拷貝預設配置以避免修改原始物件
597
- const result = deepClone(defaultConfig);
598
- return Object.entries(customConfig).reduce((acc, [key, sourceValue])=>{
599
- const targetValue = acc[key];
600
- // 如果來源值為 null 或 undefined,跳過
601
- if (sourceValue === null || sourceValue === undefined) {
602
- return acc;
603
- }
604
- // 如果目標物件中對應的值是物件,且來源值是布林值
605
- // 這是特殊情況:用布林值覆蓋整個嵌套物件
606
- if (typeof targetValue === 'object' && targetValue !== null && !Array.isArray(targetValue) && typeof sourceValue === 'boolean') {
607
- // 將嵌套物件的所有屬性設為該布林值
608
- acc[key] = setBooleanToNestedObject(targetValue, sourceValue);
609
- } else if (typeof targetValue === 'object' && targetValue !== null && !Array.isArray(targetValue) && typeof sourceValue === 'object' && sourceValue !== null && !Array.isArray(sourceValue)) {
610
- acc[key] = mergeConfig(targetValue, sourceValue);
611
- } else {
612
- acc[key] = sourceValue;
613
- }
614
- return acc;
615
- }, result);
616
- };
617
-
618
- /**
619
- * debounce function
620
- *
621
- * @param {Function} fn function to be executed
622
- * @param {number} delay time to wait before executing the function
623
- *
624
- * @example
625
- * const debouncedFunction = debounce((a: number, b: number) => console.log(a + b), 1000);
626
- * debouncedFunction(1, 2);
627
- * debouncedFunction(3, 4);
628
- * // after 1 second
629
- * // 7
630
- */ const debounce = (fn, delay)=>{
631
- let timeout;
632
- // if (isAsync) {
633
- // return (...args: P[]) =>
634
- // new Promise((resolve) => {
635
- // if (timeout) {
636
- // clearTimeout(timeout);
637
- // }
638
- // timeout = setTimeout(() => {
639
- // resolve(fn(...args));
640
- // }, delay);
641
- // });
642
- // }
643
- return (...args)=>{
644
- if (timeout) {
645
- clearTimeout(timeout);
646
- }
647
- timeout = setTimeout(()=>{
648
- fn(...args);
649
- }, delay);
650
- };
651
- };
652
- /**
653
- * throttle function
654
- *
655
- * @param {Function} fn function to be executed
656
- * @param {number} delay time to wait before executing the function
657
- *
658
- * @example
659
- * const throttledFunction = throttle((a: number, b: number) => a + b, 1000);
660
- * throttledFunction(1, 2); // 3
661
- * throttledFunction(3, 4); // undefined
662
- * setTimeout(() => {
663
- * throttledFunction(5, 6); // 11
664
- * }, 2000);
665
- */ const throttle = (fn, delay)=>{
666
- let wait = false;
667
- return (...args)=>{
668
- if (wait) return undefined;
669
- const val = fn(...args);
670
- wait = true;
671
- setTimeout(()=>{
672
- wait = false;
673
- }, delay);
674
- return val;
675
- };
676
- };
677
-
678
- /**
679
- * Wait for a given amount of time
680
- * @param ms time to wait in milliseconds
681
- */ const wait = (ms)=>{
682
- };
683
-
684
- /**
685
- * 將單層物件轉換為 URLSearchParams 物件
686
- * - 會自動排除 null、undefined、空字串和空陣列的值
687
- * - 陣列會以逗號連接為字串
688
- *
689
- * @param obj - 要轉換的物件,只支援單層物件,其中值可以是 string、number、boolean、null、undefined 或字串陣列
690
- * @returns URLSearchParams 實例
691
- *
692
- * @example
693
- * const params = { a: '1', b: 123, c: false, d: ['1','2'], e: null, f: undefined, g: '', h: [] };
694
- * objectToSearchParams(params).toString(); // 'a=1&b=123&c=false&d=1%2C2'
695
- */ const objectToSearchParams = (obj)=>{
696
- const searchParams = new URLSearchParams();
697
- if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {
698
- return searchParams;
699
- }
700
- Object.entries(omitByValue(obj, undefined, null, '', [])).forEach(([key, value])=>{
701
- // 如果是陣列,則以逗號連接
702
- const paramValue = Array.isArray(value) ? value.join(',') : String(value);
703
- searchParams.append(key, paramValue);
704
- });
705
- return searchParams;
706
- };
707
- /**
708
- * 將字串值轉換為指定的型別
709
- *
710
- * @param value - 要轉換的字串值
711
- * @param targetType - 目標型別
712
- * @returns 轉換後的值
713
- */ const convertValueByType = (value, targetType)=>{
714
- switch(targetType){
715
- case 'number':
716
- return value ? Number(value) : 0;
717
- case 'boolean':
718
- return value === 'true' || value === '1';
719
- case 'array':
720
- return value ? value.split(',') : [];
721
- case 'string':
722
- default:
723
- // 預設為字串,不需要轉換
724
- return value;
725
- }
726
- };
727
- /**
728
- * 將 URLSearchParams 或字串轉換為物件
729
- *
730
- * @param searchParams - 要轉換的搜尋參數字串或 URLSearchParams 實例
731
- * @param typeMap - 可選的型別轉換映射表,用於指定每個參數的目標型別
732
- * @returns
733
- *
734
- * @example
735
- * const queryString = 'a=1&b=123&c=true&d=1,2,3&e=false';
736
- * const result = searchParamsToObject(queryString);
737
- * // result: { a: '1', b: '123', c: 'true', d: '1,2,3', e: 'false' }
738
- * const result = searchParamsToObject(queryString, {
739
- * a: 'string',
740
- * b: 'number',
741
- * c: 'boolean',
742
- * d: 'array',
743
- * e: 'boolean',
744
- * });
745
- * // result: { a: '1', b: 123, c: true, d: ['1', '2', '3'], e: false }
746
- */ const searchParamsToObject = (searchParams, typeMap = {})=>{
747
- const searchParamsInstance = typeof searchParams === 'string' ? new URLSearchParams(searchParams) : searchParams;
748
- const result = {};
749
- // 從 URLSearchParams 取得所有參數
750
- searchParamsInstance.forEach((value, key)=>{
751
- if (!key) {
752
- return;
753
- }
754
- const targetType = typeMap[key];
755
- result[key] = convertValueByType(value, targetType);
756
- });
757
- return result;
758
- };
759
-
760
16
  /**
761
17
  * convert CamelCase string to PascalCase string
762
18
  * @param str the string to convert
@@ -1421,77 +677,81 @@ function mergeRefs(refs) {
1421
677
  return dayjs__default.default(endMonth, 'YYYYMM').subtract(1911, 'year').format('YYYYMM').substring(1);
1422
678
  };
1423
679
 
680
+ exports.ByteSize = webStorage12s.ByteSize;
681
+ exports.MimeTypeMap = webStorage12s.MimeTypeMap;
682
+ exports.OtherMimeType = webStorage12s.OtherMimeType;
683
+ exports.convertBytes = webStorage12s.convertBytes;
684
+ exports.createEnumLikeObject = webStorage12s.createEnumLikeObject;
685
+ exports.debounce = webStorage12s.debounce;
686
+ exports.decodeBase64 = webStorage12s.decodeBase64;
687
+ exports.decodeJson = webStorage12s.decodeJson;
688
+ exports.deepClone = webStorage12s.deepClone;
689
+ exports.deepMerge = webStorage12s.deepMerge;
690
+ exports.encodeBase64 = webStorage12s.encodeBase64;
691
+ exports.encodeJson = webStorage12s.encodeJson;
692
+ exports.extractEnumLikeObject = webStorage12s.extractEnumLikeObject;
693
+ exports.fakeApi = webStorage12s.fakeApi;
694
+ exports.getLocalStorage = webStorage12s.getLocalStorage;
695
+ exports.getMimeType = webStorage12s.getMimeType;
696
+ exports.invariant = webStorage12s.invariant;
697
+ exports.isEqual = webStorage12s.isEqual;
698
+ exports.isServer = webStorage12s.isServer;
699
+ exports.mergeConfig = webStorage12s.mergeConfig;
700
+ exports.objectToSearchParams = webStorage12s.objectToSearchParams;
701
+ exports.omit = webStorage12s.omit;
702
+ exports.omitByValue = webStorage12s.omitByValue;
703
+ exports.parseFileInfoFromFilename = webStorage12s.parseFileInfoFromFilename;
704
+ exports.parseFilenameFromDisposition = webStorage12s.parseFilenameFromDisposition;
705
+ exports.pick = webStorage12s.pick;
706
+ exports.pickByValue = webStorage12s.pickByValue;
707
+ exports.renameKey = webStorage12s.renameKey;
708
+ exports.searchParamsToObject = webStorage12s.searchParamsToObject;
709
+ exports.setLocalStorage = webStorage12s.setLocalStorage;
710
+ exports.throttle = webStorage12s.throttle;
711
+ exports.validateFileType = webStorage12s.validateFileType;
712
+ exports.wait = webStorage12s.wait;
1424
713
  exports.QueryProvider = queryStore12s.QueryProvider;
1425
714
  exports.useQueryContext = queryStore12s.useQueryContext;
1426
715
  exports.useCountdown = useCountdown12s.useCountdown;
1427
716
  exports.useDisclosure = useDisclosure12s.useDisclosure;
1428
717
  exports.downloadFile = download12s.downloadFile;
1429
- exports.getLocalStorage = webStorage12s.getLocalStorage;
1430
- exports.setLocalStorage = webStorage12s.setLocalStorage;
1431
- exports.ByteSize = ByteSize;
1432
- exports.MimeTypeMap = MimeTypeMap;
1433
- exports.OtherMimeType = OtherMimeType;
1434
718
  exports.adToRocEra = adToRocEra;
1435
719
  exports.camelCase2PascalCase = camelCase2PascalCase;
1436
720
  exports.camelCase2SnakeCase = camelCase2SnakeCase;
1437
721
  exports.camelString2PascalString = camelString2PascalString;
1438
722
  exports.camelString2SnakeString = camelString2SnakeString;
1439
- exports.convertBytes = convertBytes;
1440
723
  exports.createDataContext = createDataContext;
1441
- exports.createEnumLikeObject = createEnumLikeObject;
1442
- exports.debounce = debounce;
1443
- exports.deepClone = deepClone;
1444
- exports.deepMerge = deepMerge;
1445
- exports.extractEnumLikeObject = extractEnumLikeObject;
1446
- exports.fakeApi = fakeApi;
1447
724
  exports.formatAmount = formatAmount;
1448
725
  exports.formatBytes = formatBytes;
1449
726
  exports.formatStarMask = formatStarMask;
1450
727
  exports.generatePeriodArray = generatePeriodArray;
1451
728
  exports.getCurrentPeriod = getCurrentPeriod;
1452
- exports.getMimeType = getMimeType;
1453
- exports.invariant = invariant;
1454
729
  exports.isChinese = isChinese;
1455
730
  exports.isDateString = isDateString;
1456
731
  exports.isDateTimeString = isDateTimeString;
1457
732
  exports.isEmail = isEmail;
1458
733
  exports.isEnglish = isEnglish;
1459
- exports.isEqual = isEqual;
1460
734
  exports.isNil = isNil;
1461
735
  exports.isNonZeroStart = isNonZeroStart;
1462
736
  exports.isNumber = isNumber;
1463
737
  exports.isNumberAtLeastN = isNumberAtLeastN;
1464
738
  exports.isNumberN = isNumberN;
1465
739
  exports.isNumberNM = isNumberNM;
1466
- exports.isServer = isServer;
1467
740
  exports.isTWMobile = isTWMobile;
1468
741
  exports.isTWPhone = isTWPhone;
1469
742
  exports.isTimeString = isTimeString;
1470
743
  exports.isValidPassword = isValidPassword;
1471
744
  exports.maskString = maskString;
1472
- exports.mergeConfig = mergeConfig;
1473
745
  exports.mergeRefs = mergeRefs;
1474
- exports.objectToSearchParams = objectToSearchParams;
1475
- exports.omit = omit;
1476
- exports.omitByValue = omitByValue;
1477
- exports.parseFileInfoFromFilename = parseFileInfoFromFilename;
1478
- exports.parseFilenameFromDisposition = parseFilenameFromDisposition;
1479
746
  exports.pascalCase2CamelCase = pascalCase2CamelCase;
1480
747
  exports.pascalCase2SnakeCase = pascalCase2SnakeCase;
1481
748
  exports.pascalString2CamelString = pascalString2CamelString;
1482
749
  exports.pascalString2SnakeString = pascalString2SnakeString;
1483
- exports.pick = pick;
1484
- exports.pickByValue = pickByValue;
1485
- exports.renameKey = renameKey;
1486
750
  exports.rocEraToAd = rocEraToAd;
1487
- exports.searchParamsToObject = searchParamsToObject;
1488
751
  exports.snakeCase2CamelCase = snakeCase2CamelCase;
1489
752
  exports.snakeCase2PascalCase = snakeCase2PascalCase;
1490
753
  exports.snakeString2CamelString = snakeString2CamelString;
1491
754
  exports.snakeString2PascalString = snakeString2PascalString;
1492
- exports.throttle = throttle;
1493
755
  exports.useValue = useValue;
1494
756
  exports.validTaxId = validTaxId;
1495
757
  exports.validateDateString = validateDateString;
1496
- exports.validateFileType = validateFileType;
1497
- exports.wait = wait;