Skip to content

Commit 5e89381

Browse files
committed
theme swither, new components, buttons, icons
1 parent cae5754 commit 5e89381

10 files changed

+606
-20
lines changed

package.json

+3
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
"preview": "vite preview"
1111
},
1212
"dependencies": {
13+
"@radix-ui/react-dialog": "^1.0.5",
1314
"@radix-ui/react-icons": "^1.3.0",
15+
"@radix-ui/react-slot": "^1.0.2",
1416
"class-variance-authority": "^0.7.0",
1517
"clsx": "^2.0.0",
18+
"lucide-react": "^0.290.0",
1619
"react": "^18.2.0",
1720
"react-dom": "^18.2.0",
1821
"react-router-dom": "^6.17.0",

public/darkbutton.png

16.7 KB
Loading

public/lightbutton.png

10.4 KB
Loading

src/App.tsx

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import { AppRoutes } from "./routes/AppRoutes";
2+
import { ThemeProvider } from "./providers/ThemeProvider";
3+
24
function App() {
3-
return <AppRoutes />;
5+
return (
6+
<ThemeProvider>
7+
<AppRoutes />
8+
</ThemeProvider>
9+
);
410
}
511
export default App;

src/components/Header.tsx

+80-18
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,92 @@
1+
import { HamburgerMenuIcon } from "@radix-ui/react-icons";
2+
import { ThemeSwitcher } from "./ThemeSwitcher";
13
import { MaxWidthComponent } from "./ui/MaxWidthComponent";
24
import { Link } from "react-router-dom";
5+
import { Button } from "./ui/button";
6+
import {
7+
Sheet,
8+
SheetContent,
9+
SheetHeader,
10+
SheetTrigger,
11+
SheetClose,
12+
} from "@/components/ui/sheet";
13+
14+
const routes = [
15+
{
16+
name: "Characters",
17+
path: "/characters",
18+
},
19+
{
20+
name: "Episodes",
21+
path: "/episodes",
22+
},
23+
{
24+
name: "Locations",
25+
path: "/locations",
26+
},
27+
];
328

429
export const Header = () => {
530
return (
631
<MaxWidthComponent
732
as="header"
8-
className="flex justify-between items-center">
9-
<div className=" flex-1 flex-grow flex items-start flex-col">
10-
<img
11-
src="/logo.png"
12-
alt="logo"
13-
className="max-w-[150px] sm:max-w-[200px]"
14-
/>
33+
className="flex justify-between items-center border-b-2">
34+
<div className=" flex items-start flex-col">
35+
<Link to="/characters">
36+
<img
37+
src="/logo.png"
38+
alt="logo"
39+
className="max-w-[150px] sm:max-w-[170px] md:max-w-[200px] cursor-pointer"
40+
/>
41+
</Link>
42+
</div>
43+
<div className="hidden flex-grow sm:flex gap-5 justify-center ">
44+
{routes.map((route) => (
45+
<Link
46+
key={route.name}
47+
to={route.path}
48+
className="font-bold
49+
hover:text-gray-500 transition-colors duration-300
50+
">
51+
{route.name}
52+
</Link>
53+
))}
54+
</div>
55+
<div className="hidden sm:flex justify-end items-center">
56+
<ThemeSwitcher />
1557
</div>
16-
<div className="hidden flex-1 sm:flex items-center gap-5 ">
17-
<div>
18-
<Link to="/">Characters</Link>
19-
</div>
20-
<div>
21-
<Link to="/episodes">Episodes</Link>
22-
</div>
23-
<div>
24-
<Link to="/locations">Locations</Link>
25-
</div>
58+
<div className="sm:hidden flex justify-end items-center">
59+
<Sheet>
60+
<SheetTrigger>
61+
<Button size={"icon"} variant={"ghost"}>
62+
<HamburgerMenuIcon className="w-6 h-6" />
63+
</Button>
64+
</SheetTrigger>
65+
<SheetContent side={"top"}>
66+
<SheetHeader>
67+
<img
68+
src="/logo.png"
69+
alt="logo"
70+
className="max-w-[150px] sm:max-w-[170px] md:max-w-[200px] cursor-pointer"
71+
/>
72+
</SheetHeader>
73+
<div className="flex flex-col gap-5 items-center mt-5 w-full">
74+
{routes.map((route) => (
75+
<SheetClose asChild>
76+
<Link
77+
key={route.name}
78+
to={route.path}
79+
className="font-bold
80+
hover:text-gray-500 transition-colors duration-300">
81+
{route.name}
82+
</Link>
83+
</SheetClose>
84+
))}
85+
<ThemeSwitcher />
86+
</div>
87+
</SheetContent>
88+
</Sheet>
2689
</div>
27-
<div className="hidden sm:flex flex-1 justify-end"></div>
2890
</MaxWidthComponent>
2991
);
3092
};

src/components/ThemeSwitcher.tsx

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { useTheme } from "@/providers/ThemeProvider";
2+
3+
export const ThemeSwitcher = () => {
4+
const { theme, setTheme } = useTheme();
5+
6+
const handleThemeSwitch = () => {
7+
if (theme === "light") {
8+
setTheme("dark");
9+
} else {
10+
setTheme("light");
11+
}
12+
};
13+
return (
14+
<div>
15+
<button
16+
onClick={handleThemeSwitch}
17+
className="hover:bg-accent py-1 px-5 rounded-lg box-border">
18+
<img
19+
className="max-w-[80px] md:max-w-[100px]"
20+
src={theme === "light" ? "/lightbutton.png" : "/darkbutton.png"}
21+
alt="Switch Theme"
22+
/>
23+
</button>
24+
</div>
25+
);
26+
};

src/components/ui/button.tsx

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import * as React from "react"
2+
import { Slot } from "@radix-ui/react-slot"
3+
import { cva, type VariantProps } from "class-variance-authority"
4+
5+
import { cn } from "@/lib/utils"
6+
7+
const buttonVariants = cva(
8+
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
9+
{
10+
variants: {
11+
variant: {
12+
default:
13+
"bg-primary text-primary-foreground shadow hover:bg-primary/90",
14+
destructive:
15+
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
16+
outline:
17+
"border border-input bg-transparent shadow-sm hover:bg-accent hover:text-accent-foreground",
18+
secondary:
19+
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
20+
ghost: "hover:bg-accent hover:text-accent-foreground",
21+
link: "text-primary underline-offset-4 hover:underline",
22+
},
23+
size: {
24+
default: "h-9 px-4 py-2",
25+
sm: "h-8 rounded-md px-3 text-xs",
26+
lg: "h-10 rounded-md px-8",
27+
icon: "h-9 w-9",
28+
},
29+
},
30+
defaultVariants: {
31+
variant: "default",
32+
size: "default",
33+
},
34+
}
35+
)
36+
37+
export interface ButtonProps
38+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
39+
VariantProps<typeof buttonVariants> {
40+
asChild?: boolean
41+
}
42+
43+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
44+
({ className, variant, size, asChild = false, ...props }, ref) => {
45+
const Comp = asChild ? Slot : "button"
46+
return (
47+
<Comp
48+
className={cn(buttonVariants({ variant, size, className }))}
49+
ref={ref}
50+
{...props}
51+
/>
52+
)
53+
}
54+
)
55+
Button.displayName = "Button"
56+
57+
export { Button, buttonVariants }

src/components/ui/sheet.tsx

+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import * as React from "react"
2+
import * as SheetPrimitive from "@radix-ui/react-dialog"
3+
import { Cross2Icon } from "@radix-ui/react-icons"
4+
import { cva, type VariantProps } from "class-variance-authority"
5+
6+
import { cn } from "@/lib/utils"
7+
8+
const Sheet = SheetPrimitive.Root
9+
10+
const SheetTrigger = SheetPrimitive.Trigger
11+
12+
const SheetClose = SheetPrimitive.Close
13+
14+
const SheetPortal = SheetPrimitive.Portal
15+
16+
const SheetOverlay = React.forwardRef<
17+
React.ElementRef<typeof SheetPrimitive.Overlay>,
18+
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
19+
>(({ className, ...props }, ref) => (
20+
<SheetPrimitive.Overlay
21+
className={cn(
22+
"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
23+
className
24+
)}
25+
{...props}
26+
ref={ref}
27+
/>
28+
))
29+
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
30+
31+
const sheetVariants = cva(
32+
"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
33+
{
34+
variants: {
35+
side: {
36+
top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
37+
bottom:
38+
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
39+
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
40+
right:
41+
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
42+
},
43+
},
44+
defaultVariants: {
45+
side: "right",
46+
},
47+
}
48+
)
49+
50+
interface SheetContentProps
51+
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
52+
VariantProps<typeof sheetVariants> {}
53+
54+
const SheetContent = React.forwardRef<
55+
React.ElementRef<typeof SheetPrimitive.Content>,
56+
SheetContentProps
57+
>(({ side = "right", className, children, ...props }, ref) => (
58+
<SheetPortal>
59+
<SheetOverlay />
60+
<SheetPrimitive.Content
61+
ref={ref}
62+
className={cn(sheetVariants({ side }), className)}
63+
{...props}
64+
>
65+
{children}
66+
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
67+
<Cross2Icon className="h-4 w-4" />
68+
<span className="sr-only">Close</span>
69+
</SheetPrimitive.Close>
70+
</SheetPrimitive.Content>
71+
</SheetPortal>
72+
))
73+
SheetContent.displayName = SheetPrimitive.Content.displayName
74+
75+
const SheetHeader = ({
76+
className,
77+
...props
78+
}: React.HTMLAttributes<HTMLDivElement>) => (
79+
<div
80+
className={cn(
81+
"flex flex-col space-y-2 text-center sm:text-left",
82+
className
83+
)}
84+
{...props}
85+
/>
86+
)
87+
SheetHeader.displayName = "SheetHeader"
88+
89+
const SheetFooter = ({
90+
className,
91+
...props
92+
}: React.HTMLAttributes<HTMLDivElement>) => (
93+
<div
94+
className={cn(
95+
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
96+
className
97+
)}
98+
{...props}
99+
/>
100+
)
101+
SheetFooter.displayName = "SheetFooter"
102+
103+
const SheetTitle = React.forwardRef<
104+
React.ElementRef<typeof SheetPrimitive.Title>,
105+
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
106+
>(({ className, ...props }, ref) => (
107+
<SheetPrimitive.Title
108+
ref={ref}
109+
className={cn("text-lg font-semibold text-foreground", className)}
110+
{...props}
111+
/>
112+
))
113+
SheetTitle.displayName = SheetPrimitive.Title.displayName
114+
115+
const SheetDescription = React.forwardRef<
116+
React.ElementRef<typeof SheetPrimitive.Description>,
117+
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
118+
>(({ className, ...props }, ref) => (
119+
<SheetPrimitive.Description
120+
ref={ref}
121+
className={cn("text-sm text-muted-foreground", className)}
122+
{...props}
123+
/>
124+
))
125+
SheetDescription.displayName = SheetPrimitive.Description.displayName
126+
127+
export {
128+
Sheet,
129+
SheetPortal,
130+
SheetOverlay,
131+
SheetTrigger,
132+
SheetClose,
133+
SheetContent,
134+
SheetHeader,
135+
SheetFooter,
136+
SheetTitle,
137+
SheetDescription,
138+
}

0 commit comments

Comments
 (0)