feat: reactive nav buttons
This commit is contained in:
@@ -31,6 +31,7 @@
|
|||||||
"react-beautiful-dnd": "^13.1.1",
|
"react-beautiful-dnd": "^13.1.1",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-icons": "^4.11.0",
|
"react-icons": "^4.11.0",
|
||||||
|
"react-lottie-player": "^1.5.5",
|
||||||
"superjson": "^1.13.1",
|
"superjson": "^1.13.1",
|
||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4"
|
||||||
},
|
},
|
||||||
|
|||||||
1
src/app/assets/boards.json
Normal file
1
src/app/assets/boards.json
Normal file
File diff suppressed because one or more lines are too long
@@ -1,33 +1,16 @@
|
|||||||
import Link from "next/link";
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
import { getServerAuthSession } from "~/server/auth";
|
import { getServerAuthSession } from "~/server/auth";
|
||||||
|
|
||||||
|
import boardsIcon from "~/app/assets/boards.json";
|
||||||
|
|
||||||
|
import ReactiveButton from "~/app/components/ReactiveButton";
|
||||||
|
|
||||||
const navigation = [
|
const navigation = [
|
||||||
{ name: "Boards", href: "/boards", icon: null, current: true },
|
{ name: "Boards", href: "/boards", icon: boardsIcon, current: true },
|
||||||
];
|
];
|
||||||
|
|
||||||
function classNames(...classes: string[]): string {
|
|
||||||
return classes.filter(Boolean).join(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
const NavButton: React.FC<{
|
|
||||||
href: string;
|
|
||||||
current: boolean;
|
|
||||||
name: string;
|
|
||||||
}> = ({ href, current, name }) => (
|
|
||||||
<Link
|
|
||||||
href={href}
|
|
||||||
className={classNames(
|
|
||||||
current ? "bg-dark-200 text-white" : "bg-dark-200 text-white",
|
|
||||||
"group flex items-center gap-x-3 rounded-md p-1.5 text-sm font-normal leading-6 text-dark-1000",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{name}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default async function Layout(props: { children: React.ReactNode }) {
|
export default async function Layout(props: { children: React.ReactNode }) {
|
||||||
const session = await getServerAuthSession();
|
const session = await getServerAuthSession();
|
||||||
|
|
||||||
@@ -49,10 +32,11 @@ export default async function Layout(props: { children: React.ReactNode }) {
|
|||||||
<ul role="list" className="-mx-2 my-6 space-y-1">
|
<ul role="list" className="-mx-2 my-6 space-y-1">
|
||||||
{navigation.map((item) => (
|
{navigation.map((item) => (
|
||||||
<li key={item.name}>
|
<li key={item.name}>
|
||||||
<NavButton
|
<ReactiveButton
|
||||||
href={item.href}
|
href={item.href}
|
||||||
current={item.current}
|
current={item.current}
|
||||||
name={item.name}
|
name={item.name}
|
||||||
|
json={item.icon}
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
|||||||
24
src/app/components/LottieIcon.tsx
Normal file
24
src/app/components/LottieIcon.tsx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import Lottie from "react-lottie-player";
|
||||||
|
|
||||||
|
type IconProps = {
|
||||||
|
isPlaying: boolean;
|
||||||
|
index: number;
|
||||||
|
json: object;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Icon: React.FC<IconProps> = ({ isPlaying, index, json }) => {
|
||||||
|
return (
|
||||||
|
<Lottie
|
||||||
|
key={index}
|
||||||
|
animationData={json}
|
||||||
|
play={isPlaying}
|
||||||
|
loop={false}
|
||||||
|
style={{ width: 20, height: 20, fill: "white" }}
|
||||||
|
rendererSettings={{ preserveAspectRatio: "xMidYMid slice" }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Icon;
|
||||||
41
src/app/components/ReactiveButton.tsx
Normal file
41
src/app/components/ReactiveButton.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
import LottieIcon from "~/app/components/LottieIcon";
|
||||||
|
|
||||||
|
function classNames(...classes: string[]): string {
|
||||||
|
return classes.filter(Boolean).join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
const Button: React.FC<{
|
||||||
|
href: string;
|
||||||
|
current: boolean;
|
||||||
|
name: string;
|
||||||
|
json: object;
|
||||||
|
}> = ({ href, current, name, json }) => {
|
||||||
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
|
const [index, setIndex] = useState(0);
|
||||||
|
|
||||||
|
const handleMouseEnter = () => {
|
||||||
|
setIsHovered(true);
|
||||||
|
setIndex((index) => index + 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
href={href}
|
||||||
|
onMouseEnter={handleMouseEnter}
|
||||||
|
className={classNames(
|
||||||
|
current ? "bg-dark-200 text-white" : "bg-dark-200 text-white",
|
||||||
|
"group flex items-center gap-x-3 rounded-md p-1.5 text-sm font-normal leading-6 text-dark-1000",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<LottieIcon index={index} json={json} isPlaying={isHovered} />
|
||||||
|
{name}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Button;
|
||||||
Reference in New Issue
Block a user