clarify what search results is doing, better variable names/function names and comments

This commit is contained in:
EthanHealy01 2025-08-15 17:48:19 +01:00
parent d664666ff8
commit 359a164751
5 changed files with 99 additions and 57 deletions

View File

@ -12,7 +12,7 @@ import { useToolWorkflow } from '../../contexts/ToolWorkflowContext';
import { ButtonConfig } from '../../types/sidebar'; import { ButtonConfig } from '../../types/sidebar';
import './quickAccessBar/QuickAccessBar.css'; import './quickAccessBar/QuickAccessBar.css';
import AllToolsNavButton from './AllToolsNavButton'; import AllToolsNavButton from './AllToolsNavButton';
import TopToolIndicator from './quickAccessBar/TopToolIndicator'; import ActiveToolButton from "./quickAccessBar/ActiveToolButton";
import { import {
isNavButtonActive, isNavButtonActive,
getNavButtonStyle, getNavButtonStyle,
@ -129,7 +129,7 @@ const QuickAccessBar = forwardRef<HTMLDivElement>(({
> >
{/* Fixed header outside scrollable area */} {/* Fixed header outside scrollable area */}
<div className="quick-access-header"> <div className="quick-access-header">
<TopToolIndicator activeButton={activeButton} setActiveButton={setActiveButton} /> <ActiveToolButton activeButton={activeButton} setActiveButton={setActiveButton} />
<AllToolsNavButton activeButton={activeButton} setActiveButton={setActiveButton} /> <AllToolsNavButton activeButton={activeButton} setActiveButton={setActiveButton} />
</div> </div>

View File

@ -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 React, { useEffect, useRef, useState } from 'react';
import { ActionIcon, Divider } from '@mantine/core'; import { ActionIcon, Divider } from '@mantine/core';
import ArrowBackRoundedIcon from '@mui/icons-material/ArrowBackRounded'; import ArrowBackRoundedIcon from '@mui/icons-material/ArrowBackRounded';
@ -5,14 +19,14 @@ import { useToolWorkflow } from '../../../contexts/ToolWorkflowContext';
import FitText from '../FitText'; import FitText from '../FitText';
import { Tooltip } from '../Tooltip'; import { Tooltip } from '../Tooltip';
interface TopToolIndicatorProps { interface ActiveToolButtonProps {
activeButton: string; activeButton: string;
setActiveButton: (id: string) => void; setActiveButton: (id: string) => void;
} }
const NAV_IDS = ['read','sign','automate']; const NAV_IDS = ['read', 'sign', 'automate'];
const TopToolIndicator: React.FC<TopToolIndicatorProps> = ({ activeButton, setActiveButton }) => { const ActiveToolButton: React.FC<ActiveToolButtonProps> = ({ activeButton, setActiveButton }) => {
const { selectedTool, selectedToolKey, leftPanelView, handleBackToTools } = useToolWorkflow(); const { selectedTool, selectedToolKey, leftPanelView, handleBackToTools } = useToolWorkflow();
// Determine if the indicator should be visible // Determine if the indicator should be visible
@ -28,10 +42,10 @@ const TopToolIndicator: React.FC<TopToolIndicatorProps> = ({ activeButton, setAc
const [isBackHover, setIsBackHover] = useState<boolean>(false); const [isBackHover, setIsBackHover] = useState<boolean>(false);
const prevKeyRef = useRef<string | null>(null); const prevKeyRef = useRef<string | null>(null);
useEffect(() => { const isSwitchingToNewTool = () => { return prevKeyRef.current && prevKeyRef.current !== selectedToolKey };
if (indicatorShouldShow) {
// If switching to a different tool while visible, replay the grow down const playGrowDown = () => {
if (prevKeyRef.current && prevKeyRef.current !== selectedToolKey) {
setIndicatorTool(selectedTool); setIndicatorTool(selectedTool);
setIndicatorVisible(true); setIndicatorVisible(true);
setReplayAnim(true); setReplayAnim(true);
@ -42,15 +56,17 @@ const TopToolIndicator: React.FC<TopToolIndicatorProps> = ({ activeButton, setAc
}, 500); }, 500);
return () => window.clearTimeout(t); return () => window.clearTimeout(t);
} }
// First show
const firstShow = () => {
setIndicatorTool(selectedTool); setIndicatorTool(selectedTool);
setIndicatorVisible(true); setIndicatorVisible(true);
setIsAnimating(true); setIsAnimating(true);
prevKeyRef.current = (selectedToolKey as string) || null; prevKeyRef.current = (selectedToolKey as string) || null;
const tShow = window.setTimeout(() => setIsAnimating(false), 500); const tShow = window.setTimeout(() => setIsAnimating(false), 500);
return () => window.clearTimeout(tShow); return () => window.clearTimeout(tShow);
} else if (indicatorTool) { }
// trigger collapse
const triggerCollapse = () => {
setIndicatorVisible(false); setIndicatorVisible(false);
setIsAnimating(true); setIsAnimating(true);
const timeout = window.setTimeout(() => { const timeout = window.setTimeout(() => {
@ -60,11 +76,21 @@ const TopToolIndicator: React.FC<TopToolIndicatorProps> = ({ activeButton, setAc
}, 500); // match CSS transition duration }, 500); // match CSS transition duration
return () => window.clearTimeout(timeout); return () => window.clearTimeout(timeout);
} }
useEffect(() => {
if (indicatorShouldShow) {
if (isSwitchingToNewTool()) {
playGrowDown();
}
firstShow()
} else if (indicatorTool) {
triggerCollapse();
}
}, [indicatorShouldShow, selectedTool, selectedToolKey]); }, [indicatorShouldShow, selectedTool, selectedToolKey]);
return ( return (
<> <>
<div style={{overflow:'visible'}} className={`current-tool-slot ${indicatorVisible ? 'visible' : ''} ${replayAnim ? 'replay' : ''}`}> <div style={{ overflow: 'visible' }} className={`current-tool-slot ${indicatorVisible ? 'visible' : ''} ${replayAnim ? 'replay' : ''}`}>
{indicatorTool && ( {indicatorTool && (
<div className="current-tool-content"> <div className="current-tool-content">
<div className="flex flex-col items-center gap-1"> <div className="flex flex-col items-center gap-1">
@ -80,7 +106,7 @@ const TopToolIndicator: React.FC<TopToolIndicatorProps> = ({ activeButton, setAc
}} }}
aria-label={isBackHover ? 'Back to all tools' : indicatorTool.name} aria-label={isBackHover ? 'Back to all tools' : indicatorTool.name}
style={{ 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)', color: isBackHover ? '#fff' : 'var(--icon-tools-color)',
border: 'none', border: 'none',
borderRadius: '8px', borderRadius: '8px',
@ -114,6 +140,6 @@ const TopToolIndicator: React.FC<TopToolIndicatorProps> = ({ activeButton, setAc
); );
}; };
export default TopToolIndicator; export default ActiveToolButton;

View File

@ -9,27 +9,38 @@ interface SearchResultsProps {
} }
const SearchResults: React.FC<SearchResultsProps> = ({ filteredTools, onSelect }) => { const SearchResults: React.FC<SearchResultsProps> = ({ filteredTools, onSelect }) => {
const groups = useMemo(() => { // Group tools by subcategory and remove duplicates
const subMap: Record<string, Array<{ id: string; tool: ToolRegistryEntry }>> = {}; const groupedToolsByCategory = useMemo(() => {
const seen = new Set<string>(); const categoryToToolsMap: Record<string, Array<{ id: string; tool: ToolRegistryEntry }>> = {};
const processedToolIds = new Set<string>();
filteredTools.forEach(([id, tool]) => { // Process each tool, skipping duplicates and grouping by subcategory
if (seen.has(id)) return; filteredTools.forEach(([toolId, toolEntry]) => {
seen.add(id); // Skip if we've already processed this tool ID (deduplication)
const sub = tool?.subcategory || 'General'; if (processedToolIds.has(toolId)) return;
if (!subMap[sub]) subMap[sub] = []; processedToolIds.add(toolId);
subMap[sub].push({ id, tool });
// 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) // Convert to sorted array format for rendering
.sort(([a], [b]) => a.localeCompare(b)) return Object.entries(categoryToToolsMap)
.map(([subcategory, tools]) => ({ .sort(([categoryA], [categoryB]) => categoryA.localeCompare(categoryB))
subcategory, .map(([categoryName, toolsInCategory]) => ({
tools categoryName,
toolsInCategory
})); }));
}, [filteredTools]); }, [filteredTools]);
if (groups.length === 0) { if (groupedToolsByCategory.length === 0) {
return ( return (
<Text c="dimmed" size="sm" p="sm"> <Text c="dimmed" size="sm" p="sm">
No tools found No tools found
@ -39,13 +50,13 @@ const SearchResults: React.FC<SearchResultsProps> = ({ filteredTools, onSelect }
return ( return (
<Stack p="sm" gap="xs"> <Stack p="sm" gap="xs">
{groups.map(group => ( {groupedToolsByCategory.map(categoryGroup => (
<Box key={group.subcategory} w="100%"> <Box key={categoryGroup.categoryName} w="100%">
<Text size="sm" fw={500} mb="0.25rem" mt="1rem" className="tool-subcategory-title"> <Text size="sm" fw={500} mb="0.25rem" mt="1rem" className="tool-subcategory-title">
{group.subcategory} {categoryGroup.categoryName}
</Text> </Text>
<Stack gap="xs"> <Stack gap="xs">
{group.tools.map(({ id, tool }) => ( {categoryGroup.toolsInCategory.map(({ id, tool }) => (
<ToolButton <ToolButton
key={id} key={id}
id={id} id={id}
@ -59,7 +70,7 @@ const SearchResults: React.FC<SearchResultsProps> = ({ filteredTools, onSelect }
</Box> </Box>
))} ))}
{/* global spacer to allow scrolling past last row in search mode */} {/* global spacer to allow scrolling past last row in search mode */}
<div aria-hidden style={{ height: 44 * 4 }} /> <div aria-hidden style={{ height: 200 }} />
</Stack> </Stack>
); );
}; };

View File

@ -15,7 +15,6 @@ import rainbowStyles from '../../styles/rainbow.module.css';
export default function ToolPanel() { export default function ToolPanel() {
const { t } = useTranslation(); const { t } = useTranslation();
const { isRainbowMode } = useRainbowThemeContext(); const { isRainbowMode } = useRainbowThemeContext();
const { colorScheme } = useMantineColorScheme();
const { sidebarRefs } = useSidebarContext(); const { sidebarRefs } = useSidebarContext();
const { toolPanelRef } = sidebarRefs; const { toolPanelRef } = sidebarRefs;
@ -55,7 +54,7 @@ export default function ToolPanel() {
{/* Search Bar - Always visible at the top */} {/* Search Bar - Always visible at the top */}
<div <div
style={{ style={{
backgroundColor: colorScheme === 'dark' ? '#1F2329' : '#EFF1F4', backgroundColor: 'var(--tool-panel-search-bg)',
padding: '0.75rem 1rem', padding: '0.75rem 1rem',
marginBottom: (leftPanelView === 'toolContent') ? '1rem' : 0, marginBottom: (leftPanelView === 'toolContent') ? '1rem' : 0,
}} }}

View File

@ -138,6 +138,9 @@
/* Placeholder text colors */ /* Placeholder text colors */
--search-text-and-icon-color: #6B7382; --search-text-and-icon-color: #6B7382;
/* Tool panel search bar background colors */
--tool-panel-search-bg: #EFF1F4;
/* container */ /* container */
--landing-paper-bg: var(--bg-surface); --landing-paper-bg: var(--bg-surface);
--landing-inner-paper-bg: #EEF8FF; --landing-inner-paper-bg: #EEF8FF;
@ -283,6 +286,9 @@
/* Placeholder text colors (dark mode) */ /* Placeholder text colors (dark mode) */
--search-text-and-icon-color: #FFFFFF !important; --search-text-and-icon-color: #FFFFFF !important;
/* Tool panel search bar background colors (dark mode) */
--tool-panel-search-bg: #1F2329;
} }
/* Dropzone drop state styling */ /* Dropzone drop state styling */