Skip to content
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

feat(refactor): Generalise useSize hook #2312

Merged
merged 2 commits into from
Nov 2, 2024
Merged
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
4 changes: 3 additions & 1 deletion src/canvas/JoinPool/Overview/PerformanceGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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();

Expand All @@ -57,7 +59,7 @@ export const PerformanceGraph = ({
const graphInnerRef = useRef<HTMLDivElement>(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.
Expand Down
8 changes: 5 additions & 3 deletions src/contexts/UI/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -25,9 +25,11 @@ export const UIProvider = ({ children }: { children: ReactNode }) => {

// Store references for main app containers.
const [containerRefs, setContainerRefsState] = useState<
Record<string, RefObject<HTMLDivElement>>
Record<string, MutableRefObject<HTMLDivElement | null>>
>({});
const setContainerRefs = (v: Record<string, RefObject<HTMLDivElement>>) => {
const setContainerRefs = (
v: Record<string, MutableRefObject<HTMLDivElement | null>>
) => {
setContainerRefsState(v);
};

Expand Down
8 changes: 5 additions & 3 deletions src/contexts/UI/types.ts
Original file line number Diff line number Diff line change
@@ -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<string, RefObject<HTMLDivElement>>) => void;
setContainerRefs: (
v: Record<string, MutableRefObject<HTMLDivElement | null>>
) => void;
sideMenuOpen: boolean;
userSideMenuMinimised: boolean;
sideMenuMinimised: boolean;
containerRefs: Record<string, RefObject<HTMLDivElement>>;
containerRefs: Record<string, MutableRefObject<HTMLDivElement | null>>;
isBraveBrowser: boolean;
}
51 changes: 30 additions & 21 deletions src/hooks/useSize/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<HTMLElement | null | undefined>,
outerElement?: MutableRefObject<HTMLElement | null | undefined>
) => {
// 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;
};
7 changes: 5 additions & 2 deletions src/modals/ValidatorGeo/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<HTMLDivElement>(null);
const size = useSize(ref?.current || undefined);
const size = useSize(ref, containerRefs?.mainInterface);
const { height, minHeight } = formatSize(size, 300);

const [pwData, setPwData] = useState<ValidatorDetail>({} as ValidatorDetail);
const [analyticsAvailable, setAnalyticsAvailable] = useState<boolean>(true);
const { pluginEnabled } = usePlugins();
Expand Down
4 changes: 3 additions & 1 deletion src/modals/ValidatorMetrics/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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 {
Expand All @@ -59,7 +61,7 @@ export const ValidatorMetrics = () => {
const [list, setList] = useState<AnyJson[]>([]);

const ref = useRef<HTMLDivElement>(null);
const size = useSize(ref?.current || undefined);
const size = useSize(ref, containerRefs?.mainInterface);
const { width, height, minHeight } = formatSize(size, 300);

const handleEraPoints = async () => {
Expand Down
4 changes: 3 additions & 1 deletion src/pages/Overview/Payouts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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',
Expand All @@ -50,7 +52,7 @@ export const Payouts = () => {
const graphInnerRef = useRef<HTMLDivElement>(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.
Expand Down
4 changes: 3 additions & 1 deletion src/pages/Payouts/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@ 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();
const { openHelp } = useHelp();
const { plugins } = usePlugins();
const { inSetup } = useStaking();
const { syncing } = useSyncing();
const { containerRefs } = useUi();
const { getData, injectBlockTimestamp } = useSubscanData([
'payouts',
'unclaimedPayouts',
Expand All @@ -43,7 +45,7 @@ export const Payouts = ({ page: { key } }: PageProps) => {
const [payoutsList, setPayoutLists] = useState<AnySubscan>([]);

const ref = useRef<HTMLDivElement>(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.
Expand Down
Loading