Update added chevrons to scrollbar - bug in size of chevrons

This commit is contained in:
2026-08-21 12:55:54 -07:00
parent 510c63e7b0
commit f5f09e31fe
@@ -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() {
</Menu.Root>
)}
{/* THE WIDGET LIBRARY MODAL (Controlled by isLibraryOpen) */}
{/* 📚📚📚 THE WIDGET LIBRARY MODAL (Controlled by isLibraryOpen) */}
<Dialog.Root
open={isLibraryOpen}
onOpenChange={(e) => setIsLibraryOpen(e.open)}
@@ -169,57 +192,106 @@ export function SettingsButton() {
<Dialog.Body p={0}>
<Tabs.Root defaultValue={categories[0]} variant="unstyled">
<Tabs.List
bg={{ base: "rgba(0, 0, 0, 0.04)", _dark: "rgba(255, 255, 255, 0.05)" }}
p={1}
mx={3}
mt={4}
mb={6}
borderRadius="full" // Back to a perfect pill shape!
display="flex"
flexWrap="nowrap" // 🚨 STRICTLY lock to one line
overflowX="auto" // 🚨 Allow horizontal scrolling
maxWidth="100%" // Ensure it doesn't break out of the modal
border="1px solid"
borderColor={{ base: "blackAlpha.50", _dark: "whiteAlpha.100" }}
css={{
// 🚨 Hide the scrollbar but keep the scrolling functionality
scrollbarWidth: "none",
"&::-webkit-scrollbar": { display: "none" },
}}
onWheel={(e) => {
if (e.currentTarget) {
e.currentTarget.scrollLeft += e.deltaY;
}
}}
>
{categories.map(cat => (
<Tabs.Trigger
key={cat}
value={cat}
px={5}
py={2}
borderRadius="full"
whiteSpace="nowrap" // Keep text on one straight line
flexShrink={0} // 🚨 Prevent the browser from trying to crush the tabs
color={{ base: "gray.500", _dark: "gray.400" }}
fontWeight="medium"
transition="background-color 0.2s, color 0.2s, box-shadow 0.2s"
_hover={{
color: { base: "gray.800", _dark: "gray.200" }
}}
_selected={{
bg: { base: "white", _dark: "rgba(255, 255, 255, 0.12)" },
color: { base: "blue.600", _dark: "blue.200" },
boxShadow: "sm",
fontWeight: "bold",
}}
>
{cat}
</Tabs.Trigger>
))}
</Tabs.List>
<Box position="relative" display="flex" mx="auto" maxWidth="100%" mt={4} mb={6}>
{/* LEFT GLASS CRESCENT */}
{canScrollLeft && (
<Flex
position="absolute"
left="0" top="0" bottom="0"
w="40px"
align="center" justify="center"
zIndex={2}
cursor="pointer"
onClick={() => 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" } }}
>
<ChevronLeft size={18} />
</Flex>
)}
<Tabs.List
ref={tabsRef}
onScroll={checkScroll}
bg={{ base: "rgba(0, 0, 0, 0.04)", _dark: "rgba(255, 255, 255, 0.05)" }}
p={1}
mx={3}
mt={4}
mb={6}
borderRadius="full"
display="flex"
flexWrap="nowrap"
overflowX="auto"
maxWidth="100%"
border="1px solid"
borderColor={{ base: "blackAlpha.50", _dark: "whiteAlpha.100" }}
css={{
scrollbarWidth: "none",
"&::-webkit-scrollbar": { display: "none" },
}}
// This is for desktop users not on trackpad to scroll the
onWheel={(e) => {
if (e.currentTarget) {
e.currentTarget.scrollLeft += e.deltaY;
}
}}
>
{categories.map(cat => (
<Tabs.Trigger
key={cat}
value={cat}
px={5}
py={2}
borderRadius="full"
whiteSpace="nowrap"
flexShrink={0}
color={{ base: "gray.500", _dark: "gray.400" }}
fontWeight="medium"
transition="background-color 0.2s, color 0.2s, box-shadow 0.2s"
_hover={{
color: { base: "gray.800", _dark: "gray.200" }
}}
_selected={{
bg: { base: "white", _dark: "rgba(255, 255, 255, 0.12)" },
color: { base: "blue.600", _dark: "blue.200" },
boxShadow: "sm",
fontWeight: "bold",
}}
>
{cat}
</Tabs.Trigger>
))}
</Tabs.List>
{canScrollRight && (
<Flex
position="absolute"
right="0" top="0" bottom="0"
w="40px"
align="center" justify="center"
zIndex={2}
cursor="pointer"
onClick={() => 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" } }}
>
<ChevronRight size={18} />
</Flex>
)}
</Box>
{categories.map(cat => (
<Tabs.Content key={cat} value={cat} p={6}>
<Grid templateColumns="repeat(auto-fill, minmax(300px, 1fr))" gap={4}>