Skip to content

#117 Add useScreenSize() Hook for Responsive Breakpoint Detection with Type Support #124 #125

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ export default Component;
- [`useSize()`](https://reacthaiku.dev/docs/hooks/useSize) - hook observes a referenced DOM element and returns its current width and height, updating the values whenever the element is resized. This is useful for dynamically tracking size changes of any resizable component.
- [`useDeviceOS()`](https://reacthaiku.dev/docs/hooks/useDeviceOS) - Detects the user's operating system, including mobile emulators, and uses string manipulation for identifying unique or new OS versions.
- [`usePreventBodyScroll()`](https://reacthaiku.dev/docs/hooks/usePreventBodyScroll) - Disables body scrolling when active and restores it upon deactivation or component unmounting. It provides a boolean state, a setter, and a toggle function for dynamic scroll control.
- [`useScreenSize()`](https://reacthaiku.dev/docs/hooks/useScreenSize) - Provide the breakponint values in string method `toString` with other method `equals` , `lessThan` , `greaterThan` `greaterThanEqual` `lessThanEqual` value in `True/False`.

### Utilities

Expand Down
18 changes: 18 additions & 0 deletions docs/demo/UseScreenSizeDemo.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useScreenSize } from 'react-haiku';
import React from 'react';


export const UseScreenSizeDemo = () => {
const screenSize = useScreenSize();

return (
<div className="demo-container-center">
<h1>Current Screen Size: {screenSize.toString()}</h1>
<p>Is the screen size medium ? {screenSize.eq("md") ? "Yes" : "No"}</p>
<p>Is the screen size less than large ? {screenSize.lt("lg") ? "Yes" : "No"}</p>
<p>Is the screen size greater than small ? {screenSize.gt("sm") ? "Yes" : "No"}</p>
<p>Is the screen size greater than or equal to small ? {screenSize.gte("sm") ? "Yes" : "No"}</p>
<p>Is the screen size less than or equal to small ? {screenSize.lte("sm") ? "Yes" : "No"}</p>
</div>
);
}
35 changes: 35 additions & 0 deletions docs/docs/hooks/useScreenSize.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# useScreenSize()

The `useScreenSize()` hook allows responsive breakpoint detection in components. It returns helper methods (`equals`, `lessThan`, `greaterThan` , `greaterThanEqual` , `lessThanEqual` ) and a string method (`toString`) representing the current screen size: `xs`, `sm`, `md`, `lg`, `xl`, or `2xl for size bigger than 1535 pixel`.

### Import

```jsx
import { useScreenSize } from 'react-haiku';
```


### Usage

import { UseScreenSizeDemo } from '../../demo/UseScreenSizeDemo.jsx';

<UseScreenSizeDemo />

```jsx
import { useScreenSize } from 'react-haiku';

export const MyComponent = () => {
const screenSize = useScreenSize();

return (
<div>
<h1>Current Screen Size: {screenSize.toString()}</h1>
<p>Is the screen size medium ? {screenSize.eq("md") ? "Yes" : "No"}</p>
<p>Is the screen size less than large ? {screenSize.lt("lg") ? "Yes" : "No"}</p>
<p>Is the screen size greater than small ? {screenSize.gt("sm") ? "Yes" : "No"}</p>
<p>Is the screen size greater than or equal to small ? {screenSize.gte("sm") ? "Yes" : "No"}</p>
<p>Is the screen size less than or equal to small ? {screenSize.lte("sm") ? "Yes" : "No"}</p>
</div>
);
};
```
62 changes: 62 additions & 0 deletions lib/hooks/useScreenSize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { useState, useCallback, useLayoutEffect } from 'react';

const breakpoints = {
xs: 639,
sm: 767,
md: 1023,
lg: 1279,
xl: 1535,
'2xl': Infinity,
};

const getBreakpoint = (width: number): keyof typeof breakpoints => {
return (Object.keys(breakpoints) as (keyof typeof breakpoints)[])
.find(bp => width <= breakpoints[bp]) ?? '2xl';
};

export const useScreenSize = () => {
const [screenSize, setScreenSize] = useState<keyof typeof breakpoints>(() =>
typeof window === 'undefined' ? 'xl' : getBreakpoint(window.innerWidth),
);

const handleResize = useCallback(() => {
setScreenSize(getBreakpoint(window.innerWidth));
}, []);

useLayoutEffect(() => {
if (typeof window === 'undefined') return;

let ticking = false;

const onResize = () => {
if (ticking) return;
ticking = true;
requestAnimationFrame(() => {
handleResize();
ticking = false;
});
};

// Initial measurement
handleResize();
window.addEventListener('resize', onResize);

return () => window.removeEventListener('resize', onResize);
}, [handleResize]);

const eq = (bp: keyof typeof breakpoints) => screenSize === bp;
const lt = (bp: keyof typeof breakpoints) => breakpoints[screenSize] < breakpoints[bp];
const gt = (bp: keyof typeof breakpoints) => breakpoints[screenSize] > breakpoints[bp];
const lte = (bp: keyof typeof breakpoints) => eq(bp) || lt(bp);
const gte = (bp: keyof typeof breakpoints) => eq(bp) || gt(bp);


return {
eq,
lt,
gt,
lte,
gte,
toString: () => screenSize,
};
};
3 changes: 1 addition & 2 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,16 @@ export { usePreventBodyScroll } from './hooks/usePreventBodyScroll';
export { usePrevious } from './hooks/usePrevious';
export { useKeyPress } from './hooks/useKeyPress';
export { useScrollDevice } from './hooks/useScrollDevice';
export { useScreenSize } from './hooks/useScreenSize';
export { usePermission, UsePermissionState } from './hooks/usePermission';
export { useTimer } from './hooks/useTimer';
export { useWebSocket } from './hooks/useWebSocket';

export { If } from './utils/If';
export { Show } from './utils/Show';
export { For } from './utils/For';
export { RenderAfter } from './utils/RenderAfter';
export { Class } from './utils/Class';
export { Switch } from './utils/Switch';
export { Image } from './utils/Image';

import ErrorBoundary from './utils/ErrorBoundary';
export { ErrorBoundary };