This commit is contained in:
2026-02-07 17:13:17 +00:00
parent b280beed7d
commit f85eb7628d
2 changed files with 136 additions and 11 deletions

View File

@@ -10,6 +10,7 @@ import { Link, Route, Routes, useNavigate, useParams } from "react-router-dom";
import { AskAI } from "@/components/ask-ai";
import { ProjectListItem } from "@/components/ProjectListItem";
import { TimeSince } from "@/components/time-since";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { type ProjectEntry, projectList, projects } from "@/projects";
import { ThemeToggle } from "./components/theme-toggle";
@@ -180,17 +181,39 @@ function Home() {
Age: <TimeSince date={new Date(2004, 10, 4, 11, 47, 0)} />
</div>
</div>
<div className="w-full max-w-5xl grid grid-cols-1 md:grid-cols-2 gap-4">
{visibleProjects.map((project, index) => (
<ProjectListItem
key={project.metadata.slug}
metadata={project.metadata}
isDevMode={isDevMode}
isActive={activeIndex !== null && index === activeIndex}
enableHover={hasPointerInteraction}
/>
))}
</div>
<Tabs defaultValue="work" className="w-full max-w-5xl gap-0">
<TabsContent value="work" className="-mb-[1.5px] p-2 border">
<div className="grid grid-cols-1 gap-2 md:grid-cols-2">
{visibleProjects.map((project, index) => (
<ProjectListItem
key={project.metadata.slug}
metadata={project.metadata}
isDevMode={isDevMode}
isActive={activeIndex !== null && index === activeIndex}
enableHover={hasPointerInteraction}
/>
))}
</div>
</TabsContent>
<TabsContent value="travel" className="-mb-[1.5px] p-2 border">
test{" "}
</TabsContent>
<TabsList variant="line" className="h-auto w-full gap-0 p-0">
<TabsTrigger
value="work"
className="border-border -mr-[1px] after:hidden data-[state=active]:text-accent"
>
Work
</TabsTrigger>
<TabsTrigger
value="travel"
className="border-border after:hidden data-[state=active]:text-accent"
>
Travel
</TabsTrigger>
</TabsList>
<TabsContent value="travel" className="pt-2" />
</Tabs>
<div className="w-full max-w-5xl grid grid-cols-1 md:grid-cols-[1fr_auto_1fr] items-center gap-3 md:gap-4">
<div className="flex items-center gap-6">
<AskAI name="me" inline />

102
src/components/ui/tabs.tsx Normal file
View File

@@ -0,0 +1,102 @@
import { cva, type VariantProps } from "class-variance-authority";
import { Tabs as TabsPrimitive } from "radix-ui";
import type * as React from "react";
import { cn } from "@/lib/utils";
function Tabs({
className,
orientation = "horizontal",
...props
}: React.ComponentProps<typeof TabsPrimitive.Root>) {
return (
<TabsPrimitive.Root
data-slot="tabs"
data-orientation={orientation}
orientation={orientation}
className={cn(
"group/tabs flex gap-2 data-[orientation=horizontal]:flex-col",
className,
)}
{...props}
/>
);
}
const tabsListVariants = cva(
cn(
"group-data-[orientation=horizontal]/tabs:h-9 group/tabs-list text-muted-foreground inline-flex",
"w-fit items-center justify-center group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col",
),
{
variants: {
variant: {
default: "bg-muted",
line: "gap-1 bg-transparent",
},
},
defaultVariants: {
variant: "default",
},
},
);
function TabsList({
className,
variant = "default",
...props
}: React.ComponentProps<typeof TabsPrimitive.List> &
VariantProps<typeof tabsListVariants>) {
return (
<TabsPrimitive.List
data-slot="tabs-list"
data-variant={variant}
className={cn(tabsListVariants({ variant }), className)}
{...props}
/>
);
}
function TabsTrigger({
className,
...props
}: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
return (
<TabsPrimitive.Trigger
data-slot="tabs-trigger"
className={cn(
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring",
"text-foreground/60 dark:text-muted-foreground data-[state=active]:text-accent",
"relative inline-flex h-[calc(100%-1px)]",
"flex-1 items-center justify-center border border-transparent",
"px-2 py-1 text-sm font-medium whitespace-nowrap group-data-[orientation=vertical]/tabs:w-full",
"group-data-[orientation=vertical]/tabs:justify-start focus-visible:ring-[3px]",
"focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50",
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
"data-[state=active]:bg-background dark:data-[state=active]:text-accent",
"dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30",
"data-[state=active]:text-accent after:bg-foreground after:absolute after:opacity-0",
"group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px]",
"group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0",
"group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 cursor-pointer",
className,
)}
{...props}
/>
);
}
function TabsContent({
className,
...props
}: React.ComponentProps<typeof TabsPrimitive.Content>) {
return (
<TabsPrimitive.Content
data-slot="tabs-content"
className={cn("flex-1 outline-none", className)}
{...props}
/>
);
}
export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants };