From 906c34f823c1082d37b6cef67c70135a70c85f8e Mon Sep 17 00:00:00 2001 From: Punit Soni Date: Thu, 1 Aug 2024 13:55:04 +0530 Subject: [PATCH] feat(hooks): use-element-size and use-resize-observer hooks added resolves #26 resolves #27 --- example/src/hooks/useElementSize/Demo.tsx | 22 ++++++++ example/src/hooks/useElementSize/index.tsx | 37 +++++++++++++ example/src/hooks/useResizeObserver/Demo.tsx | 20 +++++++ example/src/hooks/useResizeObserver/index.tsx | 36 +++++++++++++ example/src/lib/utils.ts | 6 ++- src/index.tsx | 2 + src/useElementSize/index.tsx | 54 +++++++++++++++++++ src/useResizeObserver/index.tsx | 48 +++++++++++++++++ 8 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 example/src/hooks/useElementSize/Demo.tsx create mode 100644 example/src/hooks/useElementSize/index.tsx create mode 100644 example/src/hooks/useResizeObserver/Demo.tsx create mode 100644 example/src/hooks/useResizeObserver/index.tsx create mode 100644 src/useElementSize/index.tsx create mode 100644 src/useResizeObserver/index.tsx diff --git a/example/src/hooks/useElementSize/Demo.tsx b/example/src/hooks/useElementSize/Demo.tsx new file mode 100644 index 0000000..21dc8e2 --- /dev/null +++ b/example/src/hooks/useElementSize/Demo.tsx @@ -0,0 +1,22 @@ +import { useElementSize } from '../../../../'; +import Muted from '@/common/Muted'; + +export default function Demo() { + + const { ref, height, width } = useElementSize(); + + return ( +
+ + + Height - {height} + + Width - {width} +
+ ) +} diff --git a/example/src/hooks/useElementSize/index.tsx b/example/src/hooks/useElementSize/index.tsx new file mode 100644 index 0000000..f31e405 --- /dev/null +++ b/example/src/hooks/useElementSize/index.tsx @@ -0,0 +1,37 @@ +import { lazy, Suspense } from 'react'; +import { packageName } from '@/lib/utils'; +import Demo from './Demo'; + +const Documentation = lazy(() => import('@/common/Documentation')); + +const hook = 'useElementSize'; +const info = "Custom hook to get the size (width and height) of a DOM element." + +const usage: string = `import { ${hook} } from '${packageName}'; + +const { ref, height, width } = ${hook}(); + +/* + @returns + ref - use this property to attach reference + height - get updated the height of the element + width - get updated the width of the element +*/`; + +export default function ElementSizeComponent() { + + return ( + }> + + + + + + + ) +} diff --git a/example/src/hooks/useResizeObserver/Demo.tsx b/example/src/hooks/useResizeObserver/Demo.tsx new file mode 100644 index 0000000..c09c028 --- /dev/null +++ b/example/src/hooks/useResizeObserver/Demo.tsx @@ -0,0 +1,20 @@ +import { useResizeObserver } from '../../../..'; +import Muted from '@/common/Muted'; + +export default function Demo() { + + const [ref, rect] = useResizeObserver(); + + return ( +
+ + + Rect - {JSON.stringify(rect)} +
+ ) +} diff --git a/example/src/hooks/useResizeObserver/index.tsx b/example/src/hooks/useResizeObserver/index.tsx new file mode 100644 index 0000000..de3dd2c --- /dev/null +++ b/example/src/hooks/useResizeObserver/index.tsx @@ -0,0 +1,36 @@ +import { lazy, Suspense } from 'react'; +import { packageName } from '@/lib/utils'; +import Demo from './Demo'; + +const Documentation = lazy(() => import('@/common/Documentation')); + +const hook = 'useResizeObserver'; +const info = "Custom hook to observe and get the bounding client rect of a DOM element." + +const usage: string = `import { ${hook} } from '${packageName}'; + +const { ref, rect } = ${hook}(); + +/* + @returns + ref - React ref to be attached to the target element + rect - Current bounding client rect of the element. +*/`; + +export default function ResizeObserverComponent() { + + return ( + }> + + + + + + + ) +} diff --git a/example/src/lib/utils.ts b/example/src/lib/utils.ts index 02e4de7..5efe028 100644 --- a/example/src/lib/utils.ts +++ b/example/src/lib/utils.ts @@ -38,6 +38,8 @@ const ProvidersTreeComponent = lazy(() => import('@/hooks/useProvidersTree')); const HashComponent = lazy(() => import('@/hooks/useHash')); const ClickOutsideComponent = lazy(() => import('@/hooks/useClickOutside')); const ColorSchemeComponent = lazy(() => import('@/hooks/useColorScheme')); +const ElementSizeComponent = lazy(() => import('@/hooks/useElementSize')); +const ResizeObserverComponent = lazy(() => import('@/hooks/useResizeObserver')); export const hooks = [ { key: 'useToggle', Component: ToggleComponent }, @@ -67,7 +69,9 @@ export const hooks = [ { key: 'useProvidersTree', Component: ProvidersTreeComponent }, { key: 'useHash', Component: HashComponent }, { key: 'useClickOutside', Component: ClickOutsideComponent, isNew: true }, - { key: 'useColorScheme', Component: ColorSchemeComponent, isNew: true } + { key: 'useColorScheme', Component: ColorSchemeComponent, isNew: true }, + { key: 'useElementSize', Component: ElementSizeComponent, isNew: true }, + { key: 'useResizeObserver', Component: ResizeObserverComponent, isNew: true }, ]; export const props = { diff --git a/src/index.tsx b/src/index.tsx index db9e64b..e8ec60e 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -27,5 +27,7 @@ export { default as useSpeech } from './useSpeech'; export { default as useProvidersTree } from './useProvidersTree'; export { default as useClickOutside } from './useClickOutside'; export { default as useColorScheme } from './useColorScheme'; +export { default as useElementSize } from './useElementSize'; +export { default as useResizeObserver } from './useResizeObserver'; export { useLocalStorage, useSessionStorage }; diff --git a/src/useElementSize/index.tsx b/src/useElementSize/index.tsx new file mode 100644 index 0000000..a1834c5 --- /dev/null +++ b/src/useElementSize/index.tsx @@ -0,0 +1,54 @@ +import { useEffect, useRef, useState } from 'react'; + +type Dimensions = Pick; + +/** + * Custom hook to get the size (width and height) of a DOM element. + * + * This hook returns a ref that should be attached to the target element, + * and the current dimensions (width and height) of that element. + * It uses a `ResizeObserver` to update the dimensions whenever the element is resized. + * + * @returns {Object} - An object containing the ref to be attached to the element, + * and the current width and height of the element. + * @property {Object} ref - React ref to be attached to the target element. + * @property {number} width - Current width of the element. + * @property {number} height - Current height of the element. + * + * @example + * + * function Component() { + * const { ref, width, height } = useElementSize(); + * + * return ( + * + * ); + * } + * + * @since 1.12.0 + */ +export default function useElementSize(): object { + const ref = useRef(null); + const [dimensions, setDimensions] = useState({ + width: 0, + height: 0, + }); + + useEffect(() => { + const observer = new ResizeObserver(() => { + if (ref.current) { + setDimensions({ + width: ref.current.offsetWidth, + height: ref.current.offsetHeight, + }); + } + }); + observer.observe(ref.current); + + return () => observer.disconnect(); + }, [ref]); + + return { ref, ...dimensions }; +} diff --git a/src/useResizeObserver/index.tsx b/src/useResizeObserver/index.tsx new file mode 100644 index 0000000..229854f --- /dev/null +++ b/src/useResizeObserver/index.tsx @@ -0,0 +1,48 @@ +import { useEffect, useRef, useState } from 'react'; + +type ObserverRect = Omit; + +/** + * Custom hook to observe and get the bounding client rect of a DOM element. + * + * This hook returns a ref that should be attached to the target element, + * and the current bounding client rect of that element. + * It uses a `ResizeObserver` to update the rect whenever the element is resized. + * + * @returns {Array} - An array containing the ref to be attached to the element, + * and the current bounding client rect of the element. + * @property {Object} 0 - React ref to be attached to the target element. + * @property {ObserverRect} 1 - Current bounding client rect of the element. + * + * @example + * + * function Component() { + * const [ref, rect] = useResizeObserver(); + * + * return ( + * + * ); + * } + * + * @since 1.12.0 + */ +export default function useResizeObserver(): Array { + const ref = useRef(null); + const [rect, setRect] = useState(); + + useEffect(() => { + const observer = new ResizeObserver(() => { + if (ref.current) { + const boundingRect = ref.current.getBoundingClientRect(); + setRect(boundingRect); + } + }); + observer.observe(ref.current); + + return () => observer.disconnect(); + }, [ref]); + + return [ref, rect]; +}