From e6dfb56adb21c41c2bdcc63b9eab3b5c30ae5881 Mon Sep 17 00:00:00 2001 From: liyunze <50455574+liyunze-coding@users.noreply.github.com> Date: Sun, 10 Aug 2025 12:21:29 +1000 Subject: [PATCH] added better mobile experience --- bun.lock | 3 + package.json | 1 + .../images/profile/contact_photo_small.webp | Bin 0 -> 10752 bytes public/images/{ => profile}/pfp_new.webp | Bin src/components/contact/ContactComponent.astro | 12 +- src/components/global/NavBar.astro | 13 +- src/components/home/Greeting.svelte | 100 +++---- src/components/home/Intro.astro | 2 +- src/components/skills/MoreSkills.astro | 4 +- src/components/ui/DesktopNav.tsx | 221 ++++++++++++++++ src/components/ui/HoverNavigation.tsx | 136 ---------- src/components/ui/MobileNav.tsx | 0 src/components/ui/NavigationBar.tsx | 250 ++++++++++++++++++ src/layouts/PageLayout.astro | 22 ++ src/navStore.js | 4 + src/pages/experience.astro | 6 +- src/pages/index.astro | 6 +- 17 files changed, 573 insertions(+), 207 deletions(-) create mode 100644 public/images/profile/contact_photo_small.webp rename public/images/{ => profile}/pfp_new.webp (100%) create mode 100644 src/components/ui/DesktopNav.tsx delete mode 100644 src/components/ui/HoverNavigation.tsx create mode 100644 src/components/ui/MobileNav.tsx create mode 100644 src/components/ui/NavigationBar.tsx create mode 100644 src/layouts/PageLayout.astro create mode 100644 src/navStore.js diff --git a/bun.lock b/bun.lock index 8e3e4b3..2ac9715 100644 --- a/bun.lock +++ b/bun.lock @@ -13,6 +13,7 @@ "astro": "^5.10.0", "caniuse-lite": "^1.0.30001698", "clsx": "^2.1.1", + "hamburger-react": "^2.5.2", "moment": "^2.30.1", "moment-timezone": "^0.6.0", "motion": "^12.23.12", @@ -522,6 +523,8 @@ "h3": ["h3@1.15.4", "", { "dependencies": { "cookie-es": "^1.2.2", "crossws": "^0.3.5", "defu": "^6.1.4", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.2", "radix3": "^1.1.2", "ufo": "^1.6.1", "uncrypto": "^0.1.3" } }, "sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ=="], + "hamburger-react": ["hamburger-react@2.5.2", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19" } }, "sha512-xsv/I7wQX1RDaulDnvMVCXMMRXnkwnUnkE8seD5eNVZJ4nIrjtK5LLA20l5x/pJ+mPWOzlFxW32VJ86Gbo2Njw=="], + "hast-util-from-html": ["hast-util-from-html@2.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "devlop": "^1.1.0", "hast-util-from-parse5": "^8.0.0", "parse5": "^7.0.0", "vfile": "^6.0.0", "vfile-message": "^4.0.0" } }, "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw=="], "hast-util-from-parse5": ["hast-util-from-parse5@8.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "hastscript": "^9.0.0", "property-information": "^7.0.0", "vfile": "^6.0.0", "vfile-location": "^5.0.0", "web-namespaces": "^2.0.0" } }, "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg=="], diff --git a/package.json b/package.json index c3c4345..4f36a09 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "astro": "^5.10.0", "caniuse-lite": "^1.0.30001698", "clsx": "^2.1.1", + "hamburger-react": "^2.5.2", "moment": "^2.30.1", "moment-timezone": "^0.6.0", "motion": "^12.23.12", diff --git a/public/images/profile/contact_photo_small.webp b/public/images/profile/contact_photo_small.webp new file mode 100644 index 0000000000000000000000000000000000000000..13bf3e64c518e2505740eb74ecad40669f7ed8f8 GIT binary patch literal 10752 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 BP8 -
+
Ryan

- Ryan - -

+ diff --git a/src/components/home/Greeting.svelte b/src/components/home/Greeting.svelte index 6109140..579632c 100644 --- a/src/components/home/Greeting.svelte +++ b/src/components/home/Greeting.svelte @@ -1,68 +1,68 @@ - Good {greetingState}|| diff --git a/src/components/home/Intro.astro b/src/components/home/Intro.astro index a5d2a82..5799f53 100644 --- a/src/components/home/Intro.astro +++ b/src/components/home/Intro.astro @@ -11,7 +11,7 @@ import Greeting from "./Greeting.svelte";
Ryan diff --git a/src/components/ui/DesktopNav.tsx b/src/components/ui/DesktopNav.tsx new file mode 100644 index 0000000..7fa3a5e --- /dev/null +++ b/src/components/ui/DesktopNav.tsx @@ -0,0 +1,221 @@ +// retractable navigation +// do not retract if it's on contact page + +// mobile: display hamburger + available for work + +import { cn } from "@/lib/utils"; +import { + AnimatePresence, + motion, + useScroll, + useMotionValueEvent, +} from "motion/react"; +import { Cross as HamburgerCross } from "hamburger-react"; +import { useEffect, useState } from "react"; +import pfp from "@/components/images/contact_photo_small.webp"; + +function getWindowDimensions() { + const { innerWidth: width, innerHeight: height } = window; + return { + width, + height, + }; +} + +function useWindowDimensions() { + const hasWindow = typeof window !== "undefined"; + + function getWindowDimensions() { + const width = hasWindow ? window.innerWidth : 0; + const height = hasWindow ? window.innerHeight : 0; + return { + width, + height, + }; + } + + const [windowDimensions, setWindowDimensions] = useState( + getWindowDimensions(), + ); + + useEffect(() => { + if (hasWindow) { + function handleResize() { + setWindowDimensions(getWindowDimensions()); + } + + window.addEventListener("resize", handleResize); + return () => window.removeEventListener("resize", handleResize); + } + }, [hasWindow]); + + return windowDimensions; +} + +export const HoverNavigation = ({ + items, + className, +}: { + items: { + label: string; + link: string; + }[]; + className?: string; +}) => { + const [hoveredIndex, setHoveredIndex] = useState(null); + const { scrollYProgress } = useScroll(); + const [visible, setVisible] = useState(true); + const [navVisible, setNavVisible] = useState(false); + const [availableVisible, setAvailableVisible] = useState(false); + const [onContactPage, setOnContactPage] = useState(false); + const [hamburgerOpen, setHamburgerOpen] = useState(false); + const { width } = useWindowDimensions(); + + // if on contact page, do not do width 0% + useEffect(() => { + setOnContactPage(window.location.href.endsWith("/contact")); + }, []); + + useEffect(() => { + updateNavBarState(width); + }, [width]); + + const updateNavBarState = ( + width: number, + scrollProgress: number = 0, + scrollUp: boolean = false, + ) => { + if (width && width < 1024) { + setNavVisible(false); + setAvailableVisible(true); + } else if (scrollProgress < 0.05) { + setNavVisible(true); + setAvailableVisible(false); + } else if (!onContactPage) { + setNavVisible(scrollUp); + setAvailableVisible(!scrollUp); + } else { + setNavVisible(true); + setAvailableVisible(false); + } + }; + + useMotionValueEvent(scrollYProgress, "change", (current) => { + // Check if current is not undefined and is a number + if (typeof current === "number") { + let direction = current! - scrollYProgress.getPrevious()!; + let scrollUp = direction <= 0; + let scrollProgress = scrollYProgress.get(); + + updateNavBarState(width, scrollProgress, scrollUp); + } + }); + + return ( + <> + Ryan + + + Available for work + +
+
+
+
+
+ + {items.map((item, idx) => ( + setHoveredIndex(idx)} + onMouseLeave={() => setHoveredIndex(null)} + > + + {hoveredIndex === idx && ( + + )} + + {item.label} + + ))} + + +
+ +
+ + ); +}; + +export const NavBarLink = ({ + className, + children, +}: { + className?: string; + children: React.ReactNode; +}) => { + return ( +
+ {children} +
+ ); +}; diff --git a/src/components/ui/HoverNavigation.tsx b/src/components/ui/HoverNavigation.tsx deleted file mode 100644 index 70b5819..0000000 --- a/src/components/ui/HoverNavigation.tsx +++ /dev/null @@ -1,136 +0,0 @@ -import { cn } from "@/lib/utils"; -import { - AnimatePresence, - motion, - useScroll, - useMotionValueEvent, -} from "motion/react"; - -import { useState } from "react"; - -export const HoverNavigation = ({ - items, - className, -}: { - items: { - label: string; - link: string; - }[]; - className?: string; -}) => { - let [hoveredIndex, setHoveredIndex] = useState(null); - const { scrollYProgress } = useScroll(); - const [visible, setVisible] = useState(true); - - useMotionValueEvent(scrollYProgress, "change", (current) => { - // Check if current is not undefined and is a number - if (typeof current === "number") { - let direction = current! - scrollYProgress.getPrevious()!; - - if (scrollYProgress.get() < 0.05 && current !== 1) { - setVisible(true); - } else { - if (direction > 0) { - setVisible(false); - } else { - setVisible(true); - } - } - } - }); - - return ( - <> - - - Available for work - -
-
-
-
-
- - {items.map((item, idx) => ( - setHoveredIndex(idx)} - onMouseLeave={() => setHoveredIndex(null)} - > - - {hoveredIndex === idx && ( - - )} - - {item.label} - - ))} - - - ); -}; - -export const NavBarLink = ({ - className, - children, -}: { - className?: string; - children: React.ReactNode; -}) => { - return ( -
- {children} -
- ); -}; diff --git a/src/components/ui/MobileNav.tsx b/src/components/ui/MobileNav.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/ui/NavigationBar.tsx b/src/components/ui/NavigationBar.tsx new file mode 100644 index 0000000..36ca55c --- /dev/null +++ b/src/components/ui/NavigationBar.tsx @@ -0,0 +1,250 @@ +import { cn } from "@/lib/utils"; +import { + AnimatePresence, + motion, + useScroll, + useMotionValueEvent, +} from "motion/react"; +import { Cross as HamburgerCross } from "hamburger-react"; +import { useEffect, useState } from "react"; + +function useWindowDimensions() { + const hasWindow = typeof window !== "undefined"; + + function getWindowDimensions() { + const width = hasWindow ? window.innerWidth : 0; + const height = hasWindow ? window.innerHeight : 0; + return { + width, + height, + }; + } + + const [windowDimensions, setWindowDimensions] = useState( + getWindowDimensions(), + ); + + useEffect(() => { + if (hasWindow) { + function handleResize() { + setWindowDimensions(getWindowDimensions()); + } + + window.addEventListener("resize", handleResize); + return () => window.removeEventListener("resize", handleResize); + } + }, [hasWindow]); + + return windowDimensions; +} + +export const HoverNavigation = ({ + items, + className, +}: { + items: { + label: string; + link: string; + }[]; + className?: string; +}) => { + const [hoveredIndex, setHoveredIndex] = useState(null); + const { scrollYProgress } = useScroll(); + const [navVisible, setNavVisible] = useState(true); + const [availableVisible, setAvailableVisible] = useState(false); + + const [onContactPage, setOnContactPage] = useState(false); + const [hamburgerOpen, setHamburgerOpen] = useState(false); + const { width } = useWindowDimensions(); + + // if on contact page, do not do width 0% + useEffect(() => { + setOnContactPage(window.location.href.endsWith("/contact")); + }, []); + + useEffect(() => { + updateNavBarState(width); + }, [width]); + + const updateNavBarState = ( + width: number, + scrollProgress: number = 0, + scrollUp: boolean = false, + ) => { + if (width && width < 1024) { + setNavVisible(false); + setAvailableVisible(true); + } else if (scrollProgress < 0.05) { + setNavVisible(true); + setAvailableVisible(false); + } else if (!onContactPage) { + setNavVisible(scrollUp); + setAvailableVisible(!scrollUp); + } else { + setNavVisible(true); + setAvailableVisible(false); + } + }; + + useMotionValueEvent(scrollYProgress, "change", (current) => { + // Check if current is not undefined and is a number + if (typeof current === "number") { + let direction = current! - scrollYProgress.getPrevious()!; + let scrollUp = direction <= 0; + let scrollProgress = scrollYProgress.get(); + + updateNavBarState(width, scrollProgress, scrollUp); + } + }); + + return ( + + ); +}; + +export const NavBarLink = ({ + className, + children, +}: { + className?: string; + children: React.ReactNode; +}) => { + return ( +
+ {children} +
+ ); +}; diff --git a/src/layouts/PageLayout.astro b/src/layouts/PageLayout.astro new file mode 100644 index 0000000..986e90d --- /dev/null +++ b/src/layouts/PageLayout.astro @@ -0,0 +1,22 @@ +--- +interface Props { + title: string; + description: string; + thumbnail?: string; + metaURL?: string; +} + +const { title, description, thumbnail, metaURL } = Astro.props; +import Layout from "./Layout.astro"; +import NavBar from "@/components/global/NavBar.astro"; +--- + + + + + diff --git a/src/navStore.js b/src/navStore.js new file mode 100644 index 0000000..55a9c86 --- /dev/null +++ b/src/navStore.js @@ -0,0 +1,4 @@ +import { atom } from "nanostores"; + +export const isNavVisible = atom(true); +export const isAvVisible = atom(false); diff --git a/src/pages/experience.astro b/src/pages/experience.astro index b9d881c..e4ffb11 100644 --- a/src/pages/experience.astro +++ b/src/pages/experience.astro @@ -4,16 +4,16 @@ import Layout from "../layouts/Layout.astro"; import ExperienceComponent from "../components/experience/ExperienceComponent.astro"; import MoreExperience from "../components/experience/MoreExperience.astro"; import Footer from "../components/global/FooterComponent.astro"; +import PageLayout from "@/layouts/PageLayout.astro"; --- - -
- + diff --git a/src/pages/index.astro b/src/pages/index.astro index 98f6a43..6d4f1b4 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -8,13 +8,13 @@ import Experience from "../components/experience/ExperienceComponent.astro"; import Skills from "../components/skills/SkillsComponent.astro"; import Contact from "../components/contact/ContactComponent.astro"; import Footer from "../components/global/FooterComponent.astro"; +import PageLayout from "@/layouts/PageLayout.astro"; --- - -
@@ -36,7 +36,7 @@ import Footer from "../components/global/FooterComponent.astro";