diff --git a/src/canvas/JoinPool/Overview/PerformanceGraph.tsx b/src/canvas/JoinPool/Overview/PerformanceGraph.tsx index fb5ff3bc74..a106f63477 100644 --- a/src/canvas/JoinPool/Overview/PerformanceGraph.tsx +++ b/src/canvas/JoinPool/Overview/PerformanceGraph.tsx @@ -27,6 +27,7 @@ import { formatSize } from 'library/Graphs/Utils'; import { useSize } from 'hooks/useSize'; import type { OverviewSectionProps } from '../types'; import { useTranslation } from 'react-i18next'; +import { useUi } from 'contexts/UI'; ChartJS.register( CategoryScale, @@ -47,6 +48,7 @@ export const PerformanceGraph = ({ const { t } = useTranslation(); const { mode } = useTheme(); const { openHelp } = useHelp(); + const { containerRefs } = useUi(); const { colors } = useNetwork().networkData; const { getPoolRewardPoints } = usePoolPerformance(); @@ -57,7 +59,7 @@ export const PerformanceGraph = ({ const graphInnerRef = useRef(null); // Get the size of the graph container. - const size = useSize(graphInnerRef?.current || undefined); + const size = useSize(graphInnerRef, containerRefs?.mainInterface); const { width, height } = formatSize(size, 150); // Format reward points as an array of strings, or an empty array if syncing. diff --git a/src/contexts/UI/index.tsx b/src/contexts/UI/index.tsx index 8ae576ac6b..f8f41999ab 100644 --- a/src/contexts/UI/index.tsx +++ b/src/contexts/UI/index.tsx @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-only import { localStorageOrDefault, setStateWithRef } from '@w3ux/utils'; -import type { ReactNode, RefObject } from 'react'; +import type { MutableRefObject, ReactNode } from 'react'; import { createContext, useContext, useEffect, useRef, useState } from 'react'; import { PageWidthMediumThreshold } from 'consts'; import { useEffectIgnoreInitial } from '@w3ux/hooks'; @@ -25,9 +25,11 @@ export const UIProvider = ({ children }: { children: ReactNode }) => { // Store references for main app containers. const [containerRefs, setContainerRefsState] = useState< - Record> + Record> >({}); - const setContainerRefs = (v: Record>) => { + const setContainerRefs = ( + v: Record> + ) => { setContainerRefsState(v); }; diff --git a/src/contexts/UI/types.ts b/src/contexts/UI/types.ts index dfc70f19a7..3b3140d071 100644 --- a/src/contexts/UI/types.ts +++ b/src/contexts/UI/types.ts @@ -1,15 +1,17 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { RefObject } from 'react'; +import type { MutableRefObject } from 'react'; export interface UIContextInterface { setSideMenu: (v: boolean) => void; setUserSideMenuMinimised: (v: boolean) => void; - setContainerRefs: (v: Record>) => void; + setContainerRefs: ( + v: Record> + ) => void; sideMenuOpen: boolean; userSideMenuMinimised: boolean; sideMenuMinimised: boolean; - containerRefs: Record>; + containerRefs: Record>; isBraveBrowser: boolean; } diff --git a/src/hooks/useSize/index.tsx b/src/hooks/useSize/index.tsx index 6e3d10d609..f161ee2d6a 100644 --- a/src/hooks/useSize/index.tsx +++ b/src/hooks/useSize/index.tsx @@ -2,38 +2,47 @@ // SPDX-License-Identifier: GPL-3.0-only import throttle from 'lodash.throttle'; +import type { MutableRefObject } from 'react'; import { useEffect, useState } from 'react'; -import { useUi } from 'contexts/UI'; -export const getSize = (element?: HTMLElement | undefined) => { - const width = element?.offsetWidth || 0; - const height = element?.offsetHeight || 0; - return { height, width }; -}; +// Custom hook to get the width and height of a specified element. Updates the `size` state when the +// specified "outer element" (or the window by default) resizes. +export const useSize = ( + element: MutableRefObject, + outerElement?: MutableRefObject +) => { + // Helper function to retrieve the width and height of an element + // If no element is found, default dimensions are set to 0. + const getSize = (el: HTMLElement | null = null) => { + const width = el?.offsetWidth || 0; + const height = el?.offsetHeight || 0; + return { width, height }; + }; -export const useSize = (element?: HTMLElement | undefined) => { - const { containerRefs } = useUi(); + // State to store the current width and height of the specified element. const [size, setSize] = useState<{ width: number; height: number }>( - getSize(element) + getSize(element?.current) ); - const throttleCallback = () => { - setSize(getSize(element)); - }; + // Throttle the resize event handler to limit how often size updates occur. + const resizeThrottle = throttle(() => { + setSize(getSize(element?.current)); + }, 100); + // Set up the resize event listener on mount and clean it up on unmount. useEffect(() => { - const resizeThrottle = throttle(throttleCallback, 100, { - trailing: true, - leading: false, - }); - - // listen to main interface resize if ref is available, otherwise - // fall back to window resize. - const listenFor = containerRefs?.mainInterface?.current ?? window; + // Determine the target for the resize event listener. + // If `outerElement` is provided, listen to its resize events; otherwise, listen to the window's. + const listenFor = outerElement?.current || window; + listenFor.addEventListener('resize', resizeThrottle); + + // Clean up event listener when the component unmounts to avoid memory leaks. return () => { listenFor.removeEventListener('resize', resizeThrottle); }; - }); + }, [outerElement]); + + // Return the current size of the element. return size; }; diff --git a/src/modals/ValidatorGeo/index.tsx b/src/modals/ValidatorGeo/index.tsx index 164caaf86b..bb1d664b7f 100644 --- a/src/modals/ValidatorGeo/index.tsx +++ b/src/modals/ValidatorGeo/index.tsx @@ -20,17 +20,20 @@ import { usePlugins } from 'contexts/Plugins'; import { useNetwork } from 'contexts/Network'; import { PolkaWatchController } from 'controllers/PolkaWatch'; import { ButtonHelp } from 'kits/Buttons/ButtonHelp'; +import { useUi } from 'contexts/UI'; export const ValidatorGeo = () => { const { t } = useTranslation('modals'); + const { openHelp } = useHelp(); const { network } = useNetwork(); + const { containerRefs } = useUi(); const { options } = useOverlay().modal.config; const { address, identity } = options; - const { openHelp } = useHelp(); const ref = useRef(null); - const size = useSize(ref?.current || undefined); + const size = useSize(ref, containerRefs?.mainInterface); const { height, minHeight } = formatSize(size, 300); + const [pwData, setPwData] = useState({} as ValidatorDetail); const [analyticsAvailable, setAnalyticsAvailable] = useState(true); const { pluginEnabled } = usePlugins(); diff --git a/src/modals/ValidatorMetrics/index.tsx b/src/modals/ValidatorMetrics/index.tsx index d3c7983b8d..0e185e0bcd 100644 --- a/src/modals/ValidatorMetrics/index.tsx +++ b/src/modals/ValidatorMetrics/index.tsx @@ -26,6 +26,7 @@ import { useApi } from 'contexts/Api'; import { ButtonHelp } from 'kits/Buttons/ButtonHelp'; import { ModalPadding } from 'kits/Overlay/structure/ModalPadding'; import { planckToUnitBn } from 'library/Utils'; +import { useUi } from 'contexts/UI'; export const ValidatorMetrics = () => { const { t } = useTranslation('modals'); @@ -34,6 +35,7 @@ export const ValidatorMetrics = () => { } = useNetwork(); const { activeEra } = useApi(); const { plugins } = usePlugins(); + const { containerRefs } = useUi(); const { options } = useOverlay().modal.config; const { address, identity } = options; const { @@ -59,7 +61,7 @@ export const ValidatorMetrics = () => { const [list, setList] = useState([]); const ref = useRef(null); - const size = useSize(ref?.current || undefined); + const size = useSize(ref, containerRefs?.mainInterface); const { width, height, minHeight } = formatSize(size, 300); const handleEraPoints = async () => { diff --git a/src/pages/Overview/Payouts.tsx b/src/pages/Overview/Payouts.tsx index d9c444c43e..e80d468210 100644 --- a/src/pages/Overview/Payouts.tsx +++ b/src/pages/Overview/Payouts.tsx @@ -21,6 +21,7 @@ import { minDecimalPlaces } from '@w3ux/utils'; import { useNetwork } from 'contexts/Network'; import { useSyncing } from 'hooks/useSyncing'; import { planckToUnitBn } from 'library/Utils'; +import { useUi } from 'contexts/UI'; export const Payouts = () => { const { i18n, t } = useTranslation('pages'); @@ -33,6 +34,7 @@ export const Payouts = () => { const { inSetup } = useStaking(); const { syncing } = useSyncing(); const { plugins } = usePlugins(); + const { containerRefs } = useUi(); const { getData, injectBlockTimestamp } = useSubscanData([ 'payouts', 'unclaimedPayouts', @@ -50,7 +52,7 @@ export const Payouts = () => { const graphInnerRef = useRef(null); // Get the size of the graph container. - const size = useSize(graphInnerRef?.current || undefined); + const size = useSize(graphInnerRef, containerRefs?.mainInterface); const { width, height, minHeight } = formatSize(size, 260); // Get the last reward with its timestmap. diff --git a/src/pages/Payouts/index.tsx b/src/pages/Payouts/index.tsx index ce1b173c6a..a230a755b4 100644 --- a/src/pages/Payouts/index.tsx +++ b/src/pages/Payouts/index.tsx @@ -26,6 +26,7 @@ import { DefaultLocale, locales } from 'locale'; import { useSyncing } from 'hooks/useSyncing'; import { ButtonHelp } from 'kits/Buttons/ButtonHelp'; import { PageTitle } from 'kits/Structure/PageTitle'; +import { useUi } from 'contexts/UI'; export const Payouts = ({ page: { key } }: PageProps) => { const { i18n, t } = useTranslation(); @@ -33,6 +34,7 @@ export const Payouts = ({ page: { key } }: PageProps) => { const { plugins } = usePlugins(); const { inSetup } = useStaking(); const { syncing } = useSyncing(); + const { containerRefs } = useUi(); const { getData, injectBlockTimestamp } = useSubscanData([ 'payouts', 'unclaimedPayouts', @@ -43,7 +45,7 @@ export const Payouts = ({ page: { key } }: PageProps) => { const [payoutsList, setPayoutLists] = useState([]); const ref = useRef(null); - const size = useSize(ref?.current || undefined); + const size = useSize(ref, containerRefs?.mainInterface); const { width, height, minHeight } = formatSize(size, 280); // Get data safely from subscan hook.