import React, { useState, useRef, forwardRef, useEffect } from "react"; import { ActionIcon, Stack, Divider, Menu, Indicator } from "@mantine/core"; import { useTranslation } from 'react-i18next'; import { useNavigate, useLocation } from 'react-router-dom'; import LocalIcon from '@app/components/shared/LocalIcon'; import { useRainbowThemeContext } from "@app/components/shared/RainbowThemeProvider"; import { useIsOverflowing } from '@app/hooks/useIsOverflowing'; import { useFilesModalContext } from '@app/contexts/FilesModalContext'; import { useToolWorkflow } from '@app/contexts/ToolWorkflowContext'; import { useSidebarNavigation } from '@app/hooks/useSidebarNavigation'; import { handleUnlessSpecialClick } from '@app/utils/clickHandlers'; import { ButtonConfig } from '@app/types/sidebar'; import '@app/components/shared/quickAccessBar/QuickAccessBar.css'; import AllToolsNavButton from '@app/components/shared/AllToolsNavButton'; import ActiveToolButton from "@app/components/shared/quickAccessBar/ActiveToolButton"; import AppConfigModal from '@app/components/shared/AppConfigModal'; import { useAppConfig } from '@app/contexts/AppConfigContext'; import { useOnboarding } from '@app/contexts/OnboardingContext'; import { useLicenseAlert } from "@app/hooks/useLicenseAlert"; import { isNavButtonActive, getNavButtonStyle, getActiveNavButton, } from '@app/components/shared/quickAccessBar/QuickAccessBar'; import { Z_INDEX_OVER_FULLSCREEN_SURFACE } from '@app/styles/zIndex'; const QuickAccessBar = forwardRef((_, ref) => { const { t } = useTranslation(); const navigate = useNavigate(); const location = useLocation(); const { isRainbowMode } = useRainbowThemeContext(); const { openFilesModal, isFilesModalOpen } = useFilesModalContext(); const { handleReaderToggle, handleToolSelect, selectedToolKey, leftPanelView, toolRegistry, readerMode, resetTool } = useToolWorkflow(); const { getToolNavigation } = useSidebarNavigation(); const { config } = useAppConfig(); const { startTour } = useOnboarding(); const licenseAlert = useLicenseAlert(); const [configModalOpen, setConfigModalOpen] = useState(false); const [activeButton, setActiveButton] = useState('tools'); const scrollableRef = useRef(null); const isOverflow = useIsOverflowing(scrollableRef); const isRTL = typeof document !== 'undefined' && document.documentElement.dir === 'rtl'; // Open modal if URL is at /settings/* useEffect(() => { const isSettings = location.pathname.startsWith('/settings'); setConfigModalOpen(isSettings); }, [location.pathname]); useEffect(() => { const next = getActiveNavButton(selectedToolKey, readerMode); setActiveButton(next); }, [leftPanelView, selectedToolKey, toolRegistry, readerMode]); const handleFilesButtonClick = () => { openFilesModal(); }; // Helper function to render navigation buttons with URL support const renderNavButton = (config: ButtonConfig, index: number) => { const isActive = isNavButtonActive(config, activeButton, isFilesModalOpen, configModalOpen, selectedToolKey, leftPanelView); // Check if this button has URL navigation support const navProps = config.type === 'navigation' && (config.id === 'read' || config.id === 'automate') ? getToolNavigation(config.id) : null; const handleClick = (e?: React.MouseEvent) => { if (navProps && e) { handleUnlessSpecialClick(e, config.onClick); } else { config.onClick(); } }; // Render navigation button with conditional URL support return (
handleClick(e), 'aria-label': config.name } : { onClick: () => handleClick(), 'aria-label': config.name })} size={isActive ? 'lg' : 'md'} variant="subtle" style={getNavButtonStyle(config, activeButton, isFilesModalOpen, configModalOpen, selectedToolKey, leftPanelView)} className={isActive ? 'activeIconScale' : ''} data-testid={`${config.id}-button`} > {config.icon} {config.name}
); }; const mainButtons: ButtonConfig[] = [ { id: 'read', name: t("quickAccess.reader", "Reader"), icon: , size: 'md', isRound: false, type: 'navigation', onClick: () => { setActiveButton('read'); handleReaderToggle(); } }, { id: 'automate', name: t("quickAccess.automate", "Automate"), icon: , size: 'md', isRound: false, type: 'navigation', onClick: () => { setActiveButton('automate'); // If already on automate tool, reset it directly if (selectedToolKey === 'automate') { resetTool('automate'); } else { handleToolSelect('automate'); } } }, { id: 'files', name: t("quickAccess.files", "Files"), icon: , isRound: true, size: 'md', type: 'modal', onClick: handleFilesButtonClick }, ]; const middleButtons: ButtonConfig[] = []; //TODO: Activity //{ // id: 'activity', // name: t("quickAccess.activity", "Activity"), // icon: , // isRound: true, // size: 'lg', // type: 'navigation', // onClick: () => setActiveButton('activity') //}, const bottomButtons: ButtonConfig[] = [ { id: 'help', name: t("quickAccess.help", "Help"), icon: , isRound: true, size: 'md', type: 'action', onClick: () => { // This will be overridden by the wrapper logic }, }, { id: 'config', name: t("quickAccess.settings", "Settings"), icon: , size: 'md', type: 'modal', onClick: () => { navigate('/settings/overview'); setConfigModalOpen(true); } } ]; return (
{/* Fixed header outside scrollable area */}
{/* Conditional divider when overflowing */} {isOverflow && ( )} {/* Scrollable content area */}
{ // Prevent the wheel event from bubbling up to parent containers e.stopPropagation(); }} >
{/* Main navigation section */} {mainButtons.map((config, index) => ( {renderNavButton(config, index)} ))} {/* Divider after main buttons (creates gap) */} {middleButtons.length === 0 && ( )} {/* Middle section */} {middleButtons.length > 0 && ( <> {middleButtons.map((config, index) => ( {renderNavButton(config, index)} ))} )} {/* Spacer to push bottom buttons to bottom */}
{/* Bottom section */} {bottomButtons.map((buttonConfig, index) => { // Handle help button with menu or direct action if (buttonConfig.id === 'help') { const isAdmin = config?.isAdmin === true; // If not admin, just show button that starts tools tour directly if (!isAdmin) { return (
startTour('tools')} > {renderNavButton(buttonConfig, index)}
); } // If admin, show menu with both options return (
{renderNavButton(buttonConfig, index)}
} onClick={() => startTour('tools')} >
{t("quickAccess.helpMenu.toolsTour", "Tools Tour")}
{t("quickAccess.helpMenu.toolsTourDesc", "Learn what the tools can do")}
} onClick={() => startTour('admin')} >
{t("quickAccess.helpMenu.adminTour", "Admin Tour")}
{t("quickAccess.helpMenu.adminTourDesc", "Explore admin settings & features")}
); } const buttonNode = renderNavButton(buttonConfig, index); const shouldShowSettingsBadge = buttonConfig.id === 'config' && licenseAlert.active && licenseAlert.audience === 'admin'; return ( {shouldShowSettingsBadge ? ( {buttonNode} ) : ( buttonNode )} ); })}
setConfigModalOpen(false)} />
); }); QuickAccessBar.displayName = 'QuickAccessBar'; export default QuickAccessBar;