@gateweb/react-utils 1.9.0 → 1.11.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.d.ts +76 -11
- package/dist/cjs/index.js +82 -44
- package/dist/es/index.d.mts +76 -11
- package/dist/es/index.mjs +81 -45
- package/package.json +3 -1
package/dist/cjs/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import { AtLeastOne } from './types.js';
|
|
2
3
|
export * from './types.js';
|
|
3
|
-
import React from 'react';
|
|
4
4
|
|
|
5
5
|
declare const FILE_SIZE_UNITS: readonly ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
|
6
6
|
type FileSizeUnit = (typeof FILE_SIZE_UNITS)[number];
|
|
@@ -404,27 +404,55 @@ declare const extractEnumLikeObject: <T extends Record<string, { [P in K]: any;
|
|
|
404
404
|
*
|
|
405
405
|
* @param obj 要處理的物件
|
|
406
406
|
* @param name 生成的 enum 名稱
|
|
407
|
+
* @param scene 要使用的條件 (參考 example)
|
|
407
408
|
*
|
|
408
409
|
* @example
|
|
409
410
|
*
|
|
410
411
|
* ```ts
|
|
411
|
-
* const Status = { Enabled: { value: 'enabled', label: '啟用' }, Disabled: { value: 'disabled', label: '停用' } };
|
|
412
412
|
*
|
|
413
|
+
* // 一般使用
|
|
414
|
+
* const Status = { Enabled: { value: 'enabled', label: '啟用' }, Disabled: { value: 'disabled', label: '停用' }};
|
|
413
415
|
* const { EnumStatus, StatusList, getStatusLabel } = createEnumLikeObject(Status, 'Status');
|
|
414
|
-
*
|
|
415
416
|
* console.log(EnumStatus); // { Enabled: 'enabled', Disabled: 'disabled' }
|
|
417
|
+
* console.log(StatusList); // [ { key: 'Enabled', value: 'enabled', label: '啟用' }, { key: 'Disabled', value: 'disabled', label: '停用' }]
|
|
418
|
+
* console.log(getStatusLabel('enabled')); // '啟用'
|
|
416
419
|
*
|
|
417
|
-
*
|
|
420
|
+
* // scenes 版本
|
|
421
|
+
*
|
|
422
|
+
* Status = { Enabled: { value: 'enabled', scenes: { webA: { label: '啟用' }, webB: { label: '激活' } } }, Disabled: { value: 'disabled', scenes: { webA: { label: '停用' }, webB: { label: '停止' }}}}
|
|
423
|
+
* const { EnumStatusA, StatusAList, getStatusALabel } = createEnumLikeObject(Status, 'StatusA', 'webA');
|
|
424
|
+
* console.log(EnumStatusA); // { Enabled: 'enabled', Disabled: 'disabled' }
|
|
425
|
+
* console.log(StatusAList); // [ { key: 'Enabled', value: 'enabled', label: '啟用' }, { key: 'Disabled', value: 'disabled', label: '停用' }]
|
|
426
|
+
* console.log(getStatusALabel('enabled')); // '啟用'
|
|
427
|
+
*
|
|
428
|
+
* const { EnumStatusA, StatusAList, getStatusALabel } = createEnumLikeObject(Status, 'StatusB', 'webB');
|
|
429
|
+
* console.log(EnumStatusB); // { Enabled: 'enabled', Disabled: 'disabled' }
|
|
430
|
+
* console.log(StatusBList); // [ { key: 'Enabled', value: 'enabled', label: '激活' }, { key: 'Disabled', value: 'disabled', label: '停止' }]
|
|
431
|
+
* console.log(getStatusBLabel('enabled')); // '激活'
|
|
418
432
|
*
|
|
419
|
-
* console.log(getStatusLabel('enabled')); // '啟用'
|
|
420
433
|
* ```
|
|
421
434
|
*/
|
|
422
|
-
declare
|
|
435
|
+
declare function createEnumLikeObject<T extends Record<string, {
|
|
423
436
|
value: any;
|
|
424
437
|
label: string;
|
|
425
|
-
}>, N extends string>(obj: T, name: N)
|
|
426
|
-
|
|
427
|
-
|
|
438
|
+
}>, N extends string>(obj: T, name: N): {
|
|
439
|
+
[K in `Enum${N}` | `${N}List` | `get${N}Label`]: K extends `Enum${N}` ? {
|
|
440
|
+
[key in keyof T]: T[key]['value'];
|
|
441
|
+
} : K extends `${N}List` ? Array<{
|
|
442
|
+
key: string;
|
|
443
|
+
} & T[keyof T]> : (value: T[keyof T]['value']) => string;
|
|
444
|
+
};
|
|
445
|
+
declare function createEnumLikeObject<T extends Record<string, {
|
|
446
|
+
value: any;
|
|
447
|
+
scenes: Record<string, Record<string, any>>;
|
|
448
|
+
}>, N extends string, Scene extends keyof T[keyof T]['scenes']>(obj: T, name: N, scene: Scene): {
|
|
449
|
+
[K in `Enum${N}` | `${N}List` | `get${N}Label`]: K extends `Enum${N}` ? {
|
|
450
|
+
[key in keyof T]: T[key]['value'];
|
|
451
|
+
} : K extends `${N}List` ? Array<{
|
|
452
|
+
key: string;
|
|
453
|
+
value: T[keyof T]['value'];
|
|
454
|
+
} & T[keyof T]['scenes'][Scene]> : (value: T[keyof T]['value']) => string;
|
|
455
|
+
};
|
|
428
456
|
|
|
429
457
|
/**
|
|
430
458
|
* simulate a fake api request
|
|
@@ -947,6 +975,43 @@ declare const QueryProvider: <Q>({ children, query, handleChangeQuery, }: React.
|
|
|
947
975
|
*/
|
|
948
976
|
declare const useQueryContext: <Q>() => <T>(selector: (state: TQueryState<Q>) => T, equalityFn?: (left: T, right: T) => boolean) => T;
|
|
949
977
|
|
|
978
|
+
/**
|
|
979
|
+
* Creates a strongly-typed React Context and Provider pair for data sharing.
|
|
980
|
+
*
|
|
981
|
+
* This utility helps you avoid prop drilling by providing a reusable way to define
|
|
982
|
+
* context with strict type inference. It returns a custom hook for consuming the context
|
|
983
|
+
* and a Provider component for supplying context values.
|
|
984
|
+
*
|
|
985
|
+
* @template T - The value type for the context.
|
|
986
|
+
* @returns {Object} An object containing:
|
|
987
|
+
* - useDataContext: A custom hook to access the context value. Throws an error if used outside the provider.
|
|
988
|
+
* - DataProvider: A Provider component to wrap your component tree and supply the context value.
|
|
989
|
+
*
|
|
990
|
+
* @example
|
|
991
|
+
* // Example usage:
|
|
992
|
+
* const { useDataContext, DataProvider } = createDataContext<{ count: number }>();
|
|
993
|
+
*
|
|
994
|
+
* function Counter() {
|
|
995
|
+
* const { count } = useDataContext();
|
|
996
|
+
* return <span>{count}</span>;
|
|
997
|
+
* }
|
|
998
|
+
*
|
|
999
|
+
* function App() {
|
|
1000
|
+
* return (
|
|
1001
|
+
* <DataProvider value={{ count: 42 }}>
|
|
1002
|
+
* <Counter />
|
|
1003
|
+
* </DataProvider>
|
|
1004
|
+
* );
|
|
1005
|
+
* }
|
|
1006
|
+
*/
|
|
1007
|
+
declare const createDataContext: <T>() => {
|
|
1008
|
+
readonly useDataContext: () => T & ({} | null);
|
|
1009
|
+
readonly DataProvider: ({ children, value }: {
|
|
1010
|
+
children: ReactNode;
|
|
1011
|
+
value: T;
|
|
1012
|
+
}) => React.JSX.Element;
|
|
1013
|
+
};
|
|
1014
|
+
|
|
950
1015
|
type TCountdownActions = {
|
|
951
1016
|
/** 目前秒數 */
|
|
952
1017
|
countdown: number;
|
|
@@ -1080,4 +1145,4 @@ declare const getLocalStorage: <T>(key: string, deCode?: boolean) => T | undefin
|
|
|
1080
1145
|
*/
|
|
1081
1146
|
declare const setLocalStorage: (key: string, value: Record<string, any>, enCode?: boolean) => void;
|
|
1082
1147
|
|
|
1083
|
-
export { ByteSize, type PartialBy, QueryProvider, type RequiredBy, type TCountdownActions, adToRocEra, camelCase2PascalCase, camelCase2SnakeCase, camelString2PascalString, camelString2SnakeString, convertBytes, createEnumLikeObject, debounce, deepMerge, downloadFile, extractEnumLikeObject, fakeApi, formatAmount, formatBytes, formatStarMask, generatePeriodArray, getCurrentPeriod, getLocalStorage, getMimeType, invariant, isChinese, isDateString, isDateTimeString, isEmail, isEnglish, isEqual, isNonZeroStart, isNumber, isNumberAtLeastN, isNumberN, isNumberNM, isServer, isTWMobile, isTWPhone, isTimeString, isValidPassword, maskString, mergeRefs, objectToSearchParams, omit, omitByValue, pascalCase2CamelCase, pascalCase2SnakeCase, pascalString2CamelString, pascalString2SnakeString, pick, pickByValue, rocEraToAd, searchParamsToObject, setLocalStorage, snakeCase2CamelCase, snakeCase2PascalCase, snakeString2CamelString, snakeString2PascalString, throttle, useCountdown, useQueryContext, useValue, validTaxId, validateDateString, validateFileType, wait };
|
|
1148
|
+
export { ByteSize, type PartialBy, QueryProvider, type RequiredBy, type TCountdownActions, adToRocEra, camelCase2PascalCase, camelCase2SnakeCase, camelString2PascalString, camelString2SnakeString, convertBytes, createDataContext, createEnumLikeObject, debounce, deepMerge, downloadFile, extractEnumLikeObject, fakeApi, formatAmount, formatBytes, formatStarMask, generatePeriodArray, getCurrentPeriod, getLocalStorage, getMimeType, invariant, isChinese, isDateString, isDateTimeString, isEmail, isEnglish, isEqual, isNonZeroStart, isNumber, isNumberAtLeastN, isNumberN, isNumberNM, isServer, isTWMobile, isTWPhone, isTimeString, isValidPassword, maskString, mergeRefs, objectToSearchParams, omit, omitByValue, pascalCase2CamelCase, pascalCase2SnakeCase, pascalString2CamelString, pascalString2SnakeString, pick, pickByValue, rocEraToAd, searchParamsToObject, setLocalStorage, snakeCase2CamelCase, snakeCase2PascalCase, snakeString2CamelString, snakeString2PascalString, throttle, useCountdown, useQueryContext, useValue, validTaxId, validateDateString, validateFileType, wait };
|
package/dist/cjs/index.js
CHANGED
|
@@ -2,14 +2,15 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
2
2
|
|
|
3
3
|
var dayjs = require('dayjs');
|
|
4
4
|
var queryStoreClient = require('./queryStore-client-q_SLGgYH.js');
|
|
5
|
-
var useCountdownClient = require('./useCountdown-client-uiqhgllY.js');
|
|
6
5
|
var React = require('react');
|
|
6
|
+
var useCountdownClient = require('./useCountdown-client-uiqhgllY.js');
|
|
7
7
|
var downloadClient = require('./download-client-DKxkL92w.js');
|
|
8
8
|
var webStorageClient = require('./webStorage-client-BGQKUfrO.js');
|
|
9
9
|
|
|
10
10
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
11
|
|
|
12
12
|
var dayjs__default = /*#__PURE__*/_interopDefault(dayjs);
|
|
13
|
+
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
13
14
|
|
|
14
15
|
const FILE_SIZE_UNITS$1 = [
|
|
15
16
|
'Bytes',
|
|
@@ -177,56 +178,41 @@ const FILE_SIZE_UNITS$1 = [
|
|
|
177
178
|
...acc,
|
|
178
179
|
[key]: value[valueKey]
|
|
179
180
|
}), {});
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
*
|
|
183
|
-
* 輸入的物件格式為
|
|
184
|
-
*
|
|
185
|
-
* ```ts
|
|
186
|
-
* { key: { value: string, label: string }, key2: { value: string, label: string }, ... }
|
|
187
|
-
* ```
|
|
188
|
-
*
|
|
189
|
-
* 會生成以下三個物件
|
|
190
|
-
*
|
|
191
|
-
* enum 物件 - 根據 value 生成的 enum 物件
|
|
192
|
-
*
|
|
193
|
-
* enum 列表 - 根據 value 與 label 生成的列表
|
|
194
|
-
*
|
|
195
|
-
* 取得 label 的方法 - 傳入 value 可以取得對應的 label
|
|
196
|
-
*
|
|
197
|
-
* @param obj 要處理的物件
|
|
198
|
-
* @param name 生成的 enum 名稱
|
|
199
|
-
*
|
|
200
|
-
* @example
|
|
201
|
-
*
|
|
202
|
-
* ```ts
|
|
203
|
-
* const Status = { Enabled: { value: 'enabled', label: '啟用' }, Disabled: { value: 'disabled', label: '停用' } };
|
|
204
|
-
*
|
|
205
|
-
* const { EnumStatus, StatusList, getStatusLabel } = createEnumLikeObject(Status, 'Status');
|
|
206
|
-
*
|
|
207
|
-
* console.log(EnumStatus); // { Enabled: 'enabled', Disabled: 'disabled' }
|
|
208
|
-
*
|
|
209
|
-
* console.log(StatusList); // [ { key: 'Enabled', value: 'enabled', label: '啟用' }, { key: 'Disabled', value: 'disabled', label: '停用' } ]
|
|
210
|
-
*
|
|
211
|
-
* console.log(getStatusLabel('enabled')); // '啟用'
|
|
212
|
-
* ```
|
|
213
|
-
*/ const createEnumLikeObject = (obj, name)=>{
|
|
181
|
+
// 實作
|
|
182
|
+
function createEnumLikeObject(obj, name, scene) {
|
|
214
183
|
const Enum = extractEnumLikeObject(obj, 'value');
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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.value,
|
|
191
|
+
...item.scenes?.[scene] ?? {}
|
|
192
|
+
}));
|
|
193
|
+
} else {
|
|
194
|
+
// label only 版本
|
|
195
|
+
list = Object.entries(obj).map(([key, item])=>({
|
|
196
|
+
key,
|
|
197
|
+
...item
|
|
198
|
+
}));
|
|
199
|
+
}
|
|
200
|
+
function getLabel(value) {
|
|
220
201
|
const targetItem = list.find((item)=>item.value === value);
|
|
221
|
-
if (!targetItem) return value;
|
|
222
|
-
|
|
223
|
-
|
|
202
|
+
if (!targetItem) return String(value);
|
|
203
|
+
if ('scenes' in targetItem && scene) {
|
|
204
|
+
const sceneObj = targetItem.scenes?.[scene];
|
|
205
|
+
return sceneObj && sceneObj.label || String(value);
|
|
206
|
+
}
|
|
207
|
+
if ('label' in targetItem) return targetItem.label;
|
|
208
|
+
return String(value);
|
|
209
|
+
}
|
|
224
210
|
return {
|
|
225
211
|
[`Enum${name}`]: Enum,
|
|
226
212
|
[`${name}List`]: list,
|
|
227
213
|
[`get${name}Label`]: getLabel
|
|
228
214
|
};
|
|
229
|
-
}
|
|
215
|
+
}
|
|
230
216
|
|
|
231
217
|
/**
|
|
232
218
|
* simulate a fake api request
|
|
@@ -1058,6 +1044,57 @@ const FILE_SIZE_UNITS = [
|
|
|
1058
1044
|
return dayjs__default.default(dateString, format, true).isValid();
|
|
1059
1045
|
};
|
|
1060
1046
|
|
|
1047
|
+
/**
|
|
1048
|
+
* Creates a strongly-typed React Context and Provider pair for data sharing.
|
|
1049
|
+
*
|
|
1050
|
+
* This utility helps you avoid prop drilling by providing a reusable way to define
|
|
1051
|
+
* context with strict type inference. It returns a custom hook for consuming the context
|
|
1052
|
+
* and a Provider component for supplying context values.
|
|
1053
|
+
*
|
|
1054
|
+
* @template T - The value type for the context.
|
|
1055
|
+
* @returns {Object} An object containing:
|
|
1056
|
+
* - useDataContext: A custom hook to access the context value. Throws an error if used outside the provider.
|
|
1057
|
+
* - DataProvider: A Provider component to wrap your component tree and supply the context value.
|
|
1058
|
+
*
|
|
1059
|
+
* @example
|
|
1060
|
+
* // Example usage:
|
|
1061
|
+
* const { useDataContext, DataProvider } = createDataContext<{ count: number }>();
|
|
1062
|
+
*
|
|
1063
|
+
* function Counter() {
|
|
1064
|
+
* const { count } = useDataContext();
|
|
1065
|
+
* return <span>{count}</span>;
|
|
1066
|
+
* }
|
|
1067
|
+
*
|
|
1068
|
+
* function App() {
|
|
1069
|
+
* return (
|
|
1070
|
+
* <DataProvider value={{ count: 42 }}>
|
|
1071
|
+
* <Counter />
|
|
1072
|
+
* </DataProvider>
|
|
1073
|
+
* );
|
|
1074
|
+
* }
|
|
1075
|
+
*/ const createDataContext = ()=>{
|
|
1076
|
+
const Context = /*#__PURE__*/ React.createContext(undefined);
|
|
1077
|
+
const useDataContext = ()=>{
|
|
1078
|
+
const context = React.useContext(Context);
|
|
1079
|
+
if (context === undefined) {
|
|
1080
|
+
throw new Error(`useDataContext must be used within a DataProvider`);
|
|
1081
|
+
}
|
|
1082
|
+
return context;
|
|
1083
|
+
};
|
|
1084
|
+
const DataProvider = ({ children, value })=>{
|
|
1085
|
+
const memoized = React.useMemo(()=>value, [
|
|
1086
|
+
value
|
|
1087
|
+
]);
|
|
1088
|
+
return /*#__PURE__*/ React__default.default.createElement(Context.Provider, {
|
|
1089
|
+
value: memoized
|
|
1090
|
+
}, children);
|
|
1091
|
+
};
|
|
1092
|
+
return {
|
|
1093
|
+
useDataContext,
|
|
1094
|
+
DataProvider
|
|
1095
|
+
};
|
|
1096
|
+
};
|
|
1097
|
+
|
|
1061
1098
|
/**
|
|
1062
1099
|
* A hook to manage a value.
|
|
1063
1100
|
*
|
|
@@ -1195,6 +1232,7 @@ exports.camelCase2SnakeCase = camelCase2SnakeCase;
|
|
|
1195
1232
|
exports.camelString2PascalString = camelString2PascalString;
|
|
1196
1233
|
exports.camelString2SnakeString = camelString2SnakeString;
|
|
1197
1234
|
exports.convertBytes = convertBytes;
|
|
1235
|
+
exports.createDataContext = createDataContext;
|
|
1198
1236
|
exports.createEnumLikeObject = createEnumLikeObject;
|
|
1199
1237
|
exports.debounce = debounce;
|
|
1200
1238
|
exports.deepMerge = deepMerge;
|
package/dist/es/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import { AtLeastOne } from './types.mjs';
|
|
2
3
|
export * from './types.mjs';
|
|
3
|
-
import React from 'react';
|
|
4
4
|
|
|
5
5
|
declare const FILE_SIZE_UNITS: readonly ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
|
6
6
|
type FileSizeUnit = (typeof FILE_SIZE_UNITS)[number];
|
|
@@ -404,27 +404,55 @@ declare const extractEnumLikeObject: <T extends Record<string, { [P in K]: any;
|
|
|
404
404
|
*
|
|
405
405
|
* @param obj 要處理的物件
|
|
406
406
|
* @param name 生成的 enum 名稱
|
|
407
|
+
* @param scene 要使用的條件 (參考 example)
|
|
407
408
|
*
|
|
408
409
|
* @example
|
|
409
410
|
*
|
|
410
411
|
* ```ts
|
|
411
|
-
* const Status = { Enabled: { value: 'enabled', label: '啟用' }, Disabled: { value: 'disabled', label: '停用' } };
|
|
412
412
|
*
|
|
413
|
+
* // 一般使用
|
|
414
|
+
* const Status = { Enabled: { value: 'enabled', label: '啟用' }, Disabled: { value: 'disabled', label: '停用' }};
|
|
413
415
|
* const { EnumStatus, StatusList, getStatusLabel } = createEnumLikeObject(Status, 'Status');
|
|
414
|
-
*
|
|
415
416
|
* console.log(EnumStatus); // { Enabled: 'enabled', Disabled: 'disabled' }
|
|
417
|
+
* console.log(StatusList); // [ { key: 'Enabled', value: 'enabled', label: '啟用' }, { key: 'Disabled', value: 'disabled', label: '停用' }]
|
|
418
|
+
* console.log(getStatusLabel('enabled')); // '啟用'
|
|
416
419
|
*
|
|
417
|
-
*
|
|
420
|
+
* // scenes 版本
|
|
421
|
+
*
|
|
422
|
+
* Status = { Enabled: { value: 'enabled', scenes: { webA: { label: '啟用' }, webB: { label: '激活' } } }, Disabled: { value: 'disabled', scenes: { webA: { label: '停用' }, webB: { label: '停止' }}}}
|
|
423
|
+
* const { EnumStatusA, StatusAList, getStatusALabel } = createEnumLikeObject(Status, 'StatusA', 'webA');
|
|
424
|
+
* console.log(EnumStatusA); // { Enabled: 'enabled', Disabled: 'disabled' }
|
|
425
|
+
* console.log(StatusAList); // [ { key: 'Enabled', value: 'enabled', label: '啟用' }, { key: 'Disabled', value: 'disabled', label: '停用' }]
|
|
426
|
+
* console.log(getStatusALabel('enabled')); // '啟用'
|
|
427
|
+
*
|
|
428
|
+
* const { EnumStatusA, StatusAList, getStatusALabel } = createEnumLikeObject(Status, 'StatusB', 'webB');
|
|
429
|
+
* console.log(EnumStatusB); // { Enabled: 'enabled', Disabled: 'disabled' }
|
|
430
|
+
* console.log(StatusBList); // [ { key: 'Enabled', value: 'enabled', label: '激活' }, { key: 'Disabled', value: 'disabled', label: '停止' }]
|
|
431
|
+
* console.log(getStatusBLabel('enabled')); // '激活'
|
|
418
432
|
*
|
|
419
|
-
* console.log(getStatusLabel('enabled')); // '啟用'
|
|
420
433
|
* ```
|
|
421
434
|
*/
|
|
422
|
-
declare
|
|
435
|
+
declare function createEnumLikeObject<T extends Record<string, {
|
|
423
436
|
value: any;
|
|
424
437
|
label: string;
|
|
425
|
-
}>, N extends string>(obj: T, name: N)
|
|
426
|
-
|
|
427
|
-
|
|
438
|
+
}>, N extends string>(obj: T, name: N): {
|
|
439
|
+
[K in `Enum${N}` | `${N}List` | `get${N}Label`]: K extends `Enum${N}` ? {
|
|
440
|
+
[key in keyof T]: T[key]['value'];
|
|
441
|
+
} : K extends `${N}List` ? Array<{
|
|
442
|
+
key: string;
|
|
443
|
+
} & T[keyof T]> : (value: T[keyof T]['value']) => string;
|
|
444
|
+
};
|
|
445
|
+
declare function createEnumLikeObject<T extends Record<string, {
|
|
446
|
+
value: any;
|
|
447
|
+
scenes: Record<string, Record<string, any>>;
|
|
448
|
+
}>, N extends string, Scene extends keyof T[keyof T]['scenes']>(obj: T, name: N, scene: Scene): {
|
|
449
|
+
[K in `Enum${N}` | `${N}List` | `get${N}Label`]: K extends `Enum${N}` ? {
|
|
450
|
+
[key in keyof T]: T[key]['value'];
|
|
451
|
+
} : K extends `${N}List` ? Array<{
|
|
452
|
+
key: string;
|
|
453
|
+
value: T[keyof T]['value'];
|
|
454
|
+
} & T[keyof T]['scenes'][Scene]> : (value: T[keyof T]['value']) => string;
|
|
455
|
+
};
|
|
428
456
|
|
|
429
457
|
/**
|
|
430
458
|
* simulate a fake api request
|
|
@@ -947,6 +975,43 @@ declare const QueryProvider: <Q>({ children, query, handleChangeQuery, }: React.
|
|
|
947
975
|
*/
|
|
948
976
|
declare const useQueryContext: <Q>() => <T>(selector: (state: TQueryState<Q>) => T, equalityFn?: (left: T, right: T) => boolean) => T;
|
|
949
977
|
|
|
978
|
+
/**
|
|
979
|
+
* Creates a strongly-typed React Context and Provider pair for data sharing.
|
|
980
|
+
*
|
|
981
|
+
* This utility helps you avoid prop drilling by providing a reusable way to define
|
|
982
|
+
* context with strict type inference. It returns a custom hook for consuming the context
|
|
983
|
+
* and a Provider component for supplying context values.
|
|
984
|
+
*
|
|
985
|
+
* @template T - The value type for the context.
|
|
986
|
+
* @returns {Object} An object containing:
|
|
987
|
+
* - useDataContext: A custom hook to access the context value. Throws an error if used outside the provider.
|
|
988
|
+
* - DataProvider: A Provider component to wrap your component tree and supply the context value.
|
|
989
|
+
*
|
|
990
|
+
* @example
|
|
991
|
+
* // Example usage:
|
|
992
|
+
* const { useDataContext, DataProvider } = createDataContext<{ count: number }>();
|
|
993
|
+
*
|
|
994
|
+
* function Counter() {
|
|
995
|
+
* const { count } = useDataContext();
|
|
996
|
+
* return <span>{count}</span>;
|
|
997
|
+
* }
|
|
998
|
+
*
|
|
999
|
+
* function App() {
|
|
1000
|
+
* return (
|
|
1001
|
+
* <DataProvider value={{ count: 42 }}>
|
|
1002
|
+
* <Counter />
|
|
1003
|
+
* </DataProvider>
|
|
1004
|
+
* );
|
|
1005
|
+
* }
|
|
1006
|
+
*/
|
|
1007
|
+
declare const createDataContext: <T>() => {
|
|
1008
|
+
readonly useDataContext: () => T & ({} | null);
|
|
1009
|
+
readonly DataProvider: ({ children, value }: {
|
|
1010
|
+
children: ReactNode;
|
|
1011
|
+
value: T;
|
|
1012
|
+
}) => React.JSX.Element;
|
|
1013
|
+
};
|
|
1014
|
+
|
|
950
1015
|
type TCountdownActions = {
|
|
951
1016
|
/** 目前秒數 */
|
|
952
1017
|
countdown: number;
|
|
@@ -1080,4 +1145,4 @@ declare const getLocalStorage: <T>(key: string, deCode?: boolean) => T | undefin
|
|
|
1080
1145
|
*/
|
|
1081
1146
|
declare const setLocalStorage: (key: string, value: Record<string, any>, enCode?: boolean) => void;
|
|
1082
1147
|
|
|
1083
|
-
export { ByteSize, type PartialBy, QueryProvider, type RequiredBy, type TCountdownActions, adToRocEra, camelCase2PascalCase, camelCase2SnakeCase, camelString2PascalString, camelString2SnakeString, convertBytes, createEnumLikeObject, debounce, deepMerge, downloadFile, extractEnumLikeObject, fakeApi, formatAmount, formatBytes, formatStarMask, generatePeriodArray, getCurrentPeriod, getLocalStorage, getMimeType, invariant, isChinese, isDateString, isDateTimeString, isEmail, isEnglish, isEqual, isNonZeroStart, isNumber, isNumberAtLeastN, isNumberN, isNumberNM, isServer, isTWMobile, isTWPhone, isTimeString, isValidPassword, maskString, mergeRefs, objectToSearchParams, omit, omitByValue, pascalCase2CamelCase, pascalCase2SnakeCase, pascalString2CamelString, pascalString2SnakeString, pick, pickByValue, rocEraToAd, searchParamsToObject, setLocalStorage, snakeCase2CamelCase, snakeCase2PascalCase, snakeString2CamelString, snakeString2PascalString, throttle, useCountdown, useQueryContext, useValue, validTaxId, validateDateString, validateFileType, wait };
|
|
1148
|
+
export { ByteSize, type PartialBy, QueryProvider, type RequiredBy, type TCountdownActions, adToRocEra, camelCase2PascalCase, camelCase2SnakeCase, camelString2PascalString, camelString2SnakeString, convertBytes, createDataContext, createEnumLikeObject, debounce, deepMerge, downloadFile, extractEnumLikeObject, fakeApi, formatAmount, formatBytes, formatStarMask, generatePeriodArray, getCurrentPeriod, getLocalStorage, getMimeType, invariant, isChinese, isDateString, isDateTimeString, isEmail, isEnglish, isEqual, isNonZeroStart, isNumber, isNumberAtLeastN, isNumberN, isNumberNM, isServer, isTWMobile, isTWPhone, isTimeString, isValidPassword, maskString, mergeRefs, objectToSearchParams, omit, omitByValue, pascalCase2CamelCase, pascalCase2SnakeCase, pascalString2CamelString, pascalString2SnakeString, pick, pickByValue, rocEraToAd, searchParamsToObject, setLocalStorage, snakeCase2CamelCase, snakeCase2PascalCase, snakeString2CamelString, snakeString2PascalString, throttle, useCountdown, useQueryContext, useValue, validTaxId, validateDateString, validateFileType, wait };
|
package/dist/es/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import dayjs from 'dayjs';
|
|
2
2
|
export { Q as QueryProvider, u as useQueryContext } from './queryStore-client-vG-bXFYm.mjs';
|
|
3
|
+
import React, { useContext, useMemo, createContext, useState, useCallback } from 'react';
|
|
3
4
|
export { u as useCountdown } from './useCountdown-client-t52WIHfq.mjs';
|
|
4
|
-
import { useState, useCallback } from 'react';
|
|
5
5
|
export { d as downloadFile } from './download-client-CnaJ0p_f.mjs';
|
|
6
6
|
export { g as getLocalStorage, s as setLocalStorage } from './webStorage-client-Pd-loNCg.mjs';
|
|
7
7
|
|
|
@@ -171,56 +171,41 @@ const FILE_SIZE_UNITS$1 = [
|
|
|
171
171
|
...acc,
|
|
172
172
|
[key]: value[valueKey]
|
|
173
173
|
}), {});
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
*
|
|
177
|
-
* 輸入的物件格式為
|
|
178
|
-
*
|
|
179
|
-
* ```ts
|
|
180
|
-
* { key: { value: string, label: string }, key2: { value: string, label: string }, ... }
|
|
181
|
-
* ```
|
|
182
|
-
*
|
|
183
|
-
* 會生成以下三個物件
|
|
184
|
-
*
|
|
185
|
-
* enum 物件 - 根據 value 生成的 enum 物件
|
|
186
|
-
*
|
|
187
|
-
* enum 列表 - 根據 value 與 label 生成的列表
|
|
188
|
-
*
|
|
189
|
-
* 取得 label 的方法 - 傳入 value 可以取得對應的 label
|
|
190
|
-
*
|
|
191
|
-
* @param obj 要處理的物件
|
|
192
|
-
* @param name 生成的 enum 名稱
|
|
193
|
-
*
|
|
194
|
-
* @example
|
|
195
|
-
*
|
|
196
|
-
* ```ts
|
|
197
|
-
* const Status = { Enabled: { value: 'enabled', label: '啟用' }, Disabled: { value: 'disabled', label: '停用' } };
|
|
198
|
-
*
|
|
199
|
-
* const { EnumStatus, StatusList, getStatusLabel } = createEnumLikeObject(Status, 'Status');
|
|
200
|
-
*
|
|
201
|
-
* console.log(EnumStatus); // { Enabled: 'enabled', Disabled: 'disabled' }
|
|
202
|
-
*
|
|
203
|
-
* console.log(StatusList); // [ { key: 'Enabled', value: 'enabled', label: '啟用' }, { key: 'Disabled', value: 'disabled', label: '停用' } ]
|
|
204
|
-
*
|
|
205
|
-
* console.log(getStatusLabel('enabled')); // '啟用'
|
|
206
|
-
* ```
|
|
207
|
-
*/ const createEnumLikeObject = (obj, name)=>{
|
|
174
|
+
// 實作
|
|
175
|
+
function createEnumLikeObject(obj, name, scene) {
|
|
208
176
|
const Enum = extractEnumLikeObject(obj, 'value');
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
177
|
+
// List:根據有無 scene 做不同處理
|
|
178
|
+
let list;
|
|
179
|
+
if (scene) {
|
|
180
|
+
// scenes 版本:解構指定 scene 的 label
|
|
181
|
+
list = Object.entries(obj).map(([key, item])=>({
|
|
182
|
+
key,
|
|
183
|
+
value: item.value,
|
|
184
|
+
...item.scenes?.[scene] ?? {}
|
|
185
|
+
}));
|
|
186
|
+
} else {
|
|
187
|
+
// label only 版本
|
|
188
|
+
list = Object.entries(obj).map(([key, item])=>({
|
|
189
|
+
key,
|
|
190
|
+
...item
|
|
191
|
+
}));
|
|
192
|
+
}
|
|
193
|
+
function getLabel(value) {
|
|
214
194
|
const targetItem = list.find((item)=>item.value === value);
|
|
215
|
-
if (!targetItem) return value;
|
|
216
|
-
|
|
217
|
-
|
|
195
|
+
if (!targetItem) return String(value);
|
|
196
|
+
if ('scenes' in targetItem && scene) {
|
|
197
|
+
const sceneObj = targetItem.scenes?.[scene];
|
|
198
|
+
return sceneObj && sceneObj.label || String(value);
|
|
199
|
+
}
|
|
200
|
+
if ('label' in targetItem) return targetItem.label;
|
|
201
|
+
return String(value);
|
|
202
|
+
}
|
|
218
203
|
return {
|
|
219
204
|
[`Enum${name}`]: Enum,
|
|
220
205
|
[`${name}List`]: list,
|
|
221
206
|
[`get${name}Label`]: getLabel
|
|
222
207
|
};
|
|
223
|
-
}
|
|
208
|
+
}
|
|
224
209
|
|
|
225
210
|
/**
|
|
226
211
|
* simulate a fake api request
|
|
@@ -1052,6 +1037,57 @@ const FILE_SIZE_UNITS = [
|
|
|
1052
1037
|
return dayjs(dateString, format, true).isValid();
|
|
1053
1038
|
};
|
|
1054
1039
|
|
|
1040
|
+
/**
|
|
1041
|
+
* Creates a strongly-typed React Context and Provider pair for data sharing.
|
|
1042
|
+
*
|
|
1043
|
+
* This utility helps you avoid prop drilling by providing a reusable way to define
|
|
1044
|
+
* context with strict type inference. It returns a custom hook for consuming the context
|
|
1045
|
+
* and a Provider component for supplying context values.
|
|
1046
|
+
*
|
|
1047
|
+
* @template T - The value type for the context.
|
|
1048
|
+
* @returns {Object} An object containing:
|
|
1049
|
+
* - useDataContext: A custom hook to access the context value. Throws an error if used outside the provider.
|
|
1050
|
+
* - DataProvider: A Provider component to wrap your component tree and supply the context value.
|
|
1051
|
+
*
|
|
1052
|
+
* @example
|
|
1053
|
+
* // Example usage:
|
|
1054
|
+
* const { useDataContext, DataProvider } = createDataContext<{ count: number }>();
|
|
1055
|
+
*
|
|
1056
|
+
* function Counter() {
|
|
1057
|
+
* const { count } = useDataContext();
|
|
1058
|
+
* return <span>{count}</span>;
|
|
1059
|
+
* }
|
|
1060
|
+
*
|
|
1061
|
+
* function App() {
|
|
1062
|
+
* return (
|
|
1063
|
+
* <DataProvider value={{ count: 42 }}>
|
|
1064
|
+
* <Counter />
|
|
1065
|
+
* </DataProvider>
|
|
1066
|
+
* );
|
|
1067
|
+
* }
|
|
1068
|
+
*/ const createDataContext = ()=>{
|
|
1069
|
+
const Context = /*#__PURE__*/ createContext(undefined);
|
|
1070
|
+
const useDataContext = ()=>{
|
|
1071
|
+
const context = useContext(Context);
|
|
1072
|
+
if (context === undefined) {
|
|
1073
|
+
throw new Error(`useDataContext must be used within a DataProvider`);
|
|
1074
|
+
}
|
|
1075
|
+
return context;
|
|
1076
|
+
};
|
|
1077
|
+
const DataProvider = ({ children, value })=>{
|
|
1078
|
+
const memoized = useMemo(()=>value, [
|
|
1079
|
+
value
|
|
1080
|
+
]);
|
|
1081
|
+
return /*#__PURE__*/ React.createElement(Context.Provider, {
|
|
1082
|
+
value: memoized
|
|
1083
|
+
}, children);
|
|
1084
|
+
};
|
|
1085
|
+
return {
|
|
1086
|
+
useDataContext,
|
|
1087
|
+
DataProvider
|
|
1088
|
+
};
|
|
1089
|
+
};
|
|
1090
|
+
|
|
1055
1091
|
/**
|
|
1056
1092
|
* A hook to manage a value.
|
|
1057
1093
|
*
|
|
@@ -1176,4 +1212,4 @@ function mergeRefs(refs) {
|
|
|
1176
1212
|
return dayjs(endMonth, 'YYYYMM').subtract(1911, 'year').format('YYYYMM').substring(1);
|
|
1177
1213
|
};
|
|
1178
1214
|
|
|
1179
|
-
export { ByteSize, adToRocEra, camelCase2PascalCase, camelCase2SnakeCase, camelString2PascalString, camelString2SnakeString, convertBytes, createEnumLikeObject, debounce, deepMerge, extractEnumLikeObject, fakeApi, formatAmount, formatBytes, formatStarMask, generatePeriodArray, getCurrentPeriod, getMimeType, invariant, isChinese, isDateString, isDateTimeString, isEmail, isEnglish, isEqual, isNonZeroStart, isNumber, isNumberAtLeastN, isNumberN, isNumberNM, isServer, isTWMobile, isTWPhone, isTimeString, isValidPassword, maskString, mergeRefs, objectToSearchParams, omit, omitByValue, pascalCase2CamelCase, pascalCase2SnakeCase, pascalString2CamelString, pascalString2SnakeString, pick, pickByValue, rocEraToAd, searchParamsToObject, snakeCase2CamelCase, snakeCase2PascalCase, snakeString2CamelString, snakeString2PascalString, throttle, useValue, validTaxId, validateDateString, validateFileType, wait };
|
|
1215
|
+
export { ByteSize, adToRocEra, camelCase2PascalCase, camelCase2SnakeCase, camelString2PascalString, camelString2SnakeString, convertBytes, createDataContext, createEnumLikeObject, debounce, deepMerge, extractEnumLikeObject, fakeApi, formatAmount, formatBytes, formatStarMask, generatePeriodArray, getCurrentPeriod, getMimeType, invariant, isChinese, isDateString, isDateTimeString, isEmail, isEnglish, isEqual, isNonZeroStart, isNumber, isNumberAtLeastN, isNumberN, isNumberNM, isServer, isTWMobile, isTWPhone, isTimeString, isValidPassword, maskString, mergeRefs, objectToSearchParams, omit, omitByValue, pascalCase2CamelCase, pascalCase2SnakeCase, pascalString2CamelString, pascalString2SnakeString, pick, pickByValue, rocEraToAd, searchParamsToObject, snakeCase2CamelCase, snakeCase2PascalCase, snakeString2CamelString, snakeString2PascalString, throttle, useValue, validTaxId, validateDateString, validateFileType, wait };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gateweb/react-utils",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"description": "React Utils for GateWeb",
|
|
5
5
|
"homepage": "https://github.com/GatewebSolutions/react-utils",
|
|
6
6
|
"files": [
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"@commitlint/cli": "^19.5.0",
|
|
46
46
|
"@commitlint/config-conventional": "^19.5.0",
|
|
47
47
|
"@gateweb/eslint-config-gateweb": "^1.0.6",
|
|
48
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
48
49
|
"@testing-library/react": "^16.2.0",
|
|
49
50
|
"@types/jest": "^29.5.13",
|
|
50
51
|
"@types/node": "^22.7.7",
|
|
@@ -66,6 +67,7 @@
|
|
|
66
67
|
"scripts": {
|
|
67
68
|
"run-code": "ts-node src/period.ts",
|
|
68
69
|
"test": "vitest run",
|
|
70
|
+
"test:watch": "vitest",
|
|
69
71
|
"test:coverage": "vitest run --coverage",
|
|
70
72
|
"test:ui": "vitest --ui --coverage.enabled=true",
|
|
71
73
|
"build": "bunchee",
|