mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-22 12:19:24 +00:00
remove skeleton loader
This commit is contained in:
parent
53e9dbb66e
commit
7801872810
70
frontend/src/components/shared/AllToolsNavButton.tsx
Normal file
70
frontend/src/components/shared/AllToolsNavButton.tsx
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { ActionIcon, Tooltip } from '@mantine/core';
|
||||||
|
import AppsIcon from '@mui/icons-material/AppsRounded';
|
||||||
|
import ArrowBackIcon from '@mui/icons-material/ArrowBackRounded';
|
||||||
|
import { useToolWorkflow } from '../../contexts/ToolWorkflowContext';
|
||||||
|
|
||||||
|
interface AllToolsNavButtonProps {
|
||||||
|
activeButton: string;
|
||||||
|
setActiveButton: (id: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AllToolsNavButton: React.FC<AllToolsNavButtonProps> = ({ activeButton, setActiveButton }) => {
|
||||||
|
const { selectedTool, selectedToolKey, handleReaderToggle, handleBackToTools } = useToolWorkflow();
|
||||||
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
setActiveButton('tools');
|
||||||
|
// Preserve existing behavior used in QuickAccessBar header
|
||||||
|
handleReaderToggle();
|
||||||
|
handleBackToTools();
|
||||||
|
};
|
||||||
|
|
||||||
|
const isActive = activeButton === 'tools';
|
||||||
|
|
||||||
|
const iconNode = (() => {
|
||||||
|
if (selectedToolKey) {
|
||||||
|
if (isHovered) return <ArrowBackIcon sx={{ fontSize: '1.75rem' }} />;
|
||||||
|
return (
|
||||||
|
<span className="iconContainer">
|
||||||
|
{selectedTool?.icon ?? <AppsIcon sx={{ fontSize: '1.75rem' }} />}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<span className="iconContainer">
|
||||||
|
<AppsIcon sx={{ fontSize: '1.75rem' }} />
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
})();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip label={selectedToolKey && isHovered ? 'Back to all tools' : 'View all available tools'} position="right">
|
||||||
|
<div className="flex flex-col items-center gap-1 mt-4 mb-2">
|
||||||
|
<ActionIcon
|
||||||
|
size="lg"
|
||||||
|
variant="subtle"
|
||||||
|
onClick={handleClick}
|
||||||
|
onMouseEnter={() => setIsHovered(true)}
|
||||||
|
onMouseLeave={() => setIsHovered(false)}
|
||||||
|
style={{
|
||||||
|
backgroundColor: isActive ? 'var(--icon-tools-bg)' : 'var(--icon-inactive-bg)',
|
||||||
|
color: isActive ? 'var(--icon-tools-color)' : 'var(--icon-inactive-color)',
|
||||||
|
border: 'none',
|
||||||
|
borderRadius: '8px',
|
||||||
|
}}
|
||||||
|
className={isActive ? 'activeIconScale' : ''}
|
||||||
|
>
|
||||||
|
{iconNode}
|
||||||
|
</ActionIcon>
|
||||||
|
<span className={`all-tools-text ${isActive ? 'active' : 'inactive'}`}>
|
||||||
|
All Tools
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AllToolsNavButton;
|
||||||
|
|
||||||
|
|
@ -1,12 +1,8 @@
|
|||||||
import React, { useState, useRef, forwardRef } from "react";
|
import React, { useState, useRef, forwardRef } from "react";
|
||||||
import { ActionIcon, Stack, Tooltip, Divider } from "@mantine/core";
|
import { ActionIcon, Stack, Tooltip, Divider } from "@mantine/core";
|
||||||
import MenuBookIcon from "@mui/icons-material/MenuBookRounded";
|
import MenuBookIcon from "@mui/icons-material/MenuBookRounded";
|
||||||
import AppsIcon from "@mui/icons-material/AppsRounded";
|
|
||||||
import SettingsIcon from "@mui/icons-material/SettingsRounded";
|
import SettingsIcon from "@mui/icons-material/SettingsRounded";
|
||||||
import AutoAwesomeIcon from "@mui/icons-material/AutoAwesomeRounded";
|
|
||||||
import FolderIcon from "@mui/icons-material/FolderRounded";
|
import FolderIcon from "@mui/icons-material/FolderRounded";
|
||||||
import PersonIcon from "@mui/icons-material/PersonRounded";
|
|
||||||
import NotificationsIcon from "@mui/icons-material/NotificationsRounded";
|
|
||||||
import { useRainbowThemeContext } from "./RainbowThemeProvider";
|
import { useRainbowThemeContext } from "./RainbowThemeProvider";
|
||||||
import AppConfigModal from './AppConfigModal';
|
import AppConfigModal from './AppConfigModal';
|
||||||
import { useIsOverflowing } from '../../hooks/useIsOverflowing';
|
import { useIsOverflowing } from '../../hooks/useIsOverflowing';
|
||||||
@ -14,48 +10,7 @@ import { useFilesModalContext } from '../../contexts/FilesModalContext';
|
|||||||
import { useToolWorkflow } from '../../contexts/ToolWorkflowContext';
|
import { useToolWorkflow } from '../../contexts/ToolWorkflowContext';
|
||||||
import { ButtonConfig } from '../../types/sidebar';
|
import { ButtonConfig } from '../../types/sidebar';
|
||||||
import './QuickAccessBar.css';
|
import './QuickAccessBar.css';
|
||||||
|
import AllToolsNavButton from './AllToolsNavButton';
|
||||||
function NavHeader({
|
|
||||||
activeButton,
|
|
||||||
setActiveButton
|
|
||||||
}: {
|
|
||||||
activeButton: string;
|
|
||||||
setActiveButton: (id: string) => void;
|
|
||||||
}) {
|
|
||||||
const { handleReaderToggle, handleBackToTools } = useToolWorkflow();
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{/* All Tools button below divider */}
|
|
||||||
<Tooltip label="View all available tools" position="right">
|
|
||||||
<div className="flex flex-col items-center gap-1 mt-4 mb-2">
|
|
||||||
<ActionIcon
|
|
||||||
size="lg"
|
|
||||||
variant="subtle"
|
|
||||||
onClick={() => {
|
|
||||||
setActiveButton('tools');
|
|
||||||
handleReaderToggle();
|
|
||||||
handleBackToTools();
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
backgroundColor: activeButton === 'tools' ? 'var(--icon-tools-bg)' : 'var(--icon-inactive-bg)',
|
|
||||||
color: activeButton === 'tools' ? 'var(--icon-tools-color)' : 'var(--icon-inactive-color)',
|
|
||||||
border: 'none',
|
|
||||||
borderRadius: '8px',
|
|
||||||
}}
|
|
||||||
className={activeButton === 'tools' ? 'activeIconScale' : ''}
|
|
||||||
>
|
|
||||||
<span className="iconContainer">
|
|
||||||
<AppsIcon sx={{ fontSize: "1.75rem" }} />
|
|
||||||
</span>
|
|
||||||
</ActionIcon>
|
|
||||||
<span className={`all-tools-text ${activeButton === 'tools' ? 'active' : 'inactive'}`}>
|
|
||||||
All Tools
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const QuickAccessBar = forwardRef<HTMLDivElement>(({
|
const QuickAccessBar = forwardRef<HTMLDivElement>(({
|
||||||
}, ref) => {
|
}, ref) => {
|
||||||
@ -89,9 +44,9 @@ const QuickAccessBar = forwardRef<HTMLDivElement>(({
|
|||||||
id: 'sign',
|
id: 'sign',
|
||||||
name: 'Sign',
|
name: 'Sign',
|
||||||
icon:
|
icon:
|
||||||
<span className="material-symbols-rounded font-size-20">
|
<span className="material-symbols-rounded font-size-20">
|
||||||
signature
|
signature
|
||||||
</span>,
|
</span>,
|
||||||
tooltip: 'Sign your document',
|
tooltip: 'Sign your document',
|
||||||
size: 'lg',
|
size: 'lg',
|
||||||
isRound: false,
|
isRound: false,
|
||||||
@ -102,9 +57,9 @@ const QuickAccessBar = forwardRef<HTMLDivElement>(({
|
|||||||
id: 'automate',
|
id: 'automate',
|
||||||
name: 'Automate',
|
name: 'Automate',
|
||||||
icon:
|
icon:
|
||||||
<span className="material-symbols-rounded font-size-20">
|
<span className="material-symbols-rounded font-size-20">
|
||||||
automation
|
automation
|
||||||
</span>,
|
</span>,
|
||||||
tooltip: 'Automate workflows',
|
tooltip: 'Automate workflows',
|
||||||
size: 'lg',
|
size: 'lg',
|
||||||
isRound: false,
|
isRound: false,
|
||||||
@ -125,9 +80,9 @@ const QuickAccessBar = forwardRef<HTMLDivElement>(({
|
|||||||
id: 'activity',
|
id: 'activity',
|
||||||
name: 'Activity',
|
name: 'Activity',
|
||||||
icon:
|
icon:
|
||||||
<span className="material-symbols-rounded font-size-20">
|
<span className="material-symbols-rounded font-size-20">
|
||||||
vital_signs
|
vital_signs
|
||||||
</span>,
|
</span>,
|
||||||
tooltip: 'View activity and analytics',
|
tooltip: 'View activity and analytics',
|
||||||
isRound: true,
|
isRound: true,
|
||||||
size: 'lg',
|
size: 'lg',
|
||||||
@ -194,10 +149,8 @@ const QuickAccessBar = forwardRef<HTMLDivElement>(({
|
|||||||
>
|
>
|
||||||
{/* Fixed header outside scrollable area */}
|
{/* Fixed header outside scrollable area */}
|
||||||
<div className="quick-access-header">
|
<div className="quick-access-header">
|
||||||
<NavHeader
|
<AllToolsNavButton activeButton={activeButton} setActiveButton={setActiveButton} />
|
||||||
activeButton={activeButton}
|
|
||||||
setActiveButton={setActiveButton}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Conditional divider when overflowing */}
|
{/* Conditional divider when overflowing */}
|
||||||
@ -244,10 +197,10 @@ const QuickAccessBar = forwardRef<HTMLDivElement>(({
|
|||||||
|
|
||||||
{/* Add divider after Automate button (index 2) */}
|
{/* Add divider after Automate button (index 2) */}
|
||||||
{index === 2 && (
|
{index === 2 && (
|
||||||
<Divider
|
<Divider
|
||||||
size="xs"
|
size="xs"
|
||||||
className="content-divider"
|
className="content-divider"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
))}
|
))}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { createContext, useContext, useMemo, useRef, useEffect, useState } from 'react';
|
import React, { createContext, useContext, useMemo, useRef } from 'react';
|
||||||
import { Paper, Text, Stack, Box, Flex } from '@mantine/core';
|
import { Paper, Text, Stack, Box, Flex } from '@mantine/core';
|
||||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||||
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
||||||
@ -22,10 +22,6 @@ export interface ToolStepProps {
|
|||||||
completedMessage?: string;
|
completedMessage?: string;
|
||||||
helpText?: string;
|
helpText?: string;
|
||||||
showNumber?: boolean;
|
showNumber?: boolean;
|
||||||
/** Show a brief skeleton for smoother tool transitions */
|
|
||||||
enableInitialSkeleton?: boolean;
|
|
||||||
/** Duration for the initial skeleton in milliseconds */
|
|
||||||
initialSkeletonMs?: number;
|
|
||||||
tooltip?: {
|
tooltip?: {
|
||||||
content?: React.ReactNode;
|
content?: React.ReactNode;
|
||||||
tips?: TooltipTip[];
|
tips?: TooltipTip[];
|
||||||
@ -78,8 +74,6 @@ const ToolStep = ({
|
|||||||
completedMessage,
|
completedMessage,
|
||||||
helpText,
|
helpText,
|
||||||
showNumber,
|
showNumber,
|
||||||
enableInitialSkeleton = true,
|
|
||||||
initialSkeletonMs = 800,
|
|
||||||
tooltip
|
tooltip
|
||||||
}: ToolStepProps) => {
|
}: ToolStepProps) => {
|
||||||
if (!isVisible) return null;
|
if (!isVisible) return null;
|
||||||
@ -94,16 +88,6 @@ const ToolStep = ({
|
|||||||
|
|
||||||
const stepNumber = parent?.getStepNumber?.() || 1;
|
const stepNumber = parent?.getStepNumber?.() || 1;
|
||||||
|
|
||||||
// Show a step-sized skeleton immediately after mount to indicate tool switch
|
|
||||||
const [showInitialSkeleton, setShowInitialSkeleton] = useState(enableInitialSkeleton);
|
|
||||||
useEffect(() => {
|
|
||||||
if (!enableInitialSkeleton) return;
|
|
||||||
setShowInitialSkeleton(true);
|
|
||||||
const id = setTimeout(() => setShowInitialSkeleton(false), initialSkeletonMs);
|
|
||||||
return () => clearTimeout(id);
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper
|
<Paper
|
||||||
p="md"
|
p="md"
|
||||||
@ -125,19 +109,11 @@ const ToolStep = ({
|
|||||||
>
|
>
|
||||||
<Flex align="center" gap="sm">
|
<Flex align="center" gap="sm">
|
||||||
{shouldShowNumber && (
|
{shouldShowNumber && (
|
||||||
showInitialSkeleton ? (
|
<Text fw={500} size="lg" c="dimmed">
|
||||||
<Box w={18} h={18} bg="gray.1" style={{ borderRadius: 4 }} />
|
{stepNumber}
|
||||||
) : (
|
</Text>
|
||||||
<Text fw={500} size="lg" c="dimmed">
|
|
||||||
{stepNumber}
|
|
||||||
</Text>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
{showInitialSkeleton ? (
|
|
||||||
<Box w={80} h={20} bg="gray.1" style={{ borderRadius: 4 }} />
|
|
||||||
) : (
|
|
||||||
renderTooltipTitle(title, tooltip, isCollapsed)
|
|
||||||
)}
|
)}
|
||||||
|
{renderTooltipTitle(title, tooltip, isCollapsed)}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{isCollapsed ? (
|
{isCollapsed ? (
|
||||||
@ -157,19 +133,15 @@ const ToolStep = ({
|
|||||||
|
|
||||||
{isCollapsed ? (
|
{isCollapsed ? (
|
||||||
<Box>
|
<Box>
|
||||||
{showInitialSkeleton ? (
|
{isCompleted && completedMessage && (
|
||||||
<Box w="40%" h={14} bg="gray.1" style={{ borderRadius: 4 }} />
|
<Text size="sm" c="green">
|
||||||
) : (
|
✓ {completedMessage}
|
||||||
isCompleted && completedMessage && (
|
{onCollapsedClick && (
|
||||||
<Text size="sm" c="green">
|
<Text span c="dimmed" size="xs" ml="sm">
|
||||||
✓ {completedMessage}
|
(click to change)
|
||||||
{onCollapsedClick && (
|
</Text>
|
||||||
<Text span c="dimmed" size="xs" ml="sm">
|
)}
|
||||||
(click to change)
|
</Text>
|
||||||
</Text>
|
|
||||||
)}
|
|
||||||
</Text>
|
|
||||||
)
|
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
@ -179,16 +151,7 @@ const ToolStep = ({
|
|||||||
{helpText}
|
{helpText}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
{showInitialSkeleton ? (
|
{children}
|
||||||
<>
|
|
||||||
<Box w="80%" h={16} bg="gray.1" style={{ borderRadius: 4 }} />
|
|
||||||
<Box w="60%" h={16} bg="gray.1" style={{ borderRadius: 4 }} />
|
|
||||||
<Box w="100%" h={44} bg="gray.1" style={{ borderRadius: 8 }} />
|
|
||||||
<Box w="100%" h={44} bg="gray.1" style={{ borderRadius: 8 }} />
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
children
|
|
||||||
)}
|
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
</Paper>
|
</Paper>
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import SplitPdfPanel from "../tools/Split";
|
import SplitPdfPanel from "../tools/Split";
|
||||||
import CompressPdfPanel from "../tools/Compress";
|
import CompressPdfPanel from "../tools/Compress";
|
||||||
import MergePdfPanel from "../tools/Merge";
|
|
||||||
import OCRPanel from '../tools/OCR';
|
import OCRPanel from '../tools/OCR';
|
||||||
import ConvertPanel from '../tools/Convert';
|
import ConvertPanel from '../tools/Convert';
|
||||||
|
|
||||||
@ -282,7 +281,7 @@ export const flatToolRegistryMap: ToolRegistry = {
|
|||||||
"mergePdfs": {
|
"mergePdfs": {
|
||||||
icon: <span className="material-symbols-rounded">library_add</span>,
|
icon: <span className="material-symbols-rounded">library_add</span>,
|
||||||
name: "home.merge.title",
|
name: "home.merge.title",
|
||||||
component: MergePdfPanel,
|
component: null,
|
||||||
view: "merge",
|
view: "merge",
|
||||||
description: "home.merge.desc",
|
description: "home.merge.desc",
|
||||||
category: "Recommended Tools",
|
category: "Recommended Tools",
|
||||||
|
@ -102,7 +102,6 @@ const Compress = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
<ToolStep
|
<ToolStep
|
||||||
title="Settings"
|
title="Settings"
|
||||||
isVisible={hasFiles}
|
isVisible={hasFiles}
|
||||||
enableInitialSkeleton={false}
|
|
||||||
isCollapsed={settingsCollapsed}
|
isCollapsed={settingsCollapsed}
|
||||||
isCompleted={settingsCollapsed}
|
isCompleted={settingsCollapsed}
|
||||||
onCollapsedClick={settingsCollapsed ? handleSettingsReset : undefined}
|
onCollapsedClick={settingsCollapsed ? handleSettingsReset : undefined}
|
||||||
@ -130,7 +129,6 @@ const Compress = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
<ToolStep
|
<ToolStep
|
||||||
title="Results"
|
title="Results"
|
||||||
isVisible={hasResults}
|
isVisible={hasResults}
|
||||||
enableInitialSkeleton={false}
|
|
||||||
>
|
>
|
||||||
<Stack gap="sm">
|
<Stack gap="sm">
|
||||||
{compressOperation.status && (
|
{compressOperation.status && (
|
||||||
|
@ -163,7 +163,6 @@ const Convert = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
<ToolStep
|
<ToolStep
|
||||||
title={t("convert.results", "Results")}
|
title={t("convert.results", "Results")}
|
||||||
isVisible={hasResults}
|
isVisible={hasResults}
|
||||||
enableInitialSkeleton={false}
|
|
||||||
data-testid="conversion-results"
|
data-testid="conversion-results"
|
||||||
>
|
>
|
||||||
<Stack gap="sm">
|
<Stack gap="sm">
|
||||||
|
@ -97,7 +97,6 @@ const Split = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
<ToolStep
|
<ToolStep
|
||||||
title="Settings"
|
title="Settings"
|
||||||
isVisible={true}
|
isVisible={true}
|
||||||
enableInitialSkeleton={true}
|
|
||||||
isCollapsed={settingsCollapsed}
|
isCollapsed={settingsCollapsed}
|
||||||
isCompleted={settingsCollapsed}
|
isCompleted={settingsCollapsed}
|
||||||
onCollapsedClick={settingsCollapsed ? handleSettingsReset : undefined}
|
onCollapsedClick={settingsCollapsed ? handleSettingsReset : undefined}
|
||||||
@ -126,7 +125,6 @@ const Split = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
<ToolStep
|
<ToolStep
|
||||||
title="Results"
|
title="Results"
|
||||||
isVisible={hasResults}
|
isVisible={hasResults}
|
||||||
enableInitialSkeleton={false}
|
|
||||||
>
|
>
|
||||||
<Stack gap="sm">
|
<Stack gap="sm">
|
||||||
{splitOperation.status && (
|
{splitOperation.status && (
|
||||||
|
Loading…
x
Reference in New Issue
Block a user