@@ -17,6 +17,8 @@ export type ChartLegendCompoundProps = {
1717 totalLabel?: string;
1818 /** Callback when "View all" button is clicked */
1919 onViewAllLegendItems?: () => void;
20+ /** When true, constrains legend to max 50% height with scrolling */
21+ scrollable?: boolean;
2022};
2123
2224/**
@@ -37,6 +39,7 @@ export function ChartLegendCompound({
3739 className,
3840 totalLabel = "Total",
3941 onViewAllLegendItems,
42+ scrollable = false,
4043}: ChartLegendCompoundProps) {
4144 const { config, dataKey, dataKeys, highlight, labelFormatter } = useChartContext();
4245 const totals = useSeriesTotal();
@@ -128,11 +131,17 @@ export function ChartLegendCompound({
128131 const isHovering = (highlight.activePayload?.length ?? 0) > 0;
129132
130133 return (
131- <div className={cn("flex flex-col pt-4 text-sm", className)}>
134+ <div
135+ className={cn(
136+ "flex flex-col pt-4 text-sm",
137+ scrollable && "max-h-[50%] min-h-0",
138+ className
139+ )}
140+ >
132141 {/* Total row */}
133142 <div
134143 className={cn(
135- "flex w-full items-center justify-between gap-2 rounded px-2 py-1 transition",
144+ "flex w-full shrink-0 items-center justify-between gap-2 rounded px-2 py-1 transition",
136145 isHovering ? "text-text-bright" : "text-text-dimmed"
137146 )}
138147 >
@@ -143,62 +152,65 @@ export function ChartLegendCompound({
143152 </div>
144153
145154 {/* Separator */}
146- <div className="mx-2 my-1 border-t border-charcoal-750" />
155+ <div className="mx-2 my-1 shrink-0 border-t border-charcoal-750" />
147156
148- {legendItems.visible.map((item) => {
149- const total = currentData[item.dataKey] ?? 0;
150- const isActive = highlight.activeBarKey === item.dataKey;
157+ {/* Legend items - scrollable when scrollable prop is true */}
158+ <div className={cn("flex flex-col", scrollable && "min-h-0 flex-1 overflow-y-auto scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600")}>
159+ {legendItems.visible.map((item) => {
160+ const total = currentData[item.dataKey] ?? 0;
161+ const isActive = highlight.activeBarKey === item.dataKey;
151162
152- return (
153- <div
154- key={item.dataKey}
155- className={cn(
156- "relative flex w-full cursor-pointer items-center justify-between gap-2 rounded px-2 py-1 transition",
157- total === 0 && "opacity-50"
158- )}
159- onMouseEnter={() => highlight.setHoveredLegendItem(item.dataKey)}
160- onMouseLeave={() => highlight.reset()}
161- >
162- {/* Active highlight background */}
163- {isActive && item.color && (
164- <div
165- className="absolute inset-0 rounded opacity-10"
166- style={{ backgroundColor: item.color }}
167- />
168- )}
169- <div className="relative flex w-full items-center justify-between gap-2">
170- <div className="flex items-center gap-1.5">
171- {item.color && (
172- <div
173- className="h-3 w-1 shrink-0 rounded-[2px]"
174- style={{ backgroundColor: item.color }}
175- />
176- )}
177- <span className={isActive ? "text-text-bright" : "text-text-dimmed"}>
178- {item.label}
163+ return (
164+ <div
165+ key={item.dataKey}
166+ className={cn(
167+ "relative flex w-full cursor-pointer items-center justify-between gap-2 rounded px-2 py-1 transition",
168+ total === 0 && "opacity-50"
169+ )}
170+ onMouseEnter={() => highlight.setHoveredLegendItem(item.dataKey)}
171+ onMouseLeave={() => highlight.reset()}
172+ >
173+ {/* Active highlight background */}
174+ {isActive && item.color && (
175+ <div
176+ className="absolute inset-0 rounded opacity-10"
177+ style={{ backgroundColor: item.color }}
178+ />
179+ )}
180+ <div className="relative flex w-full items-center justify-between gap-2">
181+ <div className="flex items-center gap-1.5">
182+ {item.color && (
183+ <div
184+ className="h-3 w-1 shrink-0 rounded-[2px]"
185+ style={{ backgroundColor: item.color }}
186+ />
187+ )}
188+ <span className={isActive ? "text-text-bright" : "text-text-dimmed"}>
189+ {item.label}
190+ </span>
191+ </div>
192+ <span
193+ className={cn("tabular-nums", isActive ? "text-text-bright" : "text-text-dimmed")}
194+ >
195+ <AnimatedNumber value={total} duration={0.25} />
179196 </span>
180197 </div>
181- <span
182- className={cn("tabular-nums", isActive ? "text-text-bright" : "text-text-dimmed")}
183- >
184- <AnimatedNumber value={total} duration={0.25} />
185- </span>
186198 </div>
187- </div>
188- );
189- })}
199+ );
200+ })}
190201
191- {/* View more row - replaced by hovered hidden item when applicable */}
192- {legendItems.remaining > 0 &&
193- (legendItems.hoveredHiddenItem ? (
194- <HoveredHiddenItemRow
195- item={legendItems.hoveredHiddenItem}
196- value={currentData[legendItems.hoveredHiddenItem.dataKey] ?? 0}
197- remainingCount={legendItems.remaining - 1}
198- />
199- ) : (
200- <ViewAllDataRow remainingCount={legendItems.remaining} onViewAll={onViewAllLegendItems} />
201- ))}
202+ {/* View more row - replaced by hovered hidden item when applicable */}
203+ {legendItems.remaining > 0 &&
204+ (legendItems.hoveredHiddenItem ? (
205+ <HoveredHiddenItemRow
206+ item={legendItems.hoveredHiddenItem}
207+ value={currentData[legendItems.hoveredHiddenItem.dataKey] ?? 0}
208+ remainingCount={legendItems.remaining - 1}
209+ />
210+ ) : (
211+ <ViewAllDataRow remainingCount={legendItems.remaining} onViewAll={onViewAllLegendItems} />
212+ ))}
213+ </div>
202214 </div>
203215 );
204216}
0 commit comments