componente · @solu30/ui-kit/feedback
FilePreviewModal
Vista previa de un archivo —imagen, PDF o descarga— con acciones de bajar y de abrir aparte.
Seguir leyendo — 2 párrafos más
Generic file preview modal supporting images, PDFs, and downloadable files.
Framework-agnostic — no tRPC, no Next.js router. Consumers resolve the URL and pass it via url. Use isLoading while the URL is being fetched.
1 sitio de import en 1 de 15 verticales — un archivo que la importa cuenta una vez, monte una instancia o veinte.
- TheCharterPanel
El componente, montado
Cargando el espécimen…
export function FilePreviewSpecimen() {
const [previewDoc, setPreviewDoc] = useState<DocumentoDemo | null>(null)
const [ultimaAccion, setUltimaAccion] = useState<string | null>(null)
const focus = useFocusReturnCheck()
const cerrar = () => {
setPreviewDoc(null)
focus.notifyClosed()
}
return (
<Stage label="FilePreviewModal · los documentos de una embarcación en TheCharterPanel" tall wide>
<div className="flex w-full max-w-[420px] flex-col gap-2 px-1 py-1">
<span className="text-xs text-content/50">Documentación · Azimut 42</span>
<ul className="flex flex-col gap-1.5">
{DOCS_DEMO.map((d) => (
<li key={d.key}>
<button
type="button"
onClick={(e) => focus.openFrom(e, () => setPreviewDoc(d))}
className="flex w-full items-center justify-between gap-3 rounded-lg border border-content/10 bg-container px-3 py-2.5 text-left transition-colors hover:bg-upper"
>
<span className="min-w-0 truncate text-xs text-content">{d.name}</span>
<span className="shrink-0 font-mono text-[10px] uppercase text-content/60">
{d.url ? 'ver' : 'resolviendo…'}
</span>
</button>
</li>
))}
</ul>
<FocusReturnNote result={focus.result} />
{ultimaAccion && <p className="text-[11px] text-content/60">última acción: {ultimaAccion}</p>}
<p className="text-[11px] text-content/60">
El tercero no tiene URL: así llega en el vertical mientras la firma contra su storage no
volvió — `isLoading` prendido y `onOpenExternal` en `undefined`.
</p>
</div>
{previewDoc && (
<FilePreviewModal
name={previewDoc.name}
url={previewDoc.url}
isImage={previewDoc.isImage}
isPdf={previewDoc.key.toLowerCase().endsWith('.pdf')}
isLoading={!previewDoc.url}
onDownload={() => setUltimaAccion(`descargar ${previewDoc.name}`)}
onOpenExternal={previewDoc.url ? () => setUltimaAccion(`abrir aparte ${previewDoc.name}`) : undefined}
onClose={cerrar}
/>
)}
</Stage>
)
}De specimens/feedback.tsx · FilePreviewSpecimen: el espécimen del Muestrario, no un ejemplo oficial del paquete. Muestra cómo se usa la pieza de verdad, montada contra los tipos instalados —eso es lo que vale— pero no es documentación del kit.
Cómo se usa
import { FilePreviewModal } from '@solu30/ui-kit/feedback'Props
| Prop | Requerida | Tipo | Qué hace |
|---|---|---|---|
| name | sí | string | File name to display in header |
| url | opcional | string | URL to the file (resolved externally) |
| isImage | opcional | boolean | Whether the file is an image |
| isPdf | opcional | boolean | Whether the file is a PDF |
| isLoading | opcional | boolean | Show loading state when url is not yet resolved |
| onDownload | opcional | () => void | Called when user clicks download |
| onOpenExternal | opcional | () => void | Called when user clicks "open in new tab" |
| onClose | sí | () => void | Close handler |
Ejemplo del paquete
<FilePreviewModal
name="contract.pdf"
url={resolvedUrl}
isPdf
onDownload={() => window.open(resolvedUrl)}
onOpenExternal={() => window.open(resolvedUrl, '_blank')}
onClose={() => setOpen(false)}
/>Del @example del JSDoc de @solu30/ui-kit, sin editar: son 65 de los 541 símbolos. La sangría y los renglones en blanco son los del paquete.