aboutsummaryrefslogtreecommitdiffstats
path: root/ui/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'ui/src/components')
-rw-r--r--ui/src/components/history/HistoryRow.tsx35
-rw-r--r--ui/src/components/home/QuickActions.tsx1
-rw-r--r--ui/src/components/runbooks/List.tsx15
-rw-r--r--ui/src/components/runbooks/editor/Editor.tsx74
-rw-r--r--ui/src/components/runbooks/editor/blocks/RunBlock/index.tsx20
-rw-r--r--ui/src/components/ui/card.tsx79
-rw-r--r--ui/src/components/ui/chart.tsx363
7 files changed, 527 insertions, 60 deletions
diff --git a/ui/src/components/history/HistoryRow.tsx b/ui/src/components/history/HistoryRow.tsx
index 98d271fb..4d893e61 100644
--- a/ui/src/components/history/HistoryRow.tsx
+++ b/ui/src/components/history/HistoryRow.tsx
@@ -11,6 +11,7 @@ import "prismjs/components/prism-bash";
import Drawer from "../Drawer";
import HistoryInspect from "./HistoryInspect";
+import { cn } from "@/lib/utils";
function msToTime(ms: number) {
let milliseconds = parseInt(ms.toFixed(1));
@@ -26,25 +27,31 @@ function msToTime(ms: number) {
else return days + " Days";
}
-export default function HistoryRow({ h }: any) {
+export default function HistoryRow({ h, compact }: any) {
return (
<li
key={h.id}
- className="relative flex justify-between gap-x-6 px-4 py-5 hover:bg-gray-50 sm:px-6"
+ className={cn(
+ "relative flex justify-between gap-x-6 px-4 py-5 hover:bg-gray-50 sm:px-6",
+ { "py-5": !compact },
+ { "py-1": compact },
+ )}
>
<div className="flex min-w-0 gap-x-4">
- <div className="flex flex-col justify-center">
- <p className="flex text-xs text-gray-500 justify-center">
- {DateTime.fromMillis(h.timestamp / 1000000).toLocaleString(
- DateTime.TIME_WITH_SECONDS,
- )}
- </p>
- <p className="flex text-xs mt-1 text-gray-400 justify-center">
- {DateTime.fromMillis(h.timestamp / 1000000).toLocaleString(
- DateTime.DATE_SHORT,
- )}
- </p>
- </div>
+ {!compact && (
+ <div className="flex flex-col justify-center">
+ <p className="flex text-xs text-gray-500 justify-center">
+ {DateTime.fromMillis(h.timestamp / 1000000).toLocaleString(
+ DateTime.TIME_WITH_SECONDS,
+ )}
+ </p>
+ <p className="flex text-xs mt-1 text-gray-400 justify-center">
+ {DateTime.fromMillis(h.timestamp / 1000000).toLocaleString(
+ DateTime.DATE_SHORT,
+ )}
+ </p>
+ </div>
+ )}
<div className="min-w-0 flex-col justify-center truncate">
<Highlight
theme={themes.github}
diff --git a/ui/src/components/home/QuickActions.tsx b/ui/src/components/home/QuickActions.tsx
new file mode 100644
index 00000000..a22e4493
--- /dev/null
+++ b/ui/src/components/home/QuickActions.tsx
@@ -0,0 +1 @@
+export default function QuickActions() {}
diff --git a/ui/src/components/runbooks/List.tsx b/ui/src/components/runbooks/List.tsx
index 72c1b3b3..024bcfd1 100644
--- a/ui/src/components/runbooks/List.tsx
+++ b/ui/src/components/runbooks/List.tsx
@@ -1,12 +1,7 @@
-import { useEffect, useState } from "react";
+import { useEffect } from "react";
import {
- Input,
Button,
ButtonGroup,
- Card,
- CardBody,
- CardHeader,
- Divider,
Tooltip,
Listbox,
ListboxItem,
@@ -46,13 +41,13 @@ const NoteSidebar = () => {
<div className="overflow-y-auto flex-grow">
<Listbox
hideSelectedIcon
- items={runbooks.map((runbook) => {
+ items={runbooks.map((runbook: any): any => {
return [runbook, runbookInfo[runbook.id]];
})}
variant="flat"
aria-label="Runbook list"
selectionMode="single"
- selectedKeys={[currentRunbook]}
+ selectedKeys={currentRunbook ? [currentRunbook] : []}
itemClasses={{ base: "data-[selected=true]:bg-gray-200" }}
topContent={
<ButtonGroup className="z-20">
@@ -74,7 +69,7 @@ const NoteSidebar = () => {
</ButtonGroup>
}
>
- {([runbook, info]) => (
+ {([runbook, info]: [Runbook, { ptys: number }]) => (
<ListboxItem
key={runbook.id}
onPress={() => {
@@ -124,7 +119,7 @@ const NoteSidebar = () => {
<div className="text-xs text-gray-500">
<em>
{DateTime.fromJSDate(runbook.updated).toLocaleString(
- DateTime.DATETIME_SIMPLE,
+ DateTime.DATETIME_SHORT,
)}
</em>
</div>
diff --git a/ui/src/components/runbooks/editor/Editor.tsx b/ui/src/components/runbooks/editor/Editor.tsx
index 98a6a282..bbf594d8 100644
--- a/ui/src/components/runbooks/editor/Editor.tsx
+++ b/ui/src/components/runbooks/editor/Editor.tsx
@@ -1,37 +1,47 @@
import { useEffect, useMemo, useState } from "react";
-import "@blocknote/core/fonts/inter.css";
-import "@blocknote/mantine/style.css";
import "./index.css";
import { Spinner } from "@nextui-org/react";
+// Errors, but it all works fine and is there. Maybe missing ts defs?
+// I'll figure it out later
import {
+ // @ts-ignore
BlockNoteSchema,
+ // @ts-ignore
BlockNoteEditor,
+ // @ts-ignore
defaultBlockSpecs,
+ // @ts-ignore
filterSuggestionItems,
+ // @ts-ignore
insertOrUpdateBlock,
} from "@blocknote/core";
-import "@blocknote/core/fonts/inter.css";
-
import {
+ //@ts-ignore
SuggestionMenuController,
+ // @ts-ignore
AddBlockButton,
+ // @ts-ignore
getDefaultReactSlashMenuItems,
- useCreateBlockNote,
+ // @ts-ignore
SideMenu,
+ // @ts-ignore
SideMenuController,
} from "@blocknote/react";
import { BlockNoteView } from "@blocknote/mantine";
+import "@blocknote/core/fonts/inter.css";
+import "@blocknote/mantine/style.css";
+
import { Code } from "lucide-react";
import { useDebounceCallback } from "usehooks-ts";
import RunBlock from "@/components/runbooks/editor/blocks/RunBlock";
import { DeleteBlock } from "@/components/runbooks/editor/ui/DeleteBlockButton";
-import { useStore } from "@/state/store";
+import { AtuinState, useStore } from "@/state/store";
import Runbook from "@/state/runbooks/runbook";
// Our schema with block specs, which contain the configs and implementations for blocks
@@ -60,8 +70,10 @@ const insertRun = (editor: typeof schema.BlockNoteEditor) => ({
});
export default function Editor() {
- const runbookId = useStore((store) => store.currentRunbook);
- const refreshRunbooks = useStore((store) => store.refreshRunbooks);
+ const runbookId = useStore((store: AtuinState) => store.currentRunbook);
+ const refreshRunbooks = useStore(
+ (store: AtuinState) => store.refreshRunbooks,
+ );
let [runbook, setRunbook] = useState<Runbook | null>(null);
useEffect(() => {
@@ -76,43 +88,43 @@ export default function Editor() {
fetchRunbook();
}, [runbookId]);
- const editor = useMemo(() => {
- if (!runbook) {
- return undefined;
- }
-
- if (runbook.content) {
- return BlockNoteEditor.create({
- initialContent: JSON.parse(runbook.content),
- schema,
- });
- }
-
- return BlockNoteEditor.create({ schema });
- }, [runbook]);
-
const onChange = async () => {
if (!runbook) return;
console.log("saved!");
runbook.name = fetchName();
- runbook.content = JSON.stringify(editor.document);
+ if (editor) runbook.content = JSON.stringify(editor.document);
await runbook.save();
- await refreshRunbooks();
+ refreshRunbooks();
};
const debouncedOnChange = useDebounceCallback(onChange, 1000);
+ const editor = useMemo(() => {
+ if (!runbook) return undefined;
+ if (runbook.content) {
+ return BlockNoteEditor.create({
+ initialContent: JSON.parse(runbook.content),
+ schema,
+ });
+ }
+
+ return BlockNoteEditor.create({ schema });
+ }, [runbook]);
+
const fetchName = (): string => {
// Infer the title from the first text block
+ if (!editor) return "Untitled";
let blocks = editor.document;
for (const block of blocks) {
if (block.type == "heading" || block.type == "paragraph") {
if (block.content.length == 0) continue;
+ // @ts-ignore
if (block.content[0].text.length == 0) continue;
+ // @ts-ignore
return block.content[0].text;
}
}
@@ -120,6 +132,14 @@ export default function Editor() {
return "Untitled";
};
+ if (!runbook) {
+ return (
+ <div className="flex w-full h-full flex-col justify-center items-center">
+ <Spinner />
+ </div>
+ );
+ }
+
if (editor === undefined) {
return (
<div className="flex w-full h-full flex-col justify-center items-center">
@@ -139,7 +159,7 @@ export default function Editor() {
>
<SuggestionMenuController
triggerCharacter={"/"}
- getItems={async (query) =>
+ getItems={async (query: any) =>
filterSuggestionItems(
[...getDefaultReactSlashMenuItems(editor), insertRun(editor)],
query,
@@ -148,7 +168,7 @@ export default function Editor() {
/>
<SideMenuController
- sideMenu={(props) => (
+ sideMenu={(props: any) => (
<SideMenu {...props}>
<AddBlockButton {...props} />
<DeleteBlock {...props} />
diff --git a/ui/src/components/runbooks/editor/blocks/RunBlock/index.tsx b/ui/src/components/runbooks/editor/blocks/RunBlock/index.tsx
index 15653611..b3a96166 100644
--- a/ui/src/components/runbooks/editor/blocks/RunBlock/index.tsx
+++ b/ui/src/components/runbooks/editor/blocks/RunBlock/index.tsx
@@ -1,4 +1,4 @@
-import React from "react";
+// @ts-ignore
import { createReactBlockSpec } from "@blocknote/react";
import "./index.css";
@@ -48,7 +48,7 @@ const RunBlock = ({
],
);
- const isRunning = pty !== null;
+ const isRunning = pty !== null && pty !== "";
const handleToggle = async (event: any | null) => {
if (event) event.stopPropagation();
@@ -63,21 +63,21 @@ const RunBlock = ({
cleanupPtyTerm(pty);
if (onStop) onStop(pty);
- decRunbookPty(currentRunbook);
+ if (currentRunbook) decRunbookPty(currentRunbook);
}
if (!isRunning) {
let pty = await invoke<string>("pty_open");
if (onRun) onRun(pty);
- incRunbookPty(currentRunbook);
+ if (currentRunbook) incRunbookPty(currentRunbook);
let val = !value.endsWith("\n") ? value + "\r\n" : value;
await invoke("pty_write", { pid: pty, data: val });
}
};
- const handleCmdEnter = (view) => {
+ const handleCmdEnter = () => {
handleToggle(null);
return true;
};
@@ -145,7 +145,7 @@ export default createReactBlockSpec(
default: "bash",
},
code: { default: "" },
- pty: { default: null },
+ pty: { default: "" },
},
content: "none",
},
@@ -154,19 +154,21 @@ export default createReactBlockSpec(
render: ({ block, editor, code, type }) => {
const onInputChange = (val: string) => {
editor.updateBlock(block, {
+ // @ts-ignore
props: { ...block.props, code: val },
});
};
const onRun = (pty: string) => {
editor.updateBlock(block, {
+ // @ts-ignore
props: { ...block.props, pty: pty },
});
};
- const onStop = (pty: string) => {
- editor.updateBlock(block, {
- props: { ...block.props, pty: null },
+ const onStop = (_pty: string) => {
+ editor?.updateBlock(block, {
+ props: { ...block.props, pty: "" },
});
};
diff --git a/ui/src/components/ui/card.tsx b/ui/src/components/ui/card.tsx
new file mode 100644
index 00000000..afa13ecf
--- /dev/null
+++ b/ui/src/components/ui/card.tsx
@@ -0,0 +1,79 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+const Card = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes<HTMLDivElement>
+>(({ className, ...props }, ref) => (
+ <div
+ ref={ref}
+ className={cn(
+ "rounded-lg border bg-card text-card-foreground shadow-sm",
+ className
+ )}
+ {...props}
+ />
+))
+Card.displayName = "Card"
+
+const CardHeader = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes<HTMLDivElement>
+>(({ className, ...props }, ref) => (
+ <div
+ ref={ref}
+ className={cn("flex flex-col space-y-1.5 p-6", className)}
+ {...props}
+ />
+))
+CardHeader.displayName = "CardHeader"
+
+const CardTitle = React.forwardRef<
+ HTMLParagraphElement,
+ React.HTMLAttributes<HTMLHeadingElement>
+>(({ className, ...props }, ref) => (
+ <h3
+ ref={ref}
+ className={cn(
+ "text-2xl font-semibold leading-none tracking-tight",
+ className
+ )}
+ {...props}
+ />
+))
+CardTitle.displayName = "CardTitle"
+
+const CardDescription = React.forwardRef<
+ HTMLParagraphElement,
+ React.HTMLAttributes<HTMLParagraphElement>
+>(({ className, ...props }, ref) => (
+ <p
+ ref={ref}
+ className={cn("text-sm text-muted-foreground", className)}
+ {...props}
+ />
+))
+CardDescription.displayName = "CardDescription"
+
+const CardContent = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes<HTMLDivElement>
+>(({ className, ...props }, ref) => (
+ <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
+))
+CardContent.displayName = "CardContent"
+
+const CardFooter = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes<HTMLDivElement>
+>(({ className, ...props }, ref) => (
+ <div
+ ref={ref}
+ className={cn("flex items-center p-6 pt-0", className)}
+ {...props}
+ />
+))
+CardFooter.displayName = "CardFooter"
+
+export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
diff --git a/ui/src/components/ui/chart.tsx b/ui/src/components/ui/chart.tsx
new file mode 100644
index 00000000..a21d77ee
--- /dev/null
+++ b/ui/src/components/ui/chart.tsx
@@ -0,0 +1,363 @@
+import * as React from "react"
+import * as RechartsPrimitive from "recharts"
+
+import { cn } from "@/lib/utils"
+
+// Format: { THEME_NAME: CSS_SELECTOR }
+const THEMES = { light: "", dark: ".dark" } as const
+
+export type ChartConfig = {
+ [k in string]: {
+ label?: React.ReactNode
+ icon?: React.ComponentType
+ } & (
+ | { color?: string; theme?: never }
+ | { color?: never; theme: Record<keyof typeof THEMES, string> }
+ )
+}
+
+type ChartContextProps = {
+ config: ChartConfig
+}
+
+const ChartContext = React.createContext<ChartContextProps | null>(null)
+
+function useChart() {
+ const context = React.useContext(ChartContext)
+
+ if (!context) {
+ throw new Error("useChart must be used within a <ChartContainer />")
+ }
+
+ return context
+}
+
+const ChartContainer = React.forwardRef<
+ HTMLDivElement,
+ React.ComponentProps<"div"> & {
+ config: ChartConfig
+ children: React.ComponentProps<
+ typeof RechartsPrimitive.ResponsiveContainer
+ >["children"]
+ }
+>(({ id, className, children, config, ...props }, ref) => {
+ const uniqueId = React.useId()
+ const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`
+
+ return (
+ <ChartContext.Provider value={{ config }}>
+ <div
+ data-chart={chartId}
+ ref={ref}
+ className={cn(
+ "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",
+ className
+ )}
+ {...props}
+ >
+ <ChartStyle id={chartId} config={config} />
+ <RechartsPrimitive.ResponsiveContainer>
+ {children}
+ </RechartsPrimitive.ResponsiveContainer>
+ </div>
+ </ChartContext.Provider>
+ )
+})
+ChartContainer.displayName = "Chart"
+
+const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
+ const colorConfig = Object.entries(config).filter(
+ ([_, config]) => config.theme || config.color
+ )
+
+ if (!colorConfig.length) {
+ return null
+ }
+
+ return (
+ <style
+ dangerouslySetInnerHTML={{
+ __html: Object.entries(THEMES)
+ .map(
+ ([theme, prefix]) => `
+${prefix} [data-chart=${id}] {
+${colorConfig
+ .map(([key, itemConfig]) => {
+ const color =
+ itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
+ itemConfig.color
+ return color ? ` --color-${key}: ${color};` : null
+ })
+ .join("\n")}
+}
+`
+ )
+ .join("\n"),
+ }}
+ />
+ )
+}
+
+const ChartTooltip = RechartsPrimitive.Tooltip
+
+const ChartTooltipContent = React.forwardRef<
+ HTMLDivElement,
+ React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
+ React.ComponentProps<"div"> & {
+ hideLabel?: boolean
+ hideIndicator?: boolean
+ indicator?: "line" | "dot" | "dashed"
+ nameKey?: string
+ labelKey?: string
+ }
+>(
+ (
+ {
+ active,
+ payload,
+ className,
+ indicator = "dot",
+ hideLabel = false,
+ hideIndicator = false,
+ label,
+ labelFormatter,
+ labelClassName,
+ formatter,
+ color,
+ nameKey,
+ labelKey,
+ },
+ ref
+ ) => {
+ const { config } = useChart()
+
+ const tooltipLabel = React.useMemo(() => {
+ if (hideLabel || !payload?.length) {
+ return null
+ }
+
+ const [item] = payload
+ const key = `${labelKey || item.dataKey || item.name || "value"}`
+ const itemConfig = getPayloadConfigFromPayload(config, item, key)
+ const value =
+ !labelKey && typeof label === "string"
+ ? config[label as keyof typeof config]?.label || label
+ : itemConfig?.label
+
+ if (labelFormatter) {
+ return (
+ <div className={cn("font-medium", labelClassName)}>
+ {labelFormatter(value, payload)}
+ </div>
+ )
+ }
+
+ if (!value) {
+ return null
+ }
+
+ return <div className={cn("font-medium", labelClassName)}>{value}</div>
+ }, [
+ label,
+ labelFormatter,
+ payload,
+ hideLabel,
+ labelClassName,
+ config,
+ labelKey,
+ ])
+
+ if (!active || !payload?.length) {
+ return null
+ }
+
+ const nestLabel = payload.length === 1 && indicator !== "dot"
+
+ return (
+ <div
+ ref={ref}
+ className={cn(
+ "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",
+ className
+ )}
+ >
+ {!nestLabel ? tooltipLabel : null}
+ <div className="grid gap-1.5">
+ {payload.map((item, index) => {
+ const key = `${nameKey || item.name || item.dataKey || "value"}`
+ const itemConfig = getPayloadConfigFromPayload(config, item, key)
+ const indicatorColor = color || item.payload.fill || item.color
+
+ return (
+ <div
+ key={item.dataKey}
+ className={cn(
+ "flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",
+ indicator === "dot" && "items-center"
+ )}
+ >
+ {formatter && item?.value !== undefined && item.name ? (
+ formatter(item.value, item.name, item, index, item.payload)
+ ) : (
+ <>
+ {itemConfig?.icon ? (
+ <itemConfig.icon />
+ ) : (
+ !hideIndicator && (
+ <div
+ className={cn(
+ "shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]",
+ {
+ "h-2.5 w-2.5": indicator === "dot",
+ "w-1": indicator === "line",
+ "w-0 border-[1.5px] border-dashed bg-transparent":
+ indicator === "dashed",
+ "my-0.5": nestLabel && indicator === "dashed",
+ }
+ )}
+ style={
+ {
+ "--color-bg": indicatorColor,
+ "--color-border": indicatorColor,
+ } as React.CSSProperties
+ }
+ />
+ )
+ )}
+ <div
+ className={cn(
+ "flex flex-1 justify-between leading-none",
+ nestLabel ? "items-end" : "items-center"
+ )}
+ >
+ <div className="grid gap-1.5">
+ {nestLabel ? tooltipLabel : null}
+ <span className="text-muted-foreground">
+ {itemConfig?.label || item.name}
+ </span>
+ </div>
+ {item.value && (
+ <span className="font-mono font-medium tabular-nums text-foreground">
+ {item.value.toLocaleString()}
+ </span>
+ )}
+ </div>
+ </>
+ )}
+ </div>
+ )
+ })}
+ </div>
+ </div>
+ )
+ }
+)
+ChartTooltipContent.displayName = "ChartTooltip"
+
+const ChartLegend = RechartsPrimitive.Legend
+
+const ChartLegendContent = React.forwardRef<
+ HTMLDivElement,
+ React.ComponentProps<"div"> &
+ Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
+ hideIcon?: boolean
+ nameKey?: string
+ }
+>(
+ (
+ { className, hideIcon = false, payload, verticalAlign = "bottom", nameKey },
+ ref
+ ) => {
+ const { config } = useChart()
+
+ if (!payload?.length) {
+ return null
+ }
+
+ return (
+ <div
+ ref={ref}
+ className={cn(
+ "flex items-center justify-center gap-4",
+ verticalAlign === "top" ? "pb-3" : "pt-3",
+ className
+ )}
+ >
+ {payload.map((item) => {
+ const key = `${nameKey || item.dataKey || "value"}`
+ const itemConfig = getPayloadConfigFromPayload(config, item, key)
+
+ return (
+ <div
+ key={item.value}
+ className={cn(
+ "flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground"
+ )}
+ >
+ {itemConfig?.icon && !hideIcon ? (
+ <itemConfig.icon />
+ ) : (
+ <div
+ className="h-2 w-2 shrink-0 rounded-[2px]"
+ style={{
+ backgroundColor: item.color,
+ }}
+ />
+ )}
+ {itemConfig?.label}
+ </div>
+ )
+ })}
+ </div>
+ )
+ }
+)
+ChartLegendContent.displayName = "ChartLegend"
+
+// Helper to extract item config from a payload.
+function getPayloadConfigFromPayload(
+ config: ChartConfig,
+ payload: unknown,
+ key: string
+) {
+ if (typeof payload !== "object" || payload === null) {
+ return undefined
+ }
+
+ const payloadPayload =
+ "payload" in payload &&
+ typeof payload.payload === "object" &&
+ payload.payload !== null
+ ? payload.payload
+ : undefined
+
+ let configLabelKey: string = key
+
+ if (
+ key in payload &&
+ typeof payload[key as keyof typeof payload] === "string"
+ ) {
+ configLabelKey = payload[key as keyof typeof payload] as string
+ } else if (
+ payloadPayload &&
+ key in payloadPayload &&
+ typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
+ ) {
+ configLabelKey = payloadPayload[
+ key as keyof typeof payloadPayload
+ ] as string
+ }
+
+ return configLabelKey in config
+ ? config[configLabelKey]
+ : config[key as keyof typeof config]
+}
+
+export {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ ChartLegend,
+ ChartLegendContent,
+ ChartStyle,
+}