oooeee
This commit is contained in:
117
src/app.tsx
117
src/app.tsx
@@ -1,6 +1,6 @@
|
||||
import { Router } from "@solidjs/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 { LeftBar, RightBar } from "./components/Bars";
|
||||
import { TerminalSplash } from "./components/TerminalSplash";
|
||||
@@ -10,14 +10,37 @@ import ErrorBoundaryFallback from "./components/ErrorBoundaryFallback";
|
||||
import { BarsProvider, useBars } from "./context/bars";
|
||||
|
||||
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(() => {
|
||||
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();
|
||||
setCenterWidth(newWidth);
|
||||
};
|
||||
|
||||
// Call immediately and whenever dependencies change
|
||||
handleResize();
|
||||
|
||||
window.addEventListener("resize", handleResize);
|
||||
@@ -25,8 +48,98 @@ function AppLayout(props: { children: any }) {
|
||||
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 (
|
||||
<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 />
|
||||
<div
|
||||
style={{
|
||||
|
||||
@@ -1,30 +1,52 @@
|
||||
import { Typewriter } from "./Typewriter";
|
||||
import { useBars } from "~/context/bars";
|
||||
import { createEffect, onMount } from "solid-js";
|
||||
import { onMount, createEffect } from "solid-js";
|
||||
|
||||
export function LeftBar() {
|
||||
const { setLeftBarSize } = useBars();
|
||||
const { setLeftBarSize, leftBarVisible } = useBars();
|
||||
let ref: HTMLDivElement | undefined;
|
||||
let actualWidth = 0;
|
||||
|
||||
onMount(() => {
|
||||
if (ref) {
|
||||
const updateSize = () => {
|
||||
setLeftBarSize(ref?.offsetWidth || 0);
|
||||
actualWidth = ref?.offsetWidth || 0;
|
||||
setLeftBarSize(leftBarVisible() ? actualWidth : 0);
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
return () => resizeObserver.disconnect();
|
||||
}
|
||||
});
|
||||
|
||||
// Update size when visibility changes
|
||||
createEffect(() => {
|
||||
setLeftBarSize(leftBarVisible() ? actualWidth : 0);
|
||||
});
|
||||
|
||||
return (
|
||||
<nav
|
||||
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">
|
||||
<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,17 +59,31 @@ export function LeftBar() {
|
||||
{/*TODO:Grab and render 5 most recent blog posts here */}
|
||||
<li></li>
|
||||
</ul>
|
||||
<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">
|
||||
<a href="/">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="/blog">Blog</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 class="flex flex-col 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">
|
||||
<a href="/">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="/blog">Blog</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>
|
||||
{/* 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>
|
||||
</Typewriter>
|
||||
</nav>
|
||||
@@ -55,28 +91,51 @@ export function LeftBar() {
|
||||
}
|
||||
|
||||
export function RightBar() {
|
||||
const { setRightBarSize } = useBars();
|
||||
const { setRightBarSize, rightBarVisible } = useBars();
|
||||
let ref: HTMLDivElement | undefined;
|
||||
let actualWidth = 0;
|
||||
|
||||
onMount(() => {
|
||||
if (ref) {
|
||||
const updateSize = () => {
|
||||
setRightBarSize(ref?.offsetWidth || 0);
|
||||
actualWidth = ref?.offsetWidth || 0;
|
||||
setRightBarSize(rightBarVisible() ? actualWidth : 0);
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
return () => resizeObserver.disconnect();
|
||||
}
|
||||
});
|
||||
|
||||
// Update size when visibility changes
|
||||
createEffect(() => {
|
||||
setRightBarSize(rightBarVisible() ? actualWidth : 0);
|
||||
});
|
||||
|
||||
return (
|
||||
<nav
|
||||
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">
|
||||
<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
|
||||
|
||||
@@ -8,13 +8,25 @@ const BarsContext = createContext<{
|
||||
setRightBarSize: (size: number) => void;
|
||||
centerWidth: Accessor<number>;
|
||||
setCenterWidth: (size: number) => void;
|
||||
leftBarVisible: Accessor<boolean>;
|
||||
setLeftBarVisible: (visible: boolean) => void;
|
||||
rightBarVisible: Accessor<boolean>;
|
||||
setRightBarVisible: (visible: boolean) => void;
|
||||
toggleLeftBar: () => void;
|
||||
toggleRightBar: () => void;
|
||||
}>({
|
||||
leftBarSize: () => 0,
|
||||
setLeftBarSize: () => {},
|
||||
rightBarSize: () => 0,
|
||||
setRightBarSize: () => {},
|
||||
centerWidth: () => 0,
|
||||
setCenterWidth: () => {}
|
||||
setCenterWidth: () => {},
|
||||
leftBarVisible: () => true,
|
||||
setLeftBarVisible: () => {},
|
||||
rightBarVisible: () => true,
|
||||
setRightBarVisible: () => {},
|
||||
toggleLeftBar: () => {},
|
||||
toggleRightBar: () => {}
|
||||
});
|
||||
|
||||
export function useBars() {
|
||||
@@ -26,6 +38,11 @@ export function BarsProvider(props: { children: any }) {
|
||||
const [leftBarSize, setLeftBarSize] = createSignal(0);
|
||||
const [rightBarSize, setRightBarSize] = 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 (
|
||||
<BarsContext.Provider
|
||||
@@ -35,7 +52,13 @@ export function BarsProvider(props: { children: any }) {
|
||||
rightBarSize,
|
||||
setRightBarSize,
|
||||
centerWidth,
|
||||
setCenterWidth
|
||||
setCenterWidth,
|
||||
leftBarVisible,
|
||||
setLeftBarVisible,
|
||||
rightBarVisible,
|
||||
setRightBarVisible,
|
||||
toggleLeftBar,
|
||||
toggleRightBar
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
|
||||
@@ -186,8 +186,12 @@ export default function PostPage() {
|
||||
/>
|
||||
</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"
|
||||
style={{ "pointer-events": "none" }}
|
||||
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",
|
||||
width: `${centerWidth()}px`,
|
||||
"margin-left": `${leftBarSize()}px`
|
||||
}}
|
||||
>
|
||||
<div class="text-3xl font-light tracking-widest">
|
||||
{p().title.replaceAll("_", " ")}
|
||||
|
||||
Reference in New Issue
Block a user