Skip to content

Commit 215f531

Browse files
authored
Merge pull request #51 from 1Byte-Software/develop
Update version 1.11.0
2 parents 2480c0d + 1167129 commit 215f531

29 files changed

Lines changed: 834 additions & 11 deletions

CHANGELOG.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,21 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.11.0]
9+
10+
### Add
11+
12+
- Add `Filter` component.
13+
- Add `Tag.Group` component.
14+
- Add `type` prop for `Tag` component.
15+
- Add `useFilter` hooks.
16+
817
## [1.10.1]
918

1019
### Fixed
1120

12-
- Fix `HeaderSider` in `DashboardTemplate`.
21+
- Remove cache when header parameters change for all query.
22+
- Hidden learn more wallet in client cPanel
1323

1424
## [1.10.0]
1525

@@ -34,7 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3444
- Added chart to `SummaryCard` component.
3545
- Added `Statistic` component.
3646
- Added `Grid.useBreakpoint()` hooks.
37-
- Added `sidebarMode` for sidebarProps for `DashboardTemplate`.
47+
- Added `sidebarMode` for sidebarProps for `DashboardTemplate`.
3848
- Support responsive for `DashboardTemplate`.
3949
- Added `LineChart` component.
4050

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "1byte-react-design",
3-
"version": "1.10.1",
3+
"version": "1.11.0",
44
"description": "A simple React UI library",
55
"main": "dist/index.js",
66
"module": "dist/index.js",

pnpm-lock.yaml

Lines changed: 86 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/hooks/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './useFilter';

src/hooks/useFilter/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './useFilter';
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export const objectToURLSearchParams = (obj: Record<string, unknown>) => {
2+
return new URLSearchParams(
3+
Object.entries(obj)
4+
.filter(([_, v]) => v !== undefined)
5+
.map(([k, v]) => [k, String(v)])
6+
);
7+
};

src/hooks/useFilter/useFilter.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { useCallback, useMemo, useState } from 'react';
2+
import { useSearchParams } from 'react-router';
3+
import * as Yup from 'yup';
4+
import { objectToURLSearchParams } from './useFilter.helper';
5+
import { IUseFilterProps } from './useFilter.types';
6+
7+
/**
8+
* A React hook for managing filter state with Yup schema validation.
9+
*
10+
* Supports two modes of operation:
11+
* 1. **URL-based filter**: Keeps filter state in sync with `URLSearchParams`
12+
* 2. **Local state filter**: Keeps filter state in React state without touching the URL
13+
*
14+
* Features:
15+
* - Converts all schema fields to `notRequired` for flexible validation
16+
* - Supports default values from both `props.defaultValue` and `schema.getDefault()`
17+
* - Can strip unknown keys during validation to prevent invalid fields
18+
*
19+
* @typeParam T - The shape of the filter object, derived from the Yup schema
20+
*
21+
* @example
22+
* ```ts
23+
* const schema = Yup.object({
24+
* search: Yup.string(),
25+
* category: Yup.string().required(),
26+
* });
27+
*
28+
* const { value, onChange } = useFilter({
29+
* schema,
30+
* defaultValue: { search: '', category: 'all' },
31+
* config: { useSearchParams: true }
32+
* });
33+
*
34+
* onChange({ search: 'keyword' });
35+
* ```
36+
*/
37+
export const useFilter = <T extends NonNullable<unknown>>(props: IUseFilterProps<T>) => {
38+
const { schema, defaultValue, config } = props;
39+
const { stripUnknown = true, useSearchParams: enableSearchParams = true } = config || {};
40+
41+
// If `useSearchParams` mode is enabled, initialize state from the URL
42+
const [searchParams, setSearchParams] = useSearchParams(
43+
objectToURLSearchParams(defaultValue || {})
44+
);
45+
46+
// Local state fallback (if URL sync is disabled)
47+
const [filterState, setFilterState] = useState<T>(defaultValue || (schema.getDefault() as T));
48+
49+
// Create a "loose" version of the schema where all fields are optional
50+
const looseSchema = useMemo(() => {
51+
const newShape: Record<string, Yup.AnySchema> = {};
52+
53+
for (const key in schema.fields) {
54+
const field = schema.fields[key];
55+
if (Yup.isSchema(field)) {
56+
newShape[key] = (field as Yup.AnySchema).notRequired();
57+
}
58+
}
59+
60+
return Yup.object(newShape) as Yup.ObjectSchema<T>;
61+
}, [schema]);
62+
63+
// Compute the validated filter value from either URL params or local state
64+
const filterValue = useMemo(() => {
65+
try {
66+
const raw = enableSearchParams ? Object.fromEntries(searchParams) : filterState;
67+
68+
return looseSchema.validateSync(raw, { stripUnknown }) as Yup.InferType<typeof schema>;
69+
} catch (e) {
70+
console.debug('e', e);
71+
return schema.getDefault() as Yup.InferType<typeof schema>;
72+
}
73+
}, [searchParams, filterState, schema, stripUnknown, enableSearchParams, looseSchema]);
74+
75+
// Change handler for updating filter state
76+
const handleChangeFilter = useCallback(
77+
(filterValue: Partial<T>) => {
78+
if (enableSearchParams) {
79+
const filterValueWithoutNull = Object.entries<any>(filterValue).filter(
80+
([_, value]) => value !== null
81+
);
82+
setSearchParams(new URLSearchParams(filterValueWithoutNull));
83+
} else {
84+
setFilterState(prev => ({
85+
...prev,
86+
...filterValue,
87+
}));
88+
}
89+
},
90+
[enableSearchParams, setSearchParams]
91+
);
92+
93+
return {
94+
/** The current validated filter value */
95+
value: filterValue,
96+
97+
/** Updates the filter state or URL search params */
98+
onChange: handleChangeFilter,
99+
};
100+
};

0 commit comments

Comments
 (0)