mirror of
https://github.com/hex248/ob248.com.git
synced 2026-02-07 18:23:04 +00:00
tabs
This commit is contained in:
45
src/App.tsx
45
src/App.tsx
@@ -10,6 +10,7 @@ import { Link, Route, Routes, useNavigate, useParams } from "react-router-dom";
|
|||||||
import { AskAI } from "@/components/ask-ai";
|
import { AskAI } from "@/components/ask-ai";
|
||||||
import { ProjectListItem } from "@/components/ProjectListItem";
|
import { ProjectListItem } from "@/components/ProjectListItem";
|
||||||
import { TimeSince } from "@/components/time-since";
|
import { TimeSince } from "@/components/time-since";
|
||||||
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { type ProjectEntry, projectList, projects } from "@/projects";
|
import { type ProjectEntry, projectList, projects } from "@/projects";
|
||||||
import { ThemeToggle } from "./components/theme-toggle";
|
import { ThemeToggle } from "./components/theme-toggle";
|
||||||
|
|
||||||
@@ -180,17 +181,39 @@ function Home() {
|
|||||||
Age: <TimeSince date={new Date(2004, 10, 4, 11, 47, 0)} />
|
Age: <TimeSince date={new Date(2004, 10, 4, 11, 47, 0)} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full max-w-5xl grid grid-cols-1 md:grid-cols-2 gap-4">
|
<Tabs defaultValue="work" className="w-full max-w-5xl gap-0">
|
||||||
{visibleProjects.map((project, index) => (
|
<TabsContent value="work" className="-mb-[1.5px] p-2 border">
|
||||||
<ProjectListItem
|
<div className="grid grid-cols-1 gap-2 md:grid-cols-2">
|
||||||
key={project.metadata.slug}
|
{visibleProjects.map((project, index) => (
|
||||||
metadata={project.metadata}
|
<ProjectListItem
|
||||||
isDevMode={isDevMode}
|
key={project.metadata.slug}
|
||||||
isActive={activeIndex !== null && index === activeIndex}
|
metadata={project.metadata}
|
||||||
enableHover={hasPointerInteraction}
|
isDevMode={isDevMode}
|
||||||
/>
|
isActive={activeIndex !== null && index === activeIndex}
|
||||||
))}
|
enableHover={hasPointerInteraction}
|
||||||
</div>
|
/>
|
||||||
|
))}
|
||||||
|
</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="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">
|
<div className="flex items-center gap-6">
|
||||||
<AskAI name="me" inline />
|
<AskAI name="me" inline />
|
||||||
|
|||||||
102
src/components/ui/tabs.tsx
Normal file
102
src/components/ui/tabs.tsx
Normal 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 };
|
||||||
Reference in New Issue
Block a user