@@ -38,6 +38,14 @@ type ChartColors = {
3838type LegendPayloadEntry = NonNullable <
3939 DefaultLegendContentProps [ "payload" ]
4040> [ number ] ;
41+ type ChartLegendVariant =
42+ | "circle"
43+ | "circle-outline"
44+ | "horizontal-bar"
45+ | "rounded-square"
46+ | "rounded-square-outline"
47+ | "square"
48+ | "vertical-bar" ;
4149type PayloadEntry = LegendPayloadEntry | TooltipPayloadEntry ;
4250
4351export type ChartConfig = Record <
@@ -329,10 +337,12 @@ function ChartLegendContent({
329337 payload,
330338 verticalAlign = "bottom" ,
331339 nameKey,
340+ variant = "rounded-square" ,
332341} : ComponentProps < "div" > &
333342 DefaultLegendContentProps & {
334343 hideIcon ?: boolean ;
335344 nameKey ?: string ;
345+ variant ?: ChartLegendVariant ;
336346 } ) {
337347 const { config } = useChart ( ) ;
338348
@@ -348,39 +358,78 @@ function ChartLegendContent({
348358 className
349359 ) }
350360 >
351- { payload . map ( ( item ) => {
352- const key = `${ nameKey || item . value || item . dataKey || "value" } ` ;
353- const configKey = getPayloadConfigKey ( config , item , key ) ;
354- const itemConfig = configKey ? config [ configKey ] : undefined ;
355- const indicatorStyle = getLegendIndicatorStyle (
356- item ,
357- configKey || key ,
358- itemConfig
359- ) ;
360-
361- return (
362- < div
363- className = { cn (
364- "flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground"
365- ) }
366- key = { key }
367- >
368- { itemConfig ?. icon && ! hideIcon ? (
369- < itemConfig . icon />
370- ) : (
371- < div
372- className = "h-2 w-2 shrink-0 rounded-[2px]"
373- style = { indicatorStyle }
374- />
375- ) }
376- { itemConfig ?. label || item . value }
377- </ div >
378- ) ;
379- } ) }
361+ { payload
362+ . filter ( ( item ) => item . type !== "none" )
363+ . map ( ( item ) => {
364+ const key = `${ nameKey || item . value || item . dataKey || "value" } ` ;
365+ const configKey = getPayloadConfigKey ( config , item , key ) ;
366+ const itemConfig = configKey ? config [ configKey ] : undefined ;
367+ const dataKey = configKey || key ;
368+ let indicatorStyle = getLegendIndicatorStyle (
369+ item ,
370+ dataKey ,
371+ itemConfig
372+ ) ;
373+
374+ if (
375+ variant === "circle-outline" ||
376+ variant === "rounded-square-outline"
377+ ) {
378+ indicatorStyle = getLegendOutlineStyle ( item , dataKey , itemConfig ) ;
379+ }
380+
381+ return (
382+ < div
383+ className = { cn (
384+ "flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground"
385+ ) }
386+ key = { key }
387+ >
388+ { itemConfig ?. icon && ! hideIcon ? (
389+ < itemConfig . icon />
390+ ) : (
391+ < div
392+ className = { getLegendIndicatorClassName ( variant ) }
393+ style = { indicatorStyle }
394+ />
395+ ) }
396+ { itemConfig ?. label || item . value }
397+ </ div >
398+ ) ;
399+ } ) }
380400 </ div >
381401 ) ;
382402}
383403
404+ /** Matches EvilCharts legend variants while keeping Recharts payload support. */
405+ function getLegendIndicatorClassName ( variant : ChartLegendVariant ) {
406+ if ( variant === "circle" ) {
407+ return "h-2 w-2 shrink-0 rounded-full" ;
408+ }
409+
410+ if ( variant === "circle-outline" ) {
411+ return "h-2.5 w-2.5 shrink-0 rounded-full p-[1.5px]" ;
412+ }
413+
414+ if ( variant === "horizontal-bar" ) {
415+ return "h-1 w-3 shrink-0 rounded-[2px]" ;
416+ }
417+
418+ if ( variant === "square" ) {
419+ return "h-2 w-2 shrink-0 rounded-none" ;
420+ }
421+
422+ if ( variant === "vertical-bar" ) {
423+ return "h-3 w-1 shrink-0 rounded-[2px]" ;
424+ }
425+
426+ if ( variant === "rounded-square-outline" ) {
427+ return "h-2.5 w-2.5 shrink-0 rounded-[3px] p-[1.5px]" ;
428+ }
429+
430+ return "h-2 w-2 shrink-0 rounded-[2px]" ;
431+ }
432+
384433/** Finds the chart config entry that matches a Recharts payload item. */
385434function getPayloadConfigFromPayload (
386435 config : ChartConfig ,
@@ -557,6 +606,36 @@ function getLegendIndicatorStyle(
557606 } ;
558607}
559608
609+ /** Uses the EvilCharts mask approach so outline legend variants can show gradients. */
610+ function getLegendOutlineStyle (
611+ payload : LegendPayloadEntry ,
612+ dataKey : string ,
613+ config : ChartConfig [ string ] | undefined
614+ ) {
615+ const maskStyle = {
616+ WebkitMask :
617+ "linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)" ,
618+ WebkitMaskComposite : "xor" ,
619+ mask : "linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)" ,
620+ maskComposite : "exclude" ,
621+ } ;
622+
623+ if ( config ?. colors ) {
624+ return {
625+ ...getIndicatorStyle ( dataKey , getColorsCount ( config ) ) ,
626+ ...maskStyle ,
627+ } ;
628+ }
629+
630+ const color = getPayloadColor ( payload ) ;
631+
632+ return {
633+ background : color ,
634+ borderColor : color ,
635+ ...maskStyle ,
636+ } ;
637+ }
638+
560639/** Uses config color slots when available, otherwise Recharts payload color. */
561640function getTooltipIndicatorStyle (
562641 payload : TooltipPayloadEntry ,
0 commit comments