From f5f09e31fe240d3d91b7c2fa329e3ade28c59a22 Mon Sep 17 00:00:00 2001 From: Christopher Hight Date: Fri, 21 Aug 2026 12:55:54 -0700 Subject: [PATCH] Update added chevrons to scrollbar - bug in size of chevrons --- .../components/dashboard/SettingsButton.jsx | 178 ++++++++++++------ 1 file changed, 125 insertions(+), 53 deletions(-) diff --git a/client/src/components/dashboard/SettingsButton.jsx b/client/src/components/dashboard/SettingsButton.jsx index 21d08f1..436fc6b 100644 --- a/client/src/components/dashboard/SettingsButton.jsx +++ b/client/src/components/dashboard/SettingsButton.jsx @@ -1,4 +1,4 @@ -import {useState, useMemo, useRef} from "react"; +import {useState, useMemo, useRef, useEffect, useCallback} from "react"; import { Button, Box, @@ -13,7 +13,7 @@ import { useRecipe, useSlotRecipe } from "@chakra-ui/react"; -import {Settings, LayoutGrid, Move, Save, X} from 'lucide-react'; +import {Settings, LayoutGrid, Move, Save, X, ChevronRight, ChevronLeft} from 'lucide-react'; import {useDashboardStore} from "../../store/useDashboardStore.jsx"; import {WIDGET_REGISTRY} from "../../constants/widgetRegistry.jsx"; @@ -41,6 +41,29 @@ export function SettingsButton() { const [isLibraryOpen, setIsLibraryOpen] = useState(false); + // ⬅️ ➡️ This is the callbacks and scroll control to add the chevron arrows in the tab list + const tabsRef = useRef(null); + const [canScrollLeft, setCanScrollLeft] = useState(false); + const [canScrollRight, setCanScrollRight] = useState(false); + + const checkScroll = useCallback(() => { + if (tabsRef.current) { + const { scrollLeft, scrollWidth, clientWidth } = tabsRef.current; + setCanScrollLeft(scrollLeft > 0); + // We use -1 here to account for sub-pixel rendering in some browsers + setCanScrollRight(Math.ceil(scrollLeft + clientWidth) < scrollWidth - 1); + } + }, []); + + useEffect(() => { + if (isLibraryOpen) { + // Small timeout ensures the DOM has rendered the tabs before measuring + setTimeout(checkScroll, 50); + window.addEventListener('resize', checkScroll); + return () => window.removeEventListener('resize', checkScroll); + } + }, [isLibraryOpen, checkScroll]); + // Smart Add Handler const handleAddWidget = (widgetId, sizeKey) => { enterEditMode(); @@ -147,7 +170,7 @@ export function SettingsButton() { )} - {/* THE WIDGET LIBRARY MODAL (Controlled by isLibraryOpen) */} + {/* 📚📚📚 THE WIDGET LIBRARY MODAL (Controlled by isLibraryOpen) */} setIsLibraryOpen(e.open)} @@ -169,57 +192,106 @@ export function SettingsButton() { - { - if (e.currentTarget) { - e.currentTarget.scrollLeft += e.deltaY; - } - }} - > - {categories.map(cat => ( - - {cat} - - ))} - + + {/* LEFT GLASS CRESCENT */} + {canScrollLeft && ( + tabsRef.current.scrollBy({ left: -200, behavior: 'smooth' })} + // The Glass Look hugging the left pill edge + bg={{ base: "rgba(255, 255, 255, 0.7)", _dark: "rgba(30, 30, 30, 0.8)" }} + backdropFilter="blur(8px)" + borderTopLeftRadius="full" + borderBottomLeftRadius="full" + // Fades the inner edge so it blends into the track + boxShadow={{ base: "10px 0 15px -5px rgba(255,255,255,0.8)", _dark: "10px 0 15px -5px rgba(30,30,30,0.9)" }} + color={{ base: "gray.600", _dark: "gray.300" }} + _hover={{ color: { base: "blue.500", _dark: "blue.300" } }} + > + + + )} + { + if (e.currentTarget) { + e.currentTarget.scrollLeft += e.deltaY; + } + }} + > + {categories.map(cat => ( + + {cat} + + ))} + + {canScrollRight && ( + tabsRef.current.scrollBy({ left: 200, behavior: 'smooth' })} + // The Glass Look hugging the right pill edge + bg={{ base: "rgba(255, 255, 255, 0.7)", _dark: "rgba(30, 30, 30, 0.8)" }} + backdropFilter="blur(8px)" + borderTopRightRadius="full" + borderBottomRightRadius="full" + boxShadow={{ base: "-10px 0 15px -5px rgba(255,255,255,0.8)", _dark: "-10px 0 15px -5px rgba(30,30,30,0.9)" }} + color={{ base: "gray.600", _dark: "gray.300" }} + _hover={{ color: { base: "blue.500", _dark: "blue.300" } }} + > + + + )} + + {categories.map(cat => (