import type { ApexOptions } from 'apexcharts' export const useChartOptions = (): ApexOptions => { return { chart: { toolbar: { show: false }, zoom: { enabled: false }, background: 'transparent', animations: { enabled: true, speed: 300, animateGradually: { enabled: true, delay: 50, }, dynamicAnimation: { enabled: true, speed: 350, }, }, // https://github.com/apexcharts/react-apexcharts/issues/187#issuecomment-901969532 events: { mounted: (chart) => { chart.windowResizeHandler() }, }, }, stroke: { curve: 'smooth', width: 2, lineCap: 'round', }, grid: { show: true, borderColor: 'var(--border)', xaxis: { lines: { show: false } }, yaxis: { lines: { show: true } }, row: { colors: ['var(--background)', 'var(--background-alt)'], opacity: 0.5, }, }, xaxis: { type: 'category', // tickPlacement only works for xaxis.type 'category' charts and not for datetime charts. tickPlacement: 'on', labels: { style: { colors: 'var(--muted-more-foreground)', fontSize: '11px', fontWeight: '400', fontFamily: 'DM Mono', }, rotate: 0, hideOverlappingLabels: true, }, axisBorder: { show: true, color: 'var(--border-active)', }, axisTicks: { show: true, color: 'var(--border-active)', }, tooltip: { enabled: false }, crosshairs: { show: true, stroke: { color: 'var(--border-active)', width: 1, dashArray: 5, }, }, }, yaxis: { min: 0, tickAmount: 6, labels: { style: { colors: 'var(--muted-more-foreground)', fontSize: '11px', fontWeight: '400', fontFamily: 'DM Mono', }, formatter: (value: number) => value !== 0 ? Math.round(value).toString() : '', }, }, dataLabels: { enabled: false, }, markers: { size: 0, strokeWidth: 1, strokeColors: 'var(--border-muted)', }, legend: { show: false, showForSingleSeries: true, horizontalAlign: 'left', position: 'right', }, } } export const createChartTooltip = ( formatTooltipX: (value: string) => string, formatTooltipY: (value: number) => string, ) => ({ series: tooltipSeries, dataPointIndex, w, seriesIndex, }: { series: number[][] dataPointIndex: number w: any seriesIndex?: number }) => { // Check if this is a pie/radial chart by looking at data structure const isPieOrRadial = w?.config?.chart?.type === 'pie' || w?.config?.chart?.type === 'radialBar' || w?.config?.chart?.type === 'donut' if (isPieOrRadial && seriesIndex !== undefined) { // For pie/radial charts: show slice name and value const sliceName = w?.config?.labels?.[seriesIndex] const value = w?.globals?.series?.[seriesIndex] const color = w?.globals?.colors?.[seriesIndex] return `
${sliceName}
${formatTooltipY(value)}
` .replace(/\s+/g, ' ') .trim() } // For line/area charts: show date and series data const date = w?.globals?.initialSeries?.[0]?.data?.[dataPointIndex]?.x const yConfig = w?.config?.tooltip?.y const formatVal = (idx: number, val: number) => { const formatterContext = { series: tooltipSeries, seriesIndex: idx, dataPointIndex, w, } // ApexCharts supports tooltip.y as array of { formatter }, single { formatter }, or bare function if (Array.isArray(yConfig)) { const formatter = yConfig[idx]?.formatter if (typeof formatter === 'function') { return formatter(val, formatterContext) } } else if (typeof yConfig === 'function') { return yConfig(val, formatterContext) } else if (yConfig && typeof yConfig.formatter === 'function') { return yConfig.formatter(val, formatterContext) } return formatTooltipY(val) } const tooltipContent = `
${formatTooltipX(date)}
${tooltipSeries .map((serie, idx) => w.globals.seriesNames.length > 1 ? `
${w.globals.seriesNames[idx]}:
${formatVal(idx, serie[dataPointIndex] ?? 0)}
` : ` ${formatVal(idx, serie[dataPointIndex] ?? 0)} `, ) .join('')}
` return tooltipContent.replace(/\s+/g, ' ').trim() } type DeepPartial = { [P in keyof T]?: T[P] extends (infer U)[] ? DeepPartial[] : T[P] extends object ? DeepPartial : T[P] } const isObject = (item: unknown): item is Record => item !== null && typeof item === 'object' && !Array.isArray(item) export const mergeChartOptions = ( target: ApexOptions, ...sources: DeepPartial[] ): ApexOptions => { if (!sources.length) return target const source = sources.shift() as DeepPartial if (isObject(target) && isObject(source)) { Object.keys(source).forEach((key) => { const targetValue = target[key as keyof ApexOptions] const sourceValue = source[key as keyof ApexOptions] if (isObject(sourceValue)) { if (!targetValue) { Object.assign(target, { [key]: {} }) } mergeChartOptions( target[key as keyof ApexOptions] as ApexOptions, sourceValue as DeepPartial, ) } else if (Array.isArray(sourceValue)) { Object.assign(target, { [key]: [...sourceValue] }) } else { Object.assign(target, { [key]: sourceValue }) } }) } return mergeChartOptions(target, ...sources) }