@gateweb/react-utils 1.16.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/client.d.ts +289 -0
- package/dist/cjs/client.js +119 -0
- package/dist/cjs/index.d.ts +70 -193
- package/dist/cjs/index.js +87 -111
- package/dist/cjs/{queryStore-12s-q_SLGgYH.js → queryStore-12s-JzzXvjJC.js} +4 -0
- package/dist/cjs/store-12s-s9lu_ZPn.js +122 -0
- package/dist/cjs/webStorage-12s-0RtNO_uc.js +136 -0
- package/dist/es/client.d.mts +289 -0
- package/dist/es/client.mjs +103 -0
- package/dist/es/index.d.mts +70 -193
- package/dist/es/index.mjs +84 -101
- package/dist/es/{queryStore-12s-CFQTVwrg.mjs → queryStore-12s-Dkb-Af1n.mjs} +4 -0
- package/dist/es/store-12s-DsXc3Uo0.mjs +118 -0
- package/dist/es/webStorage-12s-Bo7x8q5t.mjs +135 -0
- package/package.json +11 -1
- package/dist/cjs/webStorage-12s-DHr9PcPl.js +0 -53
- package/dist/es/webStorage-12s-W1DItzhS.mjs +0 -52
package/dist/es/index.mjs
CHANGED
|
@@ -1,10 +1,87 @@
|
|
|
1
1
|
import dayjs from 'dayjs';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
|
|
3
|
+
/* eslint-disable no-bitwise */ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
4
|
+
/**
|
|
5
|
+
* 將位元組陣列編碼為 base64 字串
|
|
6
|
+
*
|
|
7
|
+
* @param bytes 要編碼的位元組陣列
|
|
8
|
+
* @returns base64 編碼的字串
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* const bytes = new TextEncoder().encode('Hello World');
|
|
13
|
+
* const encoded = encodeBase64(bytes);
|
|
14
|
+
* console.log(encoded); // 'SGVsbG8gV29ybGQ='
|
|
15
|
+
* ```
|
|
16
|
+
*/ const encodeBase64 = (bytes)=>{
|
|
17
|
+
let out = '';
|
|
18
|
+
for(let i = 0; i < bytes.length; i += 3){
|
|
19
|
+
const n = bytes[i] << 16 | (bytes[i + 1] || 0) << 8 | (bytes[i + 2] || 0);
|
|
20
|
+
out += chars[n >> 18 & 63] + chars[n >> 12 & 63] + (i + 1 < bytes.length ? chars[n >> 6 & 63] : '=') + (i + 2 < bytes.length ? chars[n & 63] : '=');
|
|
21
|
+
}
|
|
22
|
+
return out;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* 將 base64 字串解碼為位元組陣列
|
|
26
|
+
*
|
|
27
|
+
* @param base64 要解碼的 base64 字串(支援 base64url 格式)
|
|
28
|
+
* @returns 解碼後的位元組陣列
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* const decoded = decodeBase64('SGVsbG8gV29ybGQ=');
|
|
33
|
+
* const text = new TextDecoder().decode(decoded);
|
|
34
|
+
* console.log(text); // 'Hello World'
|
|
35
|
+
* ```
|
|
36
|
+
*/ const decodeBase64 = (base64)=>{
|
|
37
|
+
let out = base64.replace(/-/g, '+').replace(/_/g, '/'); // 支援 base64url
|
|
38
|
+
while(out.length % 4)out += '='; // 自動補 "="
|
|
39
|
+
const buffer = [];
|
|
40
|
+
for(let i = 0; i < out.length; i += 4){
|
|
41
|
+
const n = chars.indexOf(out[i]) << 18 | chars.indexOf(out[i + 1]) << 12 | (chars.indexOf(out[i + 2]) & 63) << 6 | chars.indexOf(out[i + 3]) & 63;
|
|
42
|
+
buffer.push(n >> 16 & 255);
|
|
43
|
+
if (out[i + 2] !== '=') buffer.push(n >> 8 & 255);
|
|
44
|
+
if (out[i + 3] !== '=') buffer.push(n & 255);
|
|
45
|
+
}
|
|
46
|
+
return new Uint8Array(buffer);
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* 將 JSON 資料編碼為 base64 字串
|
|
50
|
+
*
|
|
51
|
+
* @param data 要編碼的 JSON 可序列化資料
|
|
52
|
+
* @returns base64 編碼的字串
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* const data = { name: 'John', age: 30 };
|
|
57
|
+
* const encoded = encodeJson(data);
|
|
58
|
+
* console.log(encoded); // 'eyJuYW1lIjoiSm9obiIsImFnZSI6MzB9'
|
|
59
|
+
*
|
|
60
|
+
* const array = [1, 2, 3];
|
|
61
|
+
* const encodedArray = encodeJson(array);
|
|
62
|
+
* ```
|
|
63
|
+
*/ const encodeJson = (data)=>{
|
|
64
|
+
const json = JSON.stringify(data);
|
|
65
|
+
return encodeBase64(new TextEncoder().encode(json));
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* 將 base64 字串解碼為 JSON 資料
|
|
69
|
+
*
|
|
70
|
+
* @param b64 要解碼的 base64 字串
|
|
71
|
+
* @returns 解碼後的資料
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* const encoded = 'eyJuYW1lIjoiSm9obiIsImFnZSI6MzB9';
|
|
76
|
+
* const decoded = decodeJson<{ name: string; age: number }>(encoded);
|
|
77
|
+
* console.log(decoded); // { name: 'John', age: 30 }
|
|
78
|
+
*
|
|
79
|
+
* const decodedArray = decodeJson<number[]>(encodedArray);
|
|
80
|
+
* ```
|
|
81
|
+
*/ const decodeJson = (b64)=>{
|
|
82
|
+
const json = new TextDecoder().decode(decodeBase64(b64));
|
|
83
|
+
return JSON.parse(json);
|
|
84
|
+
};
|
|
8
85
|
|
|
9
86
|
const FILE_SIZE_UNITS$1 = [
|
|
10
87
|
'Bytes',
|
|
@@ -1239,100 +1316,6 @@ const FILE_SIZE_UNITS = [
|
|
|
1239
1316
|
* }
|
|
1240
1317
|
*/ const isNil = (value)=>value === null || value === undefined;
|
|
1241
1318
|
|
|
1242
|
-
/**
|
|
1243
|
-
* Creates a strongly-typed React Context and Provider pair for data sharing.
|
|
1244
|
-
*
|
|
1245
|
-
* This utility helps you avoid prop drilling by providing a reusable way to define
|
|
1246
|
-
* context with strict type inference. It returns a custom hook for consuming the context
|
|
1247
|
-
* and a Provider component for supplying context values.
|
|
1248
|
-
*
|
|
1249
|
-
* @template T - The value type for the context.
|
|
1250
|
-
* @returns {object} An object containing:
|
|
1251
|
-
* - useDataContext: A custom hook to access the context value. Throws an error if used outside the provider.
|
|
1252
|
-
* - DataProvider: A Provider component to wrap your component tree and supply the context value.
|
|
1253
|
-
*
|
|
1254
|
-
* @example
|
|
1255
|
-
* // Example usage:
|
|
1256
|
-
* const { useDataContext, DataProvider } = createDataContext<{ count: number }>();
|
|
1257
|
-
*
|
|
1258
|
-
* function Counter() {
|
|
1259
|
-
* const { count } = useDataContext();
|
|
1260
|
-
* return <span>{count}</span>;
|
|
1261
|
-
* }
|
|
1262
|
-
*
|
|
1263
|
-
* function App() {
|
|
1264
|
-
* return (
|
|
1265
|
-
* <DataProvider value={{ count: 42 }}>
|
|
1266
|
-
* <Counter />
|
|
1267
|
-
* </DataProvider>
|
|
1268
|
-
* );
|
|
1269
|
-
* }
|
|
1270
|
-
*/ const createDataContext = ()=>{
|
|
1271
|
-
const Context = /*#__PURE__*/ createContext(undefined);
|
|
1272
|
-
const useDataContext = ()=>{
|
|
1273
|
-
const context = useContext(Context);
|
|
1274
|
-
if (context === undefined) {
|
|
1275
|
-
throw new Error(`useDataContext must be used within a DataProvider`);
|
|
1276
|
-
}
|
|
1277
|
-
return context;
|
|
1278
|
-
};
|
|
1279
|
-
const DataProvider = ({ children, value })=>{
|
|
1280
|
-
const memoized = useMemo(()=>value, [
|
|
1281
|
-
value
|
|
1282
|
-
]);
|
|
1283
|
-
return /*#__PURE__*/ React.createElement(Context.Provider, {
|
|
1284
|
-
value: memoized
|
|
1285
|
-
}, children);
|
|
1286
|
-
};
|
|
1287
|
-
return {
|
|
1288
|
-
useDataContext,
|
|
1289
|
-
DataProvider
|
|
1290
|
-
};
|
|
1291
|
-
};
|
|
1292
|
-
|
|
1293
|
-
/**
|
|
1294
|
-
* A hook to manage a value.
|
|
1295
|
-
*
|
|
1296
|
-
* @example
|
|
1297
|
-
*
|
|
1298
|
-
* ```tsx
|
|
1299
|
-
* const MyComponent = ({ value }: { value?: number }) => {
|
|
1300
|
-
* const [currentValue, setCurrentValue] = useValue({ value });
|
|
1301
|
-
* };
|
|
1302
|
-
* ```
|
|
1303
|
-
*/ const useValue = ({ value, defaultValue })=>{
|
|
1304
|
-
if (value === undefined && defaultValue === undefined) {
|
|
1305
|
-
throw new Error('Either `value` or `defaultValue` must be provided.');
|
|
1306
|
-
}
|
|
1307
|
-
const isControlled = value !== undefined;
|
|
1308
|
-
const [internalValue, setInternalValue] = useState(defaultValue);
|
|
1309
|
-
const setValue = useCallback((newValue)=>{
|
|
1310
|
-
if (!isControlled) {
|
|
1311
|
-
setInternalValue(newValue);
|
|
1312
|
-
}
|
|
1313
|
-
}, [
|
|
1314
|
-
isControlled
|
|
1315
|
-
]);
|
|
1316
|
-
const currentValue = isControlled ? value : internalValue;
|
|
1317
|
-
return [
|
|
1318
|
-
currentValue,
|
|
1319
|
-
setValue
|
|
1320
|
-
];
|
|
1321
|
-
};
|
|
1322
|
-
|
|
1323
|
-
function mergeRefs(refs) {
|
|
1324
|
-
return (value)=>{
|
|
1325
|
-
refs.forEach((ref)=>{
|
|
1326
|
-
if (typeof ref === 'function') {
|
|
1327
|
-
ref(value);
|
|
1328
|
-
} else if (ref != null) {
|
|
1329
|
-
// eslint-disable-next-line no-param-reassign
|
|
1330
|
-
ref.current = value;
|
|
1331
|
-
}
|
|
1332
|
-
});
|
|
1333
|
-
};
|
|
1334
|
-
}
|
|
1335
|
-
|
|
1336
1319
|
/**
|
|
1337
1320
|
* 民國年轉西元年
|
|
1338
1321
|
* @param dateString 日期字串
|
|
@@ -1414,4 +1397,4 @@ function mergeRefs(refs) {
|
|
|
1414
1397
|
return dayjs(endMonth, 'YYYYMM').subtract(1911, 'year').format('YYYYMM').substring(1);
|
|
1415
1398
|
};
|
|
1416
1399
|
|
|
1417
|
-
export { ByteSize, MimeTypeMap, OtherMimeType, adToRocEra, camelCase2PascalCase, camelCase2SnakeCase, camelString2PascalString, camelString2SnakeString, convertBytes,
|
|
1400
|
+
export { ByteSize, MimeTypeMap, OtherMimeType, adToRocEra, camelCase2PascalCase, camelCase2SnakeCase, camelString2PascalString, camelString2SnakeString, convertBytes, createEnumLikeObject, debounce, decodeBase64, decodeJson, deepClone, deepMerge, encodeBase64, encodeJson, extractEnumLikeObject, fakeApi, formatAmount, formatBytes, formatStarMask, generatePeriodArray, getCurrentPeriod, getMimeType, invariant, isChinese, isDateString, isDateTimeString, isEmail, isEnglish, isEqual, isNil, isNonZeroStart, isNumber, isNumberAtLeastN, isNumberN, isNumberNM, isServer, isTWMobile, isTWPhone, isTimeString, isValidPassword, maskString, mergeConfig, objectToSearchParams, omit, omitByValue, parseFileInfoFromFilename, parseFilenameFromDisposition, pascalCase2CamelCase, pascalCase2SnakeCase, pascalString2CamelString, pascalString2SnakeString, pick, pickByValue, renameKey, rocEraToAd, searchParamsToObject, snakeCase2CamelCase, snakeCase2PascalCase, snakeString2CamelString, snakeString2PascalString, throttle, validTaxId, validateDateString, validateFileType, wait };
|
|
@@ -26,6 +26,8 @@ const createQueryStore = ({ query, handleChangeQuery })=>createStore()((set)=>({
|
|
|
26
26
|
const QueryContext = /*#__PURE__*/ createContext(null);
|
|
27
27
|
/**
|
|
28
28
|
* Provider to provide the store to the context
|
|
29
|
+
*
|
|
30
|
+
* @deprecated use `StoreProvider` from `core/store` instead
|
|
29
31
|
*/ const QueryProvider = ({ children, query, handleChangeQuery })=>{
|
|
30
32
|
const storeRef = useRef(null);
|
|
31
33
|
if (!storeRef.current) {
|
|
@@ -57,6 +59,8 @@ const QueryContext = /*#__PURE__*/ createContext(null);
|
|
|
57
59
|
* const result2 = useQueryContext<MyObject>()(q => q.changeQuery);
|
|
58
60
|
* const result3 = useQueryContext<MyObject>()(q => q.query);
|
|
59
61
|
* ```
|
|
62
|
+
*
|
|
63
|
+
* @deprecated use `useStoreContext` from `core/store` instead
|
|
60
64
|
*/ const useQueryContext = ()=>{
|
|
61
65
|
const store = useContext(QueryContext);
|
|
62
66
|
if (!store) throw new Error('Missing QueryContext.Provider in the tree');
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import React, { useContext, createContext, useRef } from 'react';
|
|
3
|
+
import { createStore } from 'zustand';
|
|
4
|
+
import { useStoreWithEqualityFn } from 'zustand/traditional';
|
|
5
|
+
|
|
6
|
+
const isObject = (value)=>value !== null && typeof value === 'object';
|
|
7
|
+
/**
|
|
8
|
+
* merge two objects deeply
|
|
9
|
+
*
|
|
10
|
+
* @param target - the target object
|
|
11
|
+
* @param source - the source object
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
*
|
|
15
|
+
* const obja = { a: { a1: { a11: 'value 1', a12: 'value 2' }, a2: 'value 3' }, b: 'value 4' };
|
|
16
|
+
* const objb = { a: { a1: { a13: 'value 5', a14: 'value 6' }, a3: 'value 7' }};
|
|
17
|
+
*
|
|
18
|
+
* 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' }
|
|
19
|
+
*
|
|
20
|
+
*/ const deepMerge = (target, source)=>Object.entries(source).reduce((acc, [key, value])=>{
|
|
21
|
+
if (isObject(value)) {
|
|
22
|
+
acc[key] = key in target && isObject(target[key]) ? deepMerge(target[key], value) : value;
|
|
23
|
+
} else {
|
|
24
|
+
acc[key] = value;
|
|
25
|
+
}
|
|
26
|
+
return acc;
|
|
27
|
+
}, {
|
|
28
|
+
...target
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* create a zustand store with changeStore method
|
|
33
|
+
*/ const createZustandStore = ({ store, handleChangeStore, defaultMerge })=>createStore()((set)=>({
|
|
34
|
+
store,
|
|
35
|
+
changeStore: (updater, options)=>{
|
|
36
|
+
set((pre)=>{
|
|
37
|
+
const patch = typeof updater === 'function' ? updater(pre.store) : updater;
|
|
38
|
+
const newStore = handleChangeStore ? handleChangeStore(pre.store, patch) : patch;
|
|
39
|
+
const strategy = options?.merge ?? defaultMerge;
|
|
40
|
+
if (strategy === 'shallow') {
|
|
41
|
+
return {
|
|
42
|
+
store: {
|
|
43
|
+
...pre.store,
|
|
44
|
+
...newStore
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
store: deepMerge(pre.store, newStore)
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}));
|
|
54
|
+
/**
|
|
55
|
+
* create a store context with provider and hooks
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
*
|
|
59
|
+
* ```tsx
|
|
60
|
+
* // create a store context
|
|
61
|
+
* const { StoreProvider, useStore, useStoreApi } = createStoreContext<MyObject>();
|
|
62
|
+
*
|
|
63
|
+
* // use the StoreProvider to provide the store to the context
|
|
64
|
+
* <StoreProvider store={{ key: 'value' }}>
|
|
65
|
+
* <App />
|
|
66
|
+
* </StoreProvider>
|
|
67
|
+
* ```
|
|
68
|
+
*/ const createStoreContext = ()=>{
|
|
69
|
+
const StoreContext = /*#__PURE__*/ createContext(null);
|
|
70
|
+
/**
|
|
71
|
+
* Provider to provide the store to the context
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
*
|
|
75
|
+
* ```tsx
|
|
76
|
+
* // use the StoreProvider to provide the store to the context
|
|
77
|
+
* const { StoreProvider } = createStoreContext<MyObject>();
|
|
78
|
+
*
|
|
79
|
+
* <StoreProvider store={{ key: 'value' }}>
|
|
80
|
+
* <App />
|
|
81
|
+
* </StoreProvider>
|
|
82
|
+
* ```
|
|
83
|
+
*/ const StoreProvider = ({ children, store = {}, handleChangeStore, defaultMerge = 'shallow' })=>{
|
|
84
|
+
const storeRef = useRef(null);
|
|
85
|
+
if (!storeRef.current) {
|
|
86
|
+
storeRef.current = createZustandStore({
|
|
87
|
+
store,
|
|
88
|
+
handleChangeStore,
|
|
89
|
+
defaultMerge
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return /*#__PURE__*/ React.createElement(StoreContext.Provider, {
|
|
93
|
+
value: storeRef.current
|
|
94
|
+
}, children);
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* a hook to get the store from the context
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```tsx
|
|
101
|
+
* const { useStore } = createStoreContext<MyObject>();
|
|
102
|
+
* const value = useStore(s => s.store);
|
|
103
|
+
* ```
|
|
104
|
+
*
|
|
105
|
+
* @param selector - the selector to get the state from the store
|
|
106
|
+
* @param equalityFn - the equality function to compare the previous and next state
|
|
107
|
+
*/ const useStore = (selector, equalityFn)=>{
|
|
108
|
+
const store = useContext(StoreContext);
|
|
109
|
+
if (!store) throw new Error('Missing StoreContext.Provider in the tree');
|
|
110
|
+
return useStoreWithEqualityFn(store, selector, equalityFn);
|
|
111
|
+
};
|
|
112
|
+
return {
|
|
113
|
+
StoreProvider,
|
|
114
|
+
useStore
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export { createStoreContext as c };
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
/* eslint-disable no-bitwise */ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
3
|
+
/**
|
|
4
|
+
* 將位元組陣列編碼為 base64 字串
|
|
5
|
+
*
|
|
6
|
+
* @param bytes 要編碼的位元組陣列
|
|
7
|
+
* @returns base64 編碼的字串
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* const bytes = new TextEncoder().encode('Hello World');
|
|
12
|
+
* const encoded = encodeBase64(bytes);
|
|
13
|
+
* console.log(encoded); // 'SGVsbG8gV29ybGQ='
|
|
14
|
+
* ```
|
|
15
|
+
*/ const encodeBase64 = (bytes)=>{
|
|
16
|
+
let out = '';
|
|
17
|
+
for(let i = 0; i < bytes.length; i += 3){
|
|
18
|
+
const n = bytes[i] << 16 | (bytes[i + 1] || 0) << 8 | (bytes[i + 2] || 0);
|
|
19
|
+
out += chars[n >> 18 & 63] + chars[n >> 12 & 63] + (i + 1 < bytes.length ? chars[n >> 6 & 63] : '=') + (i + 2 < bytes.length ? chars[n & 63] : '=');
|
|
20
|
+
}
|
|
21
|
+
return out;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* 將 base64 字串解碼為位元組陣列
|
|
25
|
+
*
|
|
26
|
+
* @param base64 要解碼的 base64 字串(支援 base64url 格式)
|
|
27
|
+
* @returns 解碼後的位元組陣列
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* const decoded = decodeBase64('SGVsbG8gV29ybGQ=');
|
|
32
|
+
* const text = new TextDecoder().decode(decoded);
|
|
33
|
+
* console.log(text); // 'Hello World'
|
|
34
|
+
* ```
|
|
35
|
+
*/ const decodeBase64 = (base64)=>{
|
|
36
|
+
let out = base64.replace(/-/g, '+').replace(/_/g, '/'); // 支援 base64url
|
|
37
|
+
while(out.length % 4)out += '='; // 自動補 "="
|
|
38
|
+
const buffer = [];
|
|
39
|
+
for(let i = 0; i < out.length; i += 4){
|
|
40
|
+
const n = chars.indexOf(out[i]) << 18 | chars.indexOf(out[i + 1]) << 12 | (chars.indexOf(out[i + 2]) & 63) << 6 | chars.indexOf(out[i + 3]) & 63;
|
|
41
|
+
buffer.push(n >> 16 & 255);
|
|
42
|
+
if (out[i + 2] !== '=') buffer.push(n >> 8 & 255);
|
|
43
|
+
if (out[i + 3] !== '=') buffer.push(n & 255);
|
|
44
|
+
}
|
|
45
|
+
return new Uint8Array(buffer);
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* 將 JSON 資料編碼為 base64 字串
|
|
49
|
+
*
|
|
50
|
+
* @param data 要編碼的 JSON 可序列化資料
|
|
51
|
+
* @returns base64 編碼的字串
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* const data = { name: 'John', age: 30 };
|
|
56
|
+
* const encoded = encodeJson(data);
|
|
57
|
+
* console.log(encoded); // 'eyJuYW1lIjoiSm9obiIsImFnZSI6MzB9'
|
|
58
|
+
*
|
|
59
|
+
* const array = [1, 2, 3];
|
|
60
|
+
* const encodedArray = encodeJson(array);
|
|
61
|
+
* ```
|
|
62
|
+
*/ const encodeJson = (data)=>{
|
|
63
|
+
const json = JSON.stringify(data);
|
|
64
|
+
return encodeBase64(new TextEncoder().encode(json));
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* 將 base64 字串解碼為 JSON 資料
|
|
68
|
+
*
|
|
69
|
+
* @param b64 要解碼的 base64 字串
|
|
70
|
+
* @returns 解碼後的資料
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```ts
|
|
74
|
+
* const encoded = 'eyJuYW1lIjoiSm9obiIsImFnZSI6MzB9';
|
|
75
|
+
* const decoded = decodeJson<{ name: string; age: number }>(encoded);
|
|
76
|
+
* console.log(decoded); // { name: 'John', age: 30 }
|
|
77
|
+
*
|
|
78
|
+
* const decodedArray = decodeJson<number[]>(encodedArray);
|
|
79
|
+
* ```
|
|
80
|
+
*/ const decodeJson = (b64)=>{
|
|
81
|
+
const json = new TextDecoder().decode(decodeBase64(b64));
|
|
82
|
+
return JSON.parse(json);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 從 localStorage 取得資料,支援槽狀取值(Json 物件)
|
|
87
|
+
*
|
|
88
|
+
* @param key 鍵值
|
|
89
|
+
* @param deCode 是否解碼
|
|
90
|
+
* @returns 取得的資料
|
|
91
|
+
* @example
|
|
92
|
+
* const data = getLocalStorage('key');
|
|
93
|
+
*
|
|
94
|
+
* const data = getLocalStorage('key.subKey');
|
|
95
|
+
*/ const getLocalStorage = (key, deCode = true)=>{
|
|
96
|
+
const keys = key.split('.');
|
|
97
|
+
const storage = localStorage.getItem(keys[0]);
|
|
98
|
+
if (!storage) return undefined;
|
|
99
|
+
let currentObject;
|
|
100
|
+
try {
|
|
101
|
+
if (deCode) {
|
|
102
|
+
currentObject = decodeJson(storage);
|
|
103
|
+
} else {
|
|
104
|
+
currentObject = JSON.parse(storage);
|
|
105
|
+
}
|
|
106
|
+
} catch (_error) {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
// let currentObject = JSON.parse(storage);
|
|
110
|
+
if (keys.length === 1) {
|
|
111
|
+
return currentObject;
|
|
112
|
+
}
|
|
113
|
+
keys.shift();
|
|
114
|
+
while(keys.length > 0){
|
|
115
|
+
const currentKey = keys.shift();
|
|
116
|
+
if (!currentKey) break;
|
|
117
|
+
currentObject = currentObject[currentKey];
|
|
118
|
+
if (!currentObject) break;
|
|
119
|
+
}
|
|
120
|
+
return currentObject;
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* 將資料(Json 物件)存入 localStorage
|
|
124
|
+
* @param key 鍵值
|
|
125
|
+
* @param value 可序列化的資料
|
|
126
|
+
* @param enCode 是否編碼
|
|
127
|
+
*/ const setLocalStorage = (key, value, enCode = true)=>{
|
|
128
|
+
if (enCode) {
|
|
129
|
+
localStorage.setItem(key, encodeJson(value));
|
|
130
|
+
} else {
|
|
131
|
+
localStorage.setItem(key, JSON.stringify(value));
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export { getLocalStorage as g, setLocalStorage as s };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gateweb/react-utils",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "React Utils for GateWeb",
|
|
5
5
|
"homepage": "https://github.com/GatewebSolutions/react-utils",
|
|
6
6
|
"files": [
|
|
@@ -20,6 +20,16 @@
|
|
|
20
20
|
"default": "./dist/cjs/index.js"
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
|
+
"./client": {
|
|
24
|
+
"import": {
|
|
25
|
+
"types": "./dist/es/client.d.mts",
|
|
26
|
+
"default": "./dist/es/client.mjs"
|
|
27
|
+
},
|
|
28
|
+
"require": {
|
|
29
|
+
"types": "./dist/cjs/client.d.ts",
|
|
30
|
+
"default": "./dist/cjs/client.js"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
23
33
|
"./types": {
|
|
24
34
|
"import": {
|
|
25
35
|
"types": "./dist/es/types.d.mts",
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
/**
|
|
3
|
-
* 從 localStorage 取得資料,支援槽狀取值
|
|
4
|
-
*
|
|
5
|
-
* @param key 鍵值
|
|
6
|
-
* @param deCode 是否解碼
|
|
7
|
-
* @returns 取得的資料
|
|
8
|
-
* @example
|
|
9
|
-
* const data = getLocalStorage('key');
|
|
10
|
-
*
|
|
11
|
-
* const data = getLocalStorage('key.subKey');
|
|
12
|
-
*/ const getLocalStorage = (key, deCode = true)=>{
|
|
13
|
-
const keys = key.split('.');
|
|
14
|
-
const storage = localStorage.getItem(keys[0]);
|
|
15
|
-
if (!storage) return undefined;
|
|
16
|
-
let currentObject;
|
|
17
|
-
try {
|
|
18
|
-
if (deCode) {
|
|
19
|
-
currentObject = JSON.parse(window.atob(storage));
|
|
20
|
-
} else {
|
|
21
|
-
currentObject = JSON.parse(storage);
|
|
22
|
-
}
|
|
23
|
-
} catch (_error) {
|
|
24
|
-
return undefined;
|
|
25
|
-
}
|
|
26
|
-
// let currentObject = JSON.parse(storage);
|
|
27
|
-
if (keys.length === 1) {
|
|
28
|
-
return currentObject;
|
|
29
|
-
}
|
|
30
|
-
keys.shift();
|
|
31
|
-
while(keys.length > 0){
|
|
32
|
-
const currentKey = keys.shift();
|
|
33
|
-
if (!currentKey) break;
|
|
34
|
-
currentObject = currentObject[currentKey];
|
|
35
|
-
if (!currentObject) break;
|
|
36
|
-
}
|
|
37
|
-
return currentObject;
|
|
38
|
-
};
|
|
39
|
-
/**
|
|
40
|
-
* 將資料存入 localStorage
|
|
41
|
-
* @param key 鍵值
|
|
42
|
-
* @param value 可序列化的資料
|
|
43
|
-
* @param enCode 是否編碼
|
|
44
|
-
*/ const setLocalStorage = (key, value, enCode = true)=>{
|
|
45
|
-
if (enCode) {
|
|
46
|
-
localStorage.setItem(key, window.btoa(JSON.stringify(value)));
|
|
47
|
-
} else {
|
|
48
|
-
localStorage.setItem(key, JSON.stringify(value));
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
exports.getLocalStorage = getLocalStorage;
|
|
53
|
-
exports.setLocalStorage = setLocalStorage;
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
/**
|
|
3
|
-
* 從 localStorage 取得資料,支援槽狀取值
|
|
4
|
-
*
|
|
5
|
-
* @param key 鍵值
|
|
6
|
-
* @param deCode 是否解碼
|
|
7
|
-
* @returns 取得的資料
|
|
8
|
-
* @example
|
|
9
|
-
* const data = getLocalStorage('key');
|
|
10
|
-
*
|
|
11
|
-
* const data = getLocalStorage('key.subKey');
|
|
12
|
-
*/ const getLocalStorage = (key, deCode = true)=>{
|
|
13
|
-
const keys = key.split('.');
|
|
14
|
-
const storage = localStorage.getItem(keys[0]);
|
|
15
|
-
if (!storage) return undefined;
|
|
16
|
-
let currentObject;
|
|
17
|
-
try {
|
|
18
|
-
if (deCode) {
|
|
19
|
-
currentObject = JSON.parse(window.atob(storage));
|
|
20
|
-
} else {
|
|
21
|
-
currentObject = JSON.parse(storage);
|
|
22
|
-
}
|
|
23
|
-
} catch (_error) {
|
|
24
|
-
return undefined;
|
|
25
|
-
}
|
|
26
|
-
// let currentObject = JSON.parse(storage);
|
|
27
|
-
if (keys.length === 1) {
|
|
28
|
-
return currentObject;
|
|
29
|
-
}
|
|
30
|
-
keys.shift();
|
|
31
|
-
while(keys.length > 0){
|
|
32
|
-
const currentKey = keys.shift();
|
|
33
|
-
if (!currentKey) break;
|
|
34
|
-
currentObject = currentObject[currentKey];
|
|
35
|
-
if (!currentObject) break;
|
|
36
|
-
}
|
|
37
|
-
return currentObject;
|
|
38
|
-
};
|
|
39
|
-
/**
|
|
40
|
-
* 將資料存入 localStorage
|
|
41
|
-
* @param key 鍵值
|
|
42
|
-
* @param value 可序列化的資料
|
|
43
|
-
* @param enCode 是否編碼
|
|
44
|
-
*/ const setLocalStorage = (key, value, enCode = true)=>{
|
|
45
|
-
if (enCode) {
|
|
46
|
-
localStorage.setItem(key, window.btoa(JSON.stringify(value)));
|
|
47
|
-
} else {
|
|
48
|
-
localStorage.setItem(key, JSON.stringify(value));
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
export { getLocalStorage as g, setLocalStorage as s };
|