@gateweb/react-utils 0.0.1

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.
@@ -0,0 +1,666 @@
1
+ import dayjs from 'dayjs';
2
+ export { u as useCountdown } from './useCountdown-client-t52WIHfq.mjs';
3
+ export { d as downloadFile } from './download-client-CnaJ0p_f.mjs';
4
+ export { g as getLocalStorage, s as setLocalStorage } from './webStorage-client-Pd-loNCg.mjs';
5
+
6
+ /**
7
+ * convert CamelCase string to PascalCase string
8
+ * @param str the string to convert
9
+ * @example
10
+ * camelString2PascalString('camelCase') // 'CamelCase'
11
+ * camelString2PascalString('camelCaseTest') // 'CamelCaseTest'
12
+ */ const camelString2PascalString = (str)=>`${str[0].toUpperCase()}${str.slice(1)}`;
13
+ /**
14
+ * convert CamelCase string to SnakeCase string
15
+ * @param str the string to convert
16
+ * @example
17
+ * camelString2SnakeString('camelCase') // 'camel_case'
18
+ * camelString2SnakeString('camelCaseTest') // 'camel_case_test'
19
+ */ const camelString2SnakeString = (str)=>str.replace(/[A-Z]/g, (letter)=>`_${letter.toLowerCase()}`);
20
+ /**
21
+ * convert PascalCase string to CamelCase string
22
+ * @param str the string to convert
23
+ * @example
24
+ * pascalString2CamelString('PascalCase') // 'pascalCase'
25
+ * pascalString2CamelString('PascalCaseTest') // 'pascalCaseTest'
26
+ */ const pascalString2CamelString = (str)=>`${str[0].toLowerCase()}${str.slice(1)}`;
27
+ /**
28
+ * convert PascalCase string to SnakeCase string
29
+ * @param str the string to convert
30
+ * @example
31
+ * pascalString2SnakeString('PascalCase') // 'pascal_case'
32
+ * pascalString2SnakeString('PascalCaseTest') // 'pascal_case_test'
33
+ */ const pascalString2SnakeString = (str)=>camelString2SnakeString(pascalString2CamelString(str));
34
+ /**
35
+ * convert SnakeCase string to CamelCase string
36
+ * @param str the string to convert
37
+ * @example
38
+ * snakeString2CamelString('snake_case') // 'snakeCase'
39
+ * snakeString2CamelString('snake_case_test') // 'snakeCaseTest'
40
+ */ const snakeString2CamelString = (str)=>str.replace(/(_[a-z])/g, (group)=>group.toUpperCase().replace('_', ''));
41
+ /**
42
+ * convert SnakeCase string to PascalCase string
43
+ * @param str the string to convert
44
+ * @example
45
+ * snakeString2PascalString('snake_case') // 'SnakeCase'
46
+ * snakeString2PascalString('snake_case_test') // 'SnakeCaseTest'
47
+ */ const snakeString2PascalString = (str)=>camelString2PascalString(snakeString2CamelString(str));
48
+ const transformFun = {
49
+ CamelToPascal: camelString2PascalString,
50
+ CamelToSnake: camelString2SnakeString,
51
+ PascalToCamel: pascalString2CamelString,
52
+ PascalToSnake: pascalString2SnakeString,
53
+ SnakeToCamel: snakeString2CamelString,
54
+ SnakeToPascal: snakeString2PascalString
55
+ };
56
+ const transformObjectKey = (obj, transformFunName)=>{
57
+ if (!obj || typeof obj !== 'object') return obj;
58
+ if (Array.isArray(obj)) return obj.map((item)=>transformObjectKey(item, transformFunName));
59
+ return Object.keys(obj).reduce((acc, key)=>({
60
+ ...acc,
61
+ [transformFun[transformFunName](key)]: transformObjectKey(obj[key], transformFunName)
62
+ }), {});
63
+ };
64
+ /**
65
+ * convert object key from CamelCase to PascalCase
66
+ * @param obj the object to convert
67
+ * @example
68
+ * const obj = {
69
+ * fooBar: 'fooBar',
70
+ * fooBar2: 'fooBar2',
71
+ * fooBar3: {
72
+ * fooBar4: 'fooBar4',
73
+ * fooBar5: 'fooBar5',
74
+ * },
75
+ * };
76
+ * const result = camelCase2PascalCase(obj);
77
+ * console.log(result); // { FooBar: 'fooBar', FooBar2: 'fooBar2', FooBar3: { FooBar4: 'fooBar4', FooBar5: 'fooBar5' } }
78
+ */ const camelCase2PascalCase = (obj)=>transformObjectKey(obj, 'CamelToPascal');
79
+ /**
80
+ * convert object key from CamelCase to SnakeCase
81
+ * @param obj the object to convert
82
+ * @example
83
+ * const obj = {
84
+ * fooBar: 'fooBar',
85
+ * fooBar2: 'fooBar2',
86
+ * fooBar3: {
87
+ * fooBar4: 'fooBar4',
88
+ * fooBar5: 'fooBar5',
89
+ * },
90
+ * };
91
+ * const result = camelCase2SnakeCase(obj);
92
+ * console.log(result); // { foo_bar: 'fooBar', foo_bar2: 'fooBar2', foo_bar3: { foo_bar4: 'fooBar4', foo_bar5: 'fooBar5' } }
93
+ */ const camelCase2SnakeCase = (obj)=>transformObjectKey(obj, 'CamelToSnake');
94
+ /**
95
+ * convert object key from PascalCase to CamelCase
96
+ * @param obj the object to convert
97
+ * @example
98
+ * const obj = {
99
+ * FooBar: 'fooBar',
100
+ * FooBar2: 'fooBar2',
101
+ * FooBar3: {
102
+ * FooBar4: 'fooBar4',
103
+ * FooBar5: 'fooBar5',
104
+ * },
105
+ * };
106
+ * const result = pascalCase2CamelCase(obj);
107
+ * console.log(result); // { fooBar: 'fooBar', fooBar2: 'fooBar2', fooBar3: { fooBar4: 'fooBar4', fooBar5: 'fooBar5' } }
108
+ */ const pascalCase2CamelCase = (obj)=>transformObjectKey(obj, 'PascalToCamel');
109
+ /**
110
+ * convert object key from PascalCase to SnakeCase
111
+ * @param obj the object to convert
112
+ * @example
113
+ * const obj = {
114
+ * FooBar: 'fooBar',
115
+ * FooBar2: 'fooBar2',
116
+ * FooBar3: {
117
+ * FooBar4: 'fooBar4',
118
+ * FooBar5: 'fooBar5',
119
+ * },
120
+ * };
121
+ * const result = pascalCase2SnakeCase(obj);
122
+ * console.log(result); // { foo_bar: 'fooBar', foo_bar2: 'fooBar2', foo_bar3: { foo_bar4: 'fooBar4', foo_bar5: 'fooBar5' } }
123
+ */ const pascalCase2SnakeCase = (obj)=>transformObjectKey(obj, 'PascalToSnake');
124
+ /**
125
+ * convert object key from SnakeCase to CamelCase
126
+ * @param obj the object to convert
127
+ * @example
128
+ * const obj = {
129
+ * foo_bar: 'fooBar',
130
+ * foo_bar2: 'fooBar2',
131
+ * foo_bar3: {
132
+ * foo_bar4: 'fooBar4',
133
+ * foo_bar5: 'fooBar5',
134
+ * },
135
+ * };
136
+ * const result = snakeCase2CamelCase(obj);
137
+ * console.log(result); // { fooBar: 'fooBar', fooBar2: 'fooBar2', fooBar3: { fooBar4: 'fooBar4', fooBar5: 'fooBar5' } }
138
+ */ const snakeCase2CamelCase = (obj)=>transformObjectKey(obj, 'SnakeToCamel');
139
+ /**
140
+ * convert object key from SnakeCase to PascalCase
141
+ * @param obj the object to convert
142
+ * @example
143
+ * const obj = {
144
+ * foo_bar: 'fooBar',
145
+ * foo_bar2: 'fooBar2',
146
+ * foo_bar3: {
147
+ * foo_bar4: 'fooBar4',
148
+ * foo_bar5: 'fooBar5',
149
+ * },
150
+ * };
151
+ * const result = snakeCase2PascalCase(obj);
152
+ * console.log(result); // { FooBar: 'fooBar', FooBar2: 'fooBar2', FooBar3: { FooBar4: 'fooBar4', FooBar5: 'fooBar5' } }
153
+ */ const snakeCase2PascalCase = (obj)=>transformObjectKey(obj, 'SnakeToPascal');
154
+
155
+ /**
156
+ * 檢查稅務編號是否符合正確的編號規則。
157
+ *
158
+ * @param {string} taxId - 要檢查的8位數稅務編號。
159
+ * @returns {boolean} - 如果符合規則則返回 true,否則返回 false。
160
+ *
161
+ * ### 編號檢查規則:
162
+ * 1. **基本格式檢查**:
163
+ * - 編號必須為 8 位數字。
164
+ * - 不得為「00000000」或「11111111」,這些編號被視為無效。
165
+ *
166
+ * 2. **驗證邏輯**:
167
+ * - 使用一組驗證運算子:`[1, 2, 1, 2, 1, 2, 4, 1]`。
168
+ * - 將稅務編號的每一位數字與對應的運算子相乘後,使用 `calculate` 函數計算各位數的和。
169
+ * - 計算公式為:`(product % 10) + (product - (product % 10)) / 10`
170
+ * - 將所有位數經計算後的結果加總為 `sum`。
171
+ *
172
+ * 3. **檢查規則**:
173
+ * - 如果總和 `sum` 可以被 5 整除,則編號有效。
174
+ * - 或者,若第七位數為 7,且 `(sum + 1) % 5 === 0`,則編號有效。
175
+ *
176
+ * @example
177
+ *
178
+ * validTaxId('22099131') // true
179
+ * validTaxId('84149961') // true
180
+ * validTaxId('00000000') // false
181
+ * validTaxId('11111111') // false
182
+ * validTaxId('22099132') // false
183
+ */ const validTaxId = (taxId)=>{
184
+ const invalidList = '00000000,11111111';
185
+ if (/^\d{8}$/.test(taxId) === false || invalidList.indexOf(taxId) >= 0) {
186
+ return false;
187
+ }
188
+ const validateOperator = [
189
+ 1,
190
+ 2,
191
+ 1,
192
+ 2,
193
+ 1,
194
+ 2,
195
+ 4,
196
+ 1
197
+ ];
198
+ const calculate = (product)=>product % 10 + (product - product % 10) / 10;
199
+ const sum = validateOperator.reduce((pre, cur, index)=>pre + calculate(Number(taxId[index]) * cur), 0);
200
+ return sum % 5 === 0 || taxId[6] === '7' && (sum + 1) % 5 === 0;
201
+ };
202
+ /**
203
+ * 驗證日期格式是否正確
204
+ *
205
+ * @param dateString 日期字串
206
+ * @param format 日期格式
207
+ * @example
208
+ *
209
+ * validateDateString('20210201', 'YYYYMMDD') // true
210
+ * validateDateString('2021-02-01', 'YYYY-MM-DD') // true
211
+ * validateDateString('20210201', 'YYYY-MM-DD') // false
212
+ * validateDateString('20210201', 'YYYYMM') // false
213
+ */ const validateDateString = (dateString, format)=>{
214
+ if (!dateString) return false;
215
+ // 根據 format 生成正則表達式
216
+ const regexPattern = format.replace(/YYYY/, '\\d{4}').replace(/MM/, '\\d{2}').replace(/DD/, '\\d{2}').replace(/HH/, '\\d{2}').replace(/mm/, '\\d{2}').replace(/ss/, '\\d{2}');
217
+ const regex = new RegExp(`^${regexPattern}$`);
218
+ // 先用正則驗證格式
219
+ if (!regex.test(dateString)) return false;
220
+ // 再用 dayjs 驗證是否為有效日期
221
+ return dayjs(dateString, format, true).isValid();
222
+ };
223
+
224
+ /**
225
+ * 取得去年今年以及明年期別陣列
226
+ *
227
+ * @example
228
+ * // 假設 今年為 112 年
229
+ * generatePeriodArray() // 11102 ~ 11312
230
+ */ const generatePeriodArray = ()=>{
231
+ const currentYear = new Date().getFullYear() - 1911;
232
+ const months = [
233
+ '02',
234
+ '04',
235
+ '06',
236
+ '08',
237
+ '10',
238
+ '12'
239
+ ];
240
+ const years = [
241
+ currentYear - 1,
242
+ currentYear,
243
+ currentYear + 1
244
+ ];
245
+ return years.flatMap((year)=>months.map((month)=>`${year}${month}`));
246
+ };
247
+ /**
248
+ * 取得當前期別
249
+ *
250
+ * 報稅期限次期開始15日內
251
+ *
252
+ * 雙數月沒有特殊規則,期別皆為當月開頭
253
+ *
254
+ * 單數月15號以前,期別為上個月否則為下個月開頭
255
+ *
256
+ * @example
257
+ *
258
+ * // 假設今天是 111-02-15
259
+ * getCurrentPeriod() // 11102
260
+ * // 假設今天是 111-02-16
261
+ * getCurrentPeriod() // 11102
262
+ * // 假設今天是 111-03-15
263
+ * getCurrentPeriod() // 11102
264
+ * // 假設今天是 111-03-16
265
+ * getCurrentPeriod() // 11104
266
+ */ const getCurrentPeriod = ()=>{
267
+ const now = dayjs();
268
+ const currentDate = now.format('DD');
269
+ const currentYear = now.format('YYYY');
270
+ const currentMonth = now.format('MM');
271
+ let endMonth;
272
+ if (Number(currentMonth) % 2 === 1) {
273
+ endMonth = dayjs(`${currentYear}${currentMonth}`, 'YYYYMM').add(Number(currentDate) <= 15 ? -1 : 1, 'month').format('YYYYMM');
274
+ } else {
275
+ endMonth = `${currentYear}${currentMonth}`;
276
+ }
277
+ return dayjs(endMonth, 'YYYYMM').subtract(1911, 'year').format('YYYYMM').substring(1);
278
+ };
279
+ /**
280
+ * 民國年轉西元年
281
+ * @param dateString 日期字串
282
+ * @param format 日期格式
283
+ * @example
284
+ *
285
+ * rocEraToAd('1100201') // 20210201
286
+ * rocEraToAd('11002', 'YYYYMM') // 202102
287
+ */ const rocEraToAd = (dateString, format = 'YYYYMMDD')=>{
288
+ if (!validateDateString(`0${dateString}`, format)) return dateString;
289
+ return dayjs(`0${dateString}`, format).add(1911, 'year').format(format);
290
+ };
291
+ /**
292
+ * 西元年轉民國年
293
+ * @param dateString 日期字串
294
+ * @param format 日期格式
295
+ * @example
296
+ *
297
+ * adToRocEra('20210201') // 1100201
298
+ * adToRocEra('202102', 'YYYYMM') // 11002
299
+ */ const adToRocEra = (dateString, format = 'YYYYMMDD')=>{
300
+ if (!validateDateString(dateString, format)) return dateString;
301
+ return dayjs(dateString, format).add(-1911, 'year').format(format).substring(1);
302
+ };
303
+
304
+ /**
305
+ * 檢查檔案是否為合法的 MIME 類型
306
+ *
307
+ * @param file 檔案
308
+ * @param accepts 允許的 MIME 類型
309
+ *
310
+ * @example
311
+ *
312
+ * ```js
313
+ * validateFileType({ type: 'image/png' }, ['image/png', 'image/jpeg']) // true
314
+ * validateFileType({ type: 'image/png' }, ['image/jpeg']) // false
315
+ * validateFileType({ type: 'image/png' }, ['image/*']) // true
316
+ * ```
317
+ */ const validateFileType = (file, accepts)=>{
318
+ if (accepts.length === 0) return true;
319
+ // 獲取文件的MIME類型
320
+ const fileMimeType = file.type;
321
+ return accepts.some((accept)=>{
322
+ if (accept === fileMimeType) {
323
+ return true;
324
+ }
325
+ if (accept.endsWith('/*')) {
326
+ const acceptedCategory = accept.split('/')[0];
327
+ const fileCategory = fileMimeType.split('/')[0];
328
+ if (acceptedCategory === fileCategory) {
329
+ return true;
330
+ }
331
+ }
332
+ return false;
333
+ });
334
+ };
335
+ /**
336
+ * 根據檔案副檔名取得對應的 MIME Type
337
+ *
338
+ * 目前支援的副檔名有:pdf, csv, jpeg, jpg, png, zip, txt
339
+ * 除此之外的副檔名皆回傳 application/octet-stream
340
+ *
341
+ * @param fileExtension 檔案副檔名
342
+ *
343
+ * @example
344
+ *
345
+ * getMimeType('pdf') // 'application/pdf'
346
+ * getMimeType('csv') // 'text/csv'
347
+ * getMimeType('jpeg') // 'image/jpeg'
348
+ * getMimeType('jpg') // 'image/jpeg'
349
+ * getMimeType('png') // 'image/png'
350
+ * getMimeType('txt') // 'text/plain'
351
+ * getMimeType('zip') // 'application/zip'
352
+ * getMimeType('mp4') // 'application/octet-stream'
353
+ */ const getMimeType = (fileName)=>{
354
+ switch((fileName.split('.').pop() || '').toLocaleLowerCase()){
355
+ case 'jpeg':
356
+ case 'jpg':
357
+ return 'image/jpeg';
358
+ case 'png':
359
+ return 'image/png';
360
+ case 'pdf':
361
+ return 'application/pdf';
362
+ case 'zip':
363
+ return 'application/zip';
364
+ case 'csv':
365
+ return 'text/csv';
366
+ case 'txt':
367
+ return 'text/plain';
368
+ default:
369
+ return 'application/octet-stream';
370
+ }
371
+ };
372
+
373
+ /**
374
+ * 判斷執行環境是否為 Server(node.js) 端
375
+ */ const isServer = ()=>typeof window === 'undefined';
376
+
377
+ /**
378
+ * 將物件中的某些 key 排除
379
+ *
380
+ * @param object 原始物件
381
+ * @param keys 要排除的 key
382
+ *
383
+ * @example
384
+ * const a = { a: 1, b: 2, c: 3, d: 4 };
385
+ * const b = omit(a, 'a', 'b'); // { c: 3, d: 4 }
386
+ */ const omit = (object, ...keys)=>Object.keys(object).reduce((acc, cur)=>{
387
+ if (keys.includes(cur)) {
388
+ return acc;
389
+ }
390
+ return {
391
+ ...acc,
392
+ [cur]: object[cur]
393
+ };
394
+ }, {});
395
+ /**
396
+ * 將物件中的某些 value 排除
397
+ *
398
+ * @param object 原始物件
399
+ * @param values 要排除的 value
400
+ *
401
+ * @example
402
+ * const a = { a: undefined, b: null, c: 3, d: 4 };
403
+ * const b = omitByValue(a, undefined, null); // { c: 3, d: 4 }
404
+ */ const omitByValue = (object, ...values)=>Object.entries(object).reduce((acc, [key, value])=>{
405
+ if (values.includes(value)) {
406
+ return acc;
407
+ }
408
+ return {
409
+ ...acc,
410
+ [key]: value
411
+ };
412
+ }, {});
413
+ /**
414
+ * 將指定格式的物件轉換成類似 enum 的物件
415
+ *
416
+ * @param enumObject enum 物件
417
+ * @param valueKey value 的 key
418
+ *
419
+ * @example
420
+ *
421
+ * ```js
422
+ * const myObj = {
423
+ * A: { value: 'a' },
424
+ * B: { value: 'b', other: 'other' },
425
+ * }
426
+ *
427
+ * const enumCode = extractEnumLikeObject(myObj, 'value');
428
+ * console.log(enumCode); // { A: 'a', B: 'b' }
429
+ * ```
430
+ */ const extractEnumLikeObject = (enumObject, valueKey)=>Object.entries(enumObject).reduce((acc, [key, value])=>({
431
+ ...acc,
432
+ [key]: value[valueKey]
433
+ }), {});
434
+
435
+ /**
436
+ * debounce function
437
+ *
438
+ * @param {Function} fn function to be executed
439
+ * @param {number} delay time to wait before executing the function
440
+ *
441
+ * @example
442
+ * const debouncedFunction = debounce((a: number, b: number) => console.log(a + b), 1000);
443
+ * debouncedFunction(1, 2);
444
+ * debouncedFunction(3, 4);
445
+ * // after 1 second
446
+ * // 7
447
+ */ const debounce = (fn, delay)=>{
448
+ let timeout;
449
+ // if (isAsync) {
450
+ // return (...args: P[]) =>
451
+ // new Promise((resolve) => {
452
+ // if (timeout) {
453
+ // clearTimeout(timeout);
454
+ // }
455
+ // timeout = setTimeout(() => {
456
+ // resolve(fn(...args));
457
+ // }, delay);
458
+ // });
459
+ // }
460
+ return (...args)=>{
461
+ if (timeout) {
462
+ clearTimeout(timeout);
463
+ }
464
+ timeout = setTimeout(()=>{
465
+ fn(...args);
466
+ }, delay);
467
+ };
468
+ };
469
+ /**
470
+ * throttle function
471
+ *
472
+ * @param {Function} fn function to be executed
473
+ * @param {number} delay time to wait before executing the function
474
+ *
475
+ * @example
476
+ * const throttledFunction = throttle((a: number, b: number) => a + b, 1000);
477
+ * throttledFunction(1, 2); // 3
478
+ * throttledFunction(3, 4); // undefined
479
+ * setTimeout(() => {
480
+ * throttledFunction(5, 6); // 11
481
+ * }, 2000);
482
+ */ const throttle = (fn, delay)=>{
483
+ let wait = false;
484
+ return (...args)=>{
485
+ if (wait) return undefined;
486
+ const val = fn(...args);
487
+ wait = true;
488
+ setTimeout(()=>{
489
+ wait = false;
490
+ }, delay);
491
+ return val;
492
+ };
493
+ };
494
+
495
+ /**
496
+ * 中文
497
+ *
498
+ * @param options - 選項
499
+ *
500
+ * @example
501
+ *
502
+ * isChinese().test('你好') // true
503
+ * isChinese().test('你 好') // false
504
+ * isChinese({ containSpace: true }).test('你 好') // true
505
+ * isChinese().test('123') // false
506
+ */ const isChinese = (options)=>new RegExp(`^[\u4e00-\u9fa5${options?.mark ? '\\p{P}' : ''}${options?.containSpace ? '\\s' : ''}]+$`, 'u');
507
+ /**
508
+ * 英文
509
+ *
510
+ * @param options - 選項
511
+ *
512
+ * @example
513
+ *
514
+ * isEnglish().test('abc') // true
515
+ * isEnglish().test('abc def') // false
516
+ * isEnglish({ containSpace: true }).test('abc def') // true
517
+ * isEnglish({ number: true }).test('abc123') // true
518
+ * isEnglish({ mark: true }).test('abc!') // true
519
+ * isEnglish({ fullWidth: true }).test('ABC') // true
520
+ * isEnglish({ lowercase: false }).test('abc') // false
521
+ * isEnglish({ uppercase: false }).test('ABC') // false
522
+ */ const isEnglish = (options)=>new RegExp(`^[${options?.lowercase ?? true ? 'a-z' : ''}${options?.uppercase ?? true ? 'A-Z' : ''}${options?.number ? '\\d' : ''}${options?.fullWidth ? 'A-Za-z' : ''}${options?.mark ? '\\p{P}\\p{S}' : ''}${options?.containSpace ? '\\s' : ''}]+$`, 'u');
523
+ /**
524
+ * email
525
+ *
526
+ * @example
527
+ *
528
+ * isEmail().test('123') // false
529
+ * isEmail().test('123@gmail.com') // true
530
+ */ const isEmail = ()=>// eslint-disable-next-line
531
+ /^(?!\.)(?!.*\.\.)([A-Z0-9_+-\.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9\-]*\.)+[A-Z]{2,}$/i;
532
+ /**
533
+ * 日期字串
534
+ *
535
+ * @param separator 分隔符號 (預設為 -)
536
+ *
537
+ * @example
538
+ *
539
+ * isDateString().test('2021-01-01') // true
540
+ * isDateString('/').test('2021/01/01') // true
541
+ */ const isDateString = (separator = '-')=>new RegExp(`^\\d{4}${separator}\\d{2}${separator}\\d{2}$`);
542
+ /**
543
+ * 日期時間字串
544
+ *
545
+ * @param separator 分隔符號 (預設為 -)
546
+ *
547
+ * @example
548
+ *
549
+ * isDateTimeString().test('2021-01-01 12:00:00') // true
550
+ * isDateTimeString('/').test('2021/01/01 12:00:00') // true
551
+ */ const isDateTimeString = (separator = '-')=>new RegExp(`^\\d{4}${separator}\\d{2}${separator}\\d{2} \\d{2}:\\d{2}:\\d{2}$`);
552
+ /**
553
+ * 日期時間字串 (不包含豪秒)
554
+ *
555
+ * @example
556
+ *
557
+ * isTimeString().test('12:00:00') // true
558
+ * isTimeString().test('12:00:00.123') // false
559
+ */ const isTimeString = ()=>/^\d{2}:\d{2}:\d{2}$/;
560
+
561
+ /**
562
+ * 數字
563
+ *
564
+ * @example
565
+ * isNumber().test('123') // true
566
+ */ const isNumber = ()=>/^\d+$/;
567
+ /**
568
+ * 非零開頭的數字
569
+ *
570
+ * @param allowZero 是否允許0
571
+ *
572
+ * @example
573
+ * isNonZeroNumber().test('123') // true
574
+ * isNonZeroNumber().test('0123') // false
575
+ * isNonZeroNumber().test('0') // false
576
+ * isNonZeroNumber(true).test('0') // true
577
+ */ const isNonZeroStart = (allowZero = false)=>allowZero ? /^[1-9]\d*|0$/ : /^[1-9]\d*$/;
578
+ /**
579
+ * n位數的數字
580
+ *
581
+ * @param n 位數
582
+ *
583
+ * @example
584
+ * isNumberN(3).test('123') // true
585
+ * isNumberN(3).test('1234') // false
586
+ */ const isNumberN = (n)=>new RegExp(`^\\d{${n}}$`);
587
+ /**
588
+ * n - m 位數的數字
589
+ *
590
+ * @param n 最小位數
591
+ * @param m 最大位數
592
+ *
593
+ * @example
594
+ * isNumberNM(3, 5).test('123') // true
595
+ * isNumberNM(3, 5).test('12345') // true
596
+ * isNumberNM(3, 5).test('123456') // false
597
+ */ const isNumberNM = (n, m)=>new RegExp(`^\\d{${n},${m}}$`);
598
+ /**
599
+ * 至少n位數的數字
600
+ *
601
+ * @param n 最小位數
602
+ *
603
+ * @example
604
+ * isNumberAtLeastN(3).test('123') // true
605
+ * isNumberAtLeastN(3).test('1234') // true
606
+ * isNumberAtLeastN(3).test('12') // false
607
+ */ const isNumberAtLeastN = (n)=>new RegExp(`^\\d{${n},}$`);
608
+
609
+ /**
610
+ * 台灣手機號碼
611
+ *
612
+ * 1. 09開頭
613
+ * 2. 10碼數字
614
+ *
615
+ * @example
616
+ *
617
+ * isTWMobile().test('0912345678') // true
618
+ * isTWMobile().test('091234567') // false
619
+ */ const isTWMobile = ()=>/09[0-9]{8}$/;
620
+ /**
621
+ * 台灣市話
622
+ *
623
+ * 1. 0開頭
624
+ * 2. 2~3碼區碼
625
+ * 3. 區碼與號碼之間可以有-號
626
+ * 4. 6~8碼號碼
627
+ * 5. 區碼加上號碼最多10碼
628
+ * 6. 可以有分機號碼,分機號碼為#號開頭,後面接1~5碼數字
629
+ *
630
+ * @example
631
+ *
632
+ * isTWPhone().test('0212345678') // true
633
+ * isTWPhone().test('02-12345678') // true
634
+ * isTWPhone().test('023-1234567') // true
635
+ * isTWPhone().test('02-12345678#123') // true
636
+ *
637
+ * isTWPhone().test('02123456789') // false
638
+ * isTWPhone().test('02-123456789') // false
639
+ * isTWPhone().test('02-12345678#123456') // false
640
+ */ const isTWPhone = ()=>/^(((0[2-9]-?\d{6,8})(#\d{1,5})?)|((0[2-9][0-9]-?\d{6,7})(#\d{1,5})?))$/;
641
+
642
+ /**
643
+ * 將數字轉換成金額千分位格式
644
+ *
645
+ * @param num - 數字
646
+ *
647
+ * @example
648
+ *
649
+ * formatAmount(1234567) // '1,234,567'
650
+ */ const formatAmount = (num)=>{
651
+ if (typeof num !== 'number') return '';
652
+ return num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
653
+ };
654
+ /**
655
+ * 將字串的第 n - m 個字轉換成星號
656
+ *
657
+ * @param str - 字串
658
+ * @param n - 起始位置
659
+ * @param m - 結束位置
660
+ *
661
+ * @example
662
+ *
663
+ * formatString('123456', 1, 4) // '1****6'
664
+ */ const formatStarMask = (str, n, m)=>str.slice(0, n) + '*'.repeat(m - n + 1) + str.slice(m + 1);
665
+
666
+ export { adToRocEra, camelCase2PascalCase, camelCase2SnakeCase, camelString2PascalString, camelString2SnakeString, debounce, extractEnumLikeObject, formatAmount, formatStarMask, generatePeriodArray, getCurrentPeriod, getMimeType, isChinese, isDateString, isDateTimeString, isEmail, isEnglish, isNonZeroStart, isNumber, isNumberAtLeastN, isNumberN, isNumberNM, isServer, isTWMobile, isTWPhone, isTimeString, omit, omitByValue, pascalCase2CamelCase, pascalCase2SnakeCase, pascalString2CamelString, pascalString2SnakeString, rocEraToAd, snakeCase2CamelCase, snakeCase2PascalCase, snakeString2CamelString, snakeString2PascalString, throttle, validTaxId, validateDateString, validateFileType };