@leancodepl/utils 8.4.0 → 8.5.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.
- package/README.md +465 -0
- package/index.cjs.js +255 -15
- package/index.esm.js +255 -15
- package/package.json +35 -4
- package/src/lib/addPrefix.d.ts +15 -0
- package/src/lib/assertDefined.d.ts +16 -0
- package/src/lib/assertNotEmpty.d.ts +16 -0
- package/src/lib/assertNotNull.d.ts +16 -0
- package/src/lib/downloadFile.d.ts +23 -0
- package/src/lib/ensureDefined.d.ts +16 -0
- package/src/lib/ensureNotEmpty.d.ts +16 -0
- package/src/lib/ensureNotNull.d.ts +17 -0
- package/src/lib/hooks/useBoundRunInTask.d.ts +26 -0
- package/src/lib/hooks/useDialog.d.ts +22 -0
- package/src/lib/hooks/useKeyByRoute.d.ts +28 -0
- package/src/lib/hooks/useRunInTask.d.ts +24 -0
- package/src/lib/hooks/useSetUnset.d.ts +21 -0
- package/src/lib/transformDeep.d.ts +26 -0
- package/src/lib/transformFirst.d.ts +22 -0
- package/index.esm.d.ts +0 -1
- /package/{index.cjs.d.ts → index.d.ts} +0 -0
package/index.cjs.js
CHANGED
|
@@ -3,7 +3,21 @@
|
|
|
3
3
|
var invariant = require('tiny-invariant');
|
|
4
4
|
var react = require('react');
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Adds a prefix to all keys in an object, creating a new object with prefixed keys.
|
|
8
|
+
*
|
|
9
|
+
* @template T - The type of the input object
|
|
10
|
+
* @template TPrefix - The type of the prefix string
|
|
11
|
+
* @param object - The object whose keys will be prefixed
|
|
12
|
+
* @param prefix - The prefix string to add to each key
|
|
13
|
+
* @returns A new object with all keys prefixed
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* const apiData = { userId: 1, userName: 'John' };
|
|
17
|
+
* const prefixed = addPrefix(apiData, 'api_');
|
|
18
|
+
* // Result: { api_userId: 1, api_userName: 'John' }
|
|
19
|
+
* ```
|
|
20
|
+
*/ function addPrefix(object, prefix) {
|
|
7
21
|
return Object.fromEntries(Object.entries(object).map(([key, value])=>[
|
|
8
22
|
`${prefix}${key}`,
|
|
9
23
|
value
|
|
@@ -16,10 +30,30 @@ function transformFirst(value, transformFn) {
|
|
|
16
30
|
}
|
|
17
31
|
return transformFn(value[0]) + value.slice(1);
|
|
18
32
|
}
|
|
19
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Converts the first character of a string to lowercase.
|
|
35
|
+
*
|
|
36
|
+
* @param value - The string to transform
|
|
37
|
+
* @returns The string with the first character in lowercase
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* const result = toLowerFirst('UserName');
|
|
41
|
+
* // Result: 'userName'
|
|
42
|
+
* ```
|
|
43
|
+
*/ function toLowerFirst(value) {
|
|
20
44
|
return transformFirst(value, (value)=>value.toLowerCase());
|
|
21
45
|
}
|
|
22
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Converts the first character of a string to uppercase.
|
|
48
|
+
*
|
|
49
|
+
* @param value - The string to transform
|
|
50
|
+
* @returns The string with the first character in uppercase
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* const result = toUpperFirst('userName');
|
|
54
|
+
* // Result: 'UserName'
|
|
55
|
+
* ```
|
|
56
|
+
*/ function toUpperFirst(value) {
|
|
23
57
|
return transformFirst(value, (value)=>value.toUpperCase());
|
|
24
58
|
}
|
|
25
59
|
|
|
@@ -49,36 +83,151 @@ function transformDeep(value, mode) {
|
|
|
49
83
|
}
|
|
50
84
|
return value;
|
|
51
85
|
}
|
|
52
|
-
|
|
86
|
+
/**
|
|
87
|
+
* Recursively transforms all object keys to use uncapitalized (camelCase) format.
|
|
88
|
+
*
|
|
89
|
+
* @template T - The type of the input value
|
|
90
|
+
* @param value - The value to transform (can be object, array, or primitive)
|
|
91
|
+
* @returns A new object with all keys converted to camelCase
|
|
92
|
+
* @example
|
|
93
|
+
* ```typescript
|
|
94
|
+
* const serverData = { UserId: 1, UserName: 'John', Profile: { FirstName: 'John' } };
|
|
95
|
+
* const clientData = uncapitalizeDeep(serverData);
|
|
96
|
+
* // Result: { userId: 1, userName: 'John', profile: { firstName: 'John' } }
|
|
97
|
+
* ```
|
|
98
|
+
*/ function uncapitalizeDeep(value) {
|
|
53
99
|
return transformDeep(value, "uncapitalize");
|
|
54
100
|
}
|
|
55
|
-
|
|
101
|
+
/**
|
|
102
|
+
* Recursively transforms all object keys to use capitalized (PascalCase) format.
|
|
103
|
+
*
|
|
104
|
+
* @template T - The type of the input value
|
|
105
|
+
* @param value - The value to transform (can be object, array, or primitive)
|
|
106
|
+
* @returns A new object with all keys converted to PascalCase
|
|
107
|
+
* @example
|
|
108
|
+
* ```typescript
|
|
109
|
+
* const clientData = { userId: 1, userName: 'John', profile: { firstName: 'John' } };
|
|
110
|
+
* const serverData = capitalizeDeep(clientData);
|
|
111
|
+
* // Result: { UserId: 1, UserName: 'John', Profile: { FirstName: 'John' } }
|
|
112
|
+
* ```
|
|
113
|
+
*/ function capitalizeDeep(value) {
|
|
56
114
|
return transformDeep(value, "capitalize");
|
|
57
115
|
}
|
|
58
116
|
|
|
59
|
-
|
|
117
|
+
/**
|
|
118
|
+
* Asserts that a value is not undefined. Throws an error if the value is undefined.
|
|
119
|
+
* This is a type assertion function that narrows the type to exclude undefined.
|
|
120
|
+
*
|
|
121
|
+
* @template T - The type of the value being checked
|
|
122
|
+
* @param value - The value to check for undefined
|
|
123
|
+
* @param message - Optional error message to use if assertion fails
|
|
124
|
+
* @throws {Error} When the value is undefined
|
|
125
|
+
* @example
|
|
126
|
+
* ```typescript
|
|
127
|
+
* function processUser(user?: User) {
|
|
128
|
+
* assertDefined(user);
|
|
129
|
+
* return user.name; // TypeScript knows user is defined
|
|
130
|
+
* }
|
|
131
|
+
* ```
|
|
132
|
+
*/ function assertDefined(value, message) {
|
|
60
133
|
invariant(value !== undefined, message);
|
|
61
134
|
}
|
|
62
135
|
|
|
63
|
-
|
|
136
|
+
/**
|
|
137
|
+
* Asserts that a value is not null. Throws an error if the value is null.
|
|
138
|
+
* This is a type assertion function that narrows the type to exclude null.
|
|
139
|
+
*
|
|
140
|
+
* @template T - The type of the value being checked
|
|
141
|
+
* @param value - The value to check for null
|
|
142
|
+
* @param message - Optional error message to use if assertion fails
|
|
143
|
+
* @throws {Error} When the value is null
|
|
144
|
+
* @example
|
|
145
|
+
* ```typescript
|
|
146
|
+
* function processData(data: string | null) {
|
|
147
|
+
* assertNotNull(data);
|
|
148
|
+
* return data.toUpperCase(); // TypeScript knows data is not null
|
|
149
|
+
* }
|
|
150
|
+
* ```
|
|
151
|
+
*/ function assertNotNull(value, message) {
|
|
64
152
|
invariant(value !== null, message);
|
|
65
153
|
}
|
|
66
154
|
|
|
67
|
-
|
|
155
|
+
/**
|
|
156
|
+
* Asserts that a value is not null or undefined. Throws an error if the value is null or undefined.
|
|
157
|
+
* This is a type assertion function that narrows the type to exclude null and undefined.
|
|
158
|
+
*
|
|
159
|
+
* @template T - The type of the value being checked
|
|
160
|
+
* @param value - The value to check for null or undefined
|
|
161
|
+
* @param message - Optional error message to use if assertion fails
|
|
162
|
+
* @throws {Error} When the value is null or undefined
|
|
163
|
+
* @example
|
|
164
|
+
* ```typescript
|
|
165
|
+
* function processOptionalData(data?: string | null) {
|
|
166
|
+
* assertNotEmpty(data);
|
|
167
|
+
* return data.toUpperCase(); // TypeScript knows data is not null/undefined
|
|
168
|
+
* }
|
|
169
|
+
* ```
|
|
170
|
+
*/ function assertNotEmpty(value, message) {
|
|
68
171
|
invariant(value !== null && value !== undefined, message);
|
|
69
172
|
}
|
|
70
173
|
|
|
71
|
-
|
|
174
|
+
/**
|
|
175
|
+
* Ensures that a value is defined, returning it if defined or throwing an error if undefined.
|
|
176
|
+
*
|
|
177
|
+
* @template T - The type of the value being checked
|
|
178
|
+
* @param value - The value to ensure is defined
|
|
179
|
+
* @param message - Optional error message to use if the value is undefined
|
|
180
|
+
* @returns The value if it is defined
|
|
181
|
+
* @throws {Error} When the value is undefined
|
|
182
|
+
* @example
|
|
183
|
+
* ```typescript
|
|
184
|
+
* function processUser(user?: User) {
|
|
185
|
+
* const definedUser = ensureDefined(user);
|
|
186
|
+
* return definedUser.name; // definedUser is guaranteed to be defined
|
|
187
|
+
* }
|
|
188
|
+
* ```
|
|
189
|
+
*/ function ensureDefined(value, message) {
|
|
72
190
|
assertDefined(value, message);
|
|
73
191
|
return value;
|
|
74
192
|
}
|
|
75
193
|
|
|
76
|
-
|
|
194
|
+
/**
|
|
195
|
+
* Ensures that a value is not null, returning it if not null or throwing an error if null.
|
|
196
|
+
* Unlike assertNotNull, this function returns the value for use in expressions.
|
|
197
|
+
*
|
|
198
|
+
* @template T - The type of the value being checked
|
|
199
|
+
* @param value - The value to ensure is not null
|
|
200
|
+
* @param message - Optional error message to use if the value is null
|
|
201
|
+
* @returns The value if it is not null
|
|
202
|
+
* @throws {Error} When the value is null
|
|
203
|
+
* @example
|
|
204
|
+
* ```typescript
|
|
205
|
+
* function processData(data: string | null) {
|
|
206
|
+
* const nonNullData = ensureNotNull(data);
|
|
207
|
+
* return nonNullData.toUpperCase(); // nonNullData is guaranteed to be not null
|
|
208
|
+
* }
|
|
209
|
+
* ```
|
|
210
|
+
*/ function ensureNotNull(value, message) {
|
|
77
211
|
assertNotNull(value, message);
|
|
78
212
|
return value;
|
|
79
213
|
}
|
|
80
214
|
|
|
81
|
-
|
|
215
|
+
/**
|
|
216
|
+
* Ensures that a value is not null or undefined, returning it if valid or throwing an error if empty.
|
|
217
|
+
*
|
|
218
|
+
* @template T - The type of the value being checked
|
|
219
|
+
* @param value - The value to ensure is not null or undefined
|
|
220
|
+
* @param message - Optional error message to use if the value is null or undefined
|
|
221
|
+
* @returns The value if it is not null or undefined
|
|
222
|
+
* @throws {Error} When the value is null or undefined
|
|
223
|
+
* @example
|
|
224
|
+
* ```typescript
|
|
225
|
+
* function processOptionalData(data?: string | null) {
|
|
226
|
+
* const validData = ensureNotEmpty(data);
|
|
227
|
+
* return validData.toUpperCase(); // validData is guaranteed to be not null/undefined
|
|
228
|
+
* }
|
|
229
|
+
* ```
|
|
230
|
+
*/ function ensureNotEmpty(value, message) {
|
|
82
231
|
assertNotEmpty(value, message);
|
|
83
232
|
return value;
|
|
84
233
|
}
|
|
@@ -98,7 +247,30 @@ function downloadFile(dataOrUrl, options = {}) {
|
|
|
98
247
|
}
|
|
99
248
|
}
|
|
100
249
|
|
|
101
|
-
|
|
250
|
+
/**
|
|
251
|
+
* React hook for tracking async task execution with loading state.
|
|
252
|
+
* Automatically manages a loading counter and provides a wrapper function for tasks.
|
|
253
|
+
*
|
|
254
|
+
* @returns A tuple containing [isLoading: boolean, runInTask: function]
|
|
255
|
+
* @example
|
|
256
|
+
* ```typescript
|
|
257
|
+
* function MyComponent() {
|
|
258
|
+
* const [isLoading, runInTask] = useRunInTask();
|
|
259
|
+
*
|
|
260
|
+
* const handleSave = async () => {
|
|
261
|
+
* await runInTask(async () => {
|
|
262
|
+
* await saveData();
|
|
263
|
+
* });
|
|
264
|
+
* };
|
|
265
|
+
*
|
|
266
|
+
* return (
|
|
267
|
+
* <button onClick={handleSave} disabled={isLoading}>
|
|
268
|
+
* {isLoading ? 'Saving...' : 'Save'}
|
|
269
|
+
* </button>
|
|
270
|
+
* );
|
|
271
|
+
* }
|
|
272
|
+
* ```
|
|
273
|
+
*/ function useRunInTask() {
|
|
102
274
|
const [runningTasks, setRunningTasks] = react.useState(0);
|
|
103
275
|
const runInTask = react.useCallback(async (task)=>{
|
|
104
276
|
setRunningTasks((runningTasks)=>runningTasks + 1);
|
|
@@ -126,7 +298,34 @@ function useBoundRunInTask(block) {
|
|
|
126
298
|
];
|
|
127
299
|
}
|
|
128
300
|
|
|
129
|
-
|
|
301
|
+
/**
|
|
302
|
+
* React hook for generating keys based on current route matches.
|
|
303
|
+
*
|
|
304
|
+
* @template TKey - The type of the route keys
|
|
305
|
+
* @param routeMatches - Record of route keys to match objects or arrays
|
|
306
|
+
* @returns Array of active route keys
|
|
307
|
+
* @example
|
|
308
|
+
* ```typescript
|
|
309
|
+
* function NavigationComponent() {
|
|
310
|
+
* const routeMatches = {
|
|
311
|
+
* home: useRouteMatch('/'),
|
|
312
|
+
* about: useRouteMatch('/about'),
|
|
313
|
+
* contact: useRouteMatch('/contact')
|
|
314
|
+
* };
|
|
315
|
+
*
|
|
316
|
+
* const activeRoutes = useKeyByRoute(routeMatches);
|
|
317
|
+
* // Returns ['home'] if on home page, ['about'] if on about page, etc.
|
|
318
|
+
*
|
|
319
|
+
* return (
|
|
320
|
+
* <nav>
|
|
321
|
+
* {activeRoutes.map(route => (
|
|
322
|
+
* <span key={route}>Active: {route}</span>
|
|
323
|
+
* ))}
|
|
324
|
+
* </nav>
|
|
325
|
+
* );
|
|
326
|
+
* }
|
|
327
|
+
* ```
|
|
328
|
+
*/ function useKeyByRoute(routeMatches) {
|
|
130
329
|
const keys = [];
|
|
131
330
|
for(const key in routeMatches){
|
|
132
331
|
const matches = routeMatches[key];
|
|
@@ -137,7 +336,27 @@ function useKeyByRoute(routeMatches) {
|
|
|
137
336
|
return keys;
|
|
138
337
|
}
|
|
139
338
|
|
|
140
|
-
|
|
339
|
+
/**
|
|
340
|
+
* React hook for boolean state management helpers.
|
|
341
|
+
*
|
|
342
|
+
* @param set - The state setter function from useState
|
|
343
|
+
* @returns A tuple containing [setTrue: function, setFalse: function]
|
|
344
|
+
* @example
|
|
345
|
+
* ```typescript
|
|
346
|
+
* function MyComponent() {
|
|
347
|
+
* const [isVisible, setIsVisible] = useState(false);
|
|
348
|
+
* const [show, hide] = useSetUnset(setIsVisible);
|
|
349
|
+
*
|
|
350
|
+
* return (
|
|
351
|
+
* <div>
|
|
352
|
+
* <button onClick={show}>Show</button>
|
|
353
|
+
* <button onClick={hide}>Hide</button>
|
|
354
|
+
* {isVisible && <div>Content is visible</div>}
|
|
355
|
+
* </div>
|
|
356
|
+
* );
|
|
357
|
+
* }
|
|
358
|
+
* ```
|
|
359
|
+
*/ function useSetUnset(set) {
|
|
141
360
|
return [
|
|
142
361
|
react.useCallback(()=>set(true), [
|
|
143
362
|
set
|
|
@@ -148,7 +367,28 @@ function useSetUnset(set) {
|
|
|
148
367
|
];
|
|
149
368
|
}
|
|
150
369
|
|
|
151
|
-
|
|
370
|
+
/**
|
|
371
|
+
* React hook for managing dialog state with optional callback after closing.
|
|
372
|
+
* Provides convenient open/close functions and tracks the dialog's open state.
|
|
373
|
+
*
|
|
374
|
+
* @param onAfterClose - Optional callback function to execute after the dialog closes
|
|
375
|
+
* @returns Object containing dialog state and control functions
|
|
376
|
+
* @example
|
|
377
|
+
* ```typescript
|
|
378
|
+
* function MyComponent() {
|
|
379
|
+
* const { isDialogOpen, openDialog, closeDialog } = useDialog(() => {
|
|
380
|
+
* console.log('Dialog closed');
|
|
381
|
+
* });
|
|
382
|
+
*
|
|
383
|
+
* return (
|
|
384
|
+
* <div>
|
|
385
|
+
* <button onClick={openDialog}>Open Dialog</button>
|
|
386
|
+
* {isDialogOpen && <Dialog onClose={closeDialog} />}
|
|
387
|
+
* </div>
|
|
388
|
+
* );
|
|
389
|
+
* }
|
|
390
|
+
* ```
|
|
391
|
+
*/ function useDialog(onAfterClose) {
|
|
152
392
|
const [isDialogOpen, setIsDialogOpen] = react.useState(false);
|
|
153
393
|
const [openDialog, closeDialog] = useSetUnset(setIsDialogOpen);
|
|
154
394
|
const close = react.useCallback(()=>{
|
package/index.esm.js
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
import invariant from 'tiny-invariant';
|
|
2
2
|
import { useState, useCallback, useMemo } from 'react';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Adds a prefix to all keys in an object, creating a new object with prefixed keys.
|
|
6
|
+
*
|
|
7
|
+
* @template T - The type of the input object
|
|
8
|
+
* @template TPrefix - The type of the prefix string
|
|
9
|
+
* @param object - The object whose keys will be prefixed
|
|
10
|
+
* @param prefix - The prefix string to add to each key
|
|
11
|
+
* @returns A new object with all keys prefixed
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const apiData = { userId: 1, userName: 'John' };
|
|
15
|
+
* const prefixed = addPrefix(apiData, 'api_');
|
|
16
|
+
* // Result: { api_userId: 1, api_userName: 'John' }
|
|
17
|
+
* ```
|
|
18
|
+
*/ function addPrefix(object, prefix) {
|
|
5
19
|
return Object.fromEntries(Object.entries(object).map(([key, value])=>[
|
|
6
20
|
`${prefix}${key}`,
|
|
7
21
|
value
|
|
@@ -14,10 +28,30 @@ function transformFirst(value, transformFn) {
|
|
|
14
28
|
}
|
|
15
29
|
return transformFn(value[0]) + value.slice(1);
|
|
16
30
|
}
|
|
17
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Converts the first character of a string to lowercase.
|
|
33
|
+
*
|
|
34
|
+
* @param value - The string to transform
|
|
35
|
+
* @returns The string with the first character in lowercase
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* const result = toLowerFirst('UserName');
|
|
39
|
+
* // Result: 'userName'
|
|
40
|
+
* ```
|
|
41
|
+
*/ function toLowerFirst(value) {
|
|
18
42
|
return transformFirst(value, (value)=>value.toLowerCase());
|
|
19
43
|
}
|
|
20
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Converts the first character of a string to uppercase.
|
|
46
|
+
*
|
|
47
|
+
* @param value - The string to transform
|
|
48
|
+
* @returns The string with the first character in uppercase
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* const result = toUpperFirst('userName');
|
|
52
|
+
* // Result: 'UserName'
|
|
53
|
+
* ```
|
|
54
|
+
*/ function toUpperFirst(value) {
|
|
21
55
|
return transformFirst(value, (value)=>value.toUpperCase());
|
|
22
56
|
}
|
|
23
57
|
|
|
@@ -47,36 +81,151 @@ function transformDeep(value, mode) {
|
|
|
47
81
|
}
|
|
48
82
|
return value;
|
|
49
83
|
}
|
|
50
|
-
|
|
84
|
+
/**
|
|
85
|
+
* Recursively transforms all object keys to use uncapitalized (camelCase) format.
|
|
86
|
+
*
|
|
87
|
+
* @template T - The type of the input value
|
|
88
|
+
* @param value - The value to transform (can be object, array, or primitive)
|
|
89
|
+
* @returns A new object with all keys converted to camelCase
|
|
90
|
+
* @example
|
|
91
|
+
* ```typescript
|
|
92
|
+
* const serverData = { UserId: 1, UserName: 'John', Profile: { FirstName: 'John' } };
|
|
93
|
+
* const clientData = uncapitalizeDeep(serverData);
|
|
94
|
+
* // Result: { userId: 1, userName: 'John', profile: { firstName: 'John' } }
|
|
95
|
+
* ```
|
|
96
|
+
*/ function uncapitalizeDeep(value) {
|
|
51
97
|
return transformDeep(value, "uncapitalize");
|
|
52
98
|
}
|
|
53
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Recursively transforms all object keys to use capitalized (PascalCase) format.
|
|
101
|
+
*
|
|
102
|
+
* @template T - The type of the input value
|
|
103
|
+
* @param value - The value to transform (can be object, array, or primitive)
|
|
104
|
+
* @returns A new object with all keys converted to PascalCase
|
|
105
|
+
* @example
|
|
106
|
+
* ```typescript
|
|
107
|
+
* const clientData = { userId: 1, userName: 'John', profile: { firstName: 'John' } };
|
|
108
|
+
* const serverData = capitalizeDeep(clientData);
|
|
109
|
+
* // Result: { UserId: 1, UserName: 'John', Profile: { FirstName: 'John' } }
|
|
110
|
+
* ```
|
|
111
|
+
*/ function capitalizeDeep(value) {
|
|
54
112
|
return transformDeep(value, "capitalize");
|
|
55
113
|
}
|
|
56
114
|
|
|
57
|
-
|
|
115
|
+
/**
|
|
116
|
+
* Asserts that a value is not undefined. Throws an error if the value is undefined.
|
|
117
|
+
* This is a type assertion function that narrows the type to exclude undefined.
|
|
118
|
+
*
|
|
119
|
+
* @template T - The type of the value being checked
|
|
120
|
+
* @param value - The value to check for undefined
|
|
121
|
+
* @param message - Optional error message to use if assertion fails
|
|
122
|
+
* @throws {Error} When the value is undefined
|
|
123
|
+
* @example
|
|
124
|
+
* ```typescript
|
|
125
|
+
* function processUser(user?: User) {
|
|
126
|
+
* assertDefined(user);
|
|
127
|
+
* return user.name; // TypeScript knows user is defined
|
|
128
|
+
* }
|
|
129
|
+
* ```
|
|
130
|
+
*/ function assertDefined(value, message) {
|
|
58
131
|
invariant(value !== undefined, message);
|
|
59
132
|
}
|
|
60
133
|
|
|
61
|
-
|
|
134
|
+
/**
|
|
135
|
+
* Asserts that a value is not null. Throws an error if the value is null.
|
|
136
|
+
* This is a type assertion function that narrows the type to exclude null.
|
|
137
|
+
*
|
|
138
|
+
* @template T - The type of the value being checked
|
|
139
|
+
* @param value - The value to check for null
|
|
140
|
+
* @param message - Optional error message to use if assertion fails
|
|
141
|
+
* @throws {Error} When the value is null
|
|
142
|
+
* @example
|
|
143
|
+
* ```typescript
|
|
144
|
+
* function processData(data: string | null) {
|
|
145
|
+
* assertNotNull(data);
|
|
146
|
+
* return data.toUpperCase(); // TypeScript knows data is not null
|
|
147
|
+
* }
|
|
148
|
+
* ```
|
|
149
|
+
*/ function assertNotNull(value, message) {
|
|
62
150
|
invariant(value !== null, message);
|
|
63
151
|
}
|
|
64
152
|
|
|
65
|
-
|
|
153
|
+
/**
|
|
154
|
+
* Asserts that a value is not null or undefined. Throws an error if the value is null or undefined.
|
|
155
|
+
* This is a type assertion function that narrows the type to exclude null and undefined.
|
|
156
|
+
*
|
|
157
|
+
* @template T - The type of the value being checked
|
|
158
|
+
* @param value - The value to check for null or undefined
|
|
159
|
+
* @param message - Optional error message to use if assertion fails
|
|
160
|
+
* @throws {Error} When the value is null or undefined
|
|
161
|
+
* @example
|
|
162
|
+
* ```typescript
|
|
163
|
+
* function processOptionalData(data?: string | null) {
|
|
164
|
+
* assertNotEmpty(data);
|
|
165
|
+
* return data.toUpperCase(); // TypeScript knows data is not null/undefined
|
|
166
|
+
* }
|
|
167
|
+
* ```
|
|
168
|
+
*/ function assertNotEmpty(value, message) {
|
|
66
169
|
invariant(value !== null && value !== undefined, message);
|
|
67
170
|
}
|
|
68
171
|
|
|
69
|
-
|
|
172
|
+
/**
|
|
173
|
+
* Ensures that a value is defined, returning it if defined or throwing an error if undefined.
|
|
174
|
+
*
|
|
175
|
+
* @template T - The type of the value being checked
|
|
176
|
+
* @param value - The value to ensure is defined
|
|
177
|
+
* @param message - Optional error message to use if the value is undefined
|
|
178
|
+
* @returns The value if it is defined
|
|
179
|
+
* @throws {Error} When the value is undefined
|
|
180
|
+
* @example
|
|
181
|
+
* ```typescript
|
|
182
|
+
* function processUser(user?: User) {
|
|
183
|
+
* const definedUser = ensureDefined(user);
|
|
184
|
+
* return definedUser.name; // definedUser is guaranteed to be defined
|
|
185
|
+
* }
|
|
186
|
+
* ```
|
|
187
|
+
*/ function ensureDefined(value, message) {
|
|
70
188
|
assertDefined(value, message);
|
|
71
189
|
return value;
|
|
72
190
|
}
|
|
73
191
|
|
|
74
|
-
|
|
192
|
+
/**
|
|
193
|
+
* Ensures that a value is not null, returning it if not null or throwing an error if null.
|
|
194
|
+
* Unlike assertNotNull, this function returns the value for use in expressions.
|
|
195
|
+
*
|
|
196
|
+
* @template T - The type of the value being checked
|
|
197
|
+
* @param value - The value to ensure is not null
|
|
198
|
+
* @param message - Optional error message to use if the value is null
|
|
199
|
+
* @returns The value if it is not null
|
|
200
|
+
* @throws {Error} When the value is null
|
|
201
|
+
* @example
|
|
202
|
+
* ```typescript
|
|
203
|
+
* function processData(data: string | null) {
|
|
204
|
+
* const nonNullData = ensureNotNull(data);
|
|
205
|
+
* return nonNullData.toUpperCase(); // nonNullData is guaranteed to be not null
|
|
206
|
+
* }
|
|
207
|
+
* ```
|
|
208
|
+
*/ function ensureNotNull(value, message) {
|
|
75
209
|
assertNotNull(value, message);
|
|
76
210
|
return value;
|
|
77
211
|
}
|
|
78
212
|
|
|
79
|
-
|
|
213
|
+
/**
|
|
214
|
+
* Ensures that a value is not null or undefined, returning it if valid or throwing an error if empty.
|
|
215
|
+
*
|
|
216
|
+
* @template T - The type of the value being checked
|
|
217
|
+
* @param value - The value to ensure is not null or undefined
|
|
218
|
+
* @param message - Optional error message to use if the value is null or undefined
|
|
219
|
+
* @returns The value if it is not null or undefined
|
|
220
|
+
* @throws {Error} When the value is null or undefined
|
|
221
|
+
* @example
|
|
222
|
+
* ```typescript
|
|
223
|
+
* function processOptionalData(data?: string | null) {
|
|
224
|
+
* const validData = ensureNotEmpty(data);
|
|
225
|
+
* return validData.toUpperCase(); // validData is guaranteed to be not null/undefined
|
|
226
|
+
* }
|
|
227
|
+
* ```
|
|
228
|
+
*/ function ensureNotEmpty(value, message) {
|
|
80
229
|
assertNotEmpty(value, message);
|
|
81
230
|
return value;
|
|
82
231
|
}
|
|
@@ -96,7 +245,30 @@ function downloadFile(dataOrUrl, options = {}) {
|
|
|
96
245
|
}
|
|
97
246
|
}
|
|
98
247
|
|
|
99
|
-
|
|
248
|
+
/**
|
|
249
|
+
* React hook for tracking async task execution with loading state.
|
|
250
|
+
* Automatically manages a loading counter and provides a wrapper function for tasks.
|
|
251
|
+
*
|
|
252
|
+
* @returns A tuple containing [isLoading: boolean, runInTask: function]
|
|
253
|
+
* @example
|
|
254
|
+
* ```typescript
|
|
255
|
+
* function MyComponent() {
|
|
256
|
+
* const [isLoading, runInTask] = useRunInTask();
|
|
257
|
+
*
|
|
258
|
+
* const handleSave = async () => {
|
|
259
|
+
* await runInTask(async () => {
|
|
260
|
+
* await saveData();
|
|
261
|
+
* });
|
|
262
|
+
* };
|
|
263
|
+
*
|
|
264
|
+
* return (
|
|
265
|
+
* <button onClick={handleSave} disabled={isLoading}>
|
|
266
|
+
* {isLoading ? 'Saving...' : 'Save'}
|
|
267
|
+
* </button>
|
|
268
|
+
* );
|
|
269
|
+
* }
|
|
270
|
+
* ```
|
|
271
|
+
*/ function useRunInTask() {
|
|
100
272
|
const [runningTasks, setRunningTasks] = useState(0);
|
|
101
273
|
const runInTask = useCallback(async (task)=>{
|
|
102
274
|
setRunningTasks((runningTasks)=>runningTasks + 1);
|
|
@@ -124,7 +296,34 @@ function useBoundRunInTask(block) {
|
|
|
124
296
|
];
|
|
125
297
|
}
|
|
126
298
|
|
|
127
|
-
|
|
299
|
+
/**
|
|
300
|
+
* React hook for generating keys based on current route matches.
|
|
301
|
+
*
|
|
302
|
+
* @template TKey - The type of the route keys
|
|
303
|
+
* @param routeMatches - Record of route keys to match objects or arrays
|
|
304
|
+
* @returns Array of active route keys
|
|
305
|
+
* @example
|
|
306
|
+
* ```typescript
|
|
307
|
+
* function NavigationComponent() {
|
|
308
|
+
* const routeMatches = {
|
|
309
|
+
* home: useRouteMatch('/'),
|
|
310
|
+
* about: useRouteMatch('/about'),
|
|
311
|
+
* contact: useRouteMatch('/contact')
|
|
312
|
+
* };
|
|
313
|
+
*
|
|
314
|
+
* const activeRoutes = useKeyByRoute(routeMatches);
|
|
315
|
+
* // Returns ['home'] if on home page, ['about'] if on about page, etc.
|
|
316
|
+
*
|
|
317
|
+
* return (
|
|
318
|
+
* <nav>
|
|
319
|
+
* {activeRoutes.map(route => (
|
|
320
|
+
* <span key={route}>Active: {route}</span>
|
|
321
|
+
* ))}
|
|
322
|
+
* </nav>
|
|
323
|
+
* );
|
|
324
|
+
* }
|
|
325
|
+
* ```
|
|
326
|
+
*/ function useKeyByRoute(routeMatches) {
|
|
128
327
|
const keys = [];
|
|
129
328
|
for(const key in routeMatches){
|
|
130
329
|
const matches = routeMatches[key];
|
|
@@ -135,7 +334,27 @@ function useKeyByRoute(routeMatches) {
|
|
|
135
334
|
return keys;
|
|
136
335
|
}
|
|
137
336
|
|
|
138
|
-
|
|
337
|
+
/**
|
|
338
|
+
* React hook for boolean state management helpers.
|
|
339
|
+
*
|
|
340
|
+
* @param set - The state setter function from useState
|
|
341
|
+
* @returns A tuple containing [setTrue: function, setFalse: function]
|
|
342
|
+
* @example
|
|
343
|
+
* ```typescript
|
|
344
|
+
* function MyComponent() {
|
|
345
|
+
* const [isVisible, setIsVisible] = useState(false);
|
|
346
|
+
* const [show, hide] = useSetUnset(setIsVisible);
|
|
347
|
+
*
|
|
348
|
+
* return (
|
|
349
|
+
* <div>
|
|
350
|
+
* <button onClick={show}>Show</button>
|
|
351
|
+
* <button onClick={hide}>Hide</button>
|
|
352
|
+
* {isVisible && <div>Content is visible</div>}
|
|
353
|
+
* </div>
|
|
354
|
+
* );
|
|
355
|
+
* }
|
|
356
|
+
* ```
|
|
357
|
+
*/ function useSetUnset(set) {
|
|
139
358
|
return [
|
|
140
359
|
useCallback(()=>set(true), [
|
|
141
360
|
set
|
|
@@ -146,7 +365,28 @@ function useSetUnset(set) {
|
|
|
146
365
|
];
|
|
147
366
|
}
|
|
148
367
|
|
|
149
|
-
|
|
368
|
+
/**
|
|
369
|
+
* React hook for managing dialog state with optional callback after closing.
|
|
370
|
+
* Provides convenient open/close functions and tracks the dialog's open state.
|
|
371
|
+
*
|
|
372
|
+
* @param onAfterClose - Optional callback function to execute after the dialog closes
|
|
373
|
+
* @returns Object containing dialog state and control functions
|
|
374
|
+
* @example
|
|
375
|
+
* ```typescript
|
|
376
|
+
* function MyComponent() {
|
|
377
|
+
* const { isDialogOpen, openDialog, closeDialog } = useDialog(() => {
|
|
378
|
+
* console.log('Dialog closed');
|
|
379
|
+
* });
|
|
380
|
+
*
|
|
381
|
+
* return (
|
|
382
|
+
* <div>
|
|
383
|
+
* <button onClick={openDialog}>Open Dialog</button>
|
|
384
|
+
* {isDialogOpen && <Dialog onClose={closeDialog} />}
|
|
385
|
+
* </div>
|
|
386
|
+
* );
|
|
387
|
+
* }
|
|
388
|
+
* ```
|
|
389
|
+
*/ function useDialog(onAfterClose) {
|
|
150
390
|
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
|
151
391
|
const [openDialog, closeDialog] = useSetUnset(setIsDialogOpen);
|
|
152
392
|
const close = useCallback(()=>{
|