@@ -7,20 +7,19 @@ import * as RechartsPrimitive from "recharts";
77// Format: { THEME_NAME: CSS_SELECTOR }
88const THEMES = { light : "" , dark : ".dark" } as const ;
99
10- export type ChartConfig = Record <
11- string ,
12- {
10+ export type ChartConfig = {
11+ [ k in string ] : {
1312 label ?: React . ReactNode ;
1413 icon ?: React . ComponentType ;
1514 } & (
1615 | { color ?: string ; theme ?: never }
1716 | { color ?: never ; theme : Record < keyof typeof THEMES , string > }
18- )
19- > ;
17+ ) ;
18+ } ;
2019
21- interface ChartContextProps {
20+ type ChartContextProps = {
2221 config : ChartConfig ;
23- }
22+ } ;
2423
2524const ChartContext = React . createContext < ChartContextProps | null > ( null ) ;
2625
@@ -53,7 +52,7 @@ function ChartContainer({
5352 data-slot = "chart"
5453 data-chart = { chartId }
5554 className = { cn (
56- "[&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-surface]:outline-hidden flex aspect-video justify-center text-xs [&_.recharts- cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts- polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector[ stroke='#fff']]:stroke-transparent" ,
55+ "[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts- sector]:outline-hidden [&_.recharts-sector[ stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden " ,
5756 className
5857 ) }
5958 { ...props }
@@ -129,7 +128,9 @@ function ChartTooltipContent({
129128 const key = `${ labelKey || item ?. dataKey || item ?. name || "value" } ` ;
130129 const itemConfig = getPayloadConfigFromPayload ( config , item , key ) ;
131130 const value =
132- ! labelKey && typeof label === "string" ? config [ label ] ?. label || label : itemConfig ?. label ;
131+ ! labelKey && typeof label === "string"
132+ ? config [ label as keyof typeof config ] ?. label || label
133+ : itemConfig ?. label ;
133134
134135 if ( labelFormatter ) {
135136 return (
@@ -153,76 +154,78 @@ function ChartTooltipContent({
153154 return (
154155 < div
155156 className = { cn (
156- "grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl" ,
157+ "border-border/50 bg-background grid min-w-[8rem] items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl" ,
157158 className
158159 ) }
159160 >
160161 { ! nestLabel ? tooltipLabel : null }
161162 < div className = "grid gap-1.5" >
162- { payload . map ( ( item , index ) => {
163- const key = `${ nameKey || item . name || item . dataKey || "value" } ` ;
164- const itemConfig = getPayloadConfigFromPayload ( config , item , key ) ;
165- const indicatorColor = color || item . payload . fill || item . color ;
166-
167- return (
168- < div
169- key = { item . dataKey }
170- className = { cn (
171- "flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground" ,
172- indicator === "dot" && "items-center"
173- ) }
174- >
175- { formatter && item ?. value !== undefined && item . name ? (
176- formatter ( item . value , item . name , item , index , item . payload )
177- ) : (
178- < >
179- { itemConfig ?. icon ? (
180- < itemConfig . icon />
181- ) : (
182- ! hideIndicator && (
183- < div
184- className = { cn (
185- "border-(--color-border) bg-(--color-bg) shrink-0 rounded-[2px]" ,
186- {
187- "h-2.5 w-2.5" : indicator === "dot" ,
188- "w-1" : indicator === "line" ,
189- "w-0 border-[1.5px] border-dashed bg-transparent" :
190- indicator === "dashed" ,
191- "my-0.5" : nestLabel && indicator === "dashed" ,
163+ { payload
164+ . filter ( ( item ) => item . type !== "none" )
165+ . map ( ( item , index ) => {
166+ const key = `${ nameKey || item . name || item . dataKey || "value" } ` ;
167+ const itemConfig = getPayloadConfigFromPayload ( config , item , key ) ;
168+ const indicatorColor = color || item . payload . fill || item . color ;
169+
170+ return (
171+ < div
172+ key = { item . dataKey }
173+ className = { cn (
174+ "[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5" ,
175+ indicator === "dot" && "items-center"
176+ ) }
177+ >
178+ { formatter && item ?. value !== undefined && item . name ? (
179+ formatter ( item . value , item . name , item , index , item . payload )
180+ ) : (
181+ < >
182+ { itemConfig ?. icon ? (
183+ < itemConfig . icon />
184+ ) : (
185+ ! hideIndicator && (
186+ < div
187+ className = { cn (
188+ "shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)" ,
189+ {
190+ "h-2.5 w-2.5" : indicator === "dot" ,
191+ "w-1" : indicator === "line" ,
192+ "w-0 border-[1.5px] border-dashed bg-transparent" :
193+ indicator === "dashed" ,
194+ "my-0.5" : nestLabel && indicator === "dashed" ,
195+ }
196+ ) }
197+ style = {
198+ {
199+ "--color-bg" : indicatorColor ,
200+ "--color-border" : indicatorColor ,
201+ } as React . CSSProperties
192202 }
193- ) }
194- style = {
195- {
196- "--color-bg" : indicatorColor as string ,
197- "--color-border" : indicatorColor as string ,
198- } as React . CSSProperties
199- }
200- />
201- )
202- ) }
203- < div
204- className = { cn (
205- "flex flex-1 justify-between leading-none" ,
206- nestLabel ? "items-end" : "items-center"
203+ />
204+ )
207205 ) }
208- >
209- < div className = "grid gap-1.5" >
210- { nestLabel ? tooltipLabel : null }
211- < span className = "text-muted-foreground" >
212- { itemConfig ?. label || item . name }
213- </ span >
206+ < div
207+ className = { cn (
208+ "flex flex-1 justify-between leading-none" ,
209+ nestLabel ? "items-end" : "items-center"
210+ ) }
211+ >
212+ < div className = "grid gap-1.5" >
213+ { nestLabel ? tooltipLabel : null }
214+ < span className = "text-muted-foreground" >
215+ { itemConfig ?. label || item . name }
216+ </ span >
217+ </ div >
218+ { item . value && (
219+ < span className = "text-foreground font-mono font-medium tabular-nums" >
220+ { item . value . toLocaleString ( ) }
221+ </ span >
222+ ) }
214223 </ div >
215- { item . value && (
216- < span className = "font-mono font-medium tabular-nums text-foreground" >
217- { item . value . toLocaleString ( ) }
218- </ span >
219- ) }
220- </ div >
221- </ >
222- ) }
223- </ div >
224- ) ;
225- } ) }
224+ </ >
225+ ) }
226+ </ div >
227+ ) ;
228+ } ) }
226229 </ div >
227230 </ div >
228231 ) ;
@@ -255,31 +258,33 @@ function ChartLegendContent({
255258 className
256259 ) }
257260 >
258- { payload . map ( ( item ) => {
259- const key = `${ nameKey || item . dataKey || "value" } ` ;
260- const itemConfig = getPayloadConfigFromPayload ( config , item , key ) ;
261-
262- return (
263- < div
264- key = { `${ item . value } ` }
265- className = { cn (
266- "flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground"
267- ) }
268- >
269- { itemConfig ?. icon && ! hideIcon ? (
270- < itemConfig . icon />
271- ) : (
272- < div
273- className = "h-2 w-2 shrink-0 rounded-[2px]"
274- style = { {
275- backgroundColor : item . color ,
276- } }
277- />
278- ) }
279- { itemConfig ?. label }
280- </ div >
281- ) ;
282- } ) }
261+ { payload
262+ . filter ( ( item ) => item . type !== "none" )
263+ . map ( ( item ) => {
264+ const key = `${ nameKey || item . dataKey || "value" } ` ;
265+ const itemConfig = getPayloadConfigFromPayload ( config , item , key ) ;
266+
267+ return (
268+ < div
269+ key = { item . value }
270+ className = { cn (
271+ "[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3"
272+ ) }
273+ >
274+ { itemConfig ?. icon && ! hideIcon ? (
275+ < itemConfig . icon />
276+ ) : (
277+ < div
278+ className = "h-2 w-2 shrink-0 rounded-[2px]"
279+ style = { {
280+ backgroundColor : item . color ,
281+ } }
282+ />
283+ ) }
284+ { itemConfig ?. label }
285+ </ div >
286+ ) ;
287+ } ) }
283288 </ div >
284289 ) ;
285290}
@@ -307,7 +312,7 @@ function getPayloadConfigFromPayload(config: ChartConfig, payload: unknown, key:
307312 configLabelKey = payloadPayload [ key as keyof typeof payloadPayload ] as string ;
308313 }
309314
310- return configLabelKey in config ? config [ configLabelKey ] : config [ key ] ;
315+ return configLabelKey in config ? config [ configLabelKey ] : config [ key as keyof typeof config ] ;
311316}
312317
313318export {
0 commit comments