diff --git a/client/src/components/modules/menu-item.tsx b/client/src/components/modules/menu-item.tsx index a848f6ce..134f0ee4 100644 --- a/client/src/components/modules/menu-item.tsx +++ b/client/src/components/modules/menu-item.tsx @@ -29,6 +29,7 @@ export interface ArcadeMenuItemProps active?: boolean; className?: string; onClick?: () => void; + disabled?: boolean; } export const ArcadeMenuItem = React.forwardRef< @@ -36,7 +37,7 @@ export const ArcadeMenuItem = React.forwardRef< ArcadeMenuItemProps >( ( - { Icon, value, label, active, className, variant, size, onClick, ...props }, + { Icon, value, label, active, className, variant, size, onClick, disabled, ...props }, ref, ) => { const handleFocus = useCallback((e: React.FocusEvent) => { @@ -49,17 +50,20 @@ export const ArcadeMenuItem = React.forwardRef< value={value} className={cn( "w-full p-0 flex cursor-pointer select-none transition-colors data-[state=active]:bg-transparent data-[state=active]:shadow-none", + disabled && "opacity-50 cursor-not-allowed", className, )} ref={ref} + disabled={disabled} {...props} >
@@ -72,4 +76,5 @@ export const ArcadeMenuItem = React.forwardRef< }, ); +// Updated to support disabled state - TypeScript refresh export default ArcadeMenuItem; diff --git a/client/src/components/modules/tab.tsx b/client/src/components/modules/tab.tsx index 1f26b59e..47707248 100644 --- a/client/src/components/modules/tab.tsx +++ b/client/src/components/modules/tab.tsx @@ -28,11 +28,13 @@ export interface ArcadeTabProps extends VariantProps { active?: boolean; className?: string; onClick?: () => void; + disabled?: boolean; + suffix?: string; } export const ArcadeTab = React.forwardRef( ( - { Icon, value, label, active, className, variant, size, onClick, ...props }, + { Icon, value, label, active, className, variant, size, onClick, disabled, ...props }, ref, ) => { return ( @@ -40,10 +42,12 @@ export const ArcadeTab = React.forwardRef( value={value} className={cn( "p-0 flex flex-col items-center cursor-pointer select-none transition-colors data-[state=active]:bg-transparent data-[state=active]:shadow-none data-[state=active]:drop-shadow-none", + disabled && "opacity-50 cursor-not-allowed", className, )} - onClick={onClick} + onClick={disabled ? undefined : onClick} ref={ref} + disabled={disabled} {...props} >
{ defaultValue?: TabValue; order?: TabValue[]; + disabledTabs?: TabValue[]; onTabClick?: (tab: TabValue) => void; } export const ArcadeTabs = ({ - defaultValue = "activity", + defaultValue, order = [ "activity", "leaderboard", @@ -82,13 +83,32 @@ export const ArcadeTabs = ({ "items", "holders", ], + disabledTabs = ["activity"], onTabClick, variant, size, className, children, }: ArcadeTabsProps) => { - const [active, setActive] = useState(defaultValue); + const { isMobile, isPWA } = useDevice(); + + // Reorder tabs to put disabled ones at the end + const orderedTabs = useMemo(() => { + const enabledTabs = order.filter(tab => !disabledTabs.includes(tab)); + const disabledTabsInOrder = order.filter(tab => disabledTabs.includes(tab)); + return [...enabledTabs, ...disabledTabsInOrder]; + }, [order, disabledTabs]); + + // Set proper default value - use first enabled tab if default is disabled or not provided + const effectiveDefaultValue = useMemo(() => { + if (defaultValue && !disabledTabs.includes(defaultValue)) { + return defaultValue; + } + const firstEnabledTab = orderedTabs.find(tab => !disabledTabs.includes(tab)); + return firstEnabledTab || orderedTabs[0]; + }, [defaultValue, disabledTabs, orderedTabs]); + + const [active, setActive] = useState(effectiveDefaultValue); const [visibleTabs, setVisibleTabs] = useState(order); const [overflowTabs, setOverflowTabs] = useState([]); const containerRef = useRef(null); @@ -97,22 +117,30 @@ export const ArcadeTabs = ({ new Map(), ); - const { isMobile, isPWA } = useDevice(); - useEffect(() => { if (isMobile) return; if (!hiddenRef.current) return; const tabWidths = new Map(); hiddenRef.current.childNodes.forEach((node) => { const element = node as HTMLDivElement; - const tab = element.textContent?.toLowerCase(); - if (tab) { - const visible = order.includes(tab as TabValue); - tabWidths.set(tab as TabValue, { width: element.offsetWidth, visible }); + const textContent = element.textContent?.toLowerCase(); + if (textContent) { + // Extract the base tab name by removing "(coming soon)" suffix if present + const tab = textContent.replace(/\s*\(coming soon\)\s*$/, '') as TabValue; + // All tabs in orderedTabs should be visible + const visible = orderedTabs.includes(tab); + tabWidths.set(tab, { width: element.offsetWidth, visible }); + } + }); + + // Ensure all tabs in orderedTabs have entries in tabWidths + orderedTabs.forEach((tab) => { + if (!tabWidths.has(tab)) { + tabWidths.set(tab, { width: 100, visible: true }); // Default width for missing tabs } }); tabRefs.current = tabWidths; - }, [tabRefs, hiddenRef, order, isMobile]); + }, [tabRefs, hiddenRef, orderedTabs, isMobile]); useEffect(() => { if (isMobile) return; @@ -126,12 +154,13 @@ export const ArcadeTabs = ({ const newVisibleTabs: TabValue[] = []; const newOverflowTabs: TabValue[] = []; - order.forEach((tab) => { + orderedTabs.forEach((tab) => { const { width, visible } = tabRefs.current.get(tab) || { width: 0, visible: false, }; if (!visible) return; + if ( usedWidth + width <= availableWidth && newOverflowTabs.length === 0 @@ -154,8 +183,7 @@ export const ArcadeTabs = ({ observer.observe(containerRef.current!); return () => observer.disconnect(); }, [ - order, - containerRef.current, + orderedTabs, visibleTabs, overflowTabs, tabRefs, @@ -163,9 +191,9 @@ export const ArcadeTabs = ({ ]); useEffect(() => { - if (order.includes(active)) return; - setActive(defaultValue); - }, [order, active, defaultValue]); + if (orderedTabs.includes(active)) return; + setActive(effectiveDefaultValue); + }, [orderedTabs, active, effectiveDefaultValue]); const overflowActive = useMemo( () => overflowTabs.includes(active), @@ -174,7 +202,7 @@ export const ArcadeTabs = ({ return ( setActive(value as TabValue)} className="h-full flex flex-col overflow-hidden" @@ -187,7 +215,7 @@ export const ArcadeTabs = ({ )} > - {order.map((tab) => ( + {orderedTabs.map((tab) => ( onTabClick?.(tab as TabValue)} isMobile={true} + disabled={tab === "activity" ? disabledTabs.includes(tab) : false} /> ))} @@ -209,8 +238,8 @@ export const ArcadeTabs = ({ )} >
- {order.map((tab) => ( - + {orderedTabs.map((tab) => ( + ))}
{visibleTabs.map((tab) => ( @@ -220,6 +249,7 @@ export const ArcadeTabs = ({ value={active} size={size} onTabClick={() => onTabClick?.(tab as TabValue)} + disabled={tab === "activity" ? disabledTabs.includes(tab) : false} /> ))}