mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
improvement(files): show loading spinner in file preview content area (#5610)
This commit is contained in:
+17
-10
@@ -1,12 +1,14 @@
|
||||
'use client'
|
||||
|
||||
import { memo } from 'react'
|
||||
import { memo, useState } from 'react'
|
||||
import type { WorkspaceFileRecord } from '@/lib/uploads/contexts/workspace'
|
||||
import { useFileContentSource } from '@/hooks/use-file-content-source'
|
||||
import { PREVIEW_LOADING_OVERLAY } from './preview-shared'
|
||||
import { ZoomablePreview } from './zoomable-preview'
|
||||
|
||||
export const ImagePreview = memo(function ImagePreview({ file }: { file: WorkspaceFileRecord }) {
|
||||
const source = useFileContentSource()
|
||||
const [hasSettled, setHasSettled] = useState(false)
|
||||
// Version the URL on updatedAt: overwrites keep the same storage key, so an unversioned
|
||||
// URL would resolve to a previously cached copy instead of the rewritten bytes.
|
||||
const serveUrl = source.buildUrl(file.key, {
|
||||
@@ -14,14 +16,19 @@ export const ImagePreview = memo(function ImagePreview({ file }: { file: Workspa
|
||||
})
|
||||
|
||||
return (
|
||||
<ZoomablePreview className='flex flex-1' contentClassName='h-full w-full'>
|
||||
<img
|
||||
src={serveUrl}
|
||||
alt={file.name}
|
||||
className='max-h-full max-w-full select-none rounded-md object-contain'
|
||||
draggable={false}
|
||||
loading='eager'
|
||||
/>
|
||||
</ZoomablePreview>
|
||||
<div className='relative flex min-h-0 flex-1 flex-col'>
|
||||
<ZoomablePreview className='flex flex-1' contentClassName='h-full w-full'>
|
||||
<img
|
||||
src={serveUrl}
|
||||
alt={file.name}
|
||||
className='max-h-full max-w-full select-none rounded-md object-contain'
|
||||
draggable={false}
|
||||
loading='eager'
|
||||
onLoad={() => setHasSettled(true)}
|
||||
onError={() => setHasSettled(true)}
|
||||
/>
|
||||
</ZoomablePreview>
|
||||
{!hasSettled && PREVIEW_LOADING_OVERLAY}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
+24
-9
@@ -2,6 +2,7 @@
|
||||
|
||||
import { Component, type ErrorInfo, type ReactNode } from 'react'
|
||||
import { cn } from '@sim/emcn'
|
||||
import { Loader } from '@sim/emcn/icons'
|
||||
import { createLogger } from '@sim/logger'
|
||||
|
||||
const logger = createLogger('FilePreview')
|
||||
@@ -87,13 +88,20 @@ export function resolvePreviewError(
|
||||
return renderError
|
||||
}
|
||||
|
||||
/** Canonical content-area loading spinner, matching the rest of the app. */
|
||||
const PREVIEW_LOADING_SPINNER = (
|
||||
<Loader className='size-[20px] text-[var(--text-secondary)]' animate />
|
||||
)
|
||||
|
||||
/**
|
||||
* Canonical blank loading overlay for previews that render into a
|
||||
* `--surface-1` canvas. Absolutely covers the canvas (with `z-10` so it
|
||||
* paints above in-flow render targets) until the preview is ready.
|
||||
* Canonical loading overlay for previews that render into a `--surface-1`
|
||||
* canvas. Absolutely covers the canvas (with `z-10` so it paints above
|
||||
* in-flow render targets) with a centered spinner until the preview is ready.
|
||||
*/
|
||||
export const PREVIEW_LOADING_OVERLAY = (
|
||||
<div className='absolute inset-0 z-10 bg-[var(--surface-1)]' />
|
||||
<div className='absolute inset-0 z-10 flex items-center justify-center bg-[var(--surface-1)]'>
|
||||
{PREVIEW_LOADING_SPINNER}
|
||||
</div>
|
||||
)
|
||||
|
||||
interface PreviewLoadingFrameProps {
|
||||
@@ -104,14 +112,21 @@ interface PreviewLoadingFrameProps {
|
||||
}
|
||||
|
||||
/**
|
||||
* Canonical in-flow blank loading frame shown while a preview is fetching or
|
||||
* rendering. The `tone` must match the background of the loaded state it is
|
||||
* standing in for, so mount completion does not flash a different token.
|
||||
* Canonical in-flow loading frame with a centered spinner, shown while a
|
||||
* preview is fetching or rendering. The `tone` must match the background of
|
||||
* the loaded state it is standing in for, so mount completion does not flash
|
||||
* a different token.
|
||||
*/
|
||||
export function PreviewLoadingFrame({ className, tone = 'bg' }: PreviewLoadingFrameProps) {
|
||||
return (
|
||||
<div
|
||||
className={cn(tone === 'surface' ? 'bg-[var(--surface-1)]' : 'bg-[var(--bg)]', className)}
|
||||
/>
|
||||
className={cn(
|
||||
'flex items-center justify-center',
|
||||
tone === 'surface' ? 'bg-[var(--surface-1)]' : 'bg-[var(--bg)]',
|
||||
className
|
||||
)}
|
||||
>
|
||||
{PREVIEW_LOADING_SPINNER}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user