Skip to content

Commit c529238

Browse files
committed
Fix missing exported components
1 parent b4a7fe1 commit c529238

File tree

1 file changed

+255
-0
lines changed

1 file changed

+255
-0
lines changed

site/src/components/Chart/Chart.tsx

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,258 @@ ${colorConfig
115115
/>
116116
);
117117
};
118+
119+
export const ChartTooltip = RechartsPrimitive.Tooltip;
120+
121+
export const ChartTooltipContent = React.forwardRef<
122+
HTMLDivElement,
123+
React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
124+
React.ComponentProps<"div"> & {
125+
hideLabel?: boolean;
126+
hideIndicator?: boolean;
127+
indicator?: "line" | "dot" | "dashed";
128+
nameKey?: string;
129+
labelKey?: string;
130+
}
131+
>(
132+
(
133+
{
134+
active,
135+
payload,
136+
className,
137+
indicator = "dot",
138+
hideLabel = false,
139+
hideIndicator = false,
140+
label,
141+
labelFormatter,
142+
labelClassName,
143+
formatter,
144+
color,
145+
nameKey,
146+
labelKey,
147+
},
148+
ref,
149+
) => {
150+
const { config } = useChart();
151+
152+
const tooltipLabel = React.useMemo(() => {
153+
if (hideLabel || !payload?.length) {
154+
return null;
155+
}
156+
157+
const [item] = payload;
158+
const key = `${labelKey || item.dataKey || item.name || "value"}`;
159+
const itemConfig = getPayloadConfigFromPayload(config, item, key);
160+
const value =
161+
!labelKey && typeof label === "string"
162+
? config[label as keyof typeof config]?.label || label
163+
: itemConfig?.label;
164+
165+
if (labelFormatter) {
166+
return (
167+
<div className={cn("font-medium", labelClassName)}>
168+
{labelFormatter(value, payload)}
169+
</div>
170+
);
171+
}
172+
173+
if (!value) {
174+
return null;
175+
}
176+
177+
return <div className={cn("font-medium", labelClassName)}>{value}</div>;
178+
}, [
179+
label,
180+
labelFormatter,
181+
payload,
182+
hideLabel,
183+
labelClassName,
184+
config,
185+
labelKey,
186+
]);
187+
188+
if (!active || !payload?.length) {
189+
return null;
190+
}
191+
192+
const nestLabel = payload.length === 1 && indicator !== "dot";
193+
194+
return (
195+
<div
196+
ref={ref}
197+
className={cn(
198+
"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",
199+
className,
200+
)}
201+
>
202+
{!nestLabel ? tooltipLabel : null}
203+
<div className="grid gap-1.5">
204+
{payload.map((item, index) => {
205+
const key = `${nameKey || item.name || item.dataKey || "value"}`;
206+
const itemConfig = getPayloadConfigFromPayload(config, item, key);
207+
const indicatorColor = color || item.payload.fill || item.color;
208+
209+
return (
210+
<div
211+
key={item.dataKey}
212+
className={cn(
213+
"flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",
214+
indicator === "dot" && "items-center",
215+
)}
216+
>
217+
{formatter && item?.value !== undefined && item.name ? (
218+
formatter(item.value, item.name, item, index, item.payload)
219+
) : (
220+
<>
221+
{itemConfig?.icon ? (
222+
<itemConfig.icon />
223+
) : (
224+
!hideIndicator && (
225+
<div
226+
className={cn(
227+
"shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]",
228+
{
229+
"h-2.5 w-2.5": indicator === "dot",
230+
"w-1": indicator === "line",
231+
"w-0 border-[1.5px] border-dashed bg-transparent":
232+
indicator === "dashed",
233+
"my-0.5": nestLabel && indicator === "dashed",
234+
},
235+
)}
236+
style={
237+
{
238+
"--color-bg": indicatorColor,
239+
"--color-border": indicatorColor,
240+
} as React.CSSProperties
241+
}
242+
/>
243+
)
244+
)}
245+
<div
246+
className={cn(
247+
"flex flex-1 justify-between leading-none",
248+
nestLabel ? "items-end" : "items-center",
249+
)}
250+
>
251+
<div className="grid gap-1.5">
252+
{nestLabel ? tooltipLabel : null}
253+
<span className="text-muted-foreground">
254+
{itemConfig?.label || item.name}
255+
</span>
256+
</div>
257+
{item.value && (
258+
<span className="font-mono font-medium tabular-nums text-foreground">
259+
{item.value.toLocaleString()}
260+
</span>
261+
)}
262+
</div>
263+
</>
264+
)}
265+
</div>
266+
);
267+
})}
268+
</div>
269+
</div>
270+
);
271+
},
272+
);
273+
ChartTooltipContent.displayName = "ChartTooltip";
274+
275+
export const ChartLegend = RechartsPrimitive.Legend;
276+
277+
export const ChartLegendContent = React.forwardRef<
278+
HTMLDivElement,
279+
React.ComponentProps<"div"> &
280+
Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
281+
hideIcon?: boolean;
282+
nameKey?: string;
283+
}
284+
>(
285+
(
286+
{ className, hideIcon = false, payload, verticalAlign = "bottom", nameKey },
287+
ref,
288+
) => {
289+
const { config } = useChart();
290+
291+
if (!payload?.length) {
292+
return null;
293+
}
294+
295+
return (
296+
<div
297+
ref={ref}
298+
className={cn(
299+
"flex items-center justify-center gap-4",
300+
verticalAlign === "top" ? "pb-3" : "pt-3",
301+
className,
302+
)}
303+
>
304+
{payload.map((item) => {
305+
const key = `${nameKey || item.dataKey || "value"}`;
306+
const itemConfig = getPayloadConfigFromPayload(config, item, key);
307+
308+
return (
309+
<div
310+
key={item.value}
311+
className={cn(
312+
"flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground",
313+
)}
314+
>
315+
{itemConfig?.icon && !hideIcon ? (
316+
<itemConfig.icon />
317+
) : (
318+
<div
319+
className="h-2 w-2 shrink-0 rounded-[2px]"
320+
style={{
321+
backgroundColor: item.color,
322+
}}
323+
/>
324+
)}
325+
{itemConfig?.label}
326+
</div>
327+
);
328+
})}
329+
</div>
330+
);
331+
},
332+
);
333+
ChartLegendContent.displayName = "ChartLegend";
334+
335+
// Helper to extract item config from a payload.
336+
function getPayloadConfigFromPayload(
337+
config: ChartConfig,
338+
payload: unknown,
339+
key: string,
340+
) {
341+
if (typeof payload !== "object" || payload === null) {
342+
return undefined;
343+
}
344+
345+
const payloadPayload =
346+
"payload" in payload &&
347+
typeof payload.payload === "object" &&
348+
payload.payload !== null
349+
? payload.payload
350+
: undefined;
351+
352+
let configLabelKey: string = key;
353+
354+
if (
355+
key in payload &&
356+
typeof payload[key as keyof typeof payload] === "string"
357+
) {
358+
configLabelKey = payload[key as keyof typeof payload] as string;
359+
} else if (
360+
payloadPayload &&
361+
key in payloadPayload &&
362+
typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
363+
) {
364+
configLabelKey = payloadPayload[
365+
key as keyof typeof payloadPayload
366+
] as string;
367+
}
368+
369+
return configLabelKey in config
370+
? config[configLabelKey]
371+
: config[key as keyof typeof config];
372+
}

0 commit comments

Comments
 (0)