Skip to content

Commit b4a7fe1

Browse files
committed
Apply PR review commens
1 parent c0a82eb commit b4a7fe1

File tree

1 file changed

+20
-270
lines changed

1 file changed

+20
-270
lines changed

site/src/components/Chart/Chart.tsx

Lines changed: 20 additions & 270 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ type ChartContextProps = {
2323
config: ChartConfig;
2424
};
2525

26-
const ChartContext = React.createContext<ChartContextProps | null>(null);
26+
export const ChartContext = React.createContext<ChartContextProps | null>(null);
2727

2828
function useChart() {
2929
const context = React.useContext(ChartContext);
@@ -35,7 +35,7 @@ function useChart() {
3535
return context;
3636
}
3737

38-
const ChartContainer = React.forwardRef<
38+
export const ChartContainer = React.forwardRef<
3939
HTMLDivElement,
4040
React.ComponentProps<"div"> & {
4141
config: ChartConfig;
@@ -53,7 +53,19 @@ const ChartContainer = React.forwardRef<
5353
data-chart={chartId}
5454
ref={ref}
5555
className={cn(
56-
"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-layer]:outline-none [&_.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 [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",
56+
"flex aspect-video justify-center text-xs",
57+
"[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground",
58+
"[&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50",
59+
"[&_.recharts-curve.recharts-tooltip-cursor]:stroke-border",
60+
"[&_.recharts-dot[stroke='#fff']]:stroke-transparent",
61+
"[&_.recharts-layer]:outline-none",
62+
"[&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border",
63+
"[&_.recharts-radial-bar-background-sector]:fill-muted",
64+
"[&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted",
65+
"[&_.recharts-reference-line_[stroke='#ccc']]:stroke-border",
66+
"[&_.recharts-sector[stroke='#fff']]:stroke-transparent",
67+
"[&_.recharts-sector]:outline-none",
68+
"[&_.recharts-surface]:outline-none",
5769
className,
5870
)}
5971
{...props}
@@ -68,7 +80,10 @@ const ChartContainer = React.forwardRef<
6880
});
6981
ChartContainer.displayName = "Chart";
7082

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

0 commit comments

Comments
 (0)