diff --git a/src/components/BankTransactions/BankTransactions.tsx b/src/components/BankTransactions/BankTransactions.tsx index b598fc561..5d81abe6d 100644 --- a/src/components/BankTransactions/BankTransactions.tsx +++ b/src/components/BankTransactions/BankTransactions.tsx @@ -322,8 +322,8 @@ const BankTransactionsTableView = ({ const removeTransaction = (bankTransaction: BankTransaction) => removeAfterCategorize([bankTransaction.id]) - const containerRef = useElementSize((_el, _en, size) => { - if (size?.height && size?.height >= 90) { + const containerRef = useElementSize((size) => { + if (size.height >= 90) { const newShift = -Math.floor(size.height / 2) + 6 if (newShift !== shiftStickyHeader) { void debounceShiftStickyHeader(newShift) diff --git a/src/components/Tabs/Tabs.tsx b/src/components/Tabs/Tabs.tsx index a38f03c43..aeb9f3704 100644 --- a/src/components/Tabs/Tabs.tsx +++ b/src/components/Tabs/Tabs.tsx @@ -36,9 +36,9 @@ export const Tabs = ({ name, options, selected, onChange }: TabsProps) => { initialized && 'Layer__tabs--initialized', ) - const elementRef = useElementSize((_a, _b, c) => { - if (c.width && c?.width !== currentWidth) { - setCurrentWidth(c.width) + const elementRef = useElementSize((size) => { + if (size.width !== currentWidth) { + setCurrentWidth(size.width) } }) diff --git a/src/components/ui/ResponsiveComponent/ResponsiveComponent.tsx b/src/components/ui/ResponsiveComponent/ResponsiveComponent.tsx new file mode 100644 index 000000000..8d57b1893 --- /dev/null +++ b/src/components/ui/ResponsiveComponent/ResponsiveComponent.tsx @@ -0,0 +1,36 @@ +import { type ReactNode, useCallback, useState } from 'react' + +import { type ElementSize, useElementSize } from '@hooks/useElementSize/useElementSize' + +import './responsiveComponent.scss' + +export type DefaultVariant = 'Desktop' | 'SmallDesktop' | 'Mobile' + +export type VariantResolver = ({ width }: { width: number }) => T + +export interface ResponsiveComponentProps { + slots: Record + resolveVariant: VariantResolver +} + +export const ResponsiveComponent = ({ + slots, + resolveVariant, +}: ResponsiveComponentProps) => { + const [currentVariant, setCurrentVariant] = useState(null) + + const handleResize = useCallback((size: ElementSize) => { + setCurrentVariant(resolveVariant({ width: size.width })) + }, [resolveVariant]) + + const containerRef = useElementSize(handleResize) + + return ( +
+ {currentVariant !== null && slots[currentVariant]} +
+ ) +} diff --git a/src/components/ui/ResponsiveComponent/responsiveComponent.scss b/src/components/ui/ResponsiveComponent/responsiveComponent.scss new file mode 100644 index 000000000..b11092dae --- /dev/null +++ b/src/components/ui/ResponsiveComponent/responsiveComponent.scss @@ -0,0 +1,4 @@ +.Layer__ResponsiveComponent { + height: 100%; + width: 100%; +} diff --git a/src/hooks/useElementSize/useElementSize.ts b/src/hooks/useElementSize/useElementSize.ts index cf2729b3d..b9dd1350d 100644 --- a/src/hooks/useElementSize/useElementSize.ts +++ b/src/hooks/useElementSize/useElementSize.ts @@ -1,49 +1,58 @@ import { useLayoutEffect, useRef } from 'react' +export interface ElementSize { + width: number + height: number +} + export const useElementSize = ( - callback: ( - target: T, - entry: ResizeObserverEntry, - size: { - width: number - height: number - clientWidth: number - clientHeight: number - }, - ) => void, + callback: (size: ElementSize) => void, ) => { const ref = useRef(null) + const callbackRef = useRef(callback) + const isFirstRender = useRef(true) const resizeTimeout = useRef(null) + callbackRef.current = callback + useLayoutEffect(() => { const element = ref?.current - if (!element) { - return + if (!element) return + + const invokeCallback = ({ width, height }: ElementSize) => { + callbackRef.current({ width, height }) + } + + if (isFirstRender.current) { + const rect = element.getBoundingClientRect() + invokeCallback({ + width: rect.width, + height: rect.height, + }) + isFirstRender.current = false } const observer = new ResizeObserver((entries) => { + const width = entries[0].borderBoxSize[0].inlineSize + const height = entries[0].borderBoxSize[0].blockSize + if (resizeTimeout.current) { clearTimeout(resizeTimeout.current) } resizeTimeout.current = window.setTimeout(() => { - const entry = entries[0] - callback(element, entry, { - width: element.offsetWidth, - height: element.offsetHeight, - clientWidth: element.clientWidth, - clientHeight: element.clientHeight, - }) + invokeCallback({ width, height }) }, 100) }) + observer.observe(element) + return () => { observer.disconnect() if (resizeTimeout.current) { clearTimeout(resizeTimeout.current) } } - }, [callback, ref]) - + }, []) return ref }