mirror of
https://github.com/jakejarvis/jarv.is.git
synced 2025-04-26 06:45:23 -04:00
consolidate theme context/provider types
This commit is contained in:
parent
a8c1a3ba3c
commit
d09cf7ab26
@ -1,8 +1,6 @@
|
||||
import { useRouter } from "next/router";
|
||||
import NextLink from "next/link";
|
||||
import urlJoin from "url-join";
|
||||
import { styled } from "../../lib/styles/stitches.config";
|
||||
import { baseUrl } from "../../lib/config";
|
||||
import type { ComponentProps } from "react";
|
||||
|
||||
const Title = styled("h1", {
|
||||
@ -25,11 +23,10 @@ export type PageTitleProps = ComponentProps<typeof Title>;
|
||||
|
||||
const PageTitle = ({ children, ...rest }: PageTitleProps) => {
|
||||
const router = useRouter();
|
||||
const canonical = urlJoin(baseUrl, router.pathname, "/");
|
||||
|
||||
return (
|
||||
<Title {...rest}>
|
||||
<NextLink href={canonical} passHref={true}>
|
||||
<NextLink href={router.pathname} passHref={true}>
|
||||
<Link>{children}</Link>
|
||||
</NextLink>
|
||||
</Title>
|
||||
|
@ -2,53 +2,57 @@
|
||||
// https://github.com/pacocoursey/next-themes/blob/b5c2bad50de2d61ad7b52a9c5cdc801a78507d7a/index.tsx
|
||||
|
||||
import { createContext, useCallback, useEffect, useState, useRef } from "react";
|
||||
import { darkModeQuery, colorSchemes, themeStorageKey } from "../lib/styles/helpers/themes";
|
||||
import type { PropsWithChildren } from "react";
|
||||
import type { UseThemeProps } from "../hooks/use-theme";
|
||||
import { darkModeQuery, themeStorageKey } from "../lib/styles/helpers/themes";
|
||||
import type { Context, PropsWithChildren } from "react";
|
||||
|
||||
export interface ThemeProviderProps {
|
||||
/** Mapping of theme name to HTML attribute value. Object where key is the theme name and value is the attribute value */
|
||||
classNames: { [themeName: string]: string };
|
||||
/** List of all available theme names */
|
||||
export const ThemeContext: Context<{
|
||||
/** Update the theme */
|
||||
setTheme?: (theme: string) => void;
|
||||
/** List of all available theme names (probably "light", "dark", and "system") */
|
||||
themes?: string[];
|
||||
/** Whether to indicate to browsers which color scheme is used (dark or light) for built-in UI like inputs and buttons */
|
||||
enableColorScheme?: boolean;
|
||||
}
|
||||
|
||||
// get the user's saved theme preference
|
||||
const getUserTheme = (key: string, fallback?: string) => {
|
||||
if (typeof window === "undefined") return undefined;
|
||||
|
||||
let theme: string;
|
||||
try {
|
||||
theme = localStorage.getItem(key) || undefined;
|
||||
} catch (e) {} // eslint-disable-line no-empty
|
||||
|
||||
return theme || fallback;
|
||||
};
|
||||
|
||||
// get the user's preferred theme via their OS/browser settings
|
||||
const getSystemTheme = (e?: MediaQueryList) => {
|
||||
if (!e) {
|
||||
e = window.matchMedia(darkModeQuery);
|
||||
}
|
||||
|
||||
return e.matches ? "dark" : "light";
|
||||
};
|
||||
|
||||
export const ThemeContext = createContext<UseThemeProps>({
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars
|
||||
setTheme: (_) => {},
|
||||
themes: [],
|
||||
});
|
||||
/** Active theme name ("system" if unset) */
|
||||
theme?: string;
|
||||
/** If the active theme is "system", this returns whether the system preference resolved to "dark" or "light". Otherwise, identical to `theme` */
|
||||
resolvedTheme?: string;
|
||||
}> = createContext({});
|
||||
|
||||
// provider used once in _app.tsx to wrap entire app
|
||||
export const ThemeProvider = ({
|
||||
classNames,
|
||||
themes = [...colorSchemes],
|
||||
enableColorScheme = true,
|
||||
enableColorScheme,
|
||||
children,
|
||||
}: PropsWithChildren<ThemeProviderProps>) => {
|
||||
}: PropsWithChildren<{
|
||||
/** Mapping of theme name to HTML attribute value. Object where key is the theme name and value is the attribute value */
|
||||
classNames: {
|
||||
[themeName: string]: string;
|
||||
};
|
||||
/** Whether to indicate to browsers which color scheme is used (dark or light) for built-in UI like inputs and buttons */
|
||||
enableColorScheme?: boolean;
|
||||
}>) => {
|
||||
// possible themes are derived from given classNames (probably "light" and "dark")
|
||||
const themes = Object.keys(classNames);
|
||||
|
||||
// get the user's saved theme preference
|
||||
const getUserTheme = (key: string, fallback?: string) => {
|
||||
if (typeof window === "undefined") return undefined;
|
||||
|
||||
let theme: string;
|
||||
try {
|
||||
theme = localStorage.getItem(key) || undefined;
|
||||
} catch (e) {} // eslint-disable-line no-empty
|
||||
|
||||
return theme || fallback;
|
||||
};
|
||||
|
||||
// get the user's preferred theme via their OS/browser settings
|
||||
const getSystemTheme = (e?: MediaQueryList) => {
|
||||
if (!e) {
|
||||
e = window.matchMedia(darkModeQuery);
|
||||
}
|
||||
|
||||
return e.matches ? "dark" : "light";
|
||||
};
|
||||
|
||||
const [currentTheme, setCurrentTheme] = useState(() => getUserTheme(themeStorageKey, "system"));
|
||||
const [resolvedTheme, setResolvedTheme] = useState(() => getUserTheme(themeStorageKey));
|
||||
|
||||
@ -131,7 +135,7 @@ export const ThemeProvider = ({
|
||||
if (!enableColorScheme) return;
|
||||
|
||||
const colorScheme =
|
||||
currentTheme && colorSchemes.includes(currentTheme) // light or dark
|
||||
currentTheme && themes.includes(currentTheme) // light or dark
|
||||
? currentTheme
|
||||
: // preference is unset, use the OS/browser setting
|
||||
currentTheme === "system"
|
||||
@ -146,13 +150,18 @@ export const ThemeProvider = ({
|
||||
return (
|
||||
<ThemeContext.Provider
|
||||
value={{
|
||||
setTheme,
|
||||
themes: [...themes, "system"],
|
||||
theme: currentTheme,
|
||||
resolvedTheme: currentTheme === "system" ? resolvedTheme : currentTheme,
|
||||
setTheme,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
// debugging help pls
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
ThemeContext.displayName = "ThemeContext";
|
||||
}
|
||||
|
@ -1,16 +1,5 @@
|
||||
import { useContext } from "react";
|
||||
import { ThemeContext } from "../contexts/ThemeContext";
|
||||
|
||||
export interface UseThemeProps {
|
||||
/** List of all available theme names */
|
||||
themes: string[];
|
||||
/** Active theme name */
|
||||
theme?: string;
|
||||
/** If the active theme is "system", this returns whether the system preference resolved to "dark" or "light". Otherwise, identical to `theme` */
|
||||
resolvedTheme?: string;
|
||||
/** Update the theme */
|
||||
setTheme: (theme: string) => void;
|
||||
}
|
||||
|
||||
// useTheme() function to get current theme state from pages/components/etc.
|
||||
export const useTheme = (): UseThemeProps => useContext(ThemeContext);
|
||||
// convenience hook to get access to ThemeContext's state/functions from pages/components/etc.
|
||||
export const useTheme = () => useContext(ThemeContext);
|
||||
|
@ -1,8 +1,8 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
import matter from "gray-matter";
|
||||
import { serialize } from "next-mdx-remote/serialize";
|
||||
import matter from "gray-matter";
|
||||
import urlJoin from "url-join";
|
||||
import { minify } from "terser";
|
||||
import { compiler } from "markdown-to-jsx";
|
||||
|
@ -12,9 +12,6 @@ export const themeColors = {
|
||||
dark: darkTheme.colors.backgroundOuter?.value,
|
||||
};
|
||||
|
||||
// default to a simple light or dark binary option
|
||||
export const colorSchemes = ["light", "dark"];
|
||||
|
||||
// https://web.dev/prefers-color-scheme/#the-prefers-color-scheme-media-query
|
||||
export const darkModeQuery = "(prefers-color-scheme: dark)";
|
||||
|
||||
|
@ -57,7 +57,7 @@ const App = ({ Component, pageProps }: AppProps) => {
|
||||
const getLayout = Component.getLayout || ((page) => <Layout>{page}</Layout>);
|
||||
|
||||
return (
|
||||
<ThemeProvider classNames={themeClassNames}>
|
||||
<ThemeProvider classNames={themeClassNames} enableColorScheme={true}>
|
||||
{/* all SEO config is in ../lib/config/seo.ts except for canonical URLs, which require access to next router */}
|
||||
<DefaultSeo
|
||||
{...defaultSeo}
|
||||
|
Loading…
x
Reference in New Issue
Block a user