CoolFace
Apppublic

huggingface/ai-deadlines

sourceHugging Faceupdated 3d agoView on Hugging Face
781likes
chart.tsx364 linesDownload Raw Back to ui
1import * as React from "react"2import * as RechartsPrimitive from "recharts"3 4import { cn } from "@/lib/utils"5 6// Format: { THEME_NAME: CSS_SELECTOR }7const THEMES = { light: "", dark: ".dark" } as const8 9export type ChartConfig = {10  [k in string]: {11    label?: React.ReactNode12    icon?: React.ComponentType13  } & (14    | { color?: string; theme?: never }15    | { color?: never; theme: Record<keyof typeof THEMES, string> }16  )17}18 19type ChartContextProps = {20  config: ChartConfig21}22 23const ChartContext = React.createContext<ChartContextProps | null>(null)24 25function useChart() {26  const context = React.useContext(ChartContext)27 28  if (!context) {29    throw new Error("useChart must be used within a <ChartContainer />")30  }31 32  return context33}34 35const ChartContainer = React.forwardRef<36  HTMLDivElement,37  React.ComponentProps<"div"> & {38    config: ChartConfig39    children: React.ComponentProps<40      typeof RechartsPrimitive.ResponsiveContainer41    >["children"]42  }43>(({ id, className, children, config, ...props }, ref) => {44  const uniqueId = React.useId()45  const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`46 47  return (48    <ChartContext.Provider value={{ config }}>49      <div50        data-chart={chartId}51        ref={ref}52        className={cn(53          "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",54          className55        )}56        {...props}57      >58        <ChartStyle id={chartId} config={config} />59        <RechartsPrimitive.ResponsiveContainer>60          {children}61        </RechartsPrimitive.ResponsiveContainer>62      </div>63    </ChartContext.Provider>64  )65})66ChartContainer.displayName = "Chart"67 68const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {69  const colorConfig = Object.entries(config).filter(70    ([_, config]) => config.theme || config.color71  )72 73  if (!colorConfig.length) {74    return null75  }76 77  return (78    <style79      dangerouslySetInnerHTML={{80        __html: Object.entries(THEMES)81          .map(82            ([theme, prefix]) => `83${prefix} [data-chart=${id}] {84${colorConfig85  .map(([key, itemConfig]) => {86    const color =87      itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||88      itemConfig.color89    return color ? `  --color-${key}: ${color};` : null90  })91  .join("\n")}92}93`94          )95          .join("\n"),96      }}97    />98  )99}100 101const ChartTooltip = RechartsPrimitive.Tooltip102 103const ChartTooltipContent = React.forwardRef<104  HTMLDivElement,105  React.ComponentProps<typeof RechartsPrimitive.Tooltip> &106    React.ComponentProps<"div"> & {107      hideLabel?: boolean108      hideIndicator?: boolean109      indicator?: "line" | "dot" | "dashed"110      nameKey?: string111      labelKey?: string112    }113>(114  (115    {116      active,117      payload,118      className,119      indicator = "dot",120      hideLabel = false,121      hideIndicator = false,122      label,123      labelFormatter,124      labelClassName,125      formatter,126      color,127      nameKey,128      labelKey,129    },130    ref131  ) => {132    const { config } = useChart()133 134    const tooltipLabel = React.useMemo(() => {135      if (hideLabel || !payload?.length) {136        return null137      }138 139      const [item] = payload140      const key = `${labelKey || item.dataKey || item.name || "value"}`141      const itemConfig = getPayloadConfigFromPayload(config, item, key)142      const value =143        !labelKey && typeof label === "string"144          ? config[label as keyof typeof config]?.label || label145          : itemConfig?.label146 147      if (labelFormatter) {148        return (149          <div className={cn("font-medium", labelClassName)}>150            {labelFormatter(value, payload)}151          </div>152        )153      }154 155      if (!value) {156        return null157      }158 159      return <div className={cn("font-medium", labelClassName)}>{value}</div>160    }, [161      label,162      labelFormatter,163      payload,164      hideLabel,165      labelClassName,166      config,167      labelKey,168    ])169 170    if (!active || !payload?.length) {171      return null172    }173 174    const nestLabel = payload.length === 1 && indicator !== "dot"175 176    return (177      <div178        ref={ref}179        className={cn(180          "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",181          className182        )}183      >184        {!nestLabel ? tooltipLabel : null}185        <div className="grid gap-1.5">186          {payload.map((item, index) => {187            const key = `${nameKey || item.name || item.dataKey || "value"}`188            const itemConfig = getPayloadConfigFromPayload(config, item, key)189            const indicatorColor = color || item.payload.fill || item.color190 191            return (192              <div193                key={item.dataKey}194                className={cn(195                  "flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",196                  indicator === "dot" && "items-center"197                )}198              >199                {formatter && item?.value !== undefined && item.name ? (200                  formatter(item.value, item.name, item, index, item.payload)201                ) : (202                  <>203                    {itemConfig?.icon ? (204                      <itemConfig.icon />205                    ) : (206                      !hideIndicator && (207                        <div208                          className={cn(209                            "shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]",210                            {211                              "h-2.5 w-2.5": indicator === "dot",212                              "w-1": indicator === "line",213                              "w-0 border-[1.5px] border-dashed bg-transparent":214                                indicator === "dashed",215                              "my-0.5": nestLabel && indicator === "dashed",216                            }217                          )}218                          style={219                            {220                              "--color-bg": indicatorColor,221                              "--color-border": indicatorColor,222                            } as React.CSSProperties223                          }224                        />225                      )226                    )}227                    <div228                      className={cn(229                        "flex flex-1 justify-between leading-none",230                        nestLabel ? "items-end" : "items-center"231                      )}232                    >233                      <div className="grid gap-1.5">234                        {nestLabel ? tooltipLabel : null}235                        <span className="text-muted-foreground">236                          {itemConfig?.label || item.name}237                        </span>238                      </div>239                      {item.value && (240                        <span className="font-mono font-medium tabular-nums text-foreground">241                          {item.value.toLocaleString()}242                        </span>243                      )}244                    </div>245                  </>246                )}247              </div>248            )249          })}250        </div>251      </div>252    )253  }254)255ChartTooltipContent.displayName = "ChartTooltip"256 257const ChartLegend = RechartsPrimitive.Legend258 259const ChartLegendContent = React.forwardRef<260  HTMLDivElement,261  React.ComponentProps<"div"> &262    Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {263      hideIcon?: boolean264      nameKey?: string265    }266>(267  (268    { className, hideIcon = false, payload, verticalAlign = "bottom", nameKey },269    ref270  ) => {271    const { config } = useChart()272 273    if (!payload?.length) {274      return null275    }276 277    return (278      <div279        ref={ref}280        className={cn(281          "flex items-center justify-center gap-4",282          verticalAlign === "top" ? "pb-3" : "pt-3",283          className284        )}285      >286        {payload.map((item) => {287          const key = `${nameKey || item.dataKey || "value"}`288          const itemConfig = getPayloadConfigFromPayload(config, item, key)289 290          return (291            <div292              key={item.value}293              className={cn(294                "flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground"295              )}296            >297              {itemConfig?.icon && !hideIcon ? (298                <itemConfig.icon />299              ) : (300                <div301                  className="h-2 w-2 shrink-0 rounded-[2px]"302                  style={{303                    backgroundColor: item.color,304                  }}305                />306              )}307              {itemConfig?.label}308            </div>309          )310        })}311      </div>312    )313  }314)315ChartLegendContent.displayName = "ChartLegend"316 317// Helper to extract item config from a payload.318function getPayloadConfigFromPayload(319  config: ChartConfig,320  payload: unknown,321  key: string322) {323  if (typeof payload !== "object" || payload === null) {324    return undefined325  }326 327  const payloadPayload =328    "payload" in payload &&329    typeof payload.payload === "object" &&330    payload.payload !== null331      ? payload.payload332      : undefined333 334  let configLabelKey: string = key335 336  if (337    key in payload &&338    typeof payload[key as keyof typeof payload] === "string"339  ) {340    configLabelKey = payload[key as keyof typeof payload] as string341  } else if (342    payloadPayload &&343    key in payloadPayload &&344    typeof payloadPayload[key as keyof typeof payloadPayload] === "string"345  ) {346    configLabelKey = payloadPayload[347      key as keyof typeof payloadPayload348    ] as string349  }350 351  return configLabelKey in config352    ? config[configLabelKey]353    : config[key as keyof typeof config]354}355 356export {357  ChartContainer,358  ChartTooltip,359  ChartTooltipContent,360  ChartLegend,361  ChartLegendContent,362  ChartStyle,363}364