Update added chevrons to scrollbar - bug in size of chevrons
This commit is contained in:
@@ -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,24 +192,51 @@ export function SettingsButton() {
|
||||
|
||||
<Dialog.Body p={0}>
|
||||
<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
|
||||
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" // Back to a perfect pill shape!
|
||||
borderRadius="full"
|
||||
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
|
||||
flexWrap="nowrap"
|
||||
overflowX="auto"
|
||||
maxWidth="100%"
|
||||
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" },
|
||||
}}
|
||||
// This is for desktop users not on trackpad to scroll the
|
||||
onWheel={(e) => {
|
||||
if (e.currentTarget) {
|
||||
e.currentTarget.scrollLeft += e.deltaY;
|
||||
@@ -200,8 +250,8 @@ export function SettingsButton() {
|
||||
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
|
||||
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"
|
||||
@@ -219,7 +269,29 @@ export function SettingsButton() {
|
||||
</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}>
|
||||
|
||||
Reference in New Issue
Block a user