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 { import {
Button, Button,
Box, Box,
@@ -13,7 +13,7 @@ import {
useRecipe, useRecipe,
useSlotRecipe useSlotRecipe
} from "@chakra-ui/react"; } 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 {useDashboardStore} from "../../store/useDashboardStore.jsx";
import {WIDGET_REGISTRY} from "../../constants/widgetRegistry.jsx"; import {WIDGET_REGISTRY} from "../../constants/widgetRegistry.jsx";
@@ -41,6 +41,29 @@ export function SettingsButton() {
const [isLibraryOpen, setIsLibraryOpen] = useState(false); 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 // Smart Add Handler
const handleAddWidget = (widgetId, sizeKey) => { const handleAddWidget = (widgetId, sizeKey) => {
enterEditMode(); enterEditMode();
@@ -147,7 +170,7 @@ export function SettingsButton() {
</Menu.Root> </Menu.Root>
)} )}
{/* THE WIDGET LIBRARY MODAL (Controlled by isLibraryOpen) */} {/* 📚📚📚 THE WIDGET LIBRARY MODAL (Controlled by isLibraryOpen) */}
<Dialog.Root <Dialog.Root
open={isLibraryOpen} open={isLibraryOpen}
onOpenChange={(e) => setIsLibraryOpen(e.open)} onOpenChange={(e) => setIsLibraryOpen(e.open)}
@@ -169,24 +192,51 @@ export function SettingsButton() {
<Dialog.Body p={0}> <Dialog.Body p={0}>
<Tabs.Root defaultValue={categories[0]} variant="unstyled"> <Tabs.Root defaultValue={categories[0]} variant="unstyled">
<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 <Tabs.List
ref={tabsRef}
onScroll={checkScroll}
bg={{ base: "rgba(0, 0, 0, 0.04)", _dark: "rgba(255, 255, 255, 0.05)" }} bg={{ base: "rgba(0, 0, 0, 0.04)", _dark: "rgba(255, 255, 255, 0.05)" }}
p={1} p={1}
mx={3} mx={3}
mt={4} mt={4}
mb={6} mb={6}
borderRadius="full" // Back to a perfect pill shape! borderRadius="full"
display="flex" display="flex"
flexWrap="nowrap" // 🚨 STRICTLY lock to one line flexWrap="nowrap"
overflowX="auto" // 🚨 Allow horizontal scrolling overflowX="auto"
maxWidth="100%" // Ensure it doesn't break out of the modal maxWidth="100%"
border="1px solid" border="1px solid"
borderColor={{ base: "blackAlpha.50", _dark: "whiteAlpha.100" }} borderColor={{ base: "blackAlpha.50", _dark: "whiteAlpha.100" }}
css={{ css={{
// 🚨 Hide the scrollbar but keep the scrolling functionality
scrollbarWidth: "none", scrollbarWidth: "none",
"&::-webkit-scrollbar": { display: "none" }, "&::-webkit-scrollbar": { display: "none" },
}} }}
// This is for desktop users not on trackpad to scroll the
onWheel={(e) => { onWheel={(e) => {
if (e.currentTarget) { if (e.currentTarget) {
e.currentTarget.scrollLeft += e.deltaY; e.currentTarget.scrollLeft += e.deltaY;
@@ -200,8 +250,8 @@ export function SettingsButton() {
px={5} px={5}
py={2} py={2}
borderRadius="full" borderRadius="full"
whiteSpace="nowrap" // Keep text on one straight line whiteSpace="nowrap"
flexShrink={0} // 🚨 Prevent the browser from trying to crush the tabs flexShrink={0}
color={{ base: "gray.500", _dark: "gray.400" }} color={{ base: "gray.500", _dark: "gray.400" }}
fontWeight="medium" fontWeight="medium"
transition="background-color 0.2s, color 0.2s, box-shadow 0.2s" transition="background-color 0.2s, color 0.2s, box-shadow 0.2s"
@@ -219,7 +269,29 @@ export function SettingsButton() {
</Tabs.Trigger> </Tabs.Trigger>
))} ))}
</Tabs.List> </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 => ( {categories.map(cat => (
<Tabs.Content key={cat} value={cat} p={6}> <Tabs.Content key={cat} value={cat} p={6}>
<Grid templateColumns="repeat(auto-fill, minmax(300px, 1fr))" gap={4}> <Grid templateColumns="repeat(auto-fill, minmax(300px, 1fr))" gap={4}>