All gucci!
All checks were successful
Docker Deploy / build-and-push (push) Successful in 5m18s

This commit is contained in:
Atridad Lahiji 2025-04-24 20:27:25 -06:00
parent f859a12d2c
commit 9dc9225591
Signed by: atridad
SSH key fingerprint: SHA256:LGomp8Opq0jz+7kbwNcdfTcuaLRb5Nh0k5AchDDb438
5 changed files with 137 additions and 34 deletions

View file

@ -11,6 +11,8 @@ import * as $post_slug_ from "./routes/post/[slug].tsx";
import * as $posts from "./routes/posts.tsx";
import * as $projects from "./routes/projects.tsx";
import * as $Example from "./islands/Example.tsx";
import * as $NavigationBar from "./islands/NavigationBar.tsx";
import * as $ScrollUpButton from "./islands/ScrollUpButton.tsx";
import type { Manifest } from "$fresh/server.ts";
const manifest = {
@ -26,6 +28,8 @@ const manifest = {
},
islands: {
"./islands/Example.tsx": $Example,
"./islands/NavigationBar.tsx": $NavigationBar,
"./islands/ScrollUpButton.tsx": $ScrollUpButton,
},
baseUrl: import.meta.url,
} satisfies Manifest;

83
islands/NavigationBar.tsx Normal file
View file

@ -0,0 +1,83 @@
// islands/NavigationBar.tsx
import { useComputed, useSignal } from "@preact/signals";
import { useEffect } from "preact/hooks";
import { LuCodeXml, LuHouse, LuNotebookPen } from "@preact-icons/lu";
interface NavigationBarProps {
currentPath: string;
}
export default function NavigationBar({ currentPath }: NavigationBarProps) {
const isScrolling = useSignal(false);
const prevScrollPos = useSignal(0);
const isVisible = useComputed(() => {
if (prevScrollPos.value < 50) return true;
const currentPos = typeof window !== "undefined" ? globalThis.scrollY : 0;
return prevScrollPos.value > currentPos;
});
const isPostsPath = (path: string) => {
return path.startsWith("/posts") || path.startsWith("/post/");
};
useEffect(() => {
let scrollTimer: number | undefined;
const handleScroll = () => {
isScrolling.value = true;
prevScrollPos.value = globalThis.scrollY;
if (scrollTimer) clearTimeout(scrollTimer);
scrollTimer = setTimeout(() => {
isScrolling.value = false;
}, 200);
};
globalThis.addEventListener("scroll", handleScroll);
return () => {
globalThis.removeEventListener("scroll", handleScroll);
if (scrollTimer) clearTimeout(scrollTimer);
};
}, []);
return (
<div
class={`fixed bottom-4 left-1/2 transform -translate-x-1/2 z-20 transition-all duration-300 ${
isScrolling.value ? "opacity-30" : "opacity-100"
} ${isVisible.value ? "translate-y-0" : "translate-y-20"}`}
>
<ul class="menu menu-horizontal bg-base-200 rounded-box p-2 shadow-lg gap-2">
<li>
<a href="/" class={currentPath === "/" ? "menu-active" : ""}>
<div class="tooltip" data-tip="Home">
<LuHouse class="text-xl" />
</div>
</a>
</li>
<li>
<a
href="/posts"
class={isPostsPath(currentPath) ? "menu-active" : ""}
>
<div class="tooltip" data-tip="Posts">
<LuNotebookPen class="text-xl" />
</div>
</a>
</li>
<li>
<a
href="/projects"
class={currentPath.startsWith("/projects") ? "menu-active" : ""}
>
<div class="tooltip" data-tip="Projects">
<LuCodeXml class="text-xl" />
</div>
</a>
</li>
</ul>
</div>
);
}

View file

@ -0,0 +1,45 @@
import { useSignal } from "@preact/signals";
import { useEffect } from "preact/hooks";
import { LuArrowUp } from "@preact-icons/lu";
export default function ScrollUpButton() {
const isVisible = useSignal(false);
useEffect(() => {
const checkScroll = () => {
isVisible.value = globalThis.scrollY > 300;
};
checkScroll();
globalThis.addEventListener("scroll", checkScroll);
return () => {
globalThis.removeEventListener("scroll", checkScroll);
};
}, []);
const scrollToTop = () => {
globalThis.scrollTo({
top: 0,
behavior: "smooth",
});
};
return (
<button
type="button"
onClick={scrollToTop}
class={`fixed bottom-20 right-4 z-20 bg-secondary hover:bg-primary
p-3 rounded-full shadow-lg transition-all duration-300
${
isVisible.value
? "opacity-70 translate-y-0"
: "opacity-0 translate-y-10 pointer-events-none"
}`}
aria-label="Scroll to top"
>
<LuArrowUp class="text-lg" />
</button>
);
}

View file

@ -7,7 +7,7 @@ export default function App({ Component }: PageProps) {
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Atridad Lahiji</title>
<link rel="stylesheet" href="/styles.css" />
<link rel="icon" type="image/x-icon" href="favicon.ico"></link>
<link rel="icon" type="image/x-icon" href="/favicon.ico"></link>
</head>
<body>
<Component />

View file

@ -1,7 +1,7 @@
// routes/_layout.tsx
import { PageProps } from "$fresh/server.ts";
import { Head } from "$fresh/runtime.ts";
import { LuCodeXml, LuHouse, LuNotebookPen } from "@preact-icons/lu";
import NavigationBar from "../islands/NavigationBar.tsx";
import ScrollUpButton from "../islands/ScrollUpButton.tsx";
export default function Layout({ Component, url }: PageProps) {
const currentPath = url.pathname;
@ -19,37 +19,8 @@ export default function Layout({ Component, url }: PageProps) {
<Component />
</main>
<div class="fixed bottom-4 left-1/2 transform -translate-x-1/2 z-20">
<ul class="menu menu-horizontal bg-base-200 rounded-box p-2 shadow-lg gap-2">
<li>
<a href="/" class={currentPath === "/" ? "menu-active" : ""}>
<div class="tooltip" data-tip="Home">
<LuHouse class="text-xl" />
</div>
</a>
</li>
<li>
<a
href="/posts"
class={currentPath.startsWith("/posts") ? "menu-active" : ""}
>
<div class="tooltip" data-tip="Posts">
<LuNotebookPen class="text-xl" />
</div>
</a>
</li>
<li>
<a
href="/projects"
class={currentPath.startsWith("/projects") ? "menu-active" : ""}
>
<div class="tooltip" data-tip="Projects">
<LuCodeXml class="text-xl" />
</div>
</a>
</li>
</ul>
</div>
<NavigationBar currentPath={currentPath} />
<ScrollUpButton />
</body>
</>
);