From 359a1647513637780a0392178bc7a847e05f63c8 Mon Sep 17 00:00:00 2001 From: EthanHealy01 Date: Fri, 15 Aug 2025 17:48:19 +0100 Subject: [PATCH] clarify what search results is doing, better variable names/function names and comments --- .../src/components/shared/QuickAccessBar.tsx | 4 +- ...ToolIndicator.tsx => ActiveToolButton.tsx} | 92 ++++++++++++------- .../src/components/tools/SearchResults.tsx | 51 ++++++---- frontend/src/components/tools/ToolPanel.tsx | 3 +- frontend/src/styles/theme.css | 6 ++ 5 files changed, 99 insertions(+), 57 deletions(-) rename frontend/src/components/shared/quickAccessBar/{TopToolIndicator.tsx => ActiveToolButton.tsx} (58%) diff --git a/frontend/src/components/shared/QuickAccessBar.tsx b/frontend/src/components/shared/QuickAccessBar.tsx index 5722e83d1..925890890 100644 --- a/frontend/src/components/shared/QuickAccessBar.tsx +++ b/frontend/src/components/shared/QuickAccessBar.tsx @@ -12,7 +12,7 @@ import { useToolWorkflow } from '../../contexts/ToolWorkflowContext'; import { ButtonConfig } from '../../types/sidebar'; import './quickAccessBar/QuickAccessBar.css'; import AllToolsNavButton from './AllToolsNavButton'; -import TopToolIndicator from './quickAccessBar/TopToolIndicator'; +import ActiveToolButton from "./quickAccessBar/ActiveToolButton"; import { isNavButtonActive, getNavButtonStyle, @@ -129,7 +129,7 @@ const QuickAccessBar = forwardRef(({ > {/* Fixed header outside scrollable area */}
- +
diff --git a/frontend/src/components/shared/quickAccessBar/TopToolIndicator.tsx b/frontend/src/components/shared/quickAccessBar/ActiveToolButton.tsx similarity index 58% rename from frontend/src/components/shared/quickAccessBar/TopToolIndicator.tsx rename to frontend/src/components/shared/quickAccessBar/ActiveToolButton.tsx index 84dc14469..cceaacc70 100644 --- a/frontend/src/components/shared/quickAccessBar/TopToolIndicator.tsx +++ b/frontend/src/components/shared/quickAccessBar/ActiveToolButton.tsx @@ -1,3 +1,17 @@ +/** + * ActiveToolButton - Shows the currently selected tool at the top of the Quick Access Bar + * + * When a user selects a tool from the All Tools list, this component displays the tool's + * icon and name at the top of the navigation bar. It provides a quick way to see which + * tool is currently active and offers a back button to return to the All Tools list. + * + * Features: + * - Shows tool icon and name when a tool is selected + * - Hover to reveal back arrow for returning to All Tools + * - Smooth slide-down/slide-up animations + * - Only appears for tools that don't have dedicated nav buttons (read, sign, automate) + */ + import React, { useEffect, useRef, useState } from 'react'; import { ActionIcon, Divider } from '@mantine/core'; import ArrowBackRoundedIcon from '@mui/icons-material/ArrowBackRounded'; @@ -5,14 +19,14 @@ import { useToolWorkflow } from '../../../contexts/ToolWorkflowContext'; import FitText from '../FitText'; import { Tooltip } from '../Tooltip'; -interface TopToolIndicatorProps { +interface ActiveToolButtonProps { activeButton: string; setActiveButton: (id: string) => void; } -const NAV_IDS = ['read','sign','automate']; +const NAV_IDS = ['read', 'sign', 'automate']; -const TopToolIndicator: React.FC = ({ activeButton, setActiveButton }) => { +const ActiveToolButton: React.FC = ({ activeButton, setActiveButton }) => { const { selectedTool, selectedToolKey, leftPanelView, handleBackToTools } = useToolWorkflow(); // Determine if the indicator should be visible @@ -28,43 +42,55 @@ const TopToolIndicator: React.FC = ({ activeButton, setAc const [isBackHover, setIsBackHover] = useState(false); const prevKeyRef = useRef(null); + const isSwitchingToNewTool = () => { return prevKeyRef.current && prevKeyRef.current !== selectedToolKey }; + + const playGrowDown = () => { + + setIndicatorTool(selectedTool); + setIndicatorVisible(true); + setReplayAnim(true); + setIsAnimating(true); + const t = window.setTimeout(() => { + setReplayAnim(false); + setIsAnimating(false); + }, 500); + return () => window.clearTimeout(t); + } + + const firstShow = () => { + setIndicatorTool(selectedTool); + setIndicatorVisible(true); + setIsAnimating(true); + prevKeyRef.current = (selectedToolKey as string) || null; + const tShow = window.setTimeout(() => setIsAnimating(false), 500); + return () => window.clearTimeout(tShow); + } + + const triggerCollapse = () => { + setIndicatorVisible(false); + setIsAnimating(true); + const timeout = window.setTimeout(() => { + setIndicatorTool(null); + prevKeyRef.current = null; + setIsAnimating(false); + }, 500); // match CSS transition duration + return () => window.clearTimeout(timeout); + } + useEffect(() => { if (indicatorShouldShow) { - // If switching to a different tool while visible, replay the grow down - if (prevKeyRef.current && prevKeyRef.current !== selectedToolKey) { - setIndicatorTool(selectedTool); - setIndicatorVisible(true); - setReplayAnim(true); - setIsAnimating(true); - const t = window.setTimeout(() => { - setReplayAnim(false); - setIsAnimating(false); - }, 500); - return () => window.clearTimeout(t); + if (isSwitchingToNewTool()) { + playGrowDown(); } - // First show - setIndicatorTool(selectedTool); - setIndicatorVisible(true); - setIsAnimating(true); - prevKeyRef.current = (selectedToolKey as string) || null; - const tShow = window.setTimeout(() => setIsAnimating(false), 500); - return () => window.clearTimeout(tShow); + firstShow() } else if (indicatorTool) { - // trigger collapse - setIndicatorVisible(false); - setIsAnimating(true); - const timeout = window.setTimeout(() => { - setIndicatorTool(null); - prevKeyRef.current = null; - setIsAnimating(false); - }, 500); // match CSS transition duration - return () => window.clearTimeout(timeout); + triggerCollapse(); } }, [indicatorShouldShow, selectedTool, selectedToolKey]); return ( <> -
+
{indicatorTool && (
@@ -80,7 +106,7 @@ const TopToolIndicator: React.FC = ({ activeButton, setAc }} aria-label={isBackHover ? 'Back to all tools' : indicatorTool.name} style={{ - backgroundColor: isBackHover ? '#9CA3AF' : 'var(--icon-tools-bg)', + backgroundColor: isBackHover ? 'var(--color-gray-300)' : 'var(--icon-tools-bg)', color: isBackHover ? '#fff' : 'var(--icon-tools-color)', border: 'none', borderRadius: '8px', @@ -114,6 +140,6 @@ const TopToolIndicator: React.FC = ({ activeButton, setAc ); }; -export default TopToolIndicator; +export default ActiveToolButton; diff --git a/frontend/src/components/tools/SearchResults.tsx b/frontend/src/components/tools/SearchResults.tsx index 75017fdc0..66807d809 100644 --- a/frontend/src/components/tools/SearchResults.tsx +++ b/frontend/src/components/tools/SearchResults.tsx @@ -9,27 +9,38 @@ interface SearchResultsProps { } const SearchResults: React.FC = ({ filteredTools, onSelect }) => { - const groups = useMemo(() => { - const subMap: Record> = {}; - const seen = new Set(); + // Group tools by subcategory and remove duplicates + const groupedToolsByCategory = useMemo(() => { + const categoryToToolsMap: Record> = {}; + const processedToolIds = new Set(); - filteredTools.forEach(([id, tool]) => { - if (seen.has(id)) return; - seen.add(id); - const sub = tool?.subcategory || 'General'; - if (!subMap[sub]) subMap[sub] = []; - subMap[sub].push({ id, tool }); + // Process each tool, skipping duplicates and grouping by subcategory + filteredTools.forEach(([toolId, toolEntry]) => { + // Skip if we've already processed this tool ID (deduplication) + if (processedToolIds.has(toolId)) return; + processedToolIds.add(toolId); + + // Use subcategory or default to 'General' if not specified + const categoryName = toolEntry?.subcategory || 'General'; + + // Initialize category array if it doesn't exist + if (!categoryToToolsMap[categoryName]) { + categoryToToolsMap[categoryName] = []; + } + + categoryToToolsMap[categoryName].push({ id: toolId, tool: toolEntry }); }); - return Object.entries(subMap) - .sort(([a], [b]) => a.localeCompare(b)) - .map(([subcategory, tools]) => ({ - subcategory, - tools + // Convert to sorted array format for rendering + return Object.entries(categoryToToolsMap) + .sort(([categoryA], [categoryB]) => categoryA.localeCompare(categoryB)) + .map(([categoryName, toolsInCategory]) => ({ + categoryName, + toolsInCategory })); }, [filteredTools]); - if (groups.length === 0) { + if (groupedToolsByCategory.length === 0) { return ( No tools found @@ -39,13 +50,13 @@ const SearchResults: React.FC = ({ filteredTools, onSelect } return ( - {groups.map(group => ( - + {groupedToolsByCategory.map(categoryGroup => ( + - {group.subcategory} + {categoryGroup.categoryName} - {group.tools.map(({ id, tool }) => ( + {categoryGroup.toolsInCategory.map(({ id, tool }) => ( = ({ filteredTools, onSelect } ))} {/* global spacer to allow scrolling past last row in search mode */} -
+
); }; diff --git a/frontend/src/components/tools/ToolPanel.tsx b/frontend/src/components/tools/ToolPanel.tsx index 972237c5d..480fd9d3a 100644 --- a/frontend/src/components/tools/ToolPanel.tsx +++ b/frontend/src/components/tools/ToolPanel.tsx @@ -15,7 +15,6 @@ import rainbowStyles from '../../styles/rainbow.module.css'; export default function ToolPanel() { const { t } = useTranslation(); const { isRainbowMode } = useRainbowThemeContext(); - const { colorScheme } = useMantineColorScheme(); const { sidebarRefs } = useSidebarContext(); const { toolPanelRef } = sidebarRefs; @@ -55,7 +54,7 @@ export default function ToolPanel() { {/* Search Bar - Always visible at the top */}