mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-22 12:19:24 +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 { 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>
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -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,
|
||||||
}}
|
}}
|
||||||
|
@ -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 */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user