mirror of https://github.com/MaxLeiter/Drift
remove next-themes, convert header to custom button
parent
bff7c90e5f
commit
dfe0d39fa0
@ -0,0 +1,5 @@
|
||||
import styles from "./page.module.css"
|
||||
|
||||
export default function Page({ children }: { children: React.ReactNode }) {
|
||||
return <div className={styles.page}>{children}</div>
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
.page {
|
||||
max-width: 100vw;
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
padding: 0 calc(1.34 * 16px) 0 calc(1.34 * 16px);
|
||||
margin: 0 auto 0 auto;
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
"use client"
|
||||
|
||||
import {
|
||||
FunctionComponent,
|
||||
PropsWithChildren,
|
||||
useCallback,
|
||||
useMemo
|
||||
} from "react"
|
||||
import React, { useContext, useState, createContext } from "react"
|
||||
import { DEFAULT_THEME, Theme, THEME_COOKIE_NAME } from "./theme"
|
||||
import { setCookie } from "cookies-next"
|
||||
|
||||
interface UseThemeProps {
|
||||
theme: Theme
|
||||
setTheme: (theme: Theme) => void
|
||||
}
|
||||
|
||||
const ThemeContext = createContext<UseThemeProps | null>(null)
|
||||
|
||||
export function useTheme(): {
|
||||
theme: Theme
|
||||
setTheme: (theme: Theme) => void
|
||||
} {
|
||||
return (
|
||||
useContext(ThemeContext) || {
|
||||
theme: DEFAULT_THEME,
|
||||
setTheme: () => {}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
interface Props extends PropsWithChildren<{}> {
|
||||
defaultTheme: Theme
|
||||
}
|
||||
|
||||
const ThemeClientContextProvider: FunctionComponent<Props> = ({
|
||||
defaultTheme,
|
||||
children
|
||||
}) => {
|
||||
const [theme, setThemeState] = useState<Theme>(defaultTheme)
|
||||
const setCookieAndDocument = useCallback(
|
||||
(theme: Theme) => {
|
||||
setThemeState(theme)
|
||||
setCookie(THEME_COOKIE_NAME, theme)
|
||||
document.documentElement.setAttribute("data-theme", theme)
|
||||
},
|
||||
[setThemeState]
|
||||
)
|
||||
|
||||
const setTheme = useCallback(
|
||||
(theme: Theme) => {
|
||||
setCookieAndDocument(theme)
|
||||
},
|
||||
[setCookieAndDocument]
|
||||
)
|
||||
|
||||
const value = useMemo(() => ({ theme, setTheme }), [theme, setTheme])
|
||||
|
||||
return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
|
||||
}
|
||||
|
||||
export default ThemeClientContextProvider
|
||||
@ -0,0 +1,26 @@
|
||||
import type { FunctionComponent, PropsWithChildren } from "react";
|
||||
import ThemeClientContextProvider from "./ThemeClientContextProvider";
|
||||
import ThemeServerContextProvider, {
|
||||
useServerTheme,
|
||||
} from "./ThemeServerContextProvider";
|
||||
|
||||
const ThemeProviderWrapper: FunctionComponent<PropsWithChildren<{}>> = ({
|
||||
children,
|
||||
}) => {
|
||||
const theme = useServerTheme();
|
||||
return (
|
||||
<ThemeClientContextProvider defaultTheme={theme}>
|
||||
{children}
|
||||
</ThemeClientContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
const ThemeProvider: FunctionComponent<PropsWithChildren<{}>> = ({ children }) => {
|
||||
return (
|
||||
<ThemeServerContextProvider>
|
||||
<ThemeProviderWrapper>{children}</ThemeProviderWrapper>
|
||||
</ThemeServerContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default ThemeProvider;
|
||||
@ -0,0 +1,24 @@
|
||||
import type { FunctionComponent, PropsWithChildren } from "react";
|
||||
// @ts-ignore -- createServerContext is not in @types/react atm
|
||||
import { useContext, createServerContext } from "react";
|
||||
import { cookies } from "next/headers";
|
||||
import { Theme, THEME_COOKIE_NAME } from "./theme";
|
||||
import { DEFAULT_THEME } from "./theme";
|
||||
|
||||
const ThemeContext = createServerContext<Theme | null>(null);
|
||||
|
||||
export function useServerTheme(): Theme {
|
||||
return useContext(ThemeContext);
|
||||
}
|
||||
|
||||
const ThemeServerContextProvider: FunctionComponent<PropsWithChildren<{}>> = ({
|
||||
children,
|
||||
}) => {
|
||||
const cookiesList = cookies();
|
||||
const theme = cookiesList.get(THEME_COOKIE_NAME)?.value ?? DEFAULT_THEME;
|
||||
return (
|
||||
<ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default ThemeServerContextProvider;
|
||||
@ -0,0 +1,5 @@
|
||||
export type Theme = "light" | "dark";
|
||||
|
||||
export const DEFAULT_THEME: Theme = "light";
|
||||
|
||||
export const THEME_COOKIE_NAME = "drift-theme";
|
||||
@ -1,31 +1,46 @@
|
||||
import "@styles/globals.css"
|
||||
import { ServerThemeProvider } from "next-themes"
|
||||
import { LayoutWrapper } from "./root-layout-wrapper"
|
||||
import styles from '@styles/Home.module.css';
|
||||
import { cookies } from "next/headers";
|
||||
import { getSession } from "@lib/server/session";
|
||||
import styles from "@styles/Home.module.css"
|
||||
import { getSession } from "@lib/server/session"
|
||||
import ThemeProvider from "@components/theme/ThemeProvider"
|
||||
import { THEME_COOKIE_NAME } from "@components/theme/theme"
|
||||
import { useServerTheme } from "@components/theme/ThemeServerContextProvider"
|
||||
|
||||
interface RootLayoutProps {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export default async function RootLayout({ children }: RootLayoutProps) {
|
||||
export default async function RootLayout({ children }: RootLayoutProps) {
|
||||
// TODO: this opts out of SSG
|
||||
const session = await getSession()
|
||||
return (
|
||||
<ServerThemeProvider
|
||||
disableTransitionOnChange
|
||||
attribute="data-theme"
|
||||
enableColorScheme
|
||||
>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
(function() {
|
||||
var theme = document.cookie
|
||||
.split('; ')
|
||||
.find(row => row.startsWith('${THEME_COOKIE_NAME}='))
|
||||
.split('=')[1];
|
||||
document.documentElement.setAttribute('data-theme', theme);
|
||||
console.log("theme on load", theme)
|
||||
})();
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
</head>
|
||||
<ThemeProvider>
|
||||
<body className={styles.main}>
|
||||
<LayoutWrapper signedIn={Boolean(session?.user)} isAdmin={session?.user.role === "admin"}>{children}</LayoutWrapper>
|
||||
<LayoutWrapper
|
||||
signedIn={Boolean(session?.user)}
|
||||
isAdmin={session?.user.role === "admin"}
|
||||
>
|
||||
{children}
|
||||
</LayoutWrapper>
|
||||
</body>
|
||||
</html>
|
||||
</ServerThemeProvider>
|
||||
</ThemeProvider>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,73 +1,24 @@
|
||||
"use client"
|
||||
|
||||
import Header from "@components/header"
|
||||
import { CssBaseline, GeistProvider, Page, Themes } from "@geist-ui/core/dist"
|
||||
import { ThemeProvider } from "next-themes"
|
||||
import Page from "@components/page"
|
||||
import * as RadixTooltip from "@radix-ui/react-tooltip"
|
||||
|
||||
export function LayoutWrapper({
|
||||
children,
|
||||
signedIn,
|
||||
isAdmin,
|
||||
isAdmin
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
signedIn?: boolean
|
||||
isAdmin?: boolean
|
||||
}) {
|
||||
const customTheme = Themes.createFromLight({
|
||||
type: "custom",
|
||||
palette: {
|
||||
background: "var(--bg)",
|
||||
foreground: "var(--fg)",
|
||||
accents_1: "var(--lightest-gray)",
|
||||
accents_2: "var(--lighter-gray)",
|
||||
accents_3: "var(--light-gray)",
|
||||
accents_4: "var(--gray)",
|
||||
accents_5: "var(--darker-gray)",
|
||||
accents_6: "var(--darker-gray)",
|
||||
accents_7: "var(--darkest-gray)",
|
||||
accents_8: "var(--darkest-gray)",
|
||||
border: "var(--light-gray)",
|
||||
warning: "var(--warning)"
|
||||
},
|
||||
expressiveness: {
|
||||
dropdownBoxShadow: "0 0 0 1px var(--lighter-gray)",
|
||||
shadowSmall: "0 0 0 1px var(--lighter-gray)",
|
||||
shadowLarge: "0 0 0 1px var(--lighter-gray)",
|
||||
shadowMedium: "0 0 0 1px var(--lighter-gray)"
|
||||
},
|
||||
layout: {
|
||||
gap: "var(--gap)",
|
||||
gapHalf: "var(--gap-half)",
|
||||
gapQuarter: "var(--gap-quarter)",
|
||||
gapNegative: "var(--gap-negative)",
|
||||
gapHalfNegative: "var(--gap-half-negative)",
|
||||
gapQuarterNegative: "var(--gap-quarter-negative)",
|
||||
radius: "var(--radius)"
|
||||
},
|
||||
font: {
|
||||
mono: "var(--font-mono)",
|
||||
sans: "var(--font-sans)"
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<RadixTooltip.Provider delayDuration={200}>
|
||||
<GeistProvider themes={[customTheme]} themeType={"custom"}>
|
||||
<ThemeProvider
|
||||
disableTransitionOnChange
|
||||
cookieName="drift-theme"
|
||||
attribute="data-theme"
|
||||
>
|
||||
<CssBaseline />
|
||||
<Page width={"100%"}>
|
||||
<Page.Header>
|
||||
<Header isAdmin={isAdmin} signedIn={signedIn} />
|
||||
</Page.Header>
|
||||
{children}
|
||||
</Page>
|
||||
</ThemeProvider>
|
||||
</GeistProvider>
|
||||
<Page>
|
||||
<Header isAdmin={isAdmin} signedIn={signedIn} />
|
||||
{children}
|
||||
</Page>
|
||||
</RadixTooltip.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue