From 65223896da15d2028d8246cdbc4692ce9a7513f2 Mon Sep 17 00:00:00 2001 From: shadcn Date: Sat, 31 May 2025 14:21:19 +0400 Subject: [PATCH] feat: layout toggle (#7515) * feat: implement fixed layout * feat: track layout * fix --- apps/v4/app/(app)/charts/[type]/page.tsx | 26 ++-- apps/v4/app/(app)/charts/layout.tsx | 6 +- apps/v4/app/(app)/docs/layout.tsx | 4 +- apps/v4/app/layout.tsx | 25 ++-- apps/v4/components/active-theme.tsx | 9 -- apps/v4/components/charts-nav.tsx | 2 +- apps/v4/components/docs-sidebar.tsx | 2 +- apps/v4/components/mode-switcher.tsx | 1 + apps/v4/components/site-config.tsx | 33 +++++ apps/v4/components/site-footer.tsx | 2 +- apps/v4/components/site-header.tsx | 8 +- apps/v4/hooks/use-layout.tsx | 160 +++++++++++++++++++++++ apps/v4/lib/events.ts | 1 + apps/v4/source.config.ts | 1 + apps/v4/styles/globals.css | 10 +- pnpm-lock.yaml | 4 +- 16 files changed, 246 insertions(+), 48 deletions(-) create mode 100644 apps/v4/components/site-config.tsx create mode 100644 apps/v4/hooks/use-layout.tsx diff --git a/apps/v4/app/(app)/charts/[type]/page.tsx b/apps/v4/app/(app)/charts/[type]/page.tsx index d0c23f9a6b..65cdac9ab3 100644 --- a/apps/v4/app/(app)/charts/[type]/page.tsx +++ b/apps/v4/app/(app)/charts/[type]/page.tsx @@ -48,15 +48,23 @@ export default async function ChartPage({ params }: ChartPageProps) { {type.charAt(0).toUpperCase() + type.slice(1)} Charts
- {chartList.map((chart) => ( - - - - ))} + {Array.from({ length: 12 }).map((_, index) => { + const chart = chartList[index] + return chart ? ( + + + + ) : ( +
+ ) + })}
) diff --git a/apps/v4/app/(app)/charts/layout.tsx b/apps/v4/app/(app)/charts/layout.tsx index 29db0aedce..608bb1e4e0 100644 --- a/apps/v4/app/(app)/charts/layout.tsx +++ b/apps/v4/app/(app)/charts/layout.tsx @@ -47,7 +47,7 @@ export default function ChartsLayout({ children: React.ReactNode }) { return ( -
+ <> {title} @@ -65,11 +65,11 @@ export default function ChartsLayout({ -
+
{children}
-
+ ) } diff --git a/apps/v4/app/(app)/docs/layout.tsx b/apps/v4/app/(app)/docs/layout.tsx index 1ca18824cb..65e202a987 100644 --- a/apps/v4/app/(app)/docs/layout.tsx +++ b/apps/v4/app/(app)/docs/layout.tsx @@ -8,8 +8,8 @@ export default function DocsLayout({ children: React.ReactNode }) { return ( -
- +
+
{children}
diff --git a/apps/v4/app/layout.tsx b/apps/v4/app/layout.tsx index 68fbd3221e..213c282303 100644 --- a/apps/v4/app/layout.tsx +++ b/apps/v4/app/layout.tsx @@ -1,9 +1,9 @@ import type { Metadata } from "next" -import { cookies } from "next/headers" import { META_THEME_COLORS, siteConfig } from "@/lib/config" import { fontVariables } from "@/lib/fonts" import { cn } from "@/lib/utils" +import { LayoutProvider } from "@/hooks/use-layout" import { ActiveThemeProvider } from "@/components/active-theme" import { Analytics } from "@/components/analytics" import { TailwindIndicator } from "@/components/tailwind-indicator" @@ -63,9 +63,6 @@ export default async function RootLayout({ }: Readonly<{ children: React.ReactNode }>) { - const cookieStore = await cookies() - const activeThemeValue = cookieStore.get("active_theme")?.value - return ( @@ -76,6 +73,9 @@ export default async function RootLayout({ if (localStorage.theme === 'dark' || ((!('theme' in localStorage) || localStorage.theme === 'system') && window.matchMedia('(prefers-color-scheme: dark)').matches)) { document.querySelector('meta[name="theme-color"]').setAttribute('content', '${META_THEME_COLORS.dark}') } + if (localStorage.layout) { + document.documentElement.classList.add('layout-' + localStorage.layout) + } } catch (_) {} `, }} @@ -84,18 +84,19 @@ export default async function RootLayout({ - - {children} - - - - + + + {children} + + + + + diff --git a/apps/v4/components/active-theme.tsx b/apps/v4/components/active-theme.tsx index f0348e2035..1bc94c6138 100644 --- a/apps/v4/components/active-theme.tsx +++ b/apps/v4/components/active-theme.tsx @@ -8,15 +8,8 @@ import { useState, } from "react" -const COOKIE_NAME = "active_theme" const DEFAULT_THEME = "default" -function setThemeCookie(theme: string) { - if (typeof window === "undefined") return - - document.cookie = `${COOKIE_NAME}=${theme}; path=/; max-age=31536000; SameSite=Lax; ${window.location.protocol === "https:" ? "Secure;" : ""}` -} - type ThemeContextType = { activeTheme: string setActiveTheme: (theme: string) => void @@ -36,8 +29,6 @@ export function ActiveThemeProvider({ ) useEffect(() => { - setThemeCookie(activeTheme) - Array.from(document.body.classList) .filter((className) => className.startsWith("theme-")) .forEach((className) => { diff --git a/apps/v4/components/charts-nav.tsx b/apps/v4/components/charts-nav.tsx index 722c081c52..0b2f78fbd2 100644 --- a/apps/v4/components/charts-nav.tsx +++ b/apps/v4/components/charts-nav.tsx @@ -51,7 +51,7 @@ export function ChartsNav({ {item.name} diff --git a/apps/v4/components/mode-switcher.tsx b/apps/v4/components/mode-switcher.tsx index 8262b68aba..d603805076 100644 --- a/apps/v4/components/mode-switcher.tsx +++ b/apps/v4/components/mode-switcher.tsx @@ -24,6 +24,7 @@ export function ModeSwitcher() { size="icon" className="group/toggle extend-touch-target size-8" onClick={toggleTheme} + title="Toggle theme" > ) { + const { layout, setLayout } = useLayout() + + return ( + Toggle layout + + + ) +} diff --git a/apps/v4/components/site-footer.tsx b/apps/v4/components/site-footer.tsx index 11a3885c57..1d296522c0 100644 --- a/apps/v4/components/site-footer.tsx +++ b/apps/v4/components/site-footer.tsx @@ -2,7 +2,7 @@ import { siteConfig } from "@/lib/config" export function SiteFooter() { return ( -