clear-react-router 1.4.0 → 1.4.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 +44 -36
- package/dist/hooks/useQueryParam.d.ts +2 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +67 -34
- package/dist/types/global.d.ts +4 -0
- package/dist/utils/adapter.d.ts +56 -0
- package/package.json +1 -1
- package/dist/utils/parser.d.ts +0 -21
package/README.md
CHANGED
|
@@ -302,26 +302,23 @@ useBeforeUnload(text ? onSave : undefined);
|
|
|
302
302
|
|
|
303
303
|
### `useQueryParam()`
|
|
304
304
|
|
|
305
|
-
A flexible hook for working with typed query parameters. You provide
|
|
305
|
+
A flexible hook for working with typed query parameters. You provide an adapter object with `parse` and `serialize` functions, and it returns the parsed value and a setter.
|
|
306
306
|
|
|
307
307
|
```
|
|
308
|
-
import { useQueryParam,
|
|
308
|
+
import { useQueryParam, adapter } from 'clear-react-router';
|
|
309
309
|
|
|
310
310
|
const ProductPage = () => {
|
|
311
311
|
// String parameter
|
|
312
|
-
const [brand, setBrand] = useQueryParam('brand',
|
|
312
|
+
const [brand, setBrand] = useQueryParam('brand', adapter.string, 'nike');
|
|
313
313
|
|
|
314
314
|
// Number parameter
|
|
315
|
-
const [page, setPage] = useQueryParam('page',
|
|
315
|
+
const [page, setPage] = useQueryParam('page', adapter.integer, 1);
|
|
316
316
|
|
|
317
|
-
//
|
|
318
|
-
const [
|
|
319
|
-
|
|
320
|
-
// Array of strings
|
|
321
|
-
const [colors, setColors] = useQueryParam('colors', parser.stringArray);
|
|
317
|
+
// Date parameter
|
|
318
|
+
const [date, setDate] = useQueryParam('date', adapter.date, new Date());
|
|
322
319
|
|
|
323
320
|
// Array of numbers
|
|
324
|
-
const [prices, setPrices] = useQueryParam('prices',
|
|
321
|
+
const [prices, setPrices] = useQueryParam('prices', adapter.floatArray);
|
|
325
322
|
|
|
326
323
|
return (
|
|
327
324
|
<div>
|
|
@@ -332,12 +329,17 @@ const ProductPage = () => {
|
|
|
332
329
|
);
|
|
333
330
|
}
|
|
334
331
|
```
|
|
335
|
-
|
|
332
|
+
type Adapter<T> = {
|
|
333
|
+
parse: (params: string[]) => T;
|
|
334
|
+
serialize?: (params: T) => string | string[];
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
**Signature:** `useQueryParam<T>(field: string, adapter: Adapter<T>, defaultValue?: T): [T, (arg: T | null) => void]`
|
|
336
338
|
|
|
337
339
|
| Argument | Type | Description |
|
|
338
340
|
|----------|------|-------------|
|
|
339
341
|
| `field` | `string` | The query parameter key (e.g., `'page'`, `'brand'`) |
|
|
340
|
-
| `
|
|
342
|
+
| `adapter` | `Adapter<T>` | Parser and optional serializer for params (serializer String is used in case of serializer not passed) |
|
|
341
343
|
| `defaultValue` | `T` (optional) | Default value returned when the parameter is missing or empty |
|
|
342
344
|
|
|
343
345
|
**Returns:**
|
|
@@ -345,27 +347,30 @@ const ProductPage = () => {
|
|
|
345
347
|
| Element | Type | Description |
|
|
346
348
|
|---------|------|-------------|
|
|
347
349
|
| `value` | `T` | The parsed value from the query parameter |
|
|
348
|
-
| `setValue` | `(arg: T) => void` | Function to update the query parameter |
|
|
349
|
-
|
|
350
|
-
### Built-in
|
|
351
|
-
|
|
352
|
-
|
|
|
353
|
-
|
|
354
|
-
| `
|
|
355
|
-
| `
|
|
356
|
-
| `
|
|
357
|
-
| `
|
|
358
|
-
| `
|
|
359
|
-
| `
|
|
360
|
-
| `
|
|
361
|
-
| `
|
|
350
|
+
| `setValue` | `(arg: T | null) => void` | Function to update the query parameter. Null is passed to remove the parameter. |
|
|
351
|
+
|
|
352
|
+
### Built-in Adapters
|
|
353
|
+
|
|
354
|
+
| Adapter | Input | Output | Description |
|
|
355
|
+
|---------|-------|--------|-------------|
|
|
356
|
+
| `adapter.string` | `string[]` | `string` | First value or empty string |
|
|
357
|
+
| `adapter.stringArray` | `string[]` | `string[]` | All values as array |
|
|
358
|
+
| `adapter.integer` | `string[]` | `number` | First value parsed as integer (default: `0`) |
|
|
359
|
+
| `adapter.integerArray` | `string[]` | `number[]` | All values parsed as integers |
|
|
360
|
+
| `adapter.float` | `string[]` | `number` | First value parsed as float (default: `0`) |
|
|
361
|
+
| `adapter.floatArray` | `string[]` | `number[]` | All values parsed as floats |
|
|
362
|
+
| `adapter.boolean` | `string[]` | `boolean` | First value parsed as boolean (`'true'` → `true`) |
|
|
363
|
+
| `adapter.booleanArray` | `string[]` | `boolean[]` | All values parsed as booleans |
|
|
364
|
+
| `adapter.date` | `string[]` | `Date` | First value parsed as Date from timestamp |
|
|
365
|
+
| `adapter.dateArray` | `string[]` | `Date[]` | All values parsed as Dates from timestamps |
|
|
366
|
+
| `adapter.zodSchema` | `string[]` | `T` | Validates JSON string against Zod schema |
|
|
362
367
|
|
|
363
368
|
### Using Zod Schemas
|
|
364
369
|
`useQueryParam` works seamlessly with Zod for complex validation:
|
|
365
370
|
|
|
366
371
|
```
|
|
367
372
|
import { z } from 'zod';
|
|
368
|
-
import { useQueryParam,
|
|
373
|
+
import { useQueryParam, adapter } from 'clear-react-router';
|
|
369
374
|
|
|
370
375
|
const filterSchema = z.object({
|
|
371
376
|
name: z.string(),
|
|
@@ -376,7 +381,7 @@ const filterSchema = z.object({
|
|
|
376
381
|
function ProductFilter() {
|
|
377
382
|
const [filter, setFilter] = useQueryParam(
|
|
378
383
|
'filter',
|
|
379
|
-
|
|
384
|
+
adapter.zodSchema(filterSchema),
|
|
380
385
|
{ name: '', age: 0 }
|
|
381
386
|
);
|
|
382
387
|
|
|
@@ -392,18 +397,21 @@ function ProductFilter() {
|
|
|
392
397
|
}
|
|
393
398
|
```
|
|
394
399
|
|
|
395
|
-
### Custom
|
|
396
|
-
You can write your own
|
|
400
|
+
### Custom Adapters
|
|
401
|
+
You can write your own adapter for any format:
|
|
397
402
|
|
|
398
403
|
```
|
|
399
|
-
// Custom
|
|
400
|
-
const
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
+
// Custom adapter for comma-separated values
|
|
405
|
+
const csvAdapter = {
|
|
406
|
+
parse: (params: string[]): string[] => {
|
|
407
|
+
const value = params[0] || '';
|
|
408
|
+
return value ? value.split(',').map(v => v.trim()) : [];
|
|
409
|
+
},
|
|
410
|
+
serialize: (value: string[]): string[] => value
|
|
411
|
+
}
|
|
404
412
|
|
|
405
413
|
const TagsFilter() {
|
|
406
|
-
const [tags, setTags] = useQueryParam('tags',
|
|
414
|
+
const [tags, setTags] = useQueryParam('tags', csvAdapter, []);
|
|
407
415
|
// tags: string[]
|
|
408
416
|
}
|
|
409
417
|
```
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import { Adapter } from '../types/global';
|
|
2
|
+
export declare function useQueryParam<T>(field: string, adapter: Adapter<T>, defaultValue?: T): [T, (arg: T | null) => void];
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,6 @@ export { useRouterContext } from './hooks/useRouterContext';
|
|
|
11
11
|
export { useQueryParam } from './hooks/useQueryParam';
|
|
12
12
|
export { useSearchParams } from './hooks/useSearchParams';
|
|
13
13
|
export { useHistoricalTrail } from './hooks/useHistoricalTrail';
|
|
14
|
-
export {
|
|
14
|
+
export { adapter } from './utils/adapter';
|
|
15
15
|
export { createRouter } from './utils/utils';
|
|
16
|
-
export type { RouteItem, BlockerState, Location } from './types/global';
|
|
16
|
+
export type { RouteItem, BlockerState, Location, Adapter } from './types/global';
|
package/dist/index.js
CHANGED
|
@@ -675,21 +675,28 @@ var useSearchParams = () => {
|
|
|
675
675
|
};
|
|
676
676
|
//#endregion
|
|
677
677
|
//#region hooks/useQueryParam.ts
|
|
678
|
-
function useQueryParam(field,
|
|
678
|
+
function useQueryParam(field, adapter, defaultValue) {
|
|
679
679
|
const { searchParams, setSearchParams } = useSearchParams();
|
|
680
680
|
return [useMemo(() => {
|
|
681
|
-
const
|
|
682
|
-
|
|
681
|
+
const params = searchParams.getAll(field);
|
|
682
|
+
const result = adapter.parse(params);
|
|
683
|
+
const isValid = !(result instanceof Date) || result instanceof Date && !isNaN(result.getTime());
|
|
684
|
+
if (result !== void 0 && result !== null && result !== "" && isValid) return result;
|
|
683
685
|
if (defaultValue !== void 0) return defaultValue;
|
|
684
686
|
return result;
|
|
685
687
|
}, [
|
|
686
688
|
field,
|
|
687
|
-
|
|
689
|
+
adapter,
|
|
688
690
|
searchParams,
|
|
689
691
|
defaultValue
|
|
690
692
|
]), useCallback((value) => {
|
|
691
|
-
|
|
692
|
-
|
|
693
|
+
if (!value) return setSearchParams(field, []);
|
|
694
|
+
setSearchParams(field, (adapter.serialize || String)(value));
|
|
695
|
+
}, [
|
|
696
|
+
field,
|
|
697
|
+
adapter.serialize,
|
|
698
|
+
setSearchParams
|
|
699
|
+
])];
|
|
693
700
|
}
|
|
694
701
|
//#endregion
|
|
695
702
|
//#region hooks/useHistoricalTrail.ts
|
|
@@ -706,39 +713,65 @@ var useHistoricalTrail = () => {
|
|
|
706
713
|
return trail;
|
|
707
714
|
};
|
|
708
715
|
//#endregion
|
|
709
|
-
//#region utils/
|
|
710
|
-
var
|
|
711
|
-
string: (params) => params[0] || "",
|
|
712
|
-
stringArray:
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
return isNaN(result) ? 0 : result;
|
|
716
|
+
//#region utils/adapter.ts
|
|
717
|
+
var adapter = {
|
|
718
|
+
string: { parse: (params) => params[0] || "" },
|
|
719
|
+
stringArray: {
|
|
720
|
+
parse: (params) => params,
|
|
721
|
+
serialize: (value) => value
|
|
716
722
|
},
|
|
717
|
-
|
|
718
|
-
const result = parseInt(
|
|
723
|
+
integer: { parse: (params) => {
|
|
724
|
+
const result = parseInt(params[0] || "");
|
|
719
725
|
return isNaN(result) ? 0 : result;
|
|
720
|
-
}
|
|
721
|
-
|
|
726
|
+
} },
|
|
727
|
+
integerArray: {
|
|
728
|
+
parse: (params) => params.map((el) => {
|
|
729
|
+
const result = parseInt(el);
|
|
730
|
+
return isNaN(result) ? 0 : result;
|
|
731
|
+
}),
|
|
732
|
+
serialize: (value) => value.map(String)
|
|
733
|
+
},
|
|
734
|
+
float: { parse: (params) => {
|
|
722
735
|
const result = parseFloat(params[0] || "");
|
|
723
736
|
return isNaN(result) ? 0 : result;
|
|
737
|
+
} },
|
|
738
|
+
floatArray: {
|
|
739
|
+
parse: (params) => params.map((el) => {
|
|
740
|
+
const result = parseFloat(el);
|
|
741
|
+
return isNaN(result) ? 0 : result;
|
|
742
|
+
}),
|
|
743
|
+
serialize: (value) => value.map(String)
|
|
724
744
|
},
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
745
|
+
boolean: {
|
|
746
|
+
parse: (params) => params[0]?.toLowerCase() === "true",
|
|
747
|
+
serialize: String
|
|
748
|
+
},
|
|
749
|
+
booleanArray: {
|
|
750
|
+
parse: (params) => params.map((el) => el.toLowerCase() === "true"),
|
|
751
|
+
serialize: (value) => value.map(String)
|
|
752
|
+
},
|
|
753
|
+
date: {
|
|
754
|
+
parse: (params) => new Date(Number(params[0])),
|
|
755
|
+
serialize: (arg) => String(arg.getTime())
|
|
756
|
+
},
|
|
757
|
+
dateArray: {
|
|
758
|
+
parse: (params) => params.map((param) => new Date(Number(param))),
|
|
759
|
+
serialize: (args) => args.map((arg) => String(arg.getTime()))
|
|
760
|
+
},
|
|
761
|
+
zodSchema: {
|
|
762
|
+
parse: (schema) => (params) => {
|
|
763
|
+
let parsed;
|
|
764
|
+
try {
|
|
765
|
+
parsed = JSON.parse(params[0] ?? "{}");
|
|
766
|
+
} catch {
|
|
767
|
+
throw new Error("Invalid JSON");
|
|
768
|
+
}
|
|
769
|
+
const result = schema.safeParse(parsed);
|
|
770
|
+
if (!result.success) throw new Error("Invalid schema");
|
|
771
|
+
return result.data;
|
|
772
|
+
},
|
|
773
|
+
serialize: JSON.stringify
|
|
741
774
|
}
|
|
742
775
|
};
|
|
743
776
|
//#endregion
|
|
744
|
-
export { Link, Router, RouterProvider,
|
|
777
|
+
export { Link, Router, RouterProvider, adapter, createRouter, useBeforeUnload, useBlocker, useHistoricalTrail, useLoaderState, useLocation, useNavigate, useParams, useQueryParam, useRouterContext, useSearchParams };
|
package/dist/types/global.d.ts
CHANGED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
type ZodInterface<T> = {
|
|
2
|
+
safeParse(input: unknown): {
|
|
3
|
+
success: true;
|
|
4
|
+
data: T;
|
|
5
|
+
} | {
|
|
6
|
+
success: false;
|
|
7
|
+
error: unknown;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
export declare const adapter: {
|
|
11
|
+
string: {
|
|
12
|
+
parse: (params: string[]) => string;
|
|
13
|
+
};
|
|
14
|
+
stringArray: {
|
|
15
|
+
parse: (params: string[]) => string[];
|
|
16
|
+
serialize: (value: string[]) => string[];
|
|
17
|
+
};
|
|
18
|
+
integer: {
|
|
19
|
+
parse: (params: string[]) => number;
|
|
20
|
+
};
|
|
21
|
+
integerArray: {
|
|
22
|
+
parse: (params: string[]) => number[];
|
|
23
|
+
serialize: (value: number[]) => string[];
|
|
24
|
+
};
|
|
25
|
+
float: {
|
|
26
|
+
parse: (params: string[]) => number;
|
|
27
|
+
};
|
|
28
|
+
floatArray: {
|
|
29
|
+
parse: (params: string[]) => number[];
|
|
30
|
+
serialize: (value: number[]) => string[];
|
|
31
|
+
};
|
|
32
|
+
boolean: {
|
|
33
|
+
parse: (params: string[]) => boolean;
|
|
34
|
+
serialize: StringConstructor;
|
|
35
|
+
};
|
|
36
|
+
booleanArray: {
|
|
37
|
+
parse: (params: string[]) => boolean[];
|
|
38
|
+
serialize: (value: boolean[]) => string[];
|
|
39
|
+
};
|
|
40
|
+
date: {
|
|
41
|
+
parse: (params: string[]) => Date;
|
|
42
|
+
serialize: (arg: Date) => string;
|
|
43
|
+
};
|
|
44
|
+
dateArray: {
|
|
45
|
+
parse: (params: string[]) => Date[];
|
|
46
|
+
serialize: (args: Date[]) => string[];
|
|
47
|
+
};
|
|
48
|
+
zodSchema: {
|
|
49
|
+
parse: <T>(schema: ZodInterface<T>) => (params: string[]) => T;
|
|
50
|
+
serialize: {
|
|
51
|
+
(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string;
|
|
52
|
+
(value: any, replacer?: (number | string)[] | null, space?: string | number): string;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
export {};
|
package/package.json
CHANGED
package/dist/utils/parser.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
type ZodInterface<T> = {
|
|
2
|
-
safeParse(input: unknown): {
|
|
3
|
-
success: true;
|
|
4
|
-
data: T;
|
|
5
|
-
} | {
|
|
6
|
-
success: false;
|
|
7
|
-
error: unknown;
|
|
8
|
-
};
|
|
9
|
-
};
|
|
10
|
-
export declare const parser: {
|
|
11
|
-
string: (params: string[]) => string;
|
|
12
|
-
stringArray: (params: string[]) => string[];
|
|
13
|
-
integer: (params: string[]) => number;
|
|
14
|
-
integerArray: (params: string[]) => number[];
|
|
15
|
-
float: (params: string[]) => number;
|
|
16
|
-
floatArray: (params: string[]) => number[];
|
|
17
|
-
boolean: (params: string[]) => boolean;
|
|
18
|
-
booleanArray: (params: string[]) => boolean[];
|
|
19
|
-
zodSchema: <T>(schema: ZodInterface<T>) => (params: string[]) => T;
|
|
20
|
-
};
|
|
21
|
-
export {};
|