mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-22 04:09:22 +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 { ActionIcon, Stack, Tooltip, Divider } from "@mantine/core";
|
||||
import MenuBookIcon from "@mui/icons-material/MenuBookRounded";
|
||||
import AppsIcon from "@mui/icons-material/AppsRounded";
|
||||
import SettingsIcon from "@mui/icons-material/SettingsRounded";
|
||||
import AutoAwesomeIcon from "@mui/icons-material/AutoAwesomeRounded";
|
||||
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 AppConfigModal from './AppConfigModal';
|
||||
import { useIsOverflowing } from '../../hooks/useIsOverflowing';
|
||||
@ -14,48 +10,7 @@ import { useFilesModalContext } from '../../contexts/FilesModalContext';
|
||||
import { useToolWorkflow } from '../../contexts/ToolWorkflowContext';
|
||||
import { ButtonConfig } from '../../types/sidebar';
|
||||
import './QuickAccessBar.css';
|
||||
|
||||
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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
import AllToolsNavButton from './AllToolsNavButton';
|
||||
|
||||
const QuickAccessBar = forwardRef<HTMLDivElement>(({
|
||||
}, ref) => {
|
||||
@ -88,10 +43,10 @@ const QuickAccessBar = forwardRef<HTMLDivElement>(({
|
||||
{
|
||||
id: 'sign',
|
||||
name: 'Sign',
|
||||
icon:
|
||||
<span className="material-symbols-rounded font-size-20">
|
||||
signature
|
||||
</span>,
|
||||
icon:
|
||||
<span className="material-symbols-rounded font-size-20">
|
||||
signature
|
||||
</span>,
|
||||
tooltip: 'Sign your document',
|
||||
size: 'lg',
|
||||
isRound: false,
|
||||
@ -101,10 +56,10 @@ const QuickAccessBar = forwardRef<HTMLDivElement>(({
|
||||
{
|
||||
id: 'automate',
|
||||
name: 'Automate',
|
||||
icon:
|
||||
<span className="material-symbols-rounded font-size-20">
|
||||
automation
|
||||
</span>,
|
||||
icon:
|
||||
<span className="material-symbols-rounded font-size-20">
|
||||
automation
|
||||
</span>,
|
||||
tooltip: 'Automate workflows',
|
||||
size: 'lg',
|
||||
isRound: false,
|
||||
@ -124,10 +79,10 @@ const QuickAccessBar = forwardRef<HTMLDivElement>(({
|
||||
{
|
||||
id: 'activity',
|
||||
name: 'Activity',
|
||||
icon:
|
||||
<span className="material-symbols-rounded font-size-20">
|
||||
vital_signs
|
||||
</span>,
|
||||
icon:
|
||||
<span className="material-symbols-rounded font-size-20">
|
||||
vital_signs
|
||||
</span>,
|
||||
tooltip: 'View activity and analytics',
|
||||
isRound: true,
|
||||
size: 'lg',
|
||||
@ -164,7 +119,7 @@ const QuickAccessBar = forwardRef<HTMLDivElement>(({
|
||||
|
||||
const getButtonStyle = (config: ButtonConfig) => {
|
||||
const isActive = isButtonActive(config);
|
||||
|
||||
|
||||
if (isActive) {
|
||||
return {
|
||||
backgroundColor: `var(--icon-${config.id}-bg)`,
|
||||
@ -173,7 +128,7 @@ const QuickAccessBar = forwardRef<HTMLDivElement>(({
|
||||
borderRadius: getBorderRadius(config),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// Inactive state for all buttons
|
||||
return {
|
||||
backgroundColor: 'var(--icon-inactive-bg)',
|
||||
@ -194,16 +149,14 @@ const QuickAccessBar = forwardRef<HTMLDivElement>(({
|
||||
>
|
||||
{/* Fixed header outside scrollable area */}
|
||||
<div className="quick-access-header">
|
||||
<NavHeader
|
||||
activeButton={activeButton}
|
||||
setActiveButton={setActiveButton}
|
||||
/>
|
||||
<AllToolsNavButton activeButton={activeButton} setActiveButton={setActiveButton} />
|
||||
|
||||
</div>
|
||||
|
||||
{/* Conditional divider when overflowing */}
|
||||
{isOverflow && (
|
||||
<Divider
|
||||
size="xs"
|
||||
<Divider
|
||||
size="xs"
|
||||
className="overflow-divider"
|
||||
/>
|
||||
)}
|
||||
@ -241,21 +194,21 @@ const QuickAccessBar = forwardRef<HTMLDivElement>(({
|
||||
</span>
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
|
||||
{/* Add divider after Automate button (index 2) */}
|
||||
{index === 2 && (
|
||||
<Divider
|
||||
size="xs"
|
||||
className="content-divider"
|
||||
/>
|
||||
<Divider
|
||||
size="xs"
|
||||
className="content-divider"
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</Stack>
|
||||
|
||||
|
||||
{/* Spacer to push Config button to bottom */}
|
||||
<div className="spacer" />
|
||||
|
||||
|
||||
{/* Config button at the bottom */}
|
||||
{buttonConfigs
|
||||
.filter(config => config.id === 'config')
|
||||
|
@ -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 ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
||||
@ -22,10 +22,6 @@ export interface ToolStepProps {
|
||||
completedMessage?: string;
|
||||
helpText?: string;
|
||||
showNumber?: boolean;
|
||||
/** Show a brief skeleton for smoother tool transitions */
|
||||
enableInitialSkeleton?: boolean;
|
||||
/** Duration for the initial skeleton in milliseconds */
|
||||
initialSkeletonMs?: number;
|
||||
tooltip?: {
|
||||
content?: React.ReactNode;
|
||||
tips?: TooltipTip[];
|
||||
@ -78,8 +74,6 @@ const ToolStep = ({
|
||||
completedMessage,
|
||||
helpText,
|
||||
showNumber,
|
||||
enableInitialSkeleton = true,
|
||||
initialSkeletonMs = 800,
|
||||
tooltip
|
||||
}: ToolStepProps) => {
|
||||
if (!isVisible) return null;
|
||||
@ -94,16 +88,6 @@ const ToolStep = ({
|
||||
|
||||
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 (
|
||||
<Paper
|
||||
p="md"
|
||||
@ -125,19 +109,11 @@ const ToolStep = ({
|
||||
>
|
||||
<Flex align="center" gap="sm">
|
||||
{shouldShowNumber && (
|
||||
showInitialSkeleton ? (
|
||||
<Box w={18} h={18} bg="gray.1" style={{ borderRadius: 4 }} />
|
||||
) : (
|
||||
<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)
|
||||
<Text fw={500} size="lg" c="dimmed">
|
||||
{stepNumber}
|
||||
</Text>
|
||||
)}
|
||||
{renderTooltipTitle(title, tooltip, isCollapsed)}
|
||||
</Flex>
|
||||
|
||||
{isCollapsed ? (
|
||||
@ -157,19 +133,15 @@ const ToolStep = ({
|
||||
|
||||
{isCollapsed ? (
|
||||
<Box>
|
||||
{showInitialSkeleton ? (
|
||||
<Box w="40%" h={14} bg="gray.1" style={{ borderRadius: 4 }} />
|
||||
) : (
|
||||
isCompleted && completedMessage && (
|
||||
<Text size="sm" c="green">
|
||||
✓ {completedMessage}
|
||||
{onCollapsedClick && (
|
||||
<Text span c="dimmed" size="xs" ml="sm">
|
||||
(click to change)
|
||||
</Text>
|
||||
)}
|
||||
</Text>
|
||||
)
|
||||
{isCompleted && completedMessage && (
|
||||
<Text size="sm" c="green">
|
||||
✓ {completedMessage}
|
||||
{onCollapsedClick && (
|
||||
<Text span c="dimmed" size="xs" ml="sm">
|
||||
(click to change)
|
||||
</Text>
|
||||
)}
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
) : (
|
||||
@ -179,16 +151,7 @@ const ToolStep = ({
|
||||
{helpText}
|
||||
</Text>
|
||||
)}
|
||||
{showInitialSkeleton ? (
|
||||
<>
|
||||
<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
|
||||
)}
|
||||
{children}
|
||||
</Stack>
|
||||
)}
|
||||
</Paper>
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import SplitPdfPanel from "../tools/Split";
|
||||
import CompressPdfPanel from "../tools/Compress";
|
||||
import MergePdfPanel from "../tools/Merge";
|
||||
import OCRPanel from '../tools/OCR';
|
||||
import ConvertPanel from '../tools/Convert';
|
||||
|
||||
@ -282,7 +281,7 @@ export const flatToolRegistryMap: ToolRegistry = {
|
||||
"mergePdfs": {
|
||||
icon: <span className="material-symbols-rounded">library_add</span>,
|
||||
name: "home.merge.title",
|
||||
component: MergePdfPanel,
|
||||
component: null,
|
||||
view: "merge",
|
||||
description: "home.merge.desc",
|
||||
category: "Recommended Tools",
|
||||
|
@ -102,7 +102,6 @@ const Compress = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
||||
<ToolStep
|
||||
title="Settings"
|
||||
isVisible={hasFiles}
|
||||
enableInitialSkeleton={false}
|
||||
isCollapsed={settingsCollapsed}
|
||||
isCompleted={settingsCollapsed}
|
||||
onCollapsedClick={settingsCollapsed ? handleSettingsReset : undefined}
|
||||
@ -130,7 +129,6 @@ const Compress = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
||||
<ToolStep
|
||||
title="Results"
|
||||
isVisible={hasResults}
|
||||
enableInitialSkeleton={false}
|
||||
>
|
||||
<Stack gap="sm">
|
||||
{compressOperation.status && (
|
||||
|
@ -163,7 +163,6 @@ const Convert = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
||||
<ToolStep
|
||||
title={t("convert.results", "Results")}
|
||||
isVisible={hasResults}
|
||||
enableInitialSkeleton={false}
|
||||
data-testid="conversion-results"
|
||||
>
|
||||
<Stack gap="sm">
|
||||
|
@ -97,7 +97,6 @@ const Split = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
||||
<ToolStep
|
||||
title="Settings"
|
||||
isVisible={true}
|
||||
enableInitialSkeleton={true}
|
||||
isCollapsed={settingsCollapsed}
|
||||
isCompleted={settingsCollapsed}
|
||||
onCollapsedClick={settingsCollapsed ? handleSettingsReset : undefined}
|
||||
@ -126,7 +125,6 @@ const Split = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
||||
<ToolStep
|
||||
title="Results"
|
||||
isVisible={hasResults}
|
||||
enableInitialSkeleton={false}
|
||||
>
|
||||
<Stack gap="sm">
|
||||
{splitOperation.status && (
|
||||
|
Loading…
x
Reference in New Issue
Block a user