mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-22 20:29:23 +00:00
skeleton loading
This commit is contained in:
parent
8581829f6e
commit
53e9dbb66e
@ -109,10 +109,12 @@ export default function ToolPanel() {
|
|||||||
<div className="flex-1 flex flex-col">
|
<div className="flex-1 flex flex-col">
|
||||||
{/* Tool content */}
|
{/* Tool content */}
|
||||||
<div className="flex-1 min-h-0">
|
<div className="flex-1 min-h-0">
|
||||||
<ToolRenderer
|
{selectedToolKey && (
|
||||||
selectedToolKey={selectedToolKey}
|
<ToolRenderer
|
||||||
onPreviewFile={setPreviewFile}
|
selectedToolKey={selectedToolKey}
|
||||||
/>
|
onPreviewFile={setPreviewFile}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { createContext, useContext, useMemo, useRef } from 'react';
|
import React, { createContext, useContext, useMemo, useRef, useEffect, useState } 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,6 +22,10 @@ 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[];
|
||||||
@ -74,6 +78,8 @@ const ToolStep = ({
|
|||||||
completedMessage,
|
completedMessage,
|
||||||
helpText,
|
helpText,
|
||||||
showNumber,
|
showNumber,
|
||||||
|
enableInitialSkeleton = true,
|
||||||
|
initialSkeletonMs = 800,
|
||||||
tooltip
|
tooltip
|
||||||
}: ToolStepProps) => {
|
}: ToolStepProps) => {
|
||||||
if (!isVisible) return null;
|
if (!isVisible) return null;
|
||||||
@ -88,6 +94,16 @@ 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"
|
||||||
@ -109,11 +125,19 @@ const ToolStep = ({
|
|||||||
>
|
>
|
||||||
<Flex align="center" gap="sm">
|
<Flex align="center" gap="sm">
|
||||||
{shouldShowNumber && (
|
{shouldShowNumber && (
|
||||||
<Text fw={500} size="lg" c="dimmed">
|
showInitialSkeleton ? (
|
||||||
{stepNumber}
|
<Box w={18} h={18} bg="gray.1" style={{ borderRadius: 4 }} />
|
||||||
</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 ? (
|
||||||
@ -133,15 +157,19 @@ const ToolStep = ({
|
|||||||
|
|
||||||
{isCollapsed ? (
|
{isCollapsed ? (
|
||||||
<Box>
|
<Box>
|
||||||
{isCompleted && completedMessage && (
|
{showInitialSkeleton ? (
|
||||||
<Text size="sm" c="green">
|
<Box w="40%" h={14} bg="gray.1" style={{ borderRadius: 4 }} />
|
||||||
✓ {completedMessage}
|
) : (
|
||||||
{onCollapsedClick && (
|
isCompleted && completedMessage && (
|
||||||
<Text span c="dimmed" size="xs" ml="sm">
|
<Text size="sm" c="green">
|
||||||
(click to change)
|
✓ {completedMessage}
|
||||||
</Text>
|
{onCollapsedClick && (
|
||||||
)}
|
<Text span c="dimmed" size="xs" ml="sm">
|
||||||
</Text>
|
(click to change)
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
@ -151,7 +179,16 @@ const ToolStep = ({
|
|||||||
{helpText}
|
{helpText}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
{children}
|
{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
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
</Paper>
|
</Paper>
|
||||||
|
@ -514,33 +514,6 @@ export const flatToolRegistryMap: ToolRegistry = {
|
|||||||
category: "Standard Tools",
|
category: "Standard Tools",
|
||||||
subcategory: "Page Formatting"
|
subcategory: "Page Formatting"
|
||||||
},
|
},
|
||||||
"split": {
|
|
||||||
icon: <span className="material-symbols-rounded">content_cut</span>,
|
|
||||||
name: "home.split.title",
|
|
||||||
component: null,
|
|
||||||
view: "format",
|
|
||||||
description: "home.split.desc",
|
|
||||||
category: "Standard Tools",
|
|
||||||
subcategory: "Page Formatting"
|
|
||||||
},
|
|
||||||
"split-by-chapters": {
|
|
||||||
icon: <span className="material-symbols-rounded">collections_bookmark</span>,
|
|
||||||
name: "home.splitPdfByChapters.title",
|
|
||||||
component: null,
|
|
||||||
view: "format",
|
|
||||||
description: "home.splitPdfByChapters.desc",
|
|
||||||
category: "Advanced Tools",
|
|
||||||
subcategory: "Advanced Formatting"
|
|
||||||
},
|
|
||||||
"split-by-sections": {
|
|
||||||
icon: <span className="material-symbols-rounded">grid_on</span>,
|
|
||||||
name: "home.split-by-sections.title",
|
|
||||||
component: null,
|
|
||||||
view: "format",
|
|
||||||
description: "home.split-by-sections.desc",
|
|
||||||
category: "Advanced Tools",
|
|
||||||
subcategory: "Advanced Formatting"
|
|
||||||
},
|
|
||||||
"splitPdf": {
|
"splitPdf": {
|
||||||
icon: <span className="material-symbols-rounded">content_cut</span>,
|
icon: <span className="material-symbols-rounded">content_cut</span>,
|
||||||
name: "home.split.title",
|
name: "home.split.title",
|
||||||
@ -594,7 +567,7 @@ function buildStructuredRegistry(): ToolRegistryStructured {
|
|||||||
for (const [id, tool] of entries) {
|
for (const [id, tool] of entries) {
|
||||||
const sub = tool.subcategory ?? 'General';
|
const sub = tool.subcategory ?? 'General';
|
||||||
const cat = tool.category ?? 'OTHER';
|
const cat = tool.category ?? 'OTHER';
|
||||||
// Quick access: use the existing "Recommended Tools" category
|
// Quick access: use the existing "Recommended Tools" category, this will change in future
|
||||||
if (tool.category === 'Recommended Tools') {
|
if (tool.category === 'Recommended Tools') {
|
||||||
quick.push({ id, ...tool });
|
quick.push({ id, ...tool });
|
||||||
}
|
}
|
||||||
|
@ -102,6 +102,7 @@ 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}
|
||||||
@ -129,6 +130,7 @@ 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,6 +163,7 @@ 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">
|
||||||
|
@ -96,7 +96,8 @@ const Split = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
{/* Settings Step */}
|
{/* Settings Step */}
|
||||||
<ToolStep
|
<ToolStep
|
||||||
title="Settings"
|
title="Settings"
|
||||||
isVisible={hasFiles}
|
isVisible={true}
|
||||||
|
enableInitialSkeleton={true}
|
||||||
isCollapsed={settingsCollapsed}
|
isCollapsed={settingsCollapsed}
|
||||||
isCompleted={settingsCollapsed}
|
isCompleted={settingsCollapsed}
|
||||||
onCollapsedClick={settingsCollapsed ? handleSettingsReset : undefined}
|
onCollapsedClick={settingsCollapsed ? handleSettingsReset : undefined}
|
||||||
@ -125,6 +126,7 @@ 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