1
mirror of https://github.com/jakejarvis/jarv.is.git synced 2026-06-05 20:15:31 -04:00

do react types more better too 🧠

This commit is contained in:
2025-05-19 17:58:03 -04:00
parent 51708c9b17
commit f4c69292df
41 changed files with 141 additions and 194 deletions
+1 -2
View File
@@ -4,9 +4,8 @@ import Link from "@/components/link";
import { NextjsIcon } from "@/components/icons";
import { cn } from "@/lib/utils";
import siteConfig from "@/lib/config/site";
import type { ComponentPropsWithoutRef } from "react";
const Footer = ({ className, ...rest }: ComponentPropsWithoutRef<"footer">) => {
const Footer = ({ className, ...rest }: React.ComponentProps<"footer">) => {
return (
<footer
className={cn("text-foreground/85 text-[0.8rem] leading-loose md:flex md:flex-row md:justify-between", className)}
+1 -2
View File
@@ -3,11 +3,10 @@ import Link from "@/components/link";
import Menu from "@/components/layout/menu";
import { cn } from "@/lib/utils";
import siteConfig from "@/lib/config/site";
import type { ComponentPropsWithoutRef } from "react";
import avatarImg from "@/app/avatar.jpg";
const Header = ({ className, ...rest }: ComponentPropsWithoutRef<"header">) => {
const Header = ({ className, ...rest }: React.ComponentProps<"header">) => {
return (
<header className={cn("flex items-center justify-between", className)} {...rest}>
<Link
+11 -12
View File
@@ -1,7 +1,6 @@
import { isValidElement } from "react";
import Link from "@/components/link";
import { cn } from "@/lib/utils";
import type { ComponentPropsWithoutRef } from "react";
import type { MenuItemConfig } from "@/lib/config/menu";
const MenuItem = ({
text,
@@ -10,17 +9,17 @@ const MenuItem = ({
current,
className,
...rest
}: Omit<ComponentPropsWithoutRef<typeof Link>, "href"> &
MenuItemConfig & {
current?: boolean;
}) => {
const Icon = icon;
}: Omit<React.ComponentProps<typeof Link>, "href"> & {
text?: string;
href?: `/${string}`;
icon?: React.ReactNode;
current?: boolean;
}) => {
const item = (
<>
{Icon && <Icon className="stroke-foreground/85 block h-7 w-7 md:h-5 md:w-5" />}
<div className="[&_svg]:stroke-foreground/85 inline-flex items-center [&_svg]:size-7 [&_svg]:md:size-5">
{isValidElement(icon) && icon}
{text && <span className="ml-3 text-sm leading-none font-medium tracking-wide max-md:sr-only">{text}</span>}
</>
</div>
);
// allow both navigational links and/or other interactive react components (e.g. the theme toggle)
@@ -32,7 +31,7 @@ const MenuItem = ({
aria-label={text}
data-current={current || undefined}
className={cn(
"text-foreground/85 hover:border-ring data-current:border-primary/40! mb-[-3px] inline-flex items-center p-2.5 hover:border-b-[3px] hover:no-underline data-current:border-b-[3px]",
"text-foreground/85 hover:border-ring data-current:border-primary/40! inline-flex items-center p-2.5 hover:border-b-[3px] hover:no-underline data-current:border-b-[3px]",
className
)}
{...rest}
+34 -19
View File
@@ -4,37 +4,52 @@ import { useSelectedLayoutSegment } from "next/navigation";
import MenuItem from "@/components/layout/menu-item";
import ThemeToggle from "@/components/layout/theme-toggle";
import { cn } from "@/lib/utils";
import { menuItems } from "@/lib/config/menu";
import type { ComponentPropsWithoutRef } from "react";
import { HomeIcon, PencilLineIcon, CodeXmlIcon, MailIcon } from "lucide-react";
const Menu = ({ className, ...rest }: ComponentPropsWithoutRef<"ul">) => {
const menuItems: React.ComponentProps<typeof MenuItem>[] = [
{
text: "Home",
href: "/",
icon: <HomeIcon />,
},
{
text: "Notes",
href: "/notes",
icon: <PencilLineIcon />,
},
{
text: "Projects",
href: "/projects",
icon: <CodeXmlIcon />,
},
{
text: "Contact",
href: "/contact",
icon: <MailIcon />,
},
{
icon: <ThemeToggle />,
},
];
const Menu = ({ className, ...rest }: React.ComponentProps<"div">) => {
const segment = useSelectedLayoutSegment() || "";
return (
<ul
className={cn(
"flex max-w-2/3 flex-row justify-between md:max-w-none md:justify-end md:gap-4 max-sm:[&>li]:first-of-type:hidden",
className
)}
<div
className={cn("flex max-w-2/3 flex-row justify-between md:max-w-none md:justify-end md:gap-4", className)}
{...rest}
>
{menuItems.map((item) => {
{menuItems.map((item, index) => {
const isCurrent = item.href?.split("/")[1] === segment;
return (
<li className="inline-block" key={item.href}>
<div className="mt-[3px] inline-block last:-mr-2.5 max-sm:first:hidden" key={index}>
<MenuItem {...item} current={isCurrent} />
</li>
</div>
);
})}
<li className="-mr-2.5 inline-block">
<MenuItem
// @ts-ignore
icon={ThemeToggle}
/>
</li>
</ul>
</div>
);
};
+1 -2
View File
@@ -1,13 +1,12 @@
import Link from "@/components/link";
import { cn } from "@/lib/utils";
import type { ComponentPropsWithoutRef } from "react";
const PageTitle = ({
canonical,
className,
children,
...rest
}: ComponentPropsWithoutRef<"h1"> & {
}: React.ComponentProps<"h1"> & {
canonical: string;
}) => {
return (
+1 -2
View File
@@ -2,7 +2,6 @@
import { createContext, useEffect, useState } from "react";
import { useLocalStorage, useMedia } from "react-use";
import type { PropsWithChildren } from "react";
export const ThemeContext = createContext<{
/**
@@ -19,7 +18,7 @@ export const ThemeContext = createContext<{
});
// provider used once in _app.tsx to wrap entire app
export const ThemeProvider = ({ children }: PropsWithChildren) => {
export const ThemeProvider = ({ children }: React.PropsWithChildren) => {
// keep track of if/when the user has set their theme *on this site*
const [preferredTheme, setPreferredTheme] = useLocalStorage<string>("theme", undefined, { raw: true });
// keep track of changes to the user's OS/browser dark mode setting
+9 -6
View File
@@ -3,20 +3,23 @@
import { useContext } from "react";
import { MoonIcon, SunIcon } from "lucide-react";
import { ThemeContext } from "@/components/layout/theme-context";
import type { ComponentPropsWithoutRef } from "react";
import type { LucideIcon } from "lucide-react";
import { cn } from "@/lib/utils";
const ThemeToggle = ({ ...rest }: ComponentPropsWithoutRef<LucideIcon>) => {
const ThemeToggle = ({ className, ...rest }: React.ComponentProps<"button">) => {
const { theme, setTheme } = useContext(ThemeContext);
return (
<button
onClick={() => setTheme(theme === "light" ? "dark" : "light")}
aria-label="Toggle theme"
className="hover:*:stroke-warning block cursor-pointer bg-transparent p-2.5 not-dark:[&_.lucide-moon]:hidden dark:[&_.lucide-sun]:hidden"
className={cn(
"hover:*:stroke-warning block cursor-pointer bg-transparent p-2.5 not-dark:[&_.lucide-moon]:hidden dark:[&_.lucide-sun]:hidden",
className
)}
{...rest}
>
<SunIcon {...rest} />
<MoonIcon {...rest} />
<SunIcon />
<MoonIcon />
<span className="sr-only">Toggle theme</span>
</button>
);