mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-22 04:09:22 +00:00
clarify what search results is doing, better variable names/function names and comments
This commit is contained in:
parent
d664666ff8
commit
359a164751
@ -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<HTMLDivElement>(({
|
||||
>
|
||||
{/* Fixed header outside scrollable area */}
|
||||
<div className="quick-access-header">
|
||||
<TopToolIndicator activeButton={activeButton} setActiveButton={setActiveButton} />
|
||||
<ActiveToolButton activeButton={activeButton} setActiveButton={setActiveButton} />
|
||||
<AllToolsNavButton activeButton={activeButton} setActiveButton={setActiveButton} />
|
||||
|
||||
</div>
|
||||
|
@ -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<TopToolIndicatorProps> = ({ activeButton, setActiveButton }) => {
|
||||
const ActiveToolButton: React.FC<ActiveToolButtonProps> = ({ activeButton, setActiveButton }) => {
|
||||
const { selectedTool, selectedToolKey, leftPanelView, handleBackToTools } = useToolWorkflow();
|
||||
|
||||
// Determine if the indicator should be visible
|
||||
@ -28,43 +42,55 @@ const TopToolIndicator: React.FC<TopToolIndicatorProps> = ({ activeButton, setAc
|
||||
const [isBackHover, setIsBackHover] = useState<boolean>(false);
|
||||
const prevKeyRef = useRef<string | null>(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 (
|
||||
<>
|
||||
<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 && (
|
||||
<div className="current-tool-content">
|
||||
<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}
|
||||
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<TopToolIndicatorProps> = ({ activeButton, setAc
|
||||
);
|
||||
};
|
||||
|
||||
export default TopToolIndicator;
|
||||
export default ActiveToolButton;
|
||||
|
||||
|
@ -9,27 +9,38 @@ interface SearchResultsProps {
|
||||
}
|
||||
|
||||
const SearchResults: React.FC<SearchResultsProps> = ({ filteredTools, onSelect }) => {
|
||||
const groups = useMemo(() => {
|
||||
const subMap: Record<string, Array<{ id: string; tool: ToolRegistryEntry }>> = {};
|
||||
const seen = new Set<string>();
|
||||
// Group tools by subcategory and remove duplicates
|
||||
const groupedToolsByCategory = useMemo(() => {
|
||||
const categoryToToolsMap: Record<string, Array<{ id: string; tool: ToolRegistryEntry }>> = {};
|
||||
const processedToolIds = new Set<string>();
|
||||
|
||||
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 (
|
||||
<Text c="dimmed" size="sm" p="sm">
|
||||
No tools found
|
||||
@ -39,13 +50,13 @@ const SearchResults: React.FC<SearchResultsProps> = ({ filteredTools, onSelect }
|
||||
|
||||
return (
|
||||
<Stack p="sm" gap="xs">
|
||||
{groups.map(group => (
|
||||
<Box key={group.subcategory} w="100%">
|
||||
{groupedToolsByCategory.map(categoryGroup => (
|
||||
<Box key={categoryGroup.categoryName} w="100%">
|
||||
<Text size="sm" fw={500} mb="0.25rem" mt="1rem" className="tool-subcategory-title">
|
||||
{group.subcategory}
|
||||
{categoryGroup.categoryName}
|
||||
</Text>
|
||||
<Stack gap="xs">
|
||||
{group.tools.map(({ id, tool }) => (
|
||||
{categoryGroup.toolsInCategory.map(({ id, tool }) => (
|
||||
<ToolButton
|
||||
key={id}
|
||||
id={id}
|
||||
@ -59,7 +70,7 @@ const SearchResults: React.FC<SearchResultsProps> = ({ filteredTools, onSelect }
|
||||
</Box>
|
||||
))}
|
||||
{/* 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>
|
||||
);
|
||||
};
|
||||
|
@ -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 */}
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: colorScheme === 'dark' ? '#1F2329' : '#EFF1F4',
|
||||
backgroundColor: 'var(--tool-panel-search-bg)',
|
||||
padding: '0.75rem 1rem',
|
||||
marginBottom: (leftPanelView === 'toolContent') ? '1rem' : 0,
|
||||
}}
|
||||
|
@ -138,6 +138,9 @@
|
||||
/* Placeholder text colors */
|
||||
--search-text-and-icon-color: #6B7382;
|
||||
|
||||
/* Tool panel search bar background colors */
|
||||
--tool-panel-search-bg: #EFF1F4;
|
||||
|
||||
/* container */
|
||||
--landing-paper-bg: var(--bg-surface);
|
||||
--landing-inner-paper-bg: #EEF8FF;
|
||||
@ -283,6 +286,9 @@
|
||||
|
||||
/* Placeholder text colors (dark mode) */
|
||||
--search-text-and-icon-color: #FFFFFF !important;
|
||||
|
||||
/* Tool panel search bar background colors (dark mode) */
|
||||
--tool-panel-search-bg: #1F2329;
|
||||
}
|
||||
|
||||
/* Dropzone drop state styling */
|
||||
|
Loading…
x
Reference in New Issue
Block a user