WIP on theme selector of about-me, docs: updated README.mds

api-as-package
pegasust 2022-12-01 07:53:17 +00:00
parent 70f7b14a67
commit 064a9355e6
2 changed files with 62 additions and 53 deletions

View File

@ -5,7 +5,6 @@ Scaffolded by `pnpm create turbo@latest`
This project manages my homelab infrastructure under a monorepo to keep things This project manages my homelab infrastructure under a monorepo to keep things
centralized and easily sharable across different modules centralized and easily sharable across different modules
# Turbo-repo details # Turbo-repo details
## What's inside? ## What's inside?
@ -14,11 +13,11 @@ This turborepo uses [pnpm](https://pnpm.io) as a package manager. It includes th
### Apps and Packages ### Apps and Packages
- `docs`: a [Next.js](https://nextjs.org/) app - `apps/docs`: a [Next.js](https://nextjs.org/) app
- `web`: another [Next.js](https://nextjs.org/) app - `apps/web`: another [Next.js](https://nextjs.org/) app
- `ui`: a stub React component library shared by both `web` and `docs` applications - `packages/ui`: a stub React component library shared by both `web` and `docs` applications
- `eslint-config-custom`: `eslint` configurations (includes `eslint-config-next` and `eslint-config-prettier`) - `packages/eslint-config-custom`: `eslint` configurations (includes `eslint-config-next` and `eslint-config-prettier`)
- `tsconfig`: `tsconfig.json`s used throughout the monorepo - `packages/tsconfig`: `tsconfig.json`s used throughout the monorepo
Each package/app is 100% [TypeScript](https://www.typescriptlang.org/). Each package/app is 100% [TypeScript](https://www.typescriptlang.org/).

View File

@ -20,6 +20,41 @@ import SantaMonicaHigh from "@public/assets/santa-monica-high.jpg";
import assert from "assert"; import assert from "assert";
import { Section, SectionHeader, UL } from "@components/index"; import { Section, SectionHeader, UL } from "@components/index";
type Theme = "dark" | "light";
const ThemeContext = React.createContext<{
theme: Theme,
systemTheme: Theme,
setThemePreference(theme: Theme): void,
} | undefined>(undefined);
const ThemeProvider: React.FC<{ children: React.ReactNode, prefer?: "light" | "dark" }> = ({ children, prefer }) => {
const pref = prefer ?? "light";
const [systemTheme, setSystemTheme] = useState<Theme>(pref);
const [themePref, setThemePref] = useState<Theme | undefined>(undefined);
// add a listener to system preference on which theme
// TODO: is there another way that remove event listener faster?
useEffect(() => {
console.log("useEffect called");
type EventType = { matches: boolean };
const onThemeChange = (event: EventType) => {
const newColorScheme = event.matches ? "dark" : "light";
setSystemTheme(newColorScheme);
};
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', onThemeChange);
return () => {
console.log("useEffect unmount");
window.matchMedia('(prefers-color-scheme: dark)').removeEventListener("change", onThemeChange)
}
}, []);
return <ThemeContext.Provider value={{
theme: themePref ?? systemTheme,
systemTheme: systemTheme,
setThemePreference: setThemePref,
}}>{children}</ThemeContext.Provider>
}
const useTheme = () => useContext(ThemeContext);
const circle = cva("absolute rounded-full border -z-10", { const circle = cva("absolute rounded-full border -z-10", {
variants: { variants: {
size: { size: {
@ -237,7 +272,9 @@ const ImageDisplay: React.FC<{ imageDisplayHook: ImageDisplayHook }> = ({ imageD
// setImageRepo, // setImageRepo,
imgIndex, imgIndex,
image image
} }) => <div className="relative bg-gray-100/30 rounded-md overflow-hidden"> } }) => {
const {theme} = useTheme()!;
return <div className="relative h-fit w-fit bg-gray-100/30 rounded-md">
<button className="group left-0 absolute w-5/12 h-full flex flex-row justify-start items-center" onClick={prev}> <button className="group left-0 absolute w-5/12 h-full flex flex-row justify-start items-center" onClick={prev}>
<span className={imageButton()}>{"<"}</span> <span className={imageButton()}>{"<"}</span>
</button> </button>
@ -248,13 +285,15 @@ const ImageDisplay: React.FC<{ imageDisplayHook: ImageDisplayHook }> = ({ imageD
<div className="flex flex-row justify-between items-center gap-2"> <div className="flex flex-row justify-between items-center gap-2">
{Array.from(Array(imageRepo.length).keys()).map(i => <div className={imageIndex({ {Array.from(Array(imageRepo.length).keys()).map(i => <div className={imageIndex({
active: (i == imgIndex ? "isActive" : "isInactive"), active: (i == imgIndex ? "isActive" : "isInactive"),
theme: "light" theme: theme
})} })}
key={`img-index-${i}`} />)} key={`img-index-${i}`} />)}
</div> </div>
</div> </div>
{image && <Image placeholder="blur" src={image.src} alt={image.alt} className="w-96 h-96 object-contain rounded-md overflow-hidden transition-all" />} {image && <Image placeholder="blur" src={image.src} alt={image.alt}
className="w-96 h-96 object-contain rounded-md" />}
</div> </div>
}
const MyLink: React.FC<{ href: string, children?: ReactNode }> = ({ href, children }) => <Link const MyLink: React.FC<{ href: string, children?: ReactNode }> = ({ href, children }) => <Link
className="text-amber-400/70 visited:text-purple-400/70 hover:text-amber-400 visited:hover:text-purple-400" href={href}> className="text-amber-400/70 visited:text-purple-400/70 hover:text-amber-400 visited:hover:text-purple-400" href={href}>
@ -266,7 +305,7 @@ const About = () => {
return <Article> return <Article>
<SectionHeader>About</SectionHeader> <SectionHeader>About</SectionHeader>
<div className="flex flex-row gap-4"> <div className="flex flex-row w-11/12 justify-evenly max-w-screen-lg gap-8">
<ImageDisplay imageDisplayHook={imgDisplay} /> <ImageDisplay imageDisplayHook={imgDisplay} />
<div className="text-left"> <div className="text-left">
<Section> <Section>
@ -328,7 +367,7 @@ const NavbarItem: React.FC<{
</Link> </Link>
const iconCva = cva("transition-colors", { const iconCva = cva("transition-colors group-hover:animate-bounce", {
variants: { variants: {
theme: { theme: {
light: "group-hover:fill-gray-700 fill-gray-400" light: "group-hover:fill-gray-700 fill-gray-400"
@ -339,51 +378,22 @@ const iconCva = cva("transition-colors", {
} }
}); });
const Navbar = () => <div className="flex justify-center items-center w-full z-50 sticky top-0 left-0 backdrop-blur-md h-8"> const Navbar = () => {
const _theme = useTheme();
if (!_theme) { throw new Error("no theme provider"); }
const { theme, setThemePreference } = _theme;
return <div className="flex justify-center items-center w-full z-50 sticky top-0 left-0 backdrop-blur-md h-8">
<div className="w-11/12 max-w-screen-lg flex items-center gap-4"> <div className="w-11/12 max-w-screen-lg flex items-center gap-4">
<div className="flex flex-row justify-center items-center gap-1"> <div className="flex flex-row justify-center items-center gap-1">
<NavbarItem text="GitHub" link="https://github.com/pegasust"><FaGithub className={iconCva()} /></NavbarItem> <NavbarItem text="GitHub" link="https://github.com/pegasust"><FaGithub className={iconCva()} /></NavbarItem>
<NavbarItem text="LinkedIn" link="https://linkedin.com/in/hung-tran-1963bb205/"><FaLinkedin className={iconCva()} /></NavbarItem> <NavbarItem text="LinkedIn" link="https://linkedin.com/in/hung-tran-1963bb205/"><FaLinkedin className={iconCva()} /></NavbarItem>
</div> </div>
<Link href="#brief"> <Link href="#brief">
<span className="transition-colors hover:text-amber-400 text-amber-400/50 animate-pulse">Hung Tran</span> <span className="transition-colors hover:text-amber-400 text-amber-400/50 hover:animate-pulse">Hung Tran</span>
</Link> </Link>
</div> </div>
</div> </div>
type Theme = "dark" | "light";
const ThemeContext = React.createContext<{
theme: Theme,
systemTheme: Theme,
setThemePreference(theme: Theme): void,
} | undefined>(undefined);
const ThemeProvider: React.FC<{ children: React.ReactNode, prefer?: "light" | "dark" }> = ({ children, prefer }) => {
const pref = prefer ?? "light";
const [systemTheme, setSystemTheme] = useState<Theme>(pref);
const [themePref, setThemePref] = useState<Theme | undefined>(undefined);
// add a listener to system preference on which theme
// TODO: is there another way that remove event listener faster?
useEffect(() => {
console.log("useEffect called");
type EventType = { matches: boolean };
const onThemeChange = (event: EventType) => {
const newColorScheme = event.matches ? "dark" : "light";
setSystemTheme(newColorScheme);
};
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', onThemeChange);
return () => {
console.log("useEffect unmount");
window.matchMedia('(prefers-color-scheme: dark)').removeEventListener("change", onThemeChange)
}
}, []);
return <ThemeContext.Provider value={{
theme: themePref ?? systemTheme,
systemTheme: systemTheme,
setThemePreference: setThemePref,
}}>{children}</ThemeContext.Provider>
} }
const useTheme = () => useContext(ThemeContext);
const Home: NextPage = () => { const Home: NextPage = () => {
return ( return (