Skip to content

Commit e70893d

Browse files
authored
Merge pull request #2287 from gluestack/feat/new-hooks-addition
Feat/new hooks addition
2 parents cde9a69 + 76199ca commit e70893d

File tree

12 files changed

+754
-0
lines changed

12 files changed

+754
-0
lines changed

example/storybook-nativewind/.storybook/preview.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ export const parameters = {
8686
'Others',
8787
['Fab', 'Skeleton'],
8888
],
89+
'Hooks',
90+
['useBreakPointValue', 'useMediaQuery'],
8991
'Apps',
9092
['Dashboard App', 'Starter Kit', 'Storybook App'],
9193
'Guides',

example/storybook-nativewind/babel.config.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ module.exports = function (api) {
2222
? path.resolve(__dirname, './global.css')
2323
: path.resolve(__dirname, './global-gluestack.css'),
2424

25+
'@/components/hooks': path.resolve(
26+
__dirname,
27+
'./src/core-components/hooks'
28+
),
2529
'@gluestack-ui/checkbox': path.resolve(
2630
__dirname,
2731
'../../packages/unstyled/checkbox/src'
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { Dimensions, useWindowDimensions } from 'react-native';
2+
import { useEffect, useState } from 'react';
3+
4+
import DefaultTheme from 'tailwindcss/defaultConfig';
5+
import resolveConfig from 'tailwindcss/resolveConfig';
6+
7+
const TailwindTheme = resolveConfig(DefaultTheme);
8+
const screenSize = TailwindTheme.theme.screens;
9+
10+
type breakpoints = keyof typeof screenSize | 'default';
11+
12+
type BreakPointValue = Partial<Record<breakpoints, any>>;
13+
14+
const resolveScreenWidth: any = {
15+
default: 0,
16+
};
17+
18+
Object.entries(screenSize).forEach(([key, value]) => {
19+
resolveScreenWidth[key] = parseInt(value.replace('px', ''));
20+
});
21+
22+
export const getBreakPointValue = (values: any, width: any) => {
23+
if (typeof values !== 'object') return values;
24+
25+
let finalBreakPointResolvedValue: any;
26+
const mediaQueriesBreakpoints: any = [
27+
{
28+
key: 'default',
29+
breakpoint: 0,
30+
isValid: true,
31+
},
32+
];
33+
Object.keys(resolveScreenWidth).forEach((key) => {
34+
const isValid = isValidBreakpoint(resolveScreenWidth[key], width);
35+
36+
mediaQueriesBreakpoints.push({
37+
key: key,
38+
breakpoint: resolveScreenWidth[key],
39+
isValid: isValid,
40+
});
41+
});
42+
43+
mediaQueriesBreakpoints.sort((a: any, b: any) => a.breakpoint - b.breakpoint);
44+
45+
mediaQueriesBreakpoints.forEach((breakpoint: any, index: any) => {
46+
breakpoint.value = values.hasOwnProperty(breakpoint.key)
47+
? // @ts-ignore
48+
values[breakpoint.key]
49+
: mediaQueriesBreakpoints[index - 1]?.value ||
50+
mediaQueriesBreakpoints[0]?.value;
51+
});
52+
53+
const lastValidObject = getLastValidObject(mediaQueriesBreakpoints);
54+
55+
if (!lastValidObject) {
56+
finalBreakPointResolvedValue = values;
57+
} else {
58+
finalBreakPointResolvedValue = lastValidObject?.value;
59+
}
60+
return finalBreakPointResolvedValue;
61+
};
62+
63+
export function useBreakpointValue(values: BreakPointValue): any {
64+
const { width } = useWindowDimensions();
65+
66+
const [currentBreakPointValue, setCurrentBreakPointValue] =
67+
useState(undefined);
68+
69+
useEffect(() => {
70+
if (typeof values === 'object') {
71+
const finalBreakPointResolvedValue = getBreakPointValue(values, width);
72+
setCurrentBreakPointValue(finalBreakPointResolvedValue);
73+
}
74+
}, [values, width]);
75+
76+
if (typeof values !== 'object') return values;
77+
78+
return currentBreakPointValue;
79+
}
80+
81+
export function isValidBreakpoint(
82+
breakPointWidth: any,
83+
width: any = Dimensions.get('window')?.width
84+
) {
85+
const windowWidth = width;
86+
87+
if (windowWidth >= breakPointWidth) {
88+
return true;
89+
}
90+
return false;
91+
}
92+
93+
function getLastValidObject(mediaQueries: any) {
94+
for (let i = mediaQueries.length - 1; i >= 0; i--) {
95+
if (mediaQueries[i].isValid) {
96+
return mediaQueries[i];
97+
}
98+
}
99+
return null; // No valid object found
100+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { useWindowDimensions } from 'react-native';
2+
3+
type QueryKeys =
4+
| 'maxWidth'
5+
| 'minWidth'
6+
| 'maxHeight'
7+
| 'minHeight'
8+
| 'orientation';
9+
10+
type SubQuery = {
11+
[queryKey in QueryKeys]?: number | string;
12+
};
13+
type Query = Array<SubQuery>;
14+
15+
export function useMediaQuery(query: SubQuery | Query) {
16+
const dims = useWindowDimensions();
17+
const height = dims?.height;
18+
const width = dims?.width;
19+
20+
return iterateQuery(query, height, width);
21+
}
22+
23+
function queryResolver(query: any, width?: number, height?: number) {
24+
for (const queryKey in query) {
25+
if (!calculateQuery(queryKey, query[queryKey], height, width)) {
26+
return false;
27+
}
28+
}
29+
return true;
30+
}
31+
32+
function iterateQuery(
33+
query: SubQuery | Query,
34+
height?: number,
35+
width?: number
36+
) {
37+
const queryResults = [];
38+
if (Array.isArray(query)) {
39+
query.forEach((subQuery: SubQuery) => {
40+
queryResults.push(queryResolver(subQuery, width, height));
41+
});
42+
} else {
43+
queryResults.push(queryResolver(query, width, height));
44+
}
45+
return queryResults;
46+
}
47+
48+
function calculateQuery(
49+
key: string,
50+
val?: number | string,
51+
height?: number,
52+
width?: number
53+
) {
54+
let retval;
55+
switch (key) {
56+
case 'maxWidth':
57+
retval = typeof val === 'number' && width ? width <= val : undefined;
58+
break;
59+
case 'minWidth':
60+
retval = typeof val === 'number' && width ? width >= val : undefined;
61+
break;
62+
case 'maxHeight':
63+
retval = typeof val === 'number' && height ? height <= val : undefined;
64+
break;
65+
case 'minHeight':
66+
retval = typeof val === 'number' && height ? height >= val : undefined;
67+
break;
68+
case 'orientation':
69+
if (val) {
70+
if (width && height && width > height) {
71+
retval = typeof val === 'string' && val === 'landscape';
72+
} else {
73+
retval = typeof val === 'string' && val === 'portrait';
74+
}
75+
}
76+
break;
77+
default:
78+
break;
79+
}
80+
return retval;
81+
}
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
---
2+
title: gluestack-ui Alert Component | Installation, Usage, and API
3+
4+
description: Alerts are used to communicate the status of a system, feature, or page. They indicate a specific state that may require attention from the user.
5+
6+
pageTitle: Alert
7+
8+
pageDescription: Alerts are used to communicate the status of a system, feature, or page. They indicate a specific state that may require attention from the user.
9+
10+
showHeader: true
11+
---
12+
13+
import { Meta } from '@storybook/addon-docs';
14+
15+
<Meta title="with-nativewind/Hooks/useBreakPointValue" />
16+
17+
import {
18+
VStack,
19+
Box,
20+
Text
21+
} from '../../core-components/nativewind';
22+
import { useBreakpointValue } from '../../core-components/hooks/use-break-point-value.ts';
23+
import { transformedCode } from '../../utils';
24+
import {
25+
AppProvider,
26+
CodePreview,
27+
Table,
28+
InlineCode,
29+
TableContainer,
30+
Tabs
31+
} from '@gluestack/design-system';
32+
import { CollapsibleCode } from '@gluestack/design-system';
33+
import Wrapper from '../../core-components/nativewind/Wrapper';
34+
35+
This is an illustration of **useBreakPointValue** hook.
36+
37+
<Wrapper>
38+
<CodePreview
39+
_rendererWrapper={{ px: '$2.5' }}
40+
showComponentRenderer={true}
41+
showArgsController={false}
42+
metaData={{
43+
code: `
44+
function App(){
45+
const flexDir = useBreakpointValue({
46+
default: "column",
47+
sm: "row",
48+
});
49+
return (
50+
<VStack
51+
style={{
52+
flexDirection: flexDir,
53+
gap: 10,
54+
}}
55+
>
56+
<Box className={"justify-center items-center p-4 rounded bg-blue-500"}>
57+
<Text className="font-bold text-typography-0">Box 1</Text>
58+
</Box>
59+
<Box className={"justify-center items-center p-4 rounded bg-blue-600"}>
60+
<Text className="font-bold text-typography-0">Box 2</Text>
61+
</Box>
62+
<Box className={"justify-center items-center p-4 rounded bg-blue-700"}>
63+
<Text className="font-bold text-typography-0">Box 3</Text>
64+
</Box>
65+
</VStack>
66+
);
67+
}
68+
`,
69+
transformCode: (code) => {
70+
return transformedCode(code, 'function', 'App');
71+
},
72+
scope: {
73+
VStack,
74+
Box,
75+
Text,
76+
useBreakpointValue,
77+
Wrapper,
78+
},
79+
argsType: {
80+
},
81+
}}
82+
/>
83+
</Wrapper>
84+
85+
<br />
86+
87+
## Installation
88+
89+
<Tabs value="cli" type="section">
90+
<Tabs.TabList>
91+
<Tabs.Tab value="cli">
92+
<Tabs.TabTitle>CLI</Tabs.TabTitle>
93+
</Tabs.Tab>
94+
<Tabs.Tab value="manual">
95+
<Tabs.TabTitle>Manual</Tabs.TabTitle>
96+
</Tabs.Tab>
97+
</Tabs.TabList>
98+
<Tabs.TabPanels>
99+
<Tabs.TabPanel value="cli">
100+
<>
101+
102+
### Run the following command:
103+
```bash
104+
npx gluestack-ui add useBreakPointValue
105+
```
106+
</>
107+
</Tabs.TabPanel>
108+
<Tabs.TabPanel value="manual">
109+
<>
110+
111+
112+
### Step 1: Copy and paste the following code into your project.
113+
114+
<CollapsibleCode>
115+
116+
```jsx
117+
%%-- File: core-components/hooks/use-break-point-value.ts --%%
118+
```
119+
120+
</CollapsibleCode>
121+
122+
### Step 2: Update the import paths to match your project setup.
123+
</>
124+
</Tabs.TabPanel>
125+
</Tabs.TabPanels>
126+
</Tabs>
127+
128+
## API Reference
129+
130+
To use this component in your project, include the following import statement in your file.
131+
132+
```jsx
133+
import { useBreakPointValue } from '@/components/hooks/use-break-point-value';
134+
```
135+
136+
```jsx
137+
const flexDir = useBreakpointValue({
138+
default: "column",
139+
sm: "row",
140+
});
141+
```
142+
143+
### Hook Arguments
144+
145+
This section provides a comprehensive reference list for the hook arguments, detailing descriptions, properties, types, and default behavior for easy project integration.
146+
147+
#### useBreakPointValue
148+
149+
<>
150+
<TableContainer>
151+
<Table>
152+
<Table.THead>
153+
<Table.TR>
154+
<Table.TH>
155+
<Table.TText>Arguments</Table.TText>
156+
</Table.TH>
157+
<Table.TH>
158+
<Table.TText>Type</Table.TText>
159+
</Table.TH>
160+
<Table.TH>
161+
<Table.TText>Default</Table.TText>
162+
</Table.TH>
163+
</Table.TR>
164+
</Table.THead>
165+
<Table.TBody>
166+
<Table.TR>
167+
<Table.TD>
168+
<Table.TText>
169+
<InlineCode>1</InlineCode>
170+
</Table.TText>
171+
</Table.TD>
172+
<Table.TD>
173+
<Table.TText>{`Record<breakPoints | default,value>`}</Table.TText>
174+
</Table.TD>
175+
<Table.TD>
176+
<Table.TText>-</Table.TText>
177+
</Table.TD>
178+
</Table.TR>
179+
</Table.TBody>
180+
</Table>
181+
</TableContainer>
182+
</>
183+
184+
### Return Value
185+
186+
The **useBreakPointValue** hook returns value, based on given break point value object.

0 commit comments

Comments
 (0)