oooeee
This commit is contained in:
117
src/app.tsx
117
src/app.tsx
@@ -1,6 +1,6 @@
|
|||||||
import { Router } from "@solidjs/router";
|
import { Router } from "@solidjs/router";
|
||||||
import { FileRoutes } from "@solidjs/start/router";
|
import { FileRoutes } from "@solidjs/start/router";
|
||||||
import { createEffect, ErrorBoundary, Suspense } from "solid-js";
|
import { createEffect, ErrorBoundary, Suspense, onMount, onCleanup } from "solid-js";
|
||||||
import "./app.css";
|
import "./app.css";
|
||||||
import { LeftBar, RightBar } from "./components/Bars";
|
import { LeftBar, RightBar } from "./components/Bars";
|
||||||
import { TerminalSplash } from "./components/TerminalSplash";
|
import { TerminalSplash } from "./components/TerminalSplash";
|
||||||
@@ -10,14 +10,37 @@ import ErrorBoundaryFallback from "./components/ErrorBoundaryFallback";
|
|||||||
import { BarsProvider, useBars } from "./context/bars";
|
import { BarsProvider, useBars } from "./context/bars";
|
||||||
|
|
||||||
function AppLayout(props: { children: any }) {
|
function AppLayout(props: { children: any }) {
|
||||||
const { leftBarSize, rightBarSize, setCenterWidth, centerWidth } = useBars();
|
const {
|
||||||
|
leftBarSize,
|
||||||
|
rightBarSize,
|
||||||
|
setCenterWidth,
|
||||||
|
centerWidth,
|
||||||
|
leftBarVisible,
|
||||||
|
rightBarVisible,
|
||||||
|
toggleLeftBar,
|
||||||
|
toggleRightBar,
|
||||||
|
setLeftBarVisible,
|
||||||
|
setRightBarVisible
|
||||||
|
} = useBars();
|
||||||
|
|
||||||
|
let lastScrollY = 0;
|
||||||
|
const SCROLL_THRESHOLD = 100;
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
const handleResize = () => {
|
const handleResize = () => {
|
||||||
|
const isMobile = window.innerWidth < 768; // md breakpoint
|
||||||
|
|
||||||
|
// Show bars when switching to desktop
|
||||||
|
if (!isMobile) {
|
||||||
|
setLeftBarVisible(true);
|
||||||
|
setRightBarVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
const newWidth = window.innerWidth - leftBarSize() - rightBarSize();
|
const newWidth = window.innerWidth - leftBarSize() - rightBarSize();
|
||||||
setCenterWidth(newWidth);
|
setCenterWidth(newWidth);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Call immediately and whenever dependencies change
|
||||||
handleResize();
|
handleResize();
|
||||||
|
|
||||||
window.addEventListener("resize", handleResize);
|
window.addEventListener("resize", handleResize);
|
||||||
@@ -25,8 +48,98 @@ function AppLayout(props: { children: any }) {
|
|||||||
return () => window.removeEventListener("resize", handleResize);
|
return () => window.removeEventListener("resize", handleResize);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Recalculate when bar sizes change (visibility or actual resize)
|
||||||
|
createEffect(() => {
|
||||||
|
const newWidth = window.innerWidth - leftBarSize() - rightBarSize();
|
||||||
|
setCenterWidth(newWidth);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Auto-hide on scroll (mobile only)
|
||||||
|
onMount(() => {
|
||||||
|
const handleScroll = () => {
|
||||||
|
const currentScrollY = window.scrollY;
|
||||||
|
const isMobile = window.innerWidth < 768; // md breakpoint
|
||||||
|
|
||||||
|
if (isMobile && currentScrollY > SCROLL_THRESHOLD) {
|
||||||
|
// Scrolling down past threshold - hide left bar on mobile
|
||||||
|
if (currentScrollY > lastScrollY) {
|
||||||
|
setLeftBarVisible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastScrollY = currentScrollY;
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("scroll", handleScroll, { passive: true });
|
||||||
|
|
||||||
|
onCleanup(() => {
|
||||||
|
window.removeEventListener("scroll", handleScroll);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Swipe gestures to reveal bars
|
||||||
|
onMount(() => {
|
||||||
|
let touchStartX = 0;
|
||||||
|
let touchStartY = 0;
|
||||||
|
const EDGE_THRESHOLD = 50; // pixels from edge to trigger
|
||||||
|
const SWIPE_THRESHOLD = 100; // minimum swipe distance
|
||||||
|
|
||||||
|
const handleTouchStart = (e: TouchEvent) => {
|
||||||
|
touchStartX = e.touches[0].clientX;
|
||||||
|
touchStartY = e.touches[0].clientY;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTouchEnd = (e: TouchEvent) => {
|
||||||
|
const touchEndX = e.changedTouches[0].clientX;
|
||||||
|
const touchEndY = e.changedTouches[0].clientY;
|
||||||
|
const deltaX = touchEndX - touchStartX;
|
||||||
|
const deltaY = touchEndY - touchStartY;
|
||||||
|
const isMobile = window.innerWidth < 768; // md breakpoint
|
||||||
|
|
||||||
|
// Only trigger if horizontal swipe is dominant
|
||||||
|
if (Math.abs(deltaX) > Math.abs(deltaY)) {
|
||||||
|
// Mobile: Only left bar
|
||||||
|
if (isMobile) {
|
||||||
|
// Swipe right from left edge - reveal left bar
|
||||||
|
if (touchStartX < EDGE_THRESHOLD && deltaX > SWIPE_THRESHOLD) {
|
||||||
|
setLeftBarVisible(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Desktop: Both bars
|
||||||
|
// Swipe right from left edge - reveal left bar
|
||||||
|
if (touchStartX < EDGE_THRESHOLD && deltaX > SWIPE_THRESHOLD) {
|
||||||
|
setLeftBarVisible(true);
|
||||||
|
}
|
||||||
|
// Swipe left from right edge - reveal right bar
|
||||||
|
else if (touchStartX > window.innerWidth - EDGE_THRESHOLD && deltaX < -SWIPE_THRESHOLD) {
|
||||||
|
setRightBarVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("touchstart", handleTouchStart, { passive: true });
|
||||||
|
window.addEventListener("touchend", handleTouchEnd, { passive: true });
|
||||||
|
|
||||||
|
onCleanup(() => {
|
||||||
|
window.removeEventListener("touchstart", handleTouchStart);
|
||||||
|
window.removeEventListener("touchend", handleTouchEnd);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="flex max-w-screen flex-row">
|
<div class="flex max-w-screen flex-row">
|
||||||
|
{/* Backdrop overlay - visible on mobile when sidebar is open */}
|
||||||
|
<div
|
||||||
|
class="fixed inset-0 bg-black transition-opacity duration-500 ease-out md:hidden z-40"
|
||||||
|
classList={{
|
||||||
|
"opacity-50 pointer-events-auto": leftBarVisible(),
|
||||||
|
"opacity-0 pointer-events-none": !leftBarVisible()
|
||||||
|
}}
|
||||||
|
onClick={() => setLeftBarVisible(false)}
|
||||||
|
aria-label="Close sidebar"
|
||||||
|
/>
|
||||||
|
|
||||||
<LeftBar />
|
<LeftBar />
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -1,30 +1,52 @@
|
|||||||
import { Typewriter } from "./Typewriter";
|
import { Typewriter } from "./Typewriter";
|
||||||
import { useBars } from "~/context/bars";
|
import { useBars } from "~/context/bars";
|
||||||
import { createEffect, onMount } from "solid-js";
|
import { onMount, createEffect } from "solid-js";
|
||||||
|
|
||||||
export function LeftBar() {
|
export function LeftBar() {
|
||||||
const { setLeftBarSize } = useBars();
|
const { setLeftBarSize, leftBarVisible } = useBars();
|
||||||
let ref: HTMLDivElement | undefined;
|
let ref: HTMLDivElement | undefined;
|
||||||
|
let actualWidth = 0;
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if (ref) {
|
if (ref) {
|
||||||
const updateSize = () => {
|
const updateSize = () => {
|
||||||
setLeftBarSize(ref?.offsetWidth || 0);
|
actualWidth = ref?.offsetWidth || 0;
|
||||||
|
setLeftBarSize(leftBarVisible() ? actualWidth : 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
updateSize();
|
updateSize();
|
||||||
|
|
||||||
const resizeObserver = new ResizeObserver(updateSize);
|
const resizeObserver = new ResizeObserver((entries) => {
|
||||||
|
// Use requestAnimationFrame to avoid ResizeObserver loop error
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
actualWidth = ref?.offsetWidth || 0;
|
||||||
|
setLeftBarSize(leftBarVisible() ? actualWidth : 0);
|
||||||
|
});
|
||||||
|
});
|
||||||
resizeObserver.observe(ref);
|
resizeObserver.observe(ref);
|
||||||
|
|
||||||
return () => resizeObserver.disconnect();
|
return () => resizeObserver.disconnect();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Update size when visibility changes
|
||||||
|
createEffect(() => {
|
||||||
|
setLeftBarSize(leftBarVisible() ? actualWidth : 0);
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav
|
<nav
|
||||||
ref={ref}
|
ref={ref}
|
||||||
class="border-r-overlay2 fixed h-full min-h-screen w-fit max-w-[25%] border-r-2"
|
class="border-r-overlay2 fixed h-full min-h-screen w-fit max-w-[25%] border-r-2 transition-transform duration-500 ease-out z-50"
|
||||||
|
classList={{
|
||||||
|
"-translate-x-full": !leftBarVisible(),
|
||||||
|
"translate-x-0": leftBarVisible()
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
"transition-timing-function": leftBarVisible()
|
||||||
|
? "cubic-bezier(0.34, 1.56, 0.64, 1)" // Bounce out when revealing
|
||||||
|
: "cubic-bezier(0.4, 0, 0.2, 1)" // Smooth when hiding
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Typewriter speed={10} keepAlive={10000} class="z-50 pr-8 pl-4">
|
<Typewriter speed={10} keepAlive={10000} class="z-50 pr-8 pl-4">
|
||||||
<h3 class="hover:text-subtext0 w-fit text-center text-3xl underline transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-105">
|
<h3 class="hover:text-subtext0 w-fit text-center text-3xl underline transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-105">
|
||||||
@@ -37,6 +59,7 @@ export function LeftBar() {
|
|||||||
{/*TODO:Grab and render 5 most recent blog posts here */}
|
{/*TODO:Grab and render 5 most recent blog posts here */}
|
||||||
<li></li>
|
<li></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<div class="flex flex-col gap-4">
|
||||||
<ul class="gap-4">
|
<ul class="gap-4">
|
||||||
<li class="hover:text-subtext0 w-fit transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-110 hover:font-bold">
|
<li class="hover:text-subtext0 w-fit transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-110 hover:font-bold">
|
||||||
<a href="/">Home</a>
|
<a href="/">Home</a>
|
||||||
@@ -48,6 +71,19 @@ export function LeftBar() {
|
|||||||
<a href="#services">Services</a>
|
<a href="#services">Services</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
{/* Right bar navigation merged for mobile */}
|
||||||
|
<ul class="gap-4 md:hidden border-t border-overlay0 pt-4">
|
||||||
|
<li class="hover:text-subtext0 w-fit transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-110 hover:font-bold">
|
||||||
|
<a href="#home">Home</a>
|
||||||
|
</li>
|
||||||
|
<li class="hover:text-subtext0 w-fit transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-110 hover:font-bold">
|
||||||
|
<a href="#about">About</a>
|
||||||
|
</li>
|
||||||
|
<li class="hover:text-subtext0 w-fit transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-110 hover:font-bold">
|
||||||
|
<a href="#services">Services</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Typewriter>
|
</Typewriter>
|
||||||
</nav>
|
</nav>
|
||||||
@@ -55,28 +91,51 @@ export function LeftBar() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function RightBar() {
|
export function RightBar() {
|
||||||
const { setRightBarSize } = useBars();
|
const { setRightBarSize, rightBarVisible } = useBars();
|
||||||
let ref: HTMLDivElement | undefined;
|
let ref: HTMLDivElement | undefined;
|
||||||
|
let actualWidth = 0;
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if (ref) {
|
if (ref) {
|
||||||
const updateSize = () => {
|
const updateSize = () => {
|
||||||
setRightBarSize(ref?.offsetWidth || 0);
|
actualWidth = ref?.offsetWidth || 0;
|
||||||
|
setRightBarSize(rightBarVisible() ? actualWidth : 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
updateSize();
|
updateSize();
|
||||||
|
|
||||||
const resizeObserver = new ResizeObserver(updateSize);
|
const resizeObserver = new ResizeObserver((entries) => {
|
||||||
|
// Use requestAnimationFrame to avoid ResizeObserver loop error
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
actualWidth = ref?.offsetWidth || 0;
|
||||||
|
setRightBarSize(rightBarVisible() ? actualWidth : 0);
|
||||||
|
});
|
||||||
|
});
|
||||||
resizeObserver.observe(ref);
|
resizeObserver.observe(ref);
|
||||||
|
|
||||||
return () => resizeObserver.disconnect();
|
return () => resizeObserver.disconnect();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Update size when visibility changes
|
||||||
|
createEffect(() => {
|
||||||
|
setRightBarSize(rightBarVisible() ? actualWidth : 0);
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav
|
<nav
|
||||||
ref={ref}
|
ref={ref}
|
||||||
class="border-l-overlay2 fixed right-0 h-full min-h-screen w-fit max-w-[25%] border-l-2">
|
class="border-l-overlay2 fixed right-0 h-full min-h-screen w-fit max-w-[25%] border-l-2 transition-transform duration-500 ease-out md:block hidden z-50"
|
||||||
|
classList={{
|
||||||
|
"translate-x-full": !rightBarVisible(),
|
||||||
|
"translate-x-0": rightBarVisible()
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
"transition-timing-function": rightBarVisible()
|
||||||
|
? "cubic-bezier(0.34, 1.56, 0.64, 1)" // Bounce out when revealing
|
||||||
|
: "cubic-bezier(0.4, 0, 0.2, 1)" // Smooth when hiding
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Typewriter keepAlive={false} class="z-50">
|
<Typewriter keepAlive={false} class="z-50">
|
||||||
<h3 class="hover:text-subtext0 w-fit text-center text-3xl underline transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-105">
|
<h3 class="hover:text-subtext0 w-fit text-center text-3xl underline transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-105">
|
||||||
Right Navigation
|
Right Navigation
|
||||||
|
|||||||
@@ -8,13 +8,25 @@ const BarsContext = createContext<{
|
|||||||
setRightBarSize: (size: number) => void;
|
setRightBarSize: (size: number) => void;
|
||||||
centerWidth: Accessor<number>;
|
centerWidth: Accessor<number>;
|
||||||
setCenterWidth: (size: number) => void;
|
setCenterWidth: (size: number) => void;
|
||||||
|
leftBarVisible: Accessor<boolean>;
|
||||||
|
setLeftBarVisible: (visible: boolean) => void;
|
||||||
|
rightBarVisible: Accessor<boolean>;
|
||||||
|
setRightBarVisible: (visible: boolean) => void;
|
||||||
|
toggleLeftBar: () => void;
|
||||||
|
toggleRightBar: () => void;
|
||||||
}>({
|
}>({
|
||||||
leftBarSize: () => 0,
|
leftBarSize: () => 0,
|
||||||
setLeftBarSize: () => {},
|
setLeftBarSize: () => {},
|
||||||
rightBarSize: () => 0,
|
rightBarSize: () => 0,
|
||||||
setRightBarSize: () => {},
|
setRightBarSize: () => {},
|
||||||
centerWidth: () => 0,
|
centerWidth: () => 0,
|
||||||
setCenterWidth: () => {}
|
setCenterWidth: () => {},
|
||||||
|
leftBarVisible: () => true,
|
||||||
|
setLeftBarVisible: () => {},
|
||||||
|
rightBarVisible: () => true,
|
||||||
|
setRightBarVisible: () => {},
|
||||||
|
toggleLeftBar: () => {},
|
||||||
|
toggleRightBar: () => {}
|
||||||
});
|
});
|
||||||
|
|
||||||
export function useBars() {
|
export function useBars() {
|
||||||
@@ -26,6 +38,11 @@ export function BarsProvider(props: { children: any }) {
|
|||||||
const [leftBarSize, setLeftBarSize] = createSignal(0);
|
const [leftBarSize, setLeftBarSize] = createSignal(0);
|
||||||
const [rightBarSize, setRightBarSize] = createSignal(0);
|
const [rightBarSize, setRightBarSize] = createSignal(0);
|
||||||
const [centerWidth, setCenterWidth] = createSignal(0);
|
const [centerWidth, setCenterWidth] = createSignal(0);
|
||||||
|
const [leftBarVisible, setLeftBarVisible] = createSignal(true);
|
||||||
|
const [rightBarVisible, setRightBarVisible] = createSignal(true);
|
||||||
|
|
||||||
|
const toggleLeftBar = () => setLeftBarVisible(!leftBarVisible());
|
||||||
|
const toggleRightBar = () => setRightBarVisible(!rightBarVisible());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BarsContext.Provider
|
<BarsContext.Provider
|
||||||
@@ -35,7 +52,13 @@ export function BarsProvider(props: { children: any }) {
|
|||||||
rightBarSize,
|
rightBarSize,
|
||||||
setRightBarSize,
|
setRightBarSize,
|
||||||
centerWidth,
|
centerWidth,
|
||||||
setCenterWidth
|
setCenterWidth,
|
||||||
|
leftBarVisible,
|
||||||
|
setLeftBarVisible,
|
||||||
|
rightBarVisible,
|
||||||
|
setRightBarVisible,
|
||||||
|
toggleLeftBar,
|
||||||
|
toggleRightBar
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
|
|||||||
@@ -186,8 +186,12 @@ export default function PostPage() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="text-shadow absolute top-1/2 left-1/2 z-10 w-full -translate-x-1/2 -translate-y-1/2 px-4 text-center tracking-widest text-white brightness-150 select-text"
|
class="text-shadow absolute top-1/3 z-10 my-auto px-4 text-center tracking-widest text-white brightness-150 select-text"
|
||||||
style={{ "pointer-events": "none" }}
|
style={{
|
||||||
|
"pointer-events": "none",
|
||||||
|
width: `${centerWidth()}px`,
|
||||||
|
"margin-left": `${leftBarSize()}px`
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div class="text-3xl font-light tracking-widest">
|
<div class="text-3xl font-light tracking-widest">
|
||||||
{p().title.replaceAll("_", " ")}
|
{p().title.replaceAll("_", " ")}
|
||||||
|
|||||||
Reference in New Issue
Block a user