import { ChartOptions, DeepPartial, IChartApi } from 'lightweight-charts';
import { useEffect } from 'react';
import { useChart } from '../../hooks/useChart';
import { getSizeFromEntry } from '../../utils/getSizeFromEntry';
export enum ChartAutoResizerTriggerSource {
/**
* Indicate to use `window.addEventListener('resize', handler)`.
*/
windowResize = 1 << 0,
/**
* Indicate to use `new ResizeObserver(handler).observe(parentElement)`.
*/
parentResize = 1 << 1,
}
const DEFAULT_TRIGGER_SOURCE =
typeof window !== 'undefined' && 'ResizeObserver' in window
? ChartAutoResizerTriggerSource.parentResize
: ChartAutoResizerTriggerSource.windowResize;
export type ResizeHandler = (
/**
* chart object
*/
chart: IChartApi,
/**
* new chart width
*/
width: number,
/**
* new chart height
*/
height: number,
/**
* container element
*/
container: HTMLDivElement | null,
) => void;
export interface ChartAutoResizerProps {
/**
* Source to trigger, from window's `resize` event or use `ResizeObserver`
*
* Default is `.parentResize` if `ResizeObserver` exists,
* use `.windowResize` otherwise.
*/
triggerSource?: ChartAutoResizerTriggerSource;
/**
* Callback when resize triggered.
*/
onResize?: ResizeHandler;
}
/**
* Utility component for resizing the chart when window size changed.
*
* ❗Only use inside ``.
* Turn chart option `disableAutoResize = true` to prevent multiple resizer called.
*/
export const ChartAutoResizer = ({
triggerSource = DEFAULT_TRIGGER_SOURCE,
onResize,
}: ChartAutoResizerProps) => {
const { chart, containerRef } = useChart();
useEffect(() => {
if (triggerSource === 0 || !chart) return;
const handler = (entriesOrEvent?: ResizeObserverEntry[] | Event) => {
const options: DeepPartial = chart.options();
const [observedWidth, observedHeight] = Array.isArray(entriesOrEvent)
? getSizeFromEntry(entriesOrEvent[0])
: [
containerRef.current?.parentElement
? parseInt(
getComputedStyle(containerRef.current.parentElement).width,
)
: 0,
containerRef.current?.parentElement
? parseInt(
getComputedStyle(containerRef.current.parentElement).height,
)
: 0,
];
const width = options.width || observedWidth;
const height = options.height || observedHeight;
chart.resize(width, height);
onResize?.(chart, width, height, containerRef.current);
};
// first run
handler();
// listen window's resize event
if (triggerSource & ChartAutoResizerTriggerSource.windowResize) {
window.addEventListener('resize', handler);
}
// observe parent size change
let resizeObserver: ResizeObserver | undefined;
const parent = containerRef.current?.parentElement;
if (triggerSource & ChartAutoResizerTriggerSource.parentResize && parent) {
resizeObserver = new ResizeObserver(handler);
resizeObserver.observe(parent);
}
return () => {
if (triggerSource & ChartAutoResizerTriggerSource.windowResize) {
window.removeEventListener('resize', handler);
}
// don't need to check trigger source here because `resizeObserver` is only created when needed.
if (parent) {
resizeObserver?.unobserve(parent);
}
};
}, [chart, triggerSource, containerRef, onResize]);
return null;
};