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:
@@ -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)}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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,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 (
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user