Skip to content

Commit

Permalink
feat(hooks): adds useIsVisible hook
Browse files Browse the repository at this point in the history
  • Loading branch information
Than-DE committed Dec 16, 2023
1 parent 260a4d6 commit bbd0d0f
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/src/mdx/data/mdx-hooks-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,5 @@ export const MDX_HOOKS_DATA: Record<string, Frontmatter> = {

useWindowScroll: hDocs('useWindowScroll', 'Tracks window scroll position'),
usePagination: hDocs('usePagination', 'Manages pagination state'),
useIsVisible: hDocs('useIsVisible', 'Detects if element is visible in viewport'),
};
1 change: 1 addition & 0 deletions docs/src/mdx/mdx-pages-group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export const MDX_PAGES_GROUPS: MdxPagesGroup[] = [
MDX_DATA.useViewportSize,
MDX_DATA.useWindowEvent,
MDX_DATA.useWindowScroll,
MDX_DATA.useIsVisible,
],
},

Expand Down
11 changes: 11 additions & 0 deletions docs/src/pages/hooks/use-is-visible.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { HooksDemos } from '@docs/demos';
import { Layout } from '@/layout';
import { MDX_DATA } from '@/mdx';

export default Layout(MDX_DATA.useIsVisible);

## Usage

`use-is-visible` is a hook that returns a boolean value indicating whether the target element is visible in the viewport. It makes use of the [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) to determine visibility.

<Demo data={HooksDemos.useIsVisibleDemo} />
5 changes: 5 additions & 0 deletions packages/@docs/demos/src/demos/hooks/Hooks.demos.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -262,3 +262,8 @@ export const DemoUseEyeDropperUsage = {
name: '⭐ Demo: useEyeDropperUsage',
render: renderDemo(demos.useEyeDropperUsage),
};

export const DemoUseIsVisibleUsage = {
name: '⭐ Demo: useIsVisibleUsage',
render: renderDemo(demos.useIsVisibleDemo),
};
1 change: 1 addition & 0 deletions packages/@docs/demos/src/demos/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,4 @@ export { useTextSelectionUsage } from './use-text-selection.demo.usage';
export { usePreviousUsage } from './use-previous.demo.usage';
export { useFaviconUsage } from './use-favicon.demo.usage';
export { useEyeDropperUsage } from './use-eye-dropper.demo.usage';
export { useIsVisibleDemo } from './use-is-visible.demo.usage';
48 changes: 48 additions & 0 deletions packages/@docs/demos/src/demos/hooks/use-is-visible.demo.usage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from 'react';
import { Box, Text } from '@mantine/core';
import { useIsVisible } from '@mantine/hooks';
import { MantineDemo } from '@mantinex/demo';

const code = `
import { useIsVisible } from '@mantine/hooks';
function Demo() {
const { ref, isVisible } = useIsVisible();
return (
<>
<Text ta="center">{isVisible ? 'Box is visible' : 'Scroll to see box'}</Text>
<Box h={64} style={{ overflow: 'scroll' }}>
<Box h={128}></Box>
<Box ref={ref} bg="blue" h={32} p={8}>
<Text ta="center" c="white">
A box
</Text>
</Box>
</Box>
</>
);
}
`;

function Demo() {
const { ref, isVisible } = useIsVisible();
return (
<>
<Text ta="center">{isVisible ? 'Box is visible' : 'Scroll to see box'}</Text>
<Box h={64} style={{ overflow: 'scroll' }}>
<Box h={128}></Box>
<Box ref={ref} bg="blue" h={32} p={8}>
<Text ta="center" c="white">
A box
</Text>
</Box>
</Box>
</>
);
}

export const useIsVisibleDemo: MantineDemo = {
type: 'code',
code,
component: Demo,
};
1 change: 1 addition & 0 deletions packages/@mantine/hooks/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export { usePrevious } from './use-previous/use-previous';
export { useFavicon } from './use-favicon/use-favicon';
export { useHeadroom } from './use-headroom/use-headroom';
export { useEyeDropper } from './use-eye-dropper/use-eye-dropper';
export { useIsVisible } from './use-is-visible/use-is-visible';

export type { UseMovePosition } from './use-move/use-move';
export type { OS } from './use-os/use-os';
Expand Down
23 changes: 23 additions & 0 deletions packages/@mantine/hooks/src/use-is-visible/use-is-visible.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useEffect, useMemo, useRef, useState } from 'react';

export function useIsVisible<T extends HTMLElement = any>() {
const ref = useRef<T>(null);
const [isVisible, setIsVisible] = useState(false);

const observer = useMemo(() => {
if (typeof IntersectionObserver === 'undefined') {
return null;
}
return new IntersectionObserver(([entry]) => setIsVisible(entry.isIntersecting));
}, [ref]);

useEffect(() => {
if (ref.current && observer) {
observer.observe(ref.current);
return () => observer.disconnect();
}
return () => null;
}, []);

return { ref, isVisible };
}

0 comments on commit bbd0d0f

Please sign in to comment.