motion blur image

This commit is contained in:
Nate Kelley 2025-10-07 11:17:19 -06:00
parent 9f3d08227e
commit 9a3c41bc6e
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
7 changed files with 52 additions and 19 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@ -57,4 +57,11 @@ export const Default: Story = {
args: {
selectedItem: mockSearchTextData,
},
decorators: [
(Story) => (
<div className="w-[420px] h-[420px] border-red-500 border">
<Story />
</div>
),
],
};

View File

@ -1,9 +1,12 @@
import type { SearchTextData } from '@buster/server-shared/search';
import { motion } from 'framer-motion';
import type React from 'react';
import { useState } from 'react';
import SkeletonSearchChat from '@/assets/png/skeleton-screenshot-chat.png';
import SkeletonSearchDashboard from '@/assets/png/skeleton-screenshot-dashbaord.png';
import SkeletonSearchDashboard from '@/assets/png/skeleton-screenshot-dashboard.png';
import SkeletonSearchMetric from '@/assets/png/skeleton-screenshot-metric.png';
import SkeletonSearchReport from '@/assets/png/skeleton-screenshot-report.png';
import { CircleSpinnerLoader } from '@/components/ui/loaders';
export type GlobalSearchSecondaryContentProps = {
selectedItem: SearchTextData;
@ -22,7 +25,7 @@ export const GlobalSearchSecondaryContent: React.FC<GlobalSearchSecondaryContent
screenshotUrl,
createdBy,
} = selectedItem;
console.log(selectedItem);
return (
<div className="p-3 min-w-[420px] min-h-[420px]">
<ScreenshotImage screenshotUrl={screenshotUrl} assetType={assetType} />
@ -37,33 +40,56 @@ const ScreenshotImage = ({
screenshotUrl: string | null | undefined;
assetType: SearchTextData['assetType'];
}) => {
let imageUrl = screenshotUrl;
if (!imageUrl) {
if (assetType === 'chat') {
imageUrl = SkeletonSearchChat;
} else if (assetType === 'metric_file') {
imageUrl = SkeletonSearchMetric;
} else if (assetType === 'dashboard_file') {
imageUrl = SkeletonSearchDashboard;
} else if (assetType === 'report_file') {
imageUrl = SkeletonSearchReport;
} else if (assetType === 'collection') {
imageUrl = SkeletonSearchMetric;
} else {
const _exhaustiveCheck: never = assetType;
}
const [isLoaded, setIsLoaded] = useState(false);
const [hasError, setHasError] = useState(false);
let fallbackImageUrl = '';
if (assetType === 'chat') {
fallbackImageUrl = SkeletonSearchChat;
} else if (assetType === 'metric_file') {
fallbackImageUrl = SkeletonSearchMetric;
} else if (assetType === 'dashboard_file') {
fallbackImageUrl = SkeletonSearchDashboard;
} else if (assetType === 'report_file') {
fallbackImageUrl = SkeletonSearchReport;
} else if (assetType === 'collection') {
fallbackImageUrl = SkeletonSearchMetric;
} else {
const _exhaustiveCheck: never = assetType;
}
const imageUrl = hasError || !screenshotUrl ? fallbackImageUrl : screenshotUrl;
return (
<div
className="bg-gray-100 rounded border overflow-hidden w-full h-full"
className="bg-background rounded border overflow-hidden w-full h-full relative"
style={{
height: '240px',
maxHeight: '240px',
minHeight: '240px',
}}
>
<img src={imageUrl || ''} alt="Screenshot" className="w-full h-full object-cover" />
<motion.div
className="absolute inset-0 flex items-center justify-center"
initial={{ opacity: 1 }}
animate={{ opacity: isLoaded ? 0 : 1 }}
transition={{ duration: 0.2, ease: 'easeOut' }}
>
<CircleSpinnerLoader size={18} />
</motion.div>
<motion.img
src={imageUrl}
alt="Screenshot"
className="w-full h-full object-cover object-top"
initial={{ opacity: 0, filter: 'blur(4px)' }}
animate={{ opacity: isLoaded ? 1 : 0, filter: isLoaded ? 'blur(0px)' : 'blur(4px)' }}
transition={{ duration: 0.2, ease: 'easeOut' }}
onLoad={() => setIsLoaded(true)}
onError={() => {
setHasError(true);
setIsLoaded(true);
}}
/>
</div>
);
};