import {useState, useMemo, useRef, useEffect, useCallback} from "react"; import { Button, Box, Text, Flex, Grid, Dialog, Tabs, Badge, Menu, HStack, useRecipe, useSlotRecipe } from "@chakra-ui/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"; // 🚨 NEW: Tiny helper to convert size keys into abbreviations const getSizeAbbreviation = (key) => { const mapping = {small: "S", medium: "M", large: "L", xlarge: "XL"}; return mapping[key.toLowerCase()] || key.charAt(0).toUpperCase(); }; export function SettingsButton() { // 1. ☁Store connections const isEditing = useDashboardStore(state => state.isEditing); const enterEditMode = useDashboardStore(state => state.enterEditMode); const saveEdit = useDashboardStore(state => state.saveEdit); const cancelEdit = useDashboardStore(state => state.cancelEdit); const addWidget = useDashboardStore(state => state.addWidget); const glassTabRecipe = useRecipe({key: "glassTab"}); const tabStyles = glassTabRecipe(); const [canScrollLeft, setCanScrollLeft] = useState(false); const [canScrollRight, setCanScrollRight] = useState(false); // Pull the glass container styles for the dropdown and modal const popoverRecipe = useSlotRecipe({ key: "glassPopover" }); const popoverStyles = popoverRecipe(); const dialogRecipe = useSlotRecipe({ key: "glassDialog" }); const dialogStyles = dialogRecipe(); 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 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(); addWidget(widgetId, sizeKey); }; const categorizedWidgets = useMemo(() => { return Object.entries(WIDGET_REGISTRY).reduce((acc, [id, widget]) => { const cat = widget.category || 'General'; if (!acc[cat]) acc[cat] = []; acc[cat].push({id, ...widget}); return acc; }, {}); }, []); const categories = Object.keys(categorizedWidgets); // use ref to capture html node for placing menu accordingly const menuRef = useRef(null); const getAnchorRect = () => { if (!menuRef.current) return null; return menuRef.current.getBoundingClientRect(); }; return ( <> {isEditing ? ( // πŸ“ EDIT MODE: The 3-Pill Layout {/* 1. The Add Widget Button */} setIsLibraryOpen(true)} color={{base: "blue.500", _dark: "blue.300"}} _hover={{bg: "rgba(66, 153, 225, 0.1)"}} > Widget Library {/* 2. The Save Button */} Save Layout {/* 3. The Discard Button */} Discard ) : ( // πŸ‘€ VIEW MODE: The Static Dropdown Menu {/* settings icon size set to 24 to match tabs. flexShrink 0 is CRITICAL */} Dashboard Settings Edit Layout setIsLibraryOpen(true)} cursor="pointer" _hover={{ bg: {base: "rgba(0, 0, 0, 0.05)", _dark: "rgba(255, 255, 255, 0.1)"}, WebkitTextStroke: "0.5px currentColor" }} > Widget Library )} {/* πŸ“šπŸ“šπŸ“š THE WIDGET LIBRARY MODAL (Controlled by isLibraryOpen) */} setIsLibraryOpen(e.open)} closeOnInteractOutside={true} closeOnEscapeKeyDown={true} size="xl" placement="center" > {/* Soft blur can be added to the background behind the modal */} {/**/} Widgets Shop {/* HORIZONTAL SCROLLABLE MENU OF WIDGET CATEGORIES */} {/* left glass crescent */} {canScrollLeft && ( tabsRef.current.scrollBy({left: -200, behavior: 'smooth'})}> {/* Shifted slightly left to optically center it in the crescent mass */} )} {/* the categories */} { if (e.currentTarget) e.currentTarget.scrollLeft += e.deltaY; }} > {categories.map(cat => ( {cat} ))} {/* right glass crescent */} {canScrollRight && ( tabsRef.current.scrollBy({left: 200, behavior: 'smooth'})}> {/* Shifted slightly right to optically center it in the crescent mass */} )} {/* THE WIDGET OPTIONS W/IN EACH CATEGORY */} {categories.map(cat => ( {categorizedWidgets[cat].map(widget => ( {widget.name} {widget.description} Add to Dashboard: {Object.keys(widget.allowedSizes).map(sizeKey => ( handleAddWidget(widget.id, sizeKey)} color={{base: "blue.600", _dark: "blue.300"}} > {/* THE ABBREVIATION ICON: Always visible */} {getSizeAbbreviation(sizeKey)} {/* THE EXPANDED TEXT: Only visible on hover */} Add Widget ))} ))} ))} ); }