From 1142d6f1265116951b69d7c9a88eecae9ee5bc46 Mon Sep 17 00:00:00 2001 From: Michael Freno Date: Thu, 18 Dec 2025 00:55:21 -0500 Subject: [PATCH] oooeee --- src/app.tsx | 117 +++++++++++++++++++++++++++++- src/components/Bars.tsx | 111 +++++++++++++++++++++------- src/context/bars.tsx | 27 ++++++- src/routes/blog/[title]/index.tsx | 8 +- 4 files changed, 231 insertions(+), 32 deletions(-) diff --git a/src/app.tsx b/src/app.tsx index 48249b1..461bb9a 100644 --- a/src/app.tsx +++ b/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 (
+ {/* Backdrop overlay - visible on mobile when sidebar is open */} +
setLeftBarVisible(false)} + aria-label="Close sidebar" + /> +
{ 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 (
@@ -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 (
{p().title.replaceAll("_", " ")}