diff --git a/apps/www/__registry__/default/block/charts-01.tsx b/apps/www/__registry__/default/block/charts-01.tsx new file mode 100644 index 0000000000..795987ecf4 --- /dev/null +++ b/apps/www/__registry__/default/block/charts-01.tsx @@ -0,0 +1,904 @@ +"use client" + +import { + Area, + AreaChart, + Bar, + BarChart, + CartesianGrid, + Label, + LabelList, + Line, + LineChart, + PolarAngleAxis, + RadialBar, + RadialBarChart, + Rectangle, + ReferenceLine, + XAxis, + YAxis, +} from "recharts" + +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/registry/default/ui/card" +import { + ChartContainer, + ChartTooltip, + ChartTooltipContent, +} from "@/registry/default/ui/chart" +import { Separator } from "@/registry/default/ui/separator" + +export const description = "A collection of health charts." + +export const iframeHeight = "900px" + +export const containerClassName = "min-h-screen py-12" + +export default function Charts() { + return ( +
- {`/* ${chartConfig.theme.name} */`} + {`/* ${themesConfig?.activeTheme.name} */`} {themeCode.split("\n").map((line, index) => ( {line} diff --git a/apps/www/components/chart-display.tsx b/apps/www/components/chart-display.tsx index 0fc8cd462f..4945a60bd1 100644 --- a/apps/www/components/chart-display.tsx +++ b/apps/www/components/chart-display.tsx @@ -20,7 +20,7 @@ export async function ChartDisplay({ return ( diff --git a/apps/www/components/charts-theme-switcher.tsx b/apps/www/components/charts-theme-switcher.tsx deleted file mode 100644 index 258cb0de1c..0000000000 --- a/apps/www/components/charts-theme-switcher.tsx +++ /dev/null @@ -1,132 +0,0 @@ -"use client" - -import * as React from "react" -import { useTheme } from "next-themes" - -import { getChartThemes } from "@/lib/chart-themes" -import { cn } from "@/lib/utils" -import { useChartConfig } from "@/hooks/use-chart-config" -import { Skeleton } from "@/registry/new-york/ui/skeleton" -import { - Tooltip, - TooltipContent, - TooltipTrigger, -} from "@/registry/new-york/ui/tooltip" - -type Themes = ReturnType - -export function ChartsThemeSwitcher({ - themes, - className, -}: { themes: Themes } & React.ComponentProps<"div">) { - const { theme } = useTheme() - const [mounted, setMounted] = React.useState(false) - const { chartConfig, setChartConfig } = useChartConfig() - const activeChartTheme = chartConfig?.theme || themes[0] - - React.useEffect(() => { - setMounted(true) - }, []) - - if (!mounted) { - return ( - - {themes.map((theme) => ( - - - - ))} - - ) - } - - return ( - <> - - {themes.map((chartTheme) => { - const isActive = chartTheme.name === activeChartTheme.name - const isDarkTheme = ["Midnight"].includes(chartTheme.name) - const cssVars = - mounted && theme === "dark" - ? chartTheme.cssVars.dark - : chartTheme.cssVars.light - return ( - - - - setChartConfig({ ...chartConfig, theme: chartTheme }) - } - className={cn( - "group flex h-10 w-10 items-center justify-center rounded-lg border-2", - isActive ? "border-[--color-1]" : "border-transparent", - mounted && isDarkTheme && theme !== "dark" - ? "invert-[1]" - : "" - )} - style={ - { - ...cssVars, - "--color-1": "hsl(var(--chart-1))", - "--color-2": "hsl(var(--chart-2))", - "--color-3": "hsl(var(--chart-3))", - "--color-4": "hsl(var(--chart-4))", - } as React.CSSProperties - } - > - - - - - - - {chartTheme.name} - - - - - - {chartTheme.name} - - - ) - })} - - - > - ) -} diff --git a/apps/www/components/command-menu.tsx b/apps/www/components/command-menu.tsx index cf6e5aed77..6ffed59982 100644 --- a/apps/www/components/command-menu.tsx +++ b/apps/www/components/command-menu.tsx @@ -2,7 +2,7 @@ import * as React from "react" import { useRouter } from "next/navigation" -import { DialogProps } from "@radix-ui/react-alert-dialog" +import { type DialogProps } from "@radix-ui/react-dialog" import { CircleIcon, FileIcon, diff --git a/apps/www/components/themes-selector.tsx b/apps/www/components/themes-selector.tsx new file mode 100644 index 0000000000..dd08eb27e7 --- /dev/null +++ b/apps/www/components/themes-selector.tsx @@ -0,0 +1,124 @@ +"use client" + +import * as React from "react" +import { useTheme } from "next-themes" + +import { THEMES, Theme } from "@/lib/themes" +import { cn } from "@/lib/utils" +import { useMediaQuery } from "@/hooks/use-media-query" +import { useThemesConfig } from "@/hooks/use-themes-config" +import { Skeleton } from "@/registry/new-york/ui/skeleton" +import { + ToggleGroup, + ToggleGroupItem, +} from "@/registry/new-york/ui/toggle-group" +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/registry/new-york/ui/tooltip" + +export function ThemesSwitcher({ + themes = THEMES, + className, +}: React.ComponentProps<"div"> & { themes?: Theme[] }) { + const { theme: mode } = useTheme() + const [mounted, setMounted] = React.useState(false) + const { themesConfig, setThemesConfig } = useThemesConfig() + const activeTheme = themesConfig.activeTheme + const isDesktop = useMediaQuery("(min-width: 1024px)") + + React.useEffect(() => { + setMounted(true) + }, []) + + if (!mounted) { + return ( + + {themes.map((theme) => ( + + + + ))} + + ) + } + + return ( + { + const theme = themes.find((theme) => theme.name === value) + if (!theme) { + return + } + + setThemesConfig({ ...themesConfig, activeTheme: theme }) + }} + className={cn( + "flex items-center justify-center gap-0.5 py-4 lg:flex-col lg:justify-start lg:gap-1", + className + )} + > + {themes.map((theme) => { + const isActive = theme.name === activeTheme.name + const isDarkTheme = ["Midnight"].includes(theme.name) + const cssVars = + mounted && mode === "dark" ? theme.cssVars.dark : theme.cssVars.light + + return ( + + + + + + + + + + {theme.name} + + + + + + {theme.name} + + + ) + })} + + ) +} diff --git a/apps/www/components/themes-styles.tsx b/apps/www/components/themes-styles.tsx new file mode 100644 index 0000000000..890f454776 --- /dev/null +++ b/apps/www/components/themes-styles.tsx @@ -0,0 +1,31 @@ +"use client" + +import { useThemesConfig } from "@/hooks/use-themes-config" + +export function ThemesStyle() { + const { themesConfig } = useThemesConfig() + + if (!themesConfig.activeTheme) { + return null + } + + return ( + + ) +} diff --git a/apps/www/contentlayer.config.js b/apps/www/contentlayer.config.js index a609110cac..f86f4f8f26 100644 --- a/apps/www/contentlayer.config.js +++ b/apps/www/contentlayer.config.js @@ -116,7 +116,7 @@ export default makeSource({ { getHighlighter: async () => { const theme = await loadTheme( - path.join(process.cwd(), "/lib/themes/dark.json") + path.join(process.cwd(), "/lib/highlighter-theme.json") ) return await getHighlighter({ theme }) }, diff --git a/apps/www/hooks/use-chart-config.ts b/apps/www/hooks/use-chart-config.ts deleted file mode 100644 index 18c9bd4768..0000000000 --- a/apps/www/hooks/use-chart-config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import useSWR from "swr" - -export function useChartConfig() { - const { data, mutate } = useSWR("chart:config", null) - - return { chartConfig: data, setChartConfig: mutate } -} diff --git a/apps/www/hooks/use-themes-config.ts b/apps/www/hooks/use-themes-config.ts new file mode 100644 index 0000000000..9df09eb021 --- /dev/null +++ b/apps/www/hooks/use-themes-config.ts @@ -0,0 +1,18 @@ +import { useAtom } from "jotai" +import { atomWithStorage } from "jotai/utils" + +import { THEMES, Theme } from "@/lib/themes" + +type ThemesConfig = { + activeTheme: Theme +} + +const configAtom = atomWithStorage("themes:config", { + activeTheme: THEMES[0], +}) + +export function useThemesConfig() { + const [themesConfig, setThemesConfig] = useAtom(configAtom) + + return { themesConfig, setThemesConfig } +} diff --git a/apps/www/lib/themes/dark.json b/apps/www/lib/highlighter-theme.json similarity index 100% rename from apps/www/lib/themes/dark.json rename to apps/www/lib/highlighter-theme.json diff --git a/apps/www/lib/chart-themes.ts b/apps/www/lib/themes.ts similarity index 96% rename from apps/www/lib/chart-themes.ts rename to apps/www/lib/themes.ts index bc7097ac06..44398f11b7 100644 --- a/apps/www/lib/chart-themes.ts +++ b/apps/www/lib/themes.ts @@ -1,6 +1,6 @@ import { themeColorsToCssVariables } from "@/lib/charts" -export const CHART_THEMES = [ +const _THEMES = [ { name: "Default", id: "default-shadcn", @@ -177,8 +177,8 @@ export const CHART_THEMES = [ secondaryForeground: "222.2 47.4% 11.2%", muted: "240 3.7% 15.9%", "muted-foreground": "240 5% 64.9%", - accent: "210 40% 96.1%", - accentForeground: "222.2 47.4% 11.2%", + accent: "240 3.7% 15.9%", + "accent-foreground": "0 0% 98%", destructive: "0 72% 51%", destructiveForeground: "210 40% 98%", border: "240 3.7% 15.9%", @@ -244,8 +244,8 @@ export const CHART_THEMES = [ secondaryForeground: "240 5.9% 10%", muted: "240 3.7% 15.9%", "muted-foreground": "240 5% 64.9%", - accent: "240 4.8% 95.9%", - accentForeground: "240 5.9% 10%", + accent: "240 3.7% 15.9%", + "accent-foreground": "0 0% 98%", destructive: "0 72% 51%", destructiveForeground: "0 0% 98%", border: "240 3.7% 15.9%", @@ -311,8 +311,8 @@ export const CHART_THEMES = [ secondaryForeground: "240 5.9% 10%", muted: "240 3.7% 15.9%", "muted-foreground": "240 5% 64.9%", - accent: "240 4.8% 95.9%", - accentForeground: "240 5.9% 10%", + accent: "240 3.7% 15.9%", + "accent-foreground": "0 0% 98%", destructive: "0 72% 51%", destructiveForeground: "0 0% 98%", border: "240 3.7% 15.9%", @@ -471,14 +471,12 @@ export const CHART_THEMES = [ }, ] as const -export type ChartTheme = ReturnType[number] +export const THEMES = _THEMES.map((theme) => ({ + ...theme, + cssVars: { + light: themeColorsToCssVariables(theme.colors), + dark: themeColorsToCssVariables(theme.colorsDark), + }, +})) -export function getChartThemes() { - return CHART_THEMES.map((theme) => ({ - ...theme, - cssVars: { - light: themeColorsToCssVariables(theme.colors), - dark: themeColorsToCssVariables(theme.colorsDark), - }, - })) -} +export type Theme = (typeof THEMES)[number] diff --git a/apps/www/lib/themes/light.json b/apps/www/lib/themes/light.json deleted file mode 100644 index b146dd0206..0000000000 --- a/apps/www/lib/themes/light.json +++ /dev/null @@ -1,380 +0,0 @@ -{ - "name": "Lambda Studio — Whiteout", - "semanticHighlighting": true, - "colors": { - "editorLink.activeForeground": "#ca8a0488", - "foreground": "#0008", - "button.background": "#000", - "button.foreground": "#fff", - "button.hoverBackground": "#000b", - "list.highlightForeground": "#000", - "textLink.foreground": "#000", - "scrollbar.shadow": "#fff", - "textLink.activeForeground": "#0008", - "editor.lineHighlightBackground": "#8881", - "editor.lineHighlightBorder": "#8882", - "editorCursor.foreground": "#000", - "editor.findMatchBackground": "#0008", - "editor.findMatchHighlightBackground": "#0002", - "list.activeSelectionForeground": "#000", - "list.focusForeground": "#000", - "list.hoverForeground": "#000", - "list.inactiveSelectionForeground": "#000", - "list.inactiveSelectionBackground": "#fff", - "list.focusBackground": "#fff", - "list.focusAndSelectionOutline": "#fff", - "list.focusHighlightForeground": "#000", - "list.hoverBackground": "#fff", - "list.focusOutline": "#fff", - "list.activeSelectionBackground": "#fff", - "editorIndentGuide.background": "#0002", - "editor.background": "#fff", - "editor.foreground": "#000", - "editor.foldBackground": "#fff", - "editor.hoverHighlightBackground": "#fff", - "editor.selectionBackground": "#8888", - "editor.inactiveSelectionBackground": "#8882", - "gitDecoration.modifiedResourceForeground": "#000", - "gitDecoration.untrackedResourceForeground": "#a7cb7b", - "gitDecoration.conflictingResourceForeground": "#ca8a04", - "gitDecoration.deletedResourceForeground": "#c97b89", - "listFilterWidget.background": "#fff", - "input.background": "#0001", - "titleBar.activeForeground": "#000", - "editorWidget.background": "#fff", - "editorGutter.background": "#fff", - "debugToolBar.background": "#fff", - "commandCenter.background": "#fff", - "sideBarSectionHeader.background": "#fff", - "focusBorder": "#0008", - "titleBar.activeBackground": "#fff", - "titleBar.inactiveBackground": "#fff", - "breadcrumb.background": "#fff", - "activityBar.background": "#fff", - "activityBar.foreground": "#0008", - "panel.background": "#fff", - "sideBar.background": "#fff", - "sideBarTitle.foreground": "#0008", - "tab.hoverBackground": "#fff", - "terminal.background": "#fff", - "statusBar.background": "#fff", - "statusBar.foreground": "#0008", - "selection.background": "#0002", - "editorPane.background": "#fff", - "badge.background": "#fff", - "banner.background": "#fff", - "menu.background": "#fff", - "activityBarBadge.background": "#fff", - "activityBarBadge.foreground": "#0008", - "editorLineNumber.foreground": "#0002", - "editorLineNumber.activeForeground": "#0008", - "statusBarItem.errorBackground": "#f43f5e" - }, - "semanticTokenColors": { - "comment": { - "foreground": "#0004" - }, - "keyword": { - "foreground": "#0008" - }, - "string": { - "foreground": "#0008" - }, - "selfKeyword": { - "foreground": "#000", - "bold": true - }, - "method.declaration": { - "foreground": "#000", - "bold": true - }, - "method.definition": { - "foreground": "#000", - "bold": true - }, - "method": { - "foreground": "#000", - "bold": false - }, - "function.declaration": { - "foreground": "#000", - "bold": true - }, - "function.definition": { - "foreground": "#000", - "bold": true - }, - "function": { - "foreground": "#000", - "bold": false - }, - "property": { - "foreground": "#000" - }, - "enumMember": { - "foreground": "#0008", - "bold": false - }, - "enum": { - "foreground": "#000", - "bold": true - }, - "boolean": { - "foreground": "#0008" - }, - "number": { - "foreground": "#0008" - }, - "type": { - "foreground": "#000", - "bold": true - }, - "typeAlias": { - "foreground": "#000", - "bold": true - }, - "class": { - "foreground": "#000", - "bold": true - }, - "selfTypeKeyword": { - "foreground": "#000", - "bold": true - }, - "builtinType": { - "foreground": "#000", - "bold": true - }, - "interface": { - "foreground": "#0008", - "bold": false - }, - "typeParameter": { - "foreground": "#000", - "bold": true - }, - "lifetime": { - "foreground": "#0008", - "italic": false, - "bold": false - }, - "namespace": { - "foreground": "#000" - }, - "macro": { - "foreground": "#000", - "bold": false - }, - "decorator": { - "foreground": "#000", - "bold": false - }, - "builtinAttribute": { - "foreground": "#000", - "bold": false - }, - "generic.attribute": { - "foreground": "#000" - }, - "derive": { - "foreground": "#000" - }, - "operator": { - "foreground": "#0008" - }, - "variable": { - "foreground": "#000" - }, - "variable.readonly": { - "foreground": "#0008" - }, - "parameter": { - "foreground": "#000" - }, - "variable.mutable": { - "underline": true - }, - "parameter.mutable": { - "underline": true - }, - "selfKeyword.mutable": { - "underline": true - }, - "variable.constant": { - "foreground": "#0008" - }, - "struct": { - "foreground": "#000", - "bold": true - } - }, - "tokenColors": [ - { - "name": "Fallback Operator", - "scope": ["keyword.operator"], - "settings": { - "foreground": "#0008" - } - }, - { - "name": "Fallback keywords", - "scope": [ - "storage.type.ts", - "keyword", - "keyword.other", - "keyword.control", - "storage.type", - "storage.modifier" - ], - "settings": { - "foreground": "#0008" - } - }, - { - "name": "Fallback strings", - "scope": ["string"], - "settings": { - "foreground": "#0008" - } - }, - { - "name": "Fallback JSON Properties", - "scope": ["support.type.property-name.json"], - "settings": { - "foreground": "#000" - } - }, - { - "name": "Fallback string variables", - "scope": ["string variable", "string meta.interpolation"], - "settings": { - "foreground": "#000" - } - }, - { - "name": "Fallback comments", - "scope": ["comment"], - "settings": { - "foreground": "#0004" - } - }, - { - "name": "Fallback constants", - "scope": ["constant"], - "settings": { - "foreground": "#0008" - } - }, - { - "name": "Fallback self/this", - "scope": ["variable.language.this"], - "settings": { - "foreground": "#000" - } - }, - { - "name": "Fallback types", - "scope": [ - "entity.other.alias", - "source.php support.class", - "entity.name.type", - "meta.function-call support.class", - "keyword.other.type", - "entity.other.inherited-class" - ], - "settings": { - "foreground": "#000", - "fontStyle": "bold" - } - }, - { - "name": "Fallback method calls", - "scope": ["meta.method-call entity.name.function"], - "settings": { - "foreground": "#000", - "fontStyle": "" - } - }, - { - "name": "Fallback function calls", - "scope": [ - "meta.function-call entity.name.function", - "meta.function-call support.function", - "meta.function.call entity.name.function" - ], - "settings": { - "foreground": "#000", - "fontStyle": "" - } - }, - { - "name": "Fallback enums & constants", - "scope": ["constant.enum", "constant.other"], - "settings": { - "foreground": "#0008" - } - }, - { - "name": "Fallback Properties & func arguments", - "scope": [ - "variable.other.property", - "entity.name.goto-label", - "entity.name.variable.parameter" - ], - "settings": { - "foreground": "#000" - } - }, - { - "name": "Fallback functions & methods declarations", - "scope": [ - "entity.name.function", - "support.function", - "support.function.constructor", - "entity.name.function meta.function-call meta.method-call" - ], - "settings": { - "foreground": "#000", - "fontStyle": "bold" - } - }, - { - "name": "HTML Tags", - "scope": [ - "meta.tag entity.name.tag.html", - "entity.name.tag.template.html" - ], - "settings": { - "foreground": "#000" - } - }, - { - "name": "HTML Attributes", - "scope": ["entity.other.attribute-name.html"], - "settings": { - "foreground": "#0008" - } - }, - { - "name": "HTML Custom Tag", - "scope": ["meta.tag.other.unrecognized.html entity.name.tag.html"], - "settings": { - "foreground": "#000" - } - }, - { - "name": "HTML Keywords", - "scope": ["text.html keyword"], - "settings": { - "foreground": "#000" - } - }, - { - "name": "Punctuations", - "scope": ["punctuation", "meta.brace"], - "settings": { - "foreground": "#0008" - } - } - ] -} diff --git a/apps/www/public/registry/styles/default/chart.json b/apps/www/public/registry/styles/default/chart.json index a558fc918d..a334a7f3d4 100644 --- a/apps/www/public/registry/styles/default/chart.json +++ b/apps/www/public/registry/styles/default/chart.json @@ -10,7 +10,7 @@ "files": [ { "name": "chart.tsx", - "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as RechartsPrimitive from \"recharts\"\n\nimport { cn } from \"@/lib/utils\"\n\n// Format: { THEME_NAME: CSS_SELECTOR }\nconst THEMES = { light: \"\", dark: \".dark\" } as const\n\nexport type ChartConfig = {\n [k in string]: {\n label?: React.ReactNode\n icon?: React.ComponentType\n } & (\n | { color?: string; theme?: never }\n | { color?: never; theme: Record }\n )\n}\n\ntype ChartContextProps = {\n config: ChartConfig\n}\n\nconst ChartContext = React.createContext(null)\n\nfunction useChart() {\n const context = React.useContext(ChartContext)\n\n if (!context) {\n throw new Error(\"useChart must be used within a \")\n }\n\n return context\n}\n\nconst ChartContainer = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\"> & {\n config: ChartConfig\n children: React.ComponentProps<\n typeof RechartsPrimitive.ResponsiveContainer\n >[\"children\"]\n }\n>(({ id, className, children, config, ...props }, ref) => {\n const uniqueId = React.useId()\n const chartId = `chart-${id || uniqueId.replace(/:/g, \"\")}`\n\n return (\n \n \n \n \n {children}\n \n \n \n )\n})\nChartContainer.displayName = \"Chart\"\n\nconst ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {\n const colorConfig = Object.entries(config).filter(\n ([_, config]) => config.theme || config.color\n )\n\n if (!colorConfig.length) {\n return null\n }\n\n return (\n