@gateweb/react-utils 1.12.2 → 1.14.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/README.md +134 -46
- package/dist/cjs/index.d.ts +119 -7
- package/dist/cjs/index.js +204 -35
- package/dist/cjs/types.d.ts +14 -1
- package/dist/cjs/{webStorage-client-BGQKUfrO.js → webStorage-client-DHr9PcPl.js} +1 -1
- package/dist/es/index.d.mts +119 -7
- package/dist/es/index.mjs +200 -38
- package/dist/es/{queryStore-client-vG-bXFYm.mjs → queryStore-client-CFQTVwrg.mjs} +1 -1
- package/dist/es/types.d.mts +14 -1
- package/dist/es/{webStorage-client-Pd-loNCg.mjs → webStorage-client-W1DItzhS.mjs} +1 -1
- package/package.json +27 -22
package/dist/cjs/index.js
CHANGED
|
@@ -5,7 +5,7 @@ var queryStoreClient = require('./queryStore-client-q_SLGgYH.js');
|
|
|
5
5
|
var React = require('react');
|
|
6
6
|
var useCountdownClient = require('./useCountdown-client-uiqhgllY.js');
|
|
7
7
|
var downloadClient = require('./download-client-DKxkL92w.js');
|
|
8
|
-
var webStorageClient = require('./webStorage-client-
|
|
8
|
+
var webStorageClient = require('./webStorage-client-DHr9PcPl.js');
|
|
9
9
|
|
|
10
10
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
11
|
|
|
@@ -238,11 +238,31 @@ function createEnumLikeObject(obj, name, scene) {
|
|
|
238
238
|
}, time);
|
|
239
239
|
});
|
|
240
240
|
|
|
241
|
+
const MimeTypeMap = {
|
|
242
|
+
jpeg: 'image/jpeg',
|
|
243
|
+
jpg: 'image/jpeg',
|
|
244
|
+
gif: 'image/gif',
|
|
245
|
+
png: 'image/png',
|
|
246
|
+
pdf: 'application/pdf',
|
|
247
|
+
zip: 'application/zip',
|
|
248
|
+
csv: 'text/csv',
|
|
249
|
+
ppt: 'application/vnd.ms-powerpoint',
|
|
250
|
+
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
251
|
+
xls: 'application/vnd.ms-excel',
|
|
252
|
+
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
253
|
+
doc: 'application/msword',
|
|
254
|
+
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
255
|
+
txt: 'text/plain'
|
|
256
|
+
};
|
|
257
|
+
const OtherMimeType = 'application/octet-stream';
|
|
258
|
+
|
|
241
259
|
/**
|
|
242
|
-
*
|
|
260
|
+
* 檢查檔案是否為合法的檔案類型
|
|
261
|
+
*
|
|
262
|
+
* `accepts` 可同時接受副檔名以及 MIME 類型
|
|
243
263
|
*
|
|
244
264
|
* @param file 檔案
|
|
245
|
-
* @param accepts
|
|
265
|
+
* @param accepts 允許的類型
|
|
246
266
|
*
|
|
247
267
|
* @example
|
|
248
268
|
*
|
|
@@ -250,21 +270,24 @@ function createEnumLikeObject(obj, name, scene) {
|
|
|
250
270
|
* validateFileType({ type: 'image/png' }, ['image/png', 'image/jpeg']) // true
|
|
251
271
|
* validateFileType({ type: 'image/png' }, ['image/jpeg']) // false
|
|
252
272
|
* validateFileType({ type: 'image/png' }, ['image/*']) // true
|
|
273
|
+
* validateFileType({ name: '圖片.png', type: 'image/png' }, ['.png']) // true
|
|
253
274
|
* ```
|
|
254
275
|
*/ const validateFileType = (file, accepts)=>{
|
|
255
276
|
if (accepts.length === 0) return true;
|
|
256
277
|
// 獲取文件的MIME類型
|
|
257
278
|
const fileMimeType = file.type;
|
|
279
|
+
// 提取副檔名(含 .,且轉小寫)
|
|
280
|
+
const fileExt = file.name.includes('.') ? `.${file.name.split('.').pop().toLowerCase()}` : '';
|
|
258
281
|
return accepts.some((accept)=>{
|
|
282
|
+
if (accept.startsWith('.')) {
|
|
283
|
+
// 以副檔名檢查,忽略大小寫
|
|
284
|
+
return fileExt === accept.toLowerCase();
|
|
285
|
+
}
|
|
259
286
|
if (accept === fileMimeType) {
|
|
260
287
|
return true;
|
|
261
288
|
}
|
|
262
289
|
if (accept.endsWith('/*')) {
|
|
263
|
-
|
|
264
|
-
const fileCategory = fileMimeType.split('/')[0];
|
|
265
|
-
if (acceptedCategory === fileCategory) {
|
|
266
|
-
return true;
|
|
267
|
-
}
|
|
290
|
+
return accept.split('/')[0] === fileMimeType.split('/')[0];
|
|
268
291
|
}
|
|
269
292
|
return false;
|
|
270
293
|
});
|
|
@@ -291,32 +314,70 @@ function createEnumLikeObject(obj, name, scene) {
|
|
|
291
314
|
* getMimeType('xlsx') // 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
292
315
|
* getMimeType('doc') // 'application/msword'
|
|
293
316
|
* getMimeType('docx') // 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
|
317
|
+
*
|
|
318
|
+
* @deprecated use `parseFileInfoFromFilename` instead
|
|
294
319
|
*/ const getMimeType = (fileName)=>{
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
320
|
+
const ext = (fileName.split('.').pop() || '').toLowerCase();
|
|
321
|
+
return MimeTypeMap[ext] ?? OtherMimeType;
|
|
322
|
+
};
|
|
323
|
+
/**
|
|
324
|
+
* 用來解析後端在 response header content-disposition 的內容
|
|
325
|
+
*
|
|
326
|
+
* 一般來說格式會有以下兩種
|
|
327
|
+
*
|
|
328
|
+
* - Content-Disposition: attachment; filename="file name.jpg"
|
|
329
|
+
* - Content-Disposition: attachment; filename*=UTF-8''file%20name2.jpg
|
|
330
|
+
*
|
|
331
|
+
* 如果格式正確就會取得檔案名稱(包含副檔名),優先取 filename* 內的內容
|
|
332
|
+
*
|
|
333
|
+
* @param disposition Content-Disposition
|
|
334
|
+
*
|
|
335
|
+
* @example
|
|
336
|
+
*
|
|
337
|
+
* parseFilenameFromDisposition('attachment; filename="file name1.jpg') // file name.jpg
|
|
338
|
+
* parseFilenameFromDisposition('attachment; filename*=UTF-8''file%20name2.jpg') // file name2.jpg
|
|
339
|
+
* parseFilenameFromDisposition('attachment; filename="file name.jpg; filename*=UTF-8''file%20name2.jpg') // file name2.jpg
|
|
340
|
+
*/ const parseFilenameFromDisposition = (disposition)=>{
|
|
341
|
+
// 1. 先找 filename*
|
|
342
|
+
const filenameStarMatch = disposition.match(/filename\*\s*=\s*(?:UTF-8'')?([^;]+)/i);
|
|
343
|
+
if (filenameStarMatch && filenameStarMatch[1]) {
|
|
344
|
+
// 依 RFC 5987 格式(UTF-8''URL-ENCODED),要先 decode
|
|
345
|
+
try {
|
|
346
|
+
return decodeURIComponent(filenameStarMatch[1].replace(/(^['"]|['"]$)/g, ''));
|
|
347
|
+
} catch {
|
|
348
|
+
// fallback,如果 decode 失敗,直接傳回原值
|
|
349
|
+
return filenameStarMatch[1];
|
|
350
|
+
}
|
|
319
351
|
}
|
|
352
|
+
// 2. 沒有 filename*,再找 filename
|
|
353
|
+
const filenameMatch = disposition.match(/filename\s*=\s*("?)([^";]+)\1/i);
|
|
354
|
+
if (filenameMatch && filenameMatch[2]) {
|
|
355
|
+
return filenameMatch[2];
|
|
356
|
+
}
|
|
357
|
+
// 3. 都沒有則 undefined
|
|
358
|
+
return undefined;
|
|
359
|
+
};
|
|
360
|
+
/**
|
|
361
|
+
* 解析 `filename` 回傳檔名、副檔名、MIME type
|
|
362
|
+
*
|
|
363
|
+
* @param filename 檔案名稱
|
|
364
|
+
*
|
|
365
|
+
* @example
|
|
366
|
+
*
|
|
367
|
+
* parseFileInfoFromFilename('image.jpg') // ['image', 'jpg', 'image/jpeg']
|
|
368
|
+
* parseFileInfoFromFilename('image') // ['image', '', 'application/octet-stream']
|
|
369
|
+
*/ const parseFileInfoFromFilename = (filename)=>{
|
|
370
|
+
const lastDot = filename.lastIndexOf('.');
|
|
371
|
+
if (lastDot === -1) return [
|
|
372
|
+
filename,
|
|
373
|
+
'',
|
|
374
|
+
OtherMimeType
|
|
375
|
+
]; // 沒有副檔名
|
|
376
|
+
return [
|
|
377
|
+
filename.slice(0, lastDot),
|
|
378
|
+
filename.slice(lastDot + 1),
|
|
379
|
+
MimeTypeMap[filename.slice(lastDot + 1)] ?? OtherMimeType
|
|
380
|
+
];
|
|
320
381
|
};
|
|
321
382
|
|
|
322
383
|
// const isProduction: boolean = process.env.NODE_ENV === 'production';
|
|
@@ -446,6 +507,85 @@ const isObject = (value)=>value !== null && typeof value === 'object';
|
|
|
446
507
|
}, {
|
|
447
508
|
...target
|
|
448
509
|
});
|
|
510
|
+
/**
|
|
511
|
+
* A utility function to deeply clone an object.
|
|
512
|
+
* @param obj - The object to clone.
|
|
513
|
+
*
|
|
514
|
+
* @example
|
|
515
|
+
*
|
|
516
|
+
* const original = { a: 1, b: { c: 2 } };
|
|
517
|
+
* const cloned = deepClone(original);
|
|
518
|
+
* console.log(cloned); // { a: 1, b: { c: 2 } }
|
|
519
|
+
*
|
|
520
|
+
*/ const deepClone = (obj)=>{
|
|
521
|
+
if (obj === null || typeof obj !== 'object') {
|
|
522
|
+
return obj;
|
|
523
|
+
}
|
|
524
|
+
if (obj instanceof Date) {
|
|
525
|
+
return new Date(obj.getTime());
|
|
526
|
+
}
|
|
527
|
+
if (Array.isArray(obj)) {
|
|
528
|
+
return obj.map((item)=>deepClone(item));
|
|
529
|
+
}
|
|
530
|
+
const cloned = Object.keys(obj).reduce((acc, key)=>{
|
|
531
|
+
acc[key] = deepClone(obj[key]);
|
|
532
|
+
return acc;
|
|
533
|
+
}, {});
|
|
534
|
+
return cloned;
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* 將嵌套物件的所有屬性設為指定的布林值
|
|
539
|
+
* @param obj - 目標物件
|
|
540
|
+
* @param boolValue - 布林值
|
|
541
|
+
*
|
|
542
|
+
* @example
|
|
543
|
+
* const obj = { a: { b: 1, c: 2 }, d: 3 };
|
|
544
|
+
* const result = setBooleanToNestedObject(obj, true);
|
|
545
|
+
* console.log(result); // { a: { b: true, c: true }, d: true }
|
|
546
|
+
*/ const setBooleanToNestedObject = (obj, boolValue)=>Object.entries(obj).reduce((acc, [key, value])=>{
|
|
547
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
548
|
+
// 如果是嵌套物件,遞歸處理
|
|
549
|
+
acc[key] = setBooleanToNestedObject(value, boolValue);
|
|
550
|
+
} else {
|
|
551
|
+
// 如果是基本類型,設為布林值
|
|
552
|
+
acc[key] = boolValue;
|
|
553
|
+
}
|
|
554
|
+
return acc;
|
|
555
|
+
}, {});
|
|
556
|
+
/**
|
|
557
|
+
* 深度合併配置物件的通用工具
|
|
558
|
+
*
|
|
559
|
+
* @param defaultConfig - 預設配置
|
|
560
|
+
* @param customConfig - 自訂配置
|
|
561
|
+
*
|
|
562
|
+
* @example
|
|
563
|
+
* const defaultConfig = { a: true, b: { b1: true, b2: true, b3: false }, c: true, d: false };
|
|
564
|
+
* mergeConfig(defaultConfig, { b: { b1: false }, d: true }); // { a: true, b: { b1: false, b2: true, b3: false }, c: true, d: true }
|
|
565
|
+
* mergeConfig(defaultConfig, { b: false }); // { a: true, b: { b1: false, b2: false, b3: false }, c: true, d: false }
|
|
566
|
+
* mergeConfig(defaultConfig, { b: true }); // { a: true, b: { b1: true, b2: true, b3: true }, c: true, d: false }
|
|
567
|
+
*/ const mergeConfig = (defaultConfig, customConfig = {})=>{
|
|
568
|
+
// 深拷貝預設配置以避免修改原始物件
|
|
569
|
+
const result = deepClone(defaultConfig);
|
|
570
|
+
return Object.entries(customConfig).reduce((acc, [key, sourceValue])=>{
|
|
571
|
+
const targetValue = acc[key];
|
|
572
|
+
// 如果來源值為 null 或 undefined,跳過
|
|
573
|
+
if (sourceValue === null || sourceValue === undefined) {
|
|
574
|
+
return acc;
|
|
575
|
+
}
|
|
576
|
+
// 如果目標物件中對應的值是物件,且來源值是布林值
|
|
577
|
+
// 這是特殊情況:用布林值覆蓋整個嵌套物件
|
|
578
|
+
if (typeof targetValue === 'object' && targetValue !== null && !Array.isArray(targetValue) && typeof sourceValue === 'boolean') {
|
|
579
|
+
// 將嵌套物件的所有屬性設為該布林值
|
|
580
|
+
acc[key] = setBooleanToNestedObject(targetValue, sourceValue);
|
|
581
|
+
} else if (typeof targetValue === 'object' && targetValue !== null && !Array.isArray(targetValue) && typeof sourceValue === 'object' && sourceValue !== null && !Array.isArray(sourceValue)) {
|
|
582
|
+
acc[key] = mergeConfig(targetValue, sourceValue);
|
|
583
|
+
} else {
|
|
584
|
+
acc[key] = sourceValue;
|
|
585
|
+
}
|
|
586
|
+
return acc;
|
|
587
|
+
}, result);
|
|
588
|
+
};
|
|
449
589
|
|
|
450
590
|
/**
|
|
451
591
|
* debounce function
|
|
@@ -855,8 +995,7 @@ const FILE_SIZE_UNITS = [
|
|
|
855
995
|
*
|
|
856
996
|
* isEmail().test('123') // false
|
|
857
997
|
* isEmail().test('123@gmail.com') // true
|
|
858
|
-
*/ const isEmail = ()
|
|
859
|
-
/^(?!\.)(?!.*\.\.)([A-Z0-9_+-\.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9\-]*\.)+[A-Z]{2,}$/i;
|
|
998
|
+
*/ const isEmail = ()=>/^(?!\.)(?!.*\.\.)([A-Z0-9_+-\\.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9\\-]*\.)+[A-Z]{2,}$/i;
|
|
860
999
|
/**
|
|
861
1000
|
* 日期字串
|
|
862
1001
|
*
|
|
@@ -1055,6 +1194,29 @@ const FILE_SIZE_UNITS = [
|
|
|
1055
1194
|
// 再用 dayjs 驗證是否為有效日期
|
|
1056
1195
|
return dayjs__default.default(dateString, format, true).isValid();
|
|
1057
1196
|
};
|
|
1197
|
+
/**
|
|
1198
|
+
* 判斷 `value` 是否為 `null` 或 `undefined`
|
|
1199
|
+
*
|
|
1200
|
+
* @param value 值
|
|
1201
|
+
*
|
|
1202
|
+
* @example
|
|
1203
|
+
* isNil(null); // true
|
|
1204
|
+
* isNil(undefined); // true
|
|
1205
|
+
* isNil(0); // false
|
|
1206
|
+
* isNil(''); // false
|
|
1207
|
+
* isNil(false); // false
|
|
1208
|
+
* isNil({}); // false
|
|
1209
|
+
*
|
|
1210
|
+
* // TypeScript 型別縮窄應用
|
|
1211
|
+
* function example(input?: string | null) {
|
|
1212
|
+
* if (isNil(input)) {
|
|
1213
|
+
* // input 型別會自動縮窄成 null | undefined
|
|
1214
|
+
* return 'No value';
|
|
1215
|
+
* }
|
|
1216
|
+
* // input 型別自動為 string
|
|
1217
|
+
* return input.toUpperCase();
|
|
1218
|
+
* }
|
|
1219
|
+
*/ const isNil = (value)=>value === null || value === undefined;
|
|
1058
1220
|
|
|
1059
1221
|
/**
|
|
1060
1222
|
* Creates a strongly-typed React Context and Provider pair for data sharing.
|
|
@@ -1064,7 +1226,7 @@ const FILE_SIZE_UNITS = [
|
|
|
1064
1226
|
* and a Provider component for supplying context values.
|
|
1065
1227
|
*
|
|
1066
1228
|
* @template T - The value type for the context.
|
|
1067
|
-
* @returns {
|
|
1229
|
+
* @returns {object} An object containing:
|
|
1068
1230
|
* - useDataContext: A custom hook to access the context value. Throws an error if used outside the provider.
|
|
1069
1231
|
* - DataProvider: A Provider component to wrap your component tree and supply the context value.
|
|
1070
1232
|
*
|
|
@@ -1238,6 +1400,8 @@ exports.downloadFile = downloadClient.downloadFile;
|
|
|
1238
1400
|
exports.getLocalStorage = webStorageClient.getLocalStorage;
|
|
1239
1401
|
exports.setLocalStorage = webStorageClient.setLocalStorage;
|
|
1240
1402
|
exports.ByteSize = ByteSize;
|
|
1403
|
+
exports.MimeTypeMap = MimeTypeMap;
|
|
1404
|
+
exports.OtherMimeType = OtherMimeType;
|
|
1241
1405
|
exports.adToRocEra = adToRocEra;
|
|
1242
1406
|
exports.camelCase2PascalCase = camelCase2PascalCase;
|
|
1243
1407
|
exports.camelCase2SnakeCase = camelCase2SnakeCase;
|
|
@@ -1247,6 +1411,7 @@ exports.convertBytes = convertBytes;
|
|
|
1247
1411
|
exports.createDataContext = createDataContext;
|
|
1248
1412
|
exports.createEnumLikeObject = createEnumLikeObject;
|
|
1249
1413
|
exports.debounce = debounce;
|
|
1414
|
+
exports.deepClone = deepClone;
|
|
1250
1415
|
exports.deepMerge = deepMerge;
|
|
1251
1416
|
exports.extractEnumLikeObject = extractEnumLikeObject;
|
|
1252
1417
|
exports.fakeApi = fakeApi;
|
|
@@ -1263,6 +1428,7 @@ exports.isDateTimeString = isDateTimeString;
|
|
|
1263
1428
|
exports.isEmail = isEmail;
|
|
1264
1429
|
exports.isEnglish = isEnglish;
|
|
1265
1430
|
exports.isEqual = isEqual;
|
|
1431
|
+
exports.isNil = isNil;
|
|
1266
1432
|
exports.isNonZeroStart = isNonZeroStart;
|
|
1267
1433
|
exports.isNumber = isNumber;
|
|
1268
1434
|
exports.isNumberAtLeastN = isNumberAtLeastN;
|
|
@@ -1274,10 +1440,13 @@ exports.isTWPhone = isTWPhone;
|
|
|
1274
1440
|
exports.isTimeString = isTimeString;
|
|
1275
1441
|
exports.isValidPassword = isValidPassword;
|
|
1276
1442
|
exports.maskString = maskString;
|
|
1443
|
+
exports.mergeConfig = mergeConfig;
|
|
1277
1444
|
exports.mergeRefs = mergeRefs;
|
|
1278
1445
|
exports.objectToSearchParams = objectToSearchParams;
|
|
1279
1446
|
exports.omit = omit;
|
|
1280
1447
|
exports.omitByValue = omitByValue;
|
|
1448
|
+
exports.parseFileInfoFromFilename = parseFileInfoFromFilename;
|
|
1449
|
+
exports.parseFilenameFromDisposition = parseFilenameFromDisposition;
|
|
1281
1450
|
exports.pascalCase2CamelCase = pascalCase2CamelCase;
|
|
1282
1451
|
exports.pascalCase2SnakeCase = pascalCase2SnakeCase;
|
|
1283
1452
|
exports.pascalString2CamelString = pascalString2CamelString;
|
package/dist/cjs/types.d.ts
CHANGED
|
@@ -187,5 +187,18 @@ type TChangeKeyType<T, K extends keyof T, V> = Omit<T, K> & {
|
|
|
187
187
|
type MapToType<T extends Record<any, any>, A = any> = {
|
|
188
188
|
[K in keyof T]: A;
|
|
189
189
|
};
|
|
190
|
+
/**
|
|
191
|
+
* 覆寫 T 指定屬性的型別,保留其他屬性。
|
|
192
|
+
*
|
|
193
|
+
* @template T 原始型別
|
|
194
|
+
* @template R 欲覆寫的 key-value 結構
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* type User = { id: number; name: string }
|
|
198
|
+
* type FormUser = OverrideProps<User, { id: string }> // { id: string; name: string }
|
|
199
|
+
*/
|
|
200
|
+
type OverrideProps<T, R extends {
|
|
201
|
+
[K in keyof R]: any;
|
|
202
|
+
}> = Omit<T, keyof R> & R;
|
|
190
203
|
|
|
191
|
-
export type { AtLeastOne, DeepPartial, Entries, MapToString, MapToType, OnlyOne, PopArgs, PushArgs, ShiftArgs, TChangeKeyType, TExtractValueType, UnshiftArgs };
|
|
204
|
+
export type { AtLeastOne, DeepPartial, Entries, MapToString, MapToType, OnlyOne, OverrideProps, PopArgs, PushArgs, ShiftArgs, TChangeKeyType, TExtractValueType, UnshiftArgs };
|
package/dist/es/index.d.mts
CHANGED
|
@@ -404,7 +404,9 @@ declare const extractEnumLikeObject: <T extends Record<string, { [P in K]: any;
|
|
|
404
404
|
*
|
|
405
405
|
* @param obj 要處理的物件
|
|
406
406
|
* @param name 生成的 enum 名稱
|
|
407
|
-
*
|
|
407
|
+
*
|
|
408
|
+
* @remarks
|
|
409
|
+
* 若需多場景 label,請傳入第三參數 scene,詳細用法請見範例。
|
|
408
410
|
*
|
|
409
411
|
* @example
|
|
410
412
|
*
|
|
@@ -419,7 +421,7 @@ declare const extractEnumLikeObject: <T extends Record<string, { [P in K]: any;
|
|
|
419
421
|
*
|
|
420
422
|
* // scenes 版本
|
|
421
423
|
*
|
|
422
|
-
* Status = { Enabled: { value: 'enabled', scenes: { webA: { label: '啟用' }, webB: { label: '激活' } } }, Disabled: { value: 'disabled', scenes: { webA: { label: '停用' }, webB: { label: '停止' }}}}
|
|
424
|
+
* const Status = { Enabled: { value: 'enabled', scenes: { webA: { label: '啟用' }, webB: { label: '激活' } } }, Disabled: { value: 'disabled', scenes: { webA: { label: '停用' }, webB: { label: '停止' }}}}
|
|
423
425
|
* const { EnumStatusA, StatusAList, getStatusALabel } = createEnumLikeObject(Status, 'StatusA', 'webA');
|
|
424
426
|
* console.log(EnumStatusA); // { Enabled: 'enabled', Disabled: 'disabled' }
|
|
425
427
|
* console.log(StatusAList); // [ { key: 'Enabled', value: 'enabled', label: '啟用' }, { key: 'Disabled', value: 'disabled', label: '停用' }]
|
|
@@ -431,6 +433,7 @@ declare const extractEnumLikeObject: <T extends Record<string, { [P in K]: any;
|
|
|
431
433
|
* console.log(getStatusBLabel('enabled')); // '激活'
|
|
432
434
|
*
|
|
433
435
|
* ```
|
|
436
|
+
*
|
|
434
437
|
*/
|
|
435
438
|
declare function createEnumLikeObject<T extends Record<string, {
|
|
436
439
|
value: any;
|
|
@@ -474,11 +477,33 @@ declare const fakeApi: <T>(returnValue: T, result?: boolean, time?: number) => P
|
|
|
474
477
|
message: string;
|
|
475
478
|
}>;
|
|
476
479
|
|
|
480
|
+
declare const MimeTypeMap: {
|
|
481
|
+
readonly jpeg: "image/jpeg";
|
|
482
|
+
readonly jpg: "image/jpeg";
|
|
483
|
+
readonly gif: "image/gif";
|
|
484
|
+
readonly png: "image/png";
|
|
485
|
+
readonly pdf: "application/pdf";
|
|
486
|
+
readonly zip: "application/zip";
|
|
487
|
+
readonly csv: "text/csv";
|
|
488
|
+
readonly ppt: "application/vnd.ms-powerpoint";
|
|
489
|
+
readonly pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation";
|
|
490
|
+
readonly xls: "application/vnd.ms-excel";
|
|
491
|
+
readonly xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
|
492
|
+
readonly doc: "application/msword";
|
|
493
|
+
readonly docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
|
494
|
+
readonly txt: "text/plain";
|
|
495
|
+
};
|
|
496
|
+
declare const OtherMimeType = "application/octet-stream";
|
|
497
|
+
type MimeTypeExtension = keyof typeof MimeTypeMap;
|
|
498
|
+
type MimeTypeValue = (typeof MimeTypeMap)[MimeTypeExtension] | typeof OtherMimeType;
|
|
499
|
+
|
|
477
500
|
/**
|
|
478
|
-
*
|
|
501
|
+
* 檢查檔案是否為合法的檔案類型
|
|
502
|
+
*
|
|
503
|
+
* `accepts` 可同時接受副檔名以及 MIME 類型
|
|
479
504
|
*
|
|
480
505
|
* @param file 檔案
|
|
481
|
-
* @param accepts
|
|
506
|
+
* @param accepts 允許的類型
|
|
482
507
|
*
|
|
483
508
|
* @example
|
|
484
509
|
*
|
|
@@ -486,6 +511,7 @@ declare const fakeApi: <T>(returnValue: T, result?: boolean, time?: number) => P
|
|
|
486
511
|
* validateFileType({ type: 'image/png' }, ['image/png', 'image/jpeg']) // true
|
|
487
512
|
* validateFileType({ type: 'image/png' }, ['image/jpeg']) // false
|
|
488
513
|
* validateFileType({ type: 'image/png' }, ['image/*']) // true
|
|
514
|
+
* validateFileType({ name: '圖片.png', type: 'image/png' }, ['.png']) // true
|
|
489
515
|
* ```
|
|
490
516
|
*/
|
|
491
517
|
declare const validateFileType: (file: File, accepts: string[]) => boolean;
|
|
@@ -511,8 +537,40 @@ declare const validateFileType: (file: File, accepts: string[]) => boolean;
|
|
|
511
537
|
* getMimeType('xlsx') // 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
512
538
|
* getMimeType('doc') // 'application/msword'
|
|
513
539
|
* getMimeType('docx') // 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
|
540
|
+
*
|
|
541
|
+
* @deprecated use `parseFileInfoFromFilename` instead
|
|
542
|
+
*/
|
|
543
|
+
declare const getMimeType: (fileName: string) => "image/jpeg" | "image/gif" | "image/png" | "application/pdf" | "application/zip" | "text/csv" | "application/vnd.ms-powerpoint" | "application/vnd.openxmlformats-officedocument.presentationml.presentation" | "application/vnd.ms-excel" | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" | "application/msword" | "application/vnd.openxmlformats-officedocument.wordprocessingml.document" | "text/plain";
|
|
544
|
+
/**
|
|
545
|
+
* 用來解析後端在 response header content-disposition 的內容
|
|
546
|
+
*
|
|
547
|
+
* 一般來說格式會有以下兩種
|
|
548
|
+
*
|
|
549
|
+
* - Content-Disposition: attachment; filename="file name.jpg"
|
|
550
|
+
* - Content-Disposition: attachment; filename*=UTF-8''file%20name2.jpg
|
|
551
|
+
*
|
|
552
|
+
* 如果格式正確就會取得檔案名稱(包含副檔名),優先取 filename* 內的內容
|
|
553
|
+
*
|
|
554
|
+
* @param disposition Content-Disposition
|
|
555
|
+
*
|
|
556
|
+
* @example
|
|
557
|
+
*
|
|
558
|
+
* parseFilenameFromDisposition('attachment; filename="file name1.jpg') // file name.jpg
|
|
559
|
+
* parseFilenameFromDisposition('attachment; filename*=UTF-8''file%20name2.jpg') // file name2.jpg
|
|
560
|
+
* parseFilenameFromDisposition('attachment; filename="file name.jpg; filename*=UTF-8''file%20name2.jpg') // file name2.jpg
|
|
561
|
+
*/
|
|
562
|
+
declare const parseFilenameFromDisposition: (disposition: string) => string | undefined;
|
|
563
|
+
/**
|
|
564
|
+
* 解析 `filename` 回傳檔名、副檔名、MIME type
|
|
565
|
+
*
|
|
566
|
+
* @param filename 檔案名稱
|
|
567
|
+
*
|
|
568
|
+
* @example
|
|
569
|
+
*
|
|
570
|
+
* parseFileInfoFromFilename('image.jpg') // ['image', 'jpg', 'image/jpeg']
|
|
571
|
+
* parseFileInfoFromFilename('image') // ['image', '', 'application/octet-stream']
|
|
514
572
|
*/
|
|
515
|
-
declare const
|
|
573
|
+
declare const parseFileInfoFromFilename: (filename: string) => [string, string, MimeTypeValue];
|
|
516
574
|
|
|
517
575
|
declare function invariant(condition: any, message?: string | (() => string)): asserts condition;
|
|
518
576
|
|
|
@@ -521,6 +579,23 @@ declare function invariant(condition: any, message?: string | (() => string)): a
|
|
|
521
579
|
*/
|
|
522
580
|
declare const isServer: () => boolean;
|
|
523
581
|
|
|
582
|
+
type DeepPartialWithBooleanOverride<T> = {
|
|
583
|
+
[K in keyof T]?: T[K] extends object ? T[K] extends boolean ? T[K] : DeepPartialWithBooleanOverride<T[K]> | boolean : T[K];
|
|
584
|
+
};
|
|
585
|
+
/**
|
|
586
|
+
* 深度合併配置物件的通用工具
|
|
587
|
+
*
|
|
588
|
+
* @param defaultConfig - 預設配置
|
|
589
|
+
* @param customConfig - 自訂配置
|
|
590
|
+
*
|
|
591
|
+
* @example
|
|
592
|
+
* const defaultConfig = { a: true, b: { b1: true, b2: true, b3: false }, c: true, d: false };
|
|
593
|
+
* mergeConfig(defaultConfig, { b: { b1: false }, d: true }); // { a: true, b: { b1: false, b2: true, b3: false }, c: true, d: true }
|
|
594
|
+
* mergeConfig(defaultConfig, { b: false }); // { a: true, b: { b1: false, b2: false, b3: false }, c: true, d: false }
|
|
595
|
+
* mergeConfig(defaultConfig, { b: true }); // { a: true, b: { b1: true, b2: true, b3: true }, c: true, d: false }
|
|
596
|
+
*/
|
|
597
|
+
declare const mergeConfig: <T extends Record<string, any>>(defaultConfig: T, customConfig?: DeepPartialWithBooleanOverride<T>) => T;
|
|
598
|
+
|
|
524
599
|
/**
|
|
525
600
|
* 將物件中的某些 key 排除
|
|
526
601
|
*
|
|
@@ -616,6 +691,18 @@ type RequiredBy<T, K extends keyof T, RemoveUndefined extends boolean = false> =
|
|
|
616
691
|
type PartialBy<T, K extends keyof T, RemoveUndefined extends boolean = false> = Omit<T, K> & {
|
|
617
692
|
[P in K]?: RemoveUndefined extends true ? Exclude<T[P], undefined> : T[P];
|
|
618
693
|
};
|
|
694
|
+
/**
|
|
695
|
+
* A utility function to deeply clone an object.
|
|
696
|
+
* @param obj - The object to clone.
|
|
697
|
+
*
|
|
698
|
+
* @example
|
|
699
|
+
*
|
|
700
|
+
* const original = { a: 1, b: { c: 2 } };
|
|
701
|
+
* const cloned = deepClone(original);
|
|
702
|
+
* console.log(cloned); // { a: 1, b: { c: 2 } }
|
|
703
|
+
*
|
|
704
|
+
*/
|
|
705
|
+
declare const deepClone: <T>(obj: T) => T;
|
|
619
706
|
|
|
620
707
|
/**
|
|
621
708
|
* debounce function
|
|
@@ -928,6 +1015,30 @@ declare const validTaxId: (taxId: string) => boolean;
|
|
|
928
1015
|
* validateDateString('20210201', 'YYYYMM') // false
|
|
929
1016
|
*/
|
|
930
1017
|
declare const validateDateString: (dateString: string, format: string) => boolean;
|
|
1018
|
+
/**
|
|
1019
|
+
* 判斷 `value` 是否為 `null` 或 `undefined`
|
|
1020
|
+
*
|
|
1021
|
+
* @param value 值
|
|
1022
|
+
*
|
|
1023
|
+
* @example
|
|
1024
|
+
* isNil(null); // true
|
|
1025
|
+
* isNil(undefined); // true
|
|
1026
|
+
* isNil(0); // false
|
|
1027
|
+
* isNil(''); // false
|
|
1028
|
+
* isNil(false); // false
|
|
1029
|
+
* isNil({}); // false
|
|
1030
|
+
*
|
|
1031
|
+
* // TypeScript 型別縮窄應用
|
|
1032
|
+
* function example(input?: string | null) {
|
|
1033
|
+
* if (isNil(input)) {
|
|
1034
|
+
* // input 型別會自動縮窄成 null | undefined
|
|
1035
|
+
* return 'No value';
|
|
1036
|
+
* }
|
|
1037
|
+
* // input 型別自動為 string
|
|
1038
|
+
* return input.toUpperCase();
|
|
1039
|
+
* }
|
|
1040
|
+
*/
|
|
1041
|
+
declare const isNil: (value: unknown) => value is null | undefined;
|
|
931
1042
|
|
|
932
1043
|
type TQueryProps<Q> = {
|
|
933
1044
|
/**
|
|
@@ -987,7 +1098,7 @@ declare const useQueryContext: <Q>() => <T>(selector: (state: TQueryState<Q>) =>
|
|
|
987
1098
|
* and a Provider component for supplying context values.
|
|
988
1099
|
*
|
|
989
1100
|
* @template T - The value type for the context.
|
|
990
|
-
* @returns {
|
|
1101
|
+
* @returns {object} An object containing:
|
|
991
1102
|
* - useDataContext: A custom hook to access the context value. Throws an error if used outside the provider.
|
|
992
1103
|
* - DataProvider: A Provider component to wrap your component tree and supply the context value.
|
|
993
1104
|
*
|
|
@@ -1149,4 +1260,5 @@ declare const getLocalStorage: <T>(key: string, deCode?: boolean) => T | undefin
|
|
|
1149
1260
|
*/
|
|
1150
1261
|
declare const setLocalStorage: (key: string, value: Record<string, any>, enCode?: boolean) => void;
|
|
1151
1262
|
|
|
1152
|
-
export { ByteSize,
|
|
1263
|
+
export { ByteSize, MimeTypeMap, OtherMimeType, QueryProvider, adToRocEra, camelCase2PascalCase, camelCase2SnakeCase, camelString2PascalString, camelString2SnakeString, convertBytes, createDataContext, createEnumLikeObject, debounce, deepClone, deepMerge, downloadFile, extractEnumLikeObject, fakeApi, formatAmount, formatBytes, formatStarMask, generatePeriodArray, getCurrentPeriod, getLocalStorage, getMimeType, invariant, isChinese, isDateString, isDateTimeString, isEmail, isEnglish, isEqual, isNil, isNonZeroStart, isNumber, isNumberAtLeastN, isNumberN, isNumberNM, isServer, isTWMobile, isTWPhone, isTimeString, isValidPassword, maskString, mergeConfig, mergeRefs, objectToSearchParams, omit, omitByValue, parseFileInfoFromFilename, parseFilenameFromDisposition, pascalCase2CamelCase, pascalCase2SnakeCase, pascalString2CamelString, pascalString2SnakeString, pick, pickByValue, rocEraToAd, searchParamsToObject, setLocalStorage, snakeCase2CamelCase, snakeCase2PascalCase, snakeString2CamelString, snakeString2PascalString, throttle, useCountdown, useQueryContext, useValue, validTaxId, validateDateString, validateFileType, wait };
|
|
1264
|
+
export type { MimeTypeExtension, MimeTypeValue, PartialBy, RequiredBy, TCountdownActions };
|